tma: impl helper services, cleanup hostside packets

This commit is contained in:
Michael Scire
2018-11-07 23:21:05 -08:00
parent ec8523af7c
commit 46001263f8
11 changed files with 603 additions and 18 deletions

View File

@@ -0,0 +1,26 @@
# Copyright (c) 2018 Atmosphere-NX
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
from UsbConnection import UsbConnection
import sys, time
def main(argc, argv):
with UsbConnection(None) as c:
print 'Waiting for connection...'
c.wait_connected()
print 'Connected!'
while True:
c.send_packet('AAAAAAAA')
return 0
if __name__ == '__main__':
sys.exit(main(len(sys.argv), sys.argv))

View File

@@ -0,0 +1,117 @@
# Copyright (c) 2018 Atmosphere-NX
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from UsbInterface import UsbInterface
from threading import Thread, Condition
from collections import deque
import time
class UsbConnection(UsbInterface):
# Auto connect thread func.
def auto_connect(connection):
while not connection.is_connected():
try:
connection.connect(UsbInterface())
except ValueError as e:
continue
def recv_thread(connection):
if connection.is_connected():
try:
# If we've previously been connected, PyUSB will read garbage...
connection.recv_packet()
except ValueError:
pass
while connection.is_connected():
try:
connection.recv_packet()
except Exception as e:
print 'An exception occurred:'
print 'Type: '+e.__class__.__name__
print 'Msg: '+str(e)
connection.disconnect()
connection.send_packet(None)
def send_thread(connection):
while connection.is_connected():
try:
next_packet = connection.get_next_send_packet()
if next_packet is not None:
connection.intf.send_packet(next_packet)
else:
connection.disconnect()
except Exception as e:
print 'An exception occurred:'
print 'Type: '+e.__class__.__name__
print 'Msg: '+str(e)
connection.disconnect()
def __init__(self, manager):
self.manager = manager
self.connected = False
self.intf = None
self.conn_lock, self.send_lock = Condition(), Condition()
self.send_queue = deque()
def __enter__(self):
self.conn_thrd = Thread(target=UsbConnection.auto_connect, args=(self,))
self.conn_thrd.daemon = True
self.conn_thrd.start()
return self
def __exit__(self, type, value, traceback):
time.sleep(1)
print 'Closing!'
time.sleep(1)
def wait_connected(self):
self.conn_lock.acquire()
if not self.is_connected():
self.conn_lock.wait()
self.conn_lock.release()
def is_connected(self):
return self.connected
def connect(self, intf):
# This indicates we have a connection.
self.conn_lock.acquire()
assert not self.connected
self.intf = intf
self.connected = True
self.conn_lock.notify()
self.conn_lock.release()
self.recv_thrd = Thread(target=UsbConnection.recv_thread, args=(self,))
self.send_thrd = Thread(target=UsbConnection.send_thread, args=(self,))
self.recv_thrd.daemon = True
self.send_thrd.daemon = True
self.recv_thrd.start()
self.send_thrd.start()
def disconnect(self):
self.conn_lock.acquire()
if self.connected:
self.connected = False
self.conn_lock.release()
def recv_packet(self):
hdr, body = self.intf.read_packet()
print('Got Packet: %s' % body.encode('hex'))
def send_packet(self, packet):
self.send_lock.acquire()
if len(self.send_queue) == 0x40:
self.send_lock.wait()
self.send_queue.append(packet)
if len(self.send_queue) == 1:
self.send_lock.notify()
self.send_lock.release()
def get_next_send_packet(self):
self.send_lock.acquire()
if len(self.send_queue) == 0:
self.send_lock.wait()
packet = self.send_queue.popleft()
if len(self.send_queue) == 0x3F:
self.send_lock.notify()
self.send_lock.release()
return packet

View File

@@ -0,0 +1,69 @@
# Copyright (c) 2018 Atmosphere-NX
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import usb, zlib
from struct import unpack as up, pack as pk
def crc32(s):
return zlib.crc32(s) & 0xFFFFFFFF
class UsbInterface():
def __init__(self):
self.dev = usb.core.find(idVendor=0x057e, idProduct=0x3000)
if self.dev is None:
raise ValueError('Device not found')
self.dev.set_configuration()
self.cfg = self.dev.get_active_configuration()
self.intf = usb.util.find_descriptor(self.cfg, bInterfaceClass=0xff, bInterfaceSubClass=0xff, bInterfaceProtocol=0xfc)
assert self.intf is not None
self.ep_in = usb.util.find_descriptor(
self.intf,
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
assert self.ep_in is not None
self.ep_out = usb.util.find_descriptor(
self.intf,
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
assert self.ep_out is not None
def close(self):
usb.util.dispose_resources(self.dev)
def blocking_read(self, size):
return ''.join(chr(x) for x in self.ep_in.read(size, 0xFFFFFFFFFFFFFFFF))
def blocking_write(self, data):
self.ep_out.write(data, 0xFFFFFFFFFFFFFFFF)
def read_packet(self):
hdr = self.blocking_read(0x28)
_, _, _, body_size, _, _, _, _, body_chk, hdr_chk = up('<IIIIIIIIII', hdr)
if crc32(hdr[:-4]) != hdr_chk:
raise ValueError('Invalid header checksum in received packet!')
body = self.blocking_read(body_size)
if len(body) != body_size:
raise ValueError('Failed to receive packet body!')
elif crc32(body) != body_chk:
raise ValueError('Invalid body checksum in received packet!')
return (hdr, body)
def send_packet(self, body):
hdr = pk('<IIIIIIIII', 0, 0, 0, len(body), 0, 0, 0, 0, crc32(body))
hdr += pk('<I', crc32(hdr))
self.blocking_write(hdr)
self.blocking_write(body)