Source code for facedancer.backends.MAXUSBApp

# MAXUSBApp.py
#
# Contains class definition for MAXUSBApp.

import time

from ..core import FacedancerApp

from .base            import FacedancerBackend


[docs] class MAXUSBApp(FacedancerApp, FacedancerBackend): app_name = "MAXUSB" reg_ep0_fifo = 0x00 reg_ep1_out_fifo = 0x01 reg_ep2_in_fifo = 0x02 reg_ep3_in_fifo = 0x03 reg_setup_data_fifo = 0x04 reg_ep0_byte_count = 0x05 reg_ep1_out_byte_count = 0x06 reg_ep2_in_byte_count = 0x07 reg_ep3_in_byte_count = 0x08 reg_ep_stalls = 0x09 reg_clr_togs = 0x0a reg_endpoint_irq = 0x0b reg_endpoint_interrupt_enable = 0x0c reg_usb_irq = 0x0d reg_usb_interrupt_enable = 0x0e reg_usb_control = 0x0f reg_cpu_control = 0x10 reg_pin_control = 0x11 reg_revision = 0x12 reg_function_address = 0x13 reg_io_pins = 0x14 # bitmask values for reg_endpoint_irq = 0x0b is_setup_data_avail = 0x20 # SUDAVIRQ is_in3_buffer_avail = 0x10 # IN3BAVIRQ is_in2_buffer_avail = 0x08 # IN2BAVIRQ is_out1_data_avail = 0x04 # OUT1DAVIRQ is_out0_data_avail = 0x02 # OUT0DAVIRQ is_in0_buffer_avail = 0x01 # IN0BAVIRQ # bitmask values for reg_usb_control = 0x0f usb_control_vbgate = 0x40 usb_control_connect = 0x08 # bitmask values for reg_pin_control = 0x11 interrupt_level = 0x08 full_duplex = 0x10 ep0_in_nak = (1 << 5) ep2_in_nak = (1 << 6) ep3_in_nak = (1 << 7) # TODO: Support a generic MaxUSB interface that doesn't # depend on any GoodFET details.
[docs] @staticmethod def bytes_as_hex(b, delim=" "): return delim.join(["%02x" % x for x in b])
# HACK: but given the limitations of the MAX chips, it seems necessary
[docs] def send_on_endpoint(self, ep_num, data, blocking=False): if ep_num == 0: fifo_reg = self.reg_ep0_fifo bc_reg = self.reg_ep0_byte_count elif ep_num == 2: fifo_reg = self.reg_ep2_in_fifo bc_reg = self.reg_ep2_in_byte_count elif ep_num == 3: fifo_reg = self.reg_ep3_in_fifo bc_reg = self.reg_ep3_in_byte_count else: raise ValueError('endpoint ' + str(ep_num) + ' not supported') # FIFO buffer is only 64 bytes, must loop while len(data) > 64: self.write_bytes(fifo_reg, data[:64]) self.write_register(bc_reg, 64, ack=True) data = data[64:] self.write_bytes(fifo_reg, data) self.write_register(bc_reg, len(data), ack=True) if self.verbose > 1: print(self.app_name, "wrote", self.bytes_as_hex(data), "to endpoint", ep_num)
# HACK: but given the limitations of the MAX chips, it seems necessary
[docs] def read_from_endpoint(self, ep_num): if ep_num != 1: return b'' byte_count = self.read_register(self.reg_ep1_out_byte_count) if byte_count == 0: return b'' data = self.read_bytes(self.reg_ep1_out_fifo, byte_count) if self.verbose > 1: print(self.app_name, "read", self.bytes_as_hex(data), "from endpoint", ep_num) return data
[docs] def stall_endpoint(self, ep_number, direction=0): """ Stalls an arbitrary endpoint. Args: ep_number : The endpoint number to be stalled direction : 0 for out, 1 for in """ if self.verbose > 0: print(self.app_name, "stalling endpoint {}".format(ep_number)) # TODO: Verify our behavior, here. The original facedancer code stalls # EP0 both _in_ and out, as well as uses the special STALL SETUP bit. # Is this really what we want? if ep_number == 0: self.write_register(self.reg_ep_stalls, 0x23) elif ep_number < 4: self.write_writer(self.reg_ep_stalls, 1 << (ep_num + 1)) else: raise ValueError("Invalid endpoint for MAXUSB device!")
[docs] def stall_ep0(self, direction=0): return self.stall_endpoint(0, direction)
[docs] def get_version(self): return self.read_register(self.reg_revision)
[docs] def connect(self, usb_device, max_packet_size_ep0=64, device_speed=None): if self.read_register(self.reg_usb_control) & self.usb_control_connect: self.write_register(self.reg_usb_control, self.usb_control_vbgate) time.sleep(.1) self.write_register(self.reg_usb_control, self.usb_control_vbgate | self.usb_control_connect) self.connected_device = usb_device if self.verbose > 0: print(self.app_name, "connected device", self.connected_device.name)
[docs] def disconnect(self): self.write_register(self.reg_usb_control, self.usb_control_vbgate) if self.verbose > 0: print(self.app_name, "disconnected device", self.connected_device.name) self.connected_device = None
[docs] def clear_irq_bit(self, reg, bit): self.write_register(reg, bit)
[docs] def service_irqs(self): irq = self.read_register(self.reg_endpoint_irq) in_nak = self.read_register(self.reg_pin_control) if self.verbose > 3: print(self.app_name, "read endpoint irq: 0x%02x" % irq) print(self.app_name, "read pin control: 0x%02x" % in_nak) if self.verbose > 2: if irq & ~ (self.is_in0_buffer_avail \ | self.is_in2_buffer_avail | self.is_in3_buffer_avail): print(self.app_name, "notable irq: 0x%02x" % irq) if irq & self.is_setup_data_avail: self.clear_irq_bit(self.reg_endpoint_irq, self.is_setup_data_avail) b = self.read_bytes(self.reg_setup_data_fifo, 8) if (irq & self.is_out0_data_avail) and (b[0] & 0x80 == 0x00): data_bytes_len = b[6] + (b[7] << 8) b += self.read_bytes(self.reg_ep0_fifo, data_bytes_len) req = self.connected_device.create_request(b) self.connected_device.handle_request(req) if irq & self.is_out1_data_avail: data = self.read_from_endpoint(1) if data: self.connected_device.handle_data_available(1, data) self.clear_irq_bit(self.reg_endpoint_irq, self.is_out1_data_avail) if irq & self.is_in2_buffer_avail: self.connected_device.handle_buffer_available(2) if irq & self.is_in3_buffer_avail: self.connected_device.handle_buffer_available(3) # Check to see if we've NAK'd on either of our IN endpoints, # and generate the relevant events. if in_nak & self.ep2_in_nak: self.connected_device.handle_nak(2) self.clear_irq_bit(self.reg_pin_control, in_nak | self.ep2_in_nak) if in_nak & self.ep3_in_nak: self.connected_device.handle_nak(3) self.clear_irq_bit(self.reg_pin_control, in_nak | self.ep3_in_nak)
[docs] def set_address(self, address, defer=False): """ Sets the device address of the Facedancer. Usually only used during initial configuration. Args: address : The address that the Facedancer should assume. """ # The MAXUSB chip handles this for us, so we don't need to do anything. pass
[docs] def configured(self, configuration): """ Callback that's issued when a USBDevice is configured, e.g. by the SET_CONFIGURATION request. Allows us to apply the new configuration. Args: configuration : The configuration applied by the SET_CONFIG request. """ self.validate_configuration(configuration)
# For the MAXUSB case, we don't need to do anything, though it might # be nice to print a message or store the active configuration for # use by the USBDevice, etc. etc.