Skip to content

Commit

Permalink
Add support for setting the USB speed of a Facedancer board
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinevg committed Feb 20, 2024
1 parent 51c1662 commit adf8eb2
Show file tree
Hide file tree
Showing 8 changed files with 32 additions and 47 deletions.
2 changes: 1 addition & 1 deletion facedancer/backends/MAXUSBApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def get_version(self):
return self.read_register(self.reg_revision)


def connect(self, usb_device, max_ep0_packet_size=64):
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)
Expand Down
13 changes: 3 additions & 10 deletions facedancer/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,14 @@ def get_version(self):
raise NotImplementedError


def set_device_speed(self, device_speed: DeviceSpeed=DeviceSpeed.FULL):
"""
Sets the speed to be used when connecting the Facedancer's target port.
device_speed: the requested device speed.
"""
raise NotImplementedError


def connect(self, usb_device: USBDevice, max_ep0_packet_size: int=64):
def connect(self, usb_device: USBDevice, max_packet_size_ep0: int=64, device_speed: DeviceSpeed=DeviceSpeed.FULL):
"""
Prepares backend to connect to the target host and emulate
a given device.
usb_device: The USBDevice object that represents the emulated device.
max_packet_size_ep0: Max packet size for control endpoint.
device_speed: Requested usb speed for the Facedancer board.
"""
raise NotImplementedError

Expand Down
13 changes: 8 additions & 5 deletions facedancer/backends/greatdancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def __init__(self, device=None, verbose=0, quirks=None):
self.endpoint_stalled[i] = False

# Assume a max packet size of 64 until configured otherwise.
self.max_ep0_packet_size = 64
self.max_packet_size_ep0 = 64

# Start off by assuming we're not waiting for an OUT control transfer's
# data stage. # See _handle_setup_complete_on_endpoint for details.
Expand Down Expand Up @@ -182,7 +182,7 @@ def _generate_endpoint_config_arguments(self, config):
return arguments


def connect(self, usb_device, max_ep0_packet_size=64):
def connect(self, usb_device, max_packet_size_ep0=64, device_speed=DeviceSpeed.FULL):
"""
Prepares the GreatDancer to connect to the target host and emulate
a given device.
Expand All @@ -191,7 +191,10 @@ def connect(self, usb_device, max_ep0_packet_size=64):
emulated.
"""

self.max_ep0_packet_size = max_ep0_packet_size
if device_speed != DeviceSpeed.FULL:
log.warn(f"GreatFET only supports USB Full Speed. Ignoring requested speed: {device_speed.name}")

self.max_packet_size_ep0 = max_packet_size_ep0

quirks = 0

Expand All @@ -201,7 +204,7 @@ def connect(self, usb_device, max_ep0_packet_size=64):

quirks |= self.QUIRK_MANUAL_SET_ADDRESS

self.api.connect(self.max_ep0_packet_size, quirks)
self.api.connect(self.max_packet_size_ep0, quirks)
self.connected_device = usb_device

log.info("Connecting to host.")
Expand Down Expand Up @@ -545,7 +548,7 @@ def _handle_transfer_complete_on_endpoint(self, endpoint_number, direction):
self.pending_control_request.data.extend(new_data)

all_data_received = len(self.pending_control_request.data) == self.pending_control_request.length
is_short_packet = len(new_data) < self.max_ep0_packet_size
is_short_packet = len(new_data) < self.max_packet_size_ep0

if all_data_received or is_short_packet:

Expand Down
2 changes: 1 addition & 1 deletion facedancer/backends/greathost.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def __init__(self, verbose=0, quirks=[], autoconnect=True, device=None):
self.connect()


def connect(self):
def connect(self, device_speed=None):
"""
Sets up our host to talk to the device, including turning on VBUS.
"""
Expand Down
2 changes: 1 addition & 1 deletion facedancer/backends/libusbhost.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def __init__(self, verbose=0, quirks=[], index=0, **kwargs):
pass


def connect(self):
def connect(self, device_speed=None):
"""
Sets up our host to talk to the device, including turning on VBUS.
"""
Expand Down
29 changes: 10 additions & 19 deletions facedancer/backends/moondancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def __init__(self, device: USBDevice=None, verbose: int=0, quirks: List[str]=[])
self.endpoint_stalled[i] = False

# Assume a max packet size of 64 until configured otherwise.
self.max_ep0_packet_size = 64
self.max_packet_size_ep0 = 64

# Start off by assuming we're not waiting for an OUT control transfer's
# data stage. # See handle_setup_complete_on_endpoint for details.
Expand All @@ -100,9 +100,6 @@ def __init__(self, device: USBDevice=None, verbose: int=0, quirks: List[str]=[])
# for data transfer readiness.
self.configuration = None

# By default, Cynthion's target port operates at High speed.
self.device_speed = DeviceSpeed.HIGH

