Example #1
0
    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]
Example #2
0
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)
Example #3
0
    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)
Example #4
0
    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()
Example #5
0
    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)
Example #6
0
    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
Example #7
0
    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
        )
Example #8
0
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
        )
Example #10
0
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)
Example #11
0
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
Example #12
0
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))