def __init__(self, verbose=0): descriptors = { USB.desc_type_hid: self.hid_descriptor, USB.desc_type_report: self.report_descriptor } self.endpoint = USBEndpoint( 3, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 512, # max packet size 10, # polling interval, see USB 2.0 spec Table 9-13 self.handle_buffer_available # handler function ) # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting 3, # interface class 0, # subclass 0, # protocol 0, # string index verbose, [self.endpoint], descriptors) # "l<KEY UP>s<KEY UP><ENTER><KEY UP>" empty_preamble = [0x00] * 32 text = [0x0f, 0x00, 0x16, 0x00, 0x28, 0x00] self.keys = [chr(x) for x in empty_preamble + text]
class USBKeyboardInterface(USBInterface): name = "USB keyboard interface" hid_descriptor = b'\x09\x21\x10\x01\x00\x01\x22\x2b\x00' report_descriptor = b'\x05\x01\x09\x06\xA1\x01\x05\x07\x19\xE0\x29\xE7\x15\x00\x25\x01\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x01\x19\x00\x29\x65\x15\x00\x25\x65\x75\x08\x95\x01\x81\x00\xC0' def __init__(self, verbose=0): descriptors = { USB.desc_type_hid: self.hid_descriptor, USB.desc_type_report: self.report_descriptor } self.endpoint = USBEndpoint( 3, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 512, # max packet size 10, # polling interval, see USB 2.0 spec Table 9-13 self.handle_buffer_available # handler function ) # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting 3, # interface class 0, # subclass 0, # protocol 0, # string index verbose, [self.endpoint], descriptors) # "l<KEY UP>s<KEY UP><ENTER><KEY UP>" empty_preamble = [0x00] * 32 text = [0x0f, 0x00, 0x16, 0x00, 0x28, 0x00] self.keys = [chr(x) for x in empty_preamble + text] def handle_buffer_available(self): if not self.keys: return letter = self.keys.pop(0) self.type_letter(letter) def type_letter(self, letter, modifiers=0): data = bytes([0, 0, ord(letter)]) if self.verbose > 2: print(self.name, "sending keypress 0x%02x" % ord(letter)) self.endpoint.send(data)
def __init__(self, hash, serial, hwid, sblversion, verbose=0): descriptors = {} self.hwid = hwid self.hash = hash self.serial = serial self.sblversion = sblversion self.count = 0 self.timer = None self.switch = 0 self.bytestoread = 0 self.bytestotal = 0 self.curoffset = 0 self.buffer = bytes(b'') self.loader = bytes(b'') self.receive_buffer = bytes(b'') self.endpoints = [ USBEndpoint( 1, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 512, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 self.handle_data_available # handler function ), USBEndpoint( 3, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 512, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 self.handle_buffer_available # handler function ) ] # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting USBClass(), # interface class: vendor-specific 0xff, # subclass: vendor-specific 0xff, # protocol: vendor-specific 0, # string index verbose, self.endpoints, descriptors)
def __init__(self, disk_image, verbose=0): self.disk_image = disk_image descriptors = { } self.ep_from_host = USBEndpoint( 1, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 self.handle_data_available # handler function ) self.ep_to_host = USBEndpoint( 3, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 None # handler function ) dclass = USBMassStorageClass() # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting dclass, # interface class: Mass Storage 6, # subclass: SCSI transparent command set 0x50, # protocol: bulk-only (BBB) transport 0, # string index verbose, [ self.ep_from_host, self.ep_to_host ], descriptors ) self.device_class = dclass self.device_class.set_interface(self) self.is_write_in_progress = False self.write_cbw = None self.write_base_lba = 0 self.write_length = 0 self.write_data = b'' self._initialize_scsi_commands()
def __init__(self, spi_flash, verbose=0): descriptors = { USB.desc_type_hid: self.hid_descriptor, USB.desc_type_report: self.report_descriptor } self.in_endpoint = USBEndpoint( 1, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 8, # polling interval self.handle_dev_to_host) self.out_endpoint = USBEndpoint( 2, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 8, # polling interval self.handle_host_to_dev) self.report_mode = None self.timer = 0 self.buttons = b'\x00\x00\x00' self.held = [0] * 12 self.player_lights = 0 # BT MAC address, big endian self.mac_addr = [0x12, 0x34, 0x56, 0x78, 0x90, 0xab] self.spi_flash = spi_flash USBInterface.__init__( self, 0, # interface number 0, # alternate setting HIDClass.HID_CLASS_NUMBER, # interface class 0, # subclass 0, # protocol 0, # string index verbose, [self.in_endpoint, self.out_endpoint], descriptors)
def __init__(self, verbose=0): descriptors = { USB.desc_type_hid: self.hid_descriptor, USB.desc_type_report: self.report_descriptor } self.out_endpoint = USBEndpoint( 2, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 5, # polling interval, see USB 2.0 spec Table 9-13 self.handle_out_data # handler function ) self.endpoint = USBEndpoint( 1, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 5, # polling interval, see USB 2.0 spec Table 9-13 self.handle_buffer_available # handler function ) # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting 3, # interface class 0, # subclass 0, # protocol 0, # string index verbose, [self.out_endpoint, self.endpoint], descriptors) self.packets_to_send = self.input
def __init__(self, verbose=0): descriptors = { } endpoints = [ USBEndpoint( 1, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 512, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 self.handle_data_available # handler function ), USBEndpoint( 3, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 512, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 None # handler function ) ] # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting 0xff, # interface class: vendor-specific 0xff, # subclass: vendor-specific 0xff, # protocol: vendor-specific 0, # string index verbose, endpoints, descriptors )
class USBMassStorageInterface(USBInterface): name = "USB mass storage interface" STATUS_OKAY = 0x00 STATUS_FAILURE = 0x02 # TODO: Should this be 0x01? STATUS_INCOMPLETE = -1 # Special case status that aborts before response. def __init__(self, disk_image, verbose=0): self.disk_image = disk_image descriptors = { } self.ep_from_host = USBEndpoint( 1, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 self.handle_data_available # handler function ) self.ep_to_host = USBEndpoint( 3, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 None # handler function ) # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting 8, # interface class: Mass Storage 6, # subclass: SCSI transparent command set 0x50, # protocol: bulk-only (BBB) transport 0, # string index verbose, [ self.ep_from_host, self.ep_to_host ], descriptors ) self.device_class = USBMassStorageClass() self.device_class.set_interface(self) self.is_write_in_progress = False self.write_cbw = None self.write_base_lba = 0 self.write_length = 0 self.write_data = b'' self._initialize_scsi_commands() def _register_scsi_command(self, number, name, handler=None): if handler is None: handler = self.handle_unknown_command descriptor = { "number": number, "name": name, "handler": handler } self.commands[number] = descriptor def _initialize_scsi_commands(self): self.commands = {} self._register_scsi_command(0x00, "Test Unit Ready", self.handle_ignored_event) self._register_scsi_command(0x03, "Request Sense", self.handle_sense) self._register_scsi_command(0x12, "Inquiry", self.handle_inquiry) self._register_scsi_command(0x1a, "Mode Sense (6)", self.handle_mode_sense) self._register_scsi_command(0x5a, "Mode Sense (10)", self.handle_mode_sense) self._register_scsi_command(0x1e, "Prevent/Allow Removal", self.handle_ignored_event) self._register_scsi_command(0x23, "Get Format Capacity", self.handle_get_format_capacity) self._register_scsi_command(0x25, "Get Read Capacity", self.handle_get_read_capacity) self._register_scsi_command(0x28, "Read", self.handle_read) self._register_scsi_command(0x2a, "Write (10)", self.handle_write) self._register_scsi_command(0x36, "Synchronize Cache", self.handle_ignored_event) def handle_scsi_command(self, cbw): """ Handles an SCSI command. """ opcode = cbw.cb[0] direction = cbw.flags >> 7 # If we have a handler for this routine, handle it. if opcode in self.commands: # Extract the command's data. command = self.commands[opcode] name = command['name'] handler = command['handler'] direction_name = 'IN' if direction else 'OUT' direction_arrow = "<--" if direction else "-->" expected_length = cbw.data_transfer_length if self.verbose > 0: print("{} handling {} ({}) {}:[{}]".format(direction_arrow, name.upper(), direction_name, expected_length, bytes_as_hex(cbw.cb[1:]))) # Delegate to its handler funciton. return handler(cbw) # Otherwise, run the unknown command handler. else: return self.handle_unknown_command(cbw) def handle_unknown_command(self, cbw): """ Handles unsupported SCSI commands. """ print(self.name, "received unsupported SCSI opcode 0x%x" % cbw.cb[0]) # Generate an empty response to the relevant command. if cbw.data_transfer_length > 0: response = bytes([0] * cbw.data_transfer_length) else: response = None # Return failure. return self.STATUS_FAILURE, response def handle_ignored_event(self, cbw): """ Handles SCSI events that we can safely ignore. """ # Always return success, and no response. return self.STATUS_OKAY, None def handle_sense(self, cbw): """ Handles SCSI sense requests. """ response = b'\x70\x00\xFF\x00\x00\x00\x00\x0A\x00\x00\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00' return self.STATUS_OKAY, response def handle_inquiry(self, cbw): opcode, flags, page_code, allocation_length, control = struct.unpack(">BBBHB", cbw.cb[0:6]) # Print out the details of our inquiry. if self.verbose > 1: print("-- INQUIRY ({}) flags: {} page_code: {} allocation_length: {} control: {}". \ format(opcode, flags, page_code, allocation_length, control)) response = bytes([ 0x00, # 0x00 = device present, and provides direct access to blocks 0x00, # 0x00 = media not removable, 0x80 = media removable 0x05, # 0 = no standards compliance, 3 = SPC compliant, 4 = SPC-2 compliant, 5 = SCSI compliant :) 0x02, # 0x02 = data responses follow the spec 0x14, # Additional length. 0x00, 0x00, 0x00 ]) response += b'GoodFET ' # vendor response += b'GoodFET ' # product id response += b' ' # product revision response += b'0.01' # pad up to data_transfer_length bytes diff = cbw.data_transfer_length - len(response) response += bytes([0] * diff) return self.STATUS_OKAY, response def handle_mode_sense(self, cbw): page = cbw.cb[2] & 0x3f response = b'\x07\x00\x00\x00\x00\x00\x00\x1c' if page != 0x3f: print(self.name, "unkonwn page, returning empty page") response = b'\x07\x00\x00\x00\x00\x00\x00\x00' return self.STATUS_OKAY, response def handle_get_format_capacity(self, cbw): response = bytes([ 0x00, 0x00, 0x00, 0x08, # capacity list length 0x00, 0x00, 0x10, 0x00, # number of sectors (0x1000 = 10MB) 0x10, 0x00, # reserved/descriptor code 0x02, 0x00, # 512-byte sectors ]) return self.STATUS_OKAY, response def handle_get_read_capacity(self, cbw): lastlba = self.disk_image.get_sector_count() response = bytes([ (lastlba >> 24) & 0xff, (lastlba >> 16) & 0xff, (lastlba >> 8) & 0xff, (lastlba ) & 0xff, 0x00, 0x00, 0x02, 0x00, # 512-byte blocks ]) return self.STATUS_OKAY, response def handle_read(self, cbw): base_lba = cbw.cb[2] << 24 \ | cbw.cb[3] << 16 \ | cbw.cb[4] << 8 \ | cbw.cb[5] num_blocks = cbw.cb[7] << 8 \ | cbw.cb[8] if self.verbose > 0: print("<-- performing READ (10), lba", base_lba, "+", num_blocks, "block(s)") # Note that here we send the data directly rather than putting # something in 'response' and letting the end of the switch send for block_num in range(num_blocks): data = self.disk_image.get_sector_data(base_lba + block_num) self.ep_to_host.send(data) if self.verbose > 3: print("--> responded with {} bytes".format(cbw.data_transfer_length)) return self.STATUS_OKAY, None def handle_write(self, cbw): base_lba = cbw.cb[2] << 24 \ | cbw.cb[3] << 16 \ | cbw.cb[4] << 8 \ | cbw.cb[5] num_blocks = cbw.cb[7] << 8 \ | cbw.cb[8] if self.verbose > 0: print("--> performing WRITE (10), lba", base_lba, "+", num_blocks, "block(s)") # save for later self.write_cbw = cbw self.write_base_lba = base_lba self.write_length = num_blocks * self.disk_image.get_sector_size() self.is_write_in_progress = True # because we need to snarf up the data from wire before we reply # with the CSW return self.STATUS_INCOMPLETE, None def continue_write(self, cbw, data): if self.verbose > 3: print("--> continue write with {} more bytes of data".format(len(data))) self.write_data += data if len(self.write_data) < self.write_length: # more yet to read, don't send the CSW return self.STATUS_INCOMPLETE, None self.disk_image.put_data(self.write_base_lba, self.write_data) self.is_write_in_progress = False self.write_data = b'' return self.STATUS_OKAY, None def handle_data_available(self, data): if self.is_write_in_progress: cbw = self.write_cbw status, response = self.continue_write(cbw, data) else: cbw = CommandBlockWrapper(data) status, response = self.handle_scsi_command(cbw) # If we weren't able to complete the operation, return without # transmitting a response. if status == self.STATUS_INCOMPLETE: return # If we have a response payload to transmit, transmit it. if response: if self.verbose > 2: print("--> responding with", len(response), "bytes [{}], status={}".format(bytes_as_hex(response), status)) self.ep_to_host.send(response) # Otherwise, respond with our status. csw = bytes([ ord('U'), ord('S'), ord('B'), ord('S'), cbw.tag[0], cbw.tag[1], cbw.tag[2], cbw.tag[3], 0x00, 0x00, 0x00, 0x00, status ]) self.ep_to_host.send(csw)
def __init__(self, verbose=0): """ Sets up the device's interface. """ # Contains the descriptors that will be used to respond to GET_DESCRIPTOR requests # issued with an INTERFACE context. descriptors = { } # Define each of the endpoints our emulated device will use. # We store them on the current object for convenience. # These will be used both to generate the interface descriptor and # to set up the emulated device's endpoints. self.ep_out = \ USBEndpoint( 1, # endpoint number (_not_ the address) USBEndpoint.direction_out, # endpoint direction USBEndpoint.transfer_type_bulk, # the transfer type used on this endpoint USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 # Callback function that's called when we get data _from the host_. # This is the core way we receive USB data. self.handle_EP1_data_available ) self.ep_in = \ USBEndpoint( 1, # endpoint number USBEndpoint.direction_in, # endpoint direction USBEndpoint.transfer_type_bulk, # the transfer type USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 # Callback function that's approximately called when the host makes an IN request. # This is a good way to synchronize sending data back to the device. self.handle_buffer_available ) # This array should contain a single USBEndpoint object for each supported endpoint. # This mostly mirrors an endpoint descriptor. endpoints = [ self.ep_out, self.ep_in ] # Call the parent constructor, setting up the interface desctiptor. # [You should probably use super() in your own code; this has been left over for a while!] USBInterface.__init__( self, 0, # interface number 0, # alternate setting 0xff, # interface class: vendor-specific 0xff, # subclass: vendor-specific 0xff, # protocol: vendor-specific 0, # string index verbose, endpoints, descriptors )
class USBMassStorageInterface(USBInterface): name = "USB mass storage interface" def __init__(self, disk_image, verbose=0): self.disk_image = disk_image descriptors = {} self.ep_from_host = USBEndpoint( 1, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 1024, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 self.handle_data_available # handler function ) self.ep_to_host = USBEndpoint( 3, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_bulk, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 1024, # max packet size 0, # polling interval, see USB 2.0 spec Table 9-13 None # handler function ) # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting 8, # interface class: Mass Storage 6, # subclass: SCSI transparent command set 0x50, # protocol: bulk-only (BBB) transport 0, # string index verbose, [self.ep_from_host, self.ep_to_host], descriptors) self.device_class = USBMassStorageClass() self.device_class.set_interface(self) self.is_write_in_progress = False self.write_cbw = None self.write_base_lba = 0 self.write_length = 0 self.write_data = b'' def handle_data_available(self, data): print(self.name, "handling", len(data), "bytes of SCSI data") cbw = CommandBlockWrapper(data) opcode = cbw.cb[0] status = 0 # default to success response = None # with no response data if self.is_write_in_progress: if self.verbose > 0: print(self.name, "got", len(data), "bytes of SCSI write data") self.write_data += data if len(self.write_data) < self.write_length: # more yet to read, don't send the CSW return self.disk_image.put_sector_data(self.write_base_lba, self.write_data) cbw = self.write_cbw self.is_write_in_progress = False self.write_data = b'' elif opcode == 0x00: # Test Unit Ready: just return OK status if self.verbose > 0: print(self.name, "got SCSI Test Unit Ready") elif opcode == 0x03: # Request Sense if self.verbose > 0: print(self.name, "got SCSI Request Sense, data", bytes_as_hex(cbw.cb[1:])) response = b'\x70\x00\xFF\x00\x00\x00\x00\x0A\x00\x00\x00\x00\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00' elif opcode == 0x12: # Inquiry if self.verbose > 0: print(self.name, "got SCSI Inquiry, data", bytes_as_hex(cbw.cb[1:])) response = bytes([ 0x00, # 00 for Direct, 1F for "no floppy" 0x00, # make 0x80 for removable media, 0x00 for fixed 0x00, # Version 0x01, # Response Data Format 0x14, # Additional length. 0x00, 0x00, 0x00 ]) response += b'GoodFET ' # vendor response += b'GoodFET ' # product id response += b' ' # product revision response += b'0.01' # pad up to data_transfer_length bytes #diff = cbw.data_transfer_length - len(response) #response += bytes([0] * diff) elif opcode == 0x1a or opcode == 0x5a: # Mode Sense (6 or 10) page = cbw.cb[2] & 0x3f if self.verbose > 0: print(self.name, "got SCSI Mode Sense, page code 0x%02x" % page) response = b'\x07\x00\x00\x00\x00\x00\x00\x1c' if page != 0x3f: print(self.name, "unkonwn page, returning empty page") response = b'\x07\x00\x00\x00\x00\x00\x00\x00' elif opcode == 0x1e: # Prevent/Allow Removal: feign success if self.verbose > 0: print(self.name, "got SCSI Prevent/Allow Removal") #elif opcode == 0x1a or opcode == 0x5a: # Mode Sense (6 or 10) # TODO elif opcode == 0x23: # Read Format Capacity if self.verbose > 0: print(self.name, "got SCSI Read Format Capacity") response = bytes([ 0x00, 0x00, 0x00, 0x08, # capacity list length 0x00, 0x00, 0x10, 0x00, # number of sectors (0x1000 = 10MB) 0x10, 0x00, # reserved/descriptor code 0x02, 0x00, # 512-byte sectors ]) elif opcode == 0x25: # Read Capacity if self.verbose > 0: print(self.name, "got SCSI Read Capacity, data", bytes_as_hex(cbw.cb[1:])) lastlba = self.disk_image.get_sector_count() response = bytes([ (lastlba >> 24) & 0xff, (lastlba >> 16) & 0xff, (lastlba >> 8) & 0xff, (lastlba) & 0xff, 0x00, 0x00, 0x02, 0x00, # 512-byte blocks ]) elif opcode == 0x28: # Read (10) base_lba = cbw.cb[2] << 24 \ | cbw.cb[3] << 16 \ | cbw.cb[4] << 8 \ | cbw.cb[5] num_blocks = cbw.cb[7] << 8 \ | cbw.cb[8] if self.verbose > 0: print(self.name, "got SCSI Read (10), lba", base_lba, "+", num_blocks, "block(s)") # Note that here we send the data directly rather than putting # something in 'response' and letting the end of the switch send for block_num in range(num_blocks): data = self.disk_image.get_sector_data(base_lba + block_num) self.ep_to_host.send(data) elif opcode == 0x2a: # Write (10) if self.verbose > 0: print(self.name, "got SCSI Write (10), data", bytes_as_hex(cbw.cb[1:])) base_lba = cbw.cb[1] << 24 \ | cbw.cb[2] << 16 \ | cbw.cb[3] << 8 \ | cbw.cb[4] num_blocks = cbw.cb[7] << 8 \ | cbw.cb[8] if self.verbose > 0: print(self.name, "got SCSI Write (10), lba", base_lba, "+", num_blocks, "block(s)") # save for later self.write_cbw = cbw self.write_base_lba = base_lba self.write_length = num_blocks * self.disk_image.block_size self.is_write_in_progress = True # because we need to snarf up the data from wire before we reply # with the CSW return elif opcode == 0x35: # Synchronize Cache (10): blindly OK if self.verbose > 0: print(self.name, "got Synchronize Cache (10)") else: print(self.name, "received unsupported SCSI opcode 0x%x" % opcode) status = 0x02 # command failed if cbw.data_transfer_length > 0: response = bytes([0] * cbw.data_transfer_length) if response: if self.verbose > 2: print(self.name, "responding with", len(response), "bytes:", bytes_as_hex(response)) self.ep_to_host.send(response) csw = bytes([ ord('U'), ord('S'), ord('B'), ord('S'), cbw.tag[0], cbw.tag[1], cbw.tag[2], cbw.tag[3], 0x00, 0x00, 0x00, 0x00, status ]) if self.verbose > 3: print(self.name, "responding with status =", status) self.ep_to_host.send(csw)
class USBProControllerInterface(USBInterface): name = "Fake Switch Pro Controller" # copied from a pro controller hid_descriptor = b'\x09\x21\x11\x01\x00\x01\x22\xcb\x00' report_descriptor = b'\x05\x01\x15\x00\x09\x04\xa1\x01\x85\x30\x05' \ b'\x01\x05\x09\x19\x01\x29\x0a\x15\x00\x25\x01' \ b'\x75\x01\x95\x0a\x55\x00\x65\x00\x81\x02\x05' \ b'\x09\x19\x0b\x29\x0e\x15\x00\x25\x01\x75\x01' \ b'\x95\x04\x81\x02\x75\x01\x95\x02\x81\x03\x0b' \ b'\x01\x00\x01\x00\xa1\x00\x0b\x30\x00\x01\x00' \ b'\x0b\x31\x00\x01\x00\x0b\x32\x00\x01\x00\x0b' \ b'\x35\x00\x01\x00\x15\x00\x27\xff\xff\x00\x00' \ b'\x75\x10\x95\x04\x81\x02\xc0\x0b\x39\x00\x01' \ b'\x00\x15\x00\x25\x07\x35\x00\x46\x3b\x01\x65' \ b'\x14\x75\x04\x95\x01\x81\x02\x05\x09\x19\x0f' \ b'\x29\x12\x15\x00\x25\x01\x75\x01\x95\x04\x81' \ b'\x02\x75\x08\x95\x34\x81\x03\x06\x00\xff\x85' \ b'\x21\x09\x01\x75\x08\x95\x3f\x81\x03\x85\x81' \ b'\x09\x02\x75\x08\x95\x3f\x81\x03\x85\x01\x09' \ b'\x03\x75\x08\x95\x3f\x91\x83\x85\x10\x09\x04' \ b'\x75\x08\x95\x3f\x91\x83\x85\x80\x09\x05\x75' \ b'\x08\x95\x3f\x91\x83\x85\x82\x09\x06\x75\x08' \ b'\x95\x3f\x91\x83\xc0' def __init__(self, spi_flash, verbose=0): descriptors = { USB.desc_type_hid: self.hid_descriptor, USB.desc_type_report: self.report_descriptor } self.in_endpoint = USBEndpoint( 1, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 8, # polling interval self.handle_dev_to_host) self.out_endpoint = USBEndpoint( 2, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 8, # polling interval self.handle_host_to_dev) self.report_mode = None self.timer = 0 self.buttons = b'\x00\x00\x00' self.held = [0] * 12 self.player_lights = 0 # BT MAC address, big endian self.mac_addr = [0x12, 0x34, 0x56, 0x78, 0x90, 0xab] self.spi_flash = spi_flash USBInterface.__init__( self, 0, # interface number 0, # alternate setting HIDClass.HID_CLASS_NUMBER, # interface class 0, # subclass 0, # protocol 0, # string index verbose, [self.in_endpoint, self.out_endpoint], descriptors) def handle_dev_to_host(self): if self.report_mode == 0x30: self.send_report_0x30() def handle_host_to_dev(self, data): if data == b'\x00\x00': # Not implemented (not sure what this does) print('[00 00] Ignoring') elif data[0] == 0x01: self.handle_01_command(data[1:]) elif data[0] == 0x10: # Not implemented pass elif data[0] == 0x80: self.handle_80_command(data[1:]) else: print('??? Got out data! {}'.format(data)) def handle_01_command(self, data): subcommand = data[9] subcommand_data = data[10:] def log(s): print('[01 {}] {}'.format(subcommand, s)) def ack_subcommand(ack_type, ack_data=b''): self.send_report_0x21(ack_type, subcommand, ack_data) if subcommand == 0x00: ack_subcommand(0x80, b'\x03') elif subcommand == 0x01: # Not implemented log('Request BT pairing') ack_subcommand(0x81, b'\x03') elif subcommand == 0x02: resp = b'\x03\x48\x03\x02' + bytes(self.mac_addr) + b'\x01\x01' log('Request device info - {}'.format(resp)) ack_subcommand(0x82, resp) elif subcommand == 0x03: polling_modes = { 0x00: 'NFC/IR camera active polling', 0x01: 'NFC/IR MCU configuration active polling', 0x02: 'NFC/IR data and configuration active polling', 0x03: 'IR camera data active polling', 0x23: 'MCU update state report', 0x30: 'standard', 0x31: 'NFC/IR mode', 0x3f: 'simple HID mode' } self.report_mode = subcommand_data[0] current_mode = polling_modes.get(self.report_mode, 'unknown mode') log('Set input report mode - {} ({:02x})'.format( current_mode, self.report_mode)) ack_subcommand(0x80) elif subcommand == 0x04: # FIXME: Properly implement this subcommand # currently it's hardcoded to hold L and R for about 2.5 seconds if self.buttons == b'\x40\x00\x40': self.held[0] += 1 if self.held[0] >= 256: self.held[0] = 0 self.held[1] += 1 self.held[2] += 1 if self.held[2] >= 256: self.held[2] = 0 self.held[3] += 1 if self.held[1] >= 1: self.buttons = b'\x00\x00\x00' ack_subcommand(0x83, bytes(self.held)) elif subcommand == 0x08: if subcommand_data[0] == 0x01: log('Enable shipment power state') else: log('Disable shipment power state') # TODO: Emulate SPI write self.send_report_0x21(0x80, 0x08) elif subcommand == 0x10: addr = struct.unpack('<I', subcommand_data[:4])[0] length = subcommand_data[4] spi_data = self.spi_flash.read(addr, length) log('Read SPI flash - addr {:04x}, len {:02x}, data {}'.format( addr, length, spi_data)) ack_subcommand(0x90, subcommand_data[:5] + spi_data) # TODO: fix hack # A read to this address is the last thing to happen before # we need to press L and R if addr == 0x6020: self.buttons = b'\x40\x00\x40' elif subcommand == 0x11: addr = struct.unpack('<I', subcommand_data[:4])[0] length = subcommand_data[4] spi_data = subcommand_data[5:] log('Write SPI flash - addr {:04x}, len {}, data {}'.format( addr, length, spi_data)) self.spi_flash.write(addr, spi_data) ack_subcommand(0x80, b'\x00') elif subcommand == 0x12: addr = struct.unpack('<I', subcommand_data[:4])[0] log('Erase SPI flash - addr {:04x}'.format(addr)) self.spi_flash.erase(addr) ack_subcommand(0x80, b'\x00') elif subcommand == 0x30: self.player_lights = subcommand_data[0] log('Set player lights - value {:02x}'.format(self.player_lights)) ack_subcommand(0x80) elif subcommand == 0x31: log('Request player lights - value {:02x}'.format( self.player_lights)) ack_subcommand(0xb0, self.player_lights) elif subcommand == 0x38: log('Set HOME light') elif subcommand == 0x40: if subcommand_data[0] == 1: log('Enable 6-axis IMU') else: log('Disable 6-axis IMU') ack_subcommand(0x80) elif subcommand == 0x41: gyro_sens = [250, 500, 1000, 2000] accel_sens = [8, 4, 2, 16] gyro_perf = [833, 208] accel_aa_filter_bw = [200, 100] log('Set IMU sensitivity: +/-{}dps gyro @ {}Hz, +/-{}G accel @ {}Hz' .format(gyro_sens[subcommand_data[0]], gyro_perf[subcommand_data[2]], accel_sens[subcommand_data[1]], accel_aa_filter_bw[subcommand_data[3]])) ack_subcommand(0x80) elif subcommand == 0x42: # Not implemented log('Write to IMU register - address {:04x} value {:02x}'.format( subcommand_data[0], subcommand_data[2])) elif subcommand == 0x43: # Not implemented log('Read from IMU registers - start address {:04x} len {:02x}'. format(subcommand_data[0], subcommand_data[1])) elif subcommand == 0x48: if subcommand_data[0] == 1: log('Enable vibration') else: log('Disable vibration') ack_subcommand(0x80) else: log('Unknown subcommand - data: {}'.format(subcommand_data)) ack_subcommand(0x80) def handle_80_command(self, data): opcode = data[0] def log(s): print('[80 {}] {}'.format(opcode, s)) if opcode == 0x01: log('Requested current connection status') # Fake connection status self.in_endpoint.send(b'\x81\x01\x00\x03' + bytes(self.mac_addr[::-1])) elif opcode == 0x02: log('Requested handshake with Broadcom chip') # Fake a response self.in_endpoint.send(b'\x81\x02') elif opcode == 0x03: log('Requested 3Mbps baudrate') # Fake a response self.in_endpoint.send(b'\x81\x03') elif opcode == 0x04: log('Forced USB connection only') elif opcode == 0x05: log('Allowed USB connection timeout') elif opcode == 0x06: # TODO: Implement? Is this even needed? log('Requested reset') elif opcode == 0x91: # TODO: Implement? Is this even needed? log('Requested pre-handshake') elif opcode == 0x92: # TODO: Implement? Is this even needed? log('Sent arbitrary UART command - {}'.format(data[1:])) def send_report_0x21(self, ack, subcmd_id, reply_data=b''): report_data = b'\x21' report_data += self.inc_and_get_timer() # Battery level = 9 (full, charging) # Connection info = 1 (pro controller, USB powered) report_data += b'\x91' # 3 byte button state report_data += self.get_buttons() # 3 byte left analog stick report_data += self.get_left_stick() # 3 byte right analog stick report_data += self.get_right_stick() # 1 byte vibration report report_data += self.get_vibrate_report() report_data += ack.to_bytes(1, byteorder='little') report_data += subcmd_id.to_bytes(1, byteorder='little') report_data += reply_data + (b'\x00' * (35 - len(reply_data))) self.in_endpoint.send(report_data) def send_report_0x30(self): report_data = b'\x30' report_data += self.inc_and_get_timer() # Battery level = 9 (full, charging) # Connection info = 1 (pro controller, USB powered) report_data += b'\x91' # 3 byte button state report_data += self.get_buttons() # 3 byte left analog stick report_data += self.get_left_stick() # 3 byte right analog stick report_data += self.get_right_stick() # 1 byte vibration report report_data += self.get_vibrate_report() report_data += (self.get_accel() + self.get_gyro()) * 3 self.in_endpoint.send(report_data) # TODO: Add proper implementations for these functions def get_buttons(self): return self.buttons def set_buttons(self, buttons): self.buttons = buttons def get_left_stick(self): return b'\x00\x08\x80' def get_right_stick(self): return b'\x00\x08\x80' def get_accel(self): return b'\x00\x00\x00\x00\x00\x00' def get_gyro(self): return b'\x00\x00\x00\x00\x00\x00' def get_vibrate_report(self): return b'\x00' def inc_and_get_timer(self): ret = self.timer.to_bytes(1, byteorder='little') self.timer = (self.timer + 3) % 256 return ret
class USBSwitchTASInterface(USBInterface): name = "Switch TAS Interface" input = [ b'\x00\x00\x0f\x80\x80\x80\x80\x00', b'\x00\x00\x0f\x80\x80\x80\x80\x00', b'\x00\x00\x0f\x80\x80\x80\x80\x00', b'\x00\x00\x0f\x80\x80\x80\x80\x00', b'\x00\x00\x0f\x80\x80\x80\x80\x00', b'\x00\x00\x0f\x80\x80\x80\x80\x00', b'\x00\x00\x0f\x80\x80\x80\x80\x00', b'\x00\x00\x0f\x80\x80\x80\x80\x00' ] # copied from the horipad hid_descriptor = b'\x09\x21\x11\x01\x00\x01\x22\x50\x00' report_descriptor = b'\x05\x01\t\x05\xa1\x01\x15\x00%\x015\x00E\x01u\x01\x95\x0e\x05\t\x19\x01)\x0e\x81\x02\x95\x02\x81\x01\x05\x01%\x07F;\x01u\x04\x95\x01e\x14\t9\x81Be\x00\x95\x01\x81\x01&\xff\x00F\xff\x00\t0\t1\t2\t5u\x08\x95\x04\x81\x02u\x08\x95\x01\x81\x01\xc0' def __init__(self, verbose=0): descriptors = { USB.desc_type_hid: self.hid_descriptor, USB.desc_type_report: self.report_descriptor } self.out_endpoint = USBEndpoint( 2, # endpoint number USBEndpoint.direction_out, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 5, # polling interval, see USB 2.0 spec Table 9-13 self.handle_out_data # handler function ) self.endpoint = USBEndpoint( 1, # endpoint number USBEndpoint.direction_in, USBEndpoint.transfer_type_interrupt, USBEndpoint.sync_type_none, USBEndpoint.usage_type_data, 64, # max packet size 5, # polling interval, see USB 2.0 spec Table 9-13 self.handle_buffer_available # handler function ) # TODO: un-hardcode string index (last arg before "verbose") USBInterface.__init__( self, 0, # interface number 0, # alternate setting 3, # interface class 0, # subclass 0, # protocol 0, # string index verbose, [self.out_endpoint, self.endpoint], descriptors) self.packets_to_send = self.input def handle_buffer_available(self): # two bytes buttons # hat switch # four joysticks # one byte padding #random_dir_raw = random.randint(0, 255) #random_dir = random_dir_raw.to_bytes(1, byteorder='little') if self.packets_to_send: to_send = self.packets_to_send.pop(0) self.endpoint.send(to_send) #self.endpoint.send(b'\x00\x00\x0f' + random_dir + b'\x80\x80\x80\x00') def handle_out_data(self, data): print("??? Got out data! {}".format(data))