#
# Store our list of quirks to handle.
#
Expand Down Expand Up @@ -151,16 +148,7 @@ def get_version(self):
raise NotImplementedError()


def set_device_speed(self, device_speed: DeviceSpeed=DeviceSpeed.FULL):
"""
Sets the speed to be used when connecting Cynthion's target port.
device_speed: the requested device speed.
"""
self.device_speed = device_speed


def connect(self, usb_device: USBDevice, max_ep0_packet_size: int=64):
def connect(self, usb_device: USBDevice, max_packet_size_ep0: int=64, device_speed: DeviceSpeed=DeviceSpeed.FULL):
"""
Prepares Cynthion to connect to the target host and emulate
a given device.
Expand All @@ -169,9 +157,12 @@ def connect(self, usb_device: USBDevice, max_ep0_packet_size: int=64):
emulated.
"""

log.debug(f"moondancer.connect(max_ep0_packet_size:{max_ep0_packet_size}, device_speed:{self.device_speed}, quirks:{self.quirks})")
if device_speed not in [DeviceSpeed.FULL, DeviceSpeed.HIGH]:
log.warn(f"Moondancer only supports USB Full and High Speed. Ignoring requested speed: {device_speed.name}")

log.debug(f"moondancer.connect(max_packet_size_ep0:{max_packet_size_ep0}, device_speed:{device_speed}, quirks:{self.quirks})")

self.max_ep0_packet_size = max_ep0_packet_size
self.max_packet_size_ep0 = max_packet_size_ep0

# compute our quirk flags
quirks = 0
Expand All @@ -180,13 +171,13 @@ def connect(self, usb_device: USBDevice, max_ep0_packet_size: int=64):
quirks |= QuirkFlag.MANUAL_SET_ADDRESS

# connect to target host
self.api.connect(self.max_ep0_packet_size, self.device_speed, quirks)
self.api.connect(self.max_packet_size_ep0, device_speed, quirks)
self.connected_device = usb_device

# get device name
device_name = f"{type(self.connected_device).__module__}.{type(self.connected_device).__qualname__}"

log.info(f"Connected '{device_name}' to target host.")
log.info(f"Connected {device_speed.name} speed device '{device_name}' to target host.")


def disconnect(self):
Expand Down Expand Up @@ -474,7 +465,7 @@ def handle_receive_packet(self, endpoint_number: int):
self.pending_control_request.data.extend(new_data)

all_data_received = len(self.pending_control_request.data) == self.pending_control_request.length
is_short_packet = len(new_data) < self.max_ep0_packet_size
is_short_packet = len(new_data) < self.max_packet_size_ep0

if all_data_received or is_short_packet:
# Handle the completed setup request...
Expand Down
8 changes: 5 additions & 3 deletions facedancer/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .core import FacedancerUSBApp
from .types import DescriptorTypes, LanguageIDs, USBStandardRequests
from .types import USBDirection, USBRequestType, USBRequestRecipient
from .types import DeviceSpeed

from .magic import instantiate_subordinates

Expand Down Expand Up @@ -165,12 +166,12 @@ def add_configuration(self, configuration: USBConfiguration):
configuration.parent = self


def connect(self):
def connect(self, device_speed: DeviceSpeed=DeviceSpeed.FULL):
""" Connects this device to the host; e.g. turning on our presence-detect pull up. """
if self.backend is None:
self.backend = FacedancerUSBApp()

self.backend.connect(self, self.max_packet_size_ep0)
self.backend.connect(self, max_packet_size_ep0=self.max_packet_size_ep0, device_speed=device_speed)


def disconnect(self):
Expand All @@ -182,7 +183,8 @@ async def run(self):
""" Runs the actual device emulation. """

# Sanity check to avoid common issues.
if len(self.configurations) == 0:
from .proxy import USBProxyDevice
if len(self.configurations) == 0 and not isinstance(self, USBProxyDevice):
log.error("No configurations defined on the emulated device! "
"Did you forget @use_inner_classes_automatically?")

Expand Down
10 changes: 3 additions & 7 deletions facedancer/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,15 +311,11 @@ def connect(self):
# Since we're working at the transfer levels, the packet sizes will automatically be translated, anyway.
self.max_packet_size_ep0 = 64

# Get the speed of the device being proxied and attempt to set it if the backend supports it.
# Get the USB device speed of the device being proxied.
device_speed = self.proxied_device.device_speed()
try:
# FIXME self.backend does not yet exist here so this will always fail
self.backend.set_device_speed(device_speed)
except Exception as e:
log.warning(f"-- facedancer backend does not support setting device speed: {device_speed.name} --")

super().connect()
# Connect device.
super().connect(device_speed=device_speed)

# TODO check if we still need this in facedancer v3
# skipping USB.state_attached may not be strictly correct (9.1.1.{1,2})
Expand Down

0 comments on commit adf8eb2

Please sign in to comment.