facedancer.device module

Functionality for defining USB devices.

class facedancer.device.USBBaseDevice(*, name: str = 'generic device', device_class: int = 0, device_subclass: int = 0, protocol_revision_number: int = 0, max_packet_size_ep0: int = 64, vendor_id: int = 24843, product_id: int = 18003, manufacturer_string: ~facedancer.descriptor.StringRef = <factory>, product_string: ~facedancer.descriptor.StringRef = <factory>, serial_number_string: ~facedancer.descriptor.StringRef = <factory>, supported_languages: tuple = (LanguageIDs.ENGLISH_US,), device_revision: int = 0, usb_spec_version: int = 512, device_speed: ~facedancer.types.DeviceSpeed = None, requestable_descriptors: ~typing.Dict[tuple[int, int], bytes | callable] = <factory>, configurations: ~typing.Dict[int, ~facedancer.configuration.USBConfiguration] = <factory>, backend: ~facedancer.core.FacedancerUSBApp = None)[source]

Bases: USBDescribable, USBRequestHandler

Base-most class for Facedancer USB devices. This version is very similar to the USBDevice type, except that it does not define _any_ standard handlers. This allows you the freedom to declare whatever standard requests you’d like.

Fields:
vendor_id, product_id :

The USB vendor and product ID for this device.

manufacturer_string, product_string, serial_number_string :

Python strings identifying the device to the USB host.

device_class, device_subclass, protocol_revision_number :

The USB descriptor fields that select the class, subclass, and protocol.

supported_languages :

A tuple containing all of the language IDs supported by the device.

device_revision :

Number indicating the hardware revision of this device. Typically BCD.

usb_spec_revision :

Number indicating the version of the USB specification we adhere to. Typically 0x0200.

device_speed :

Specify the device speed for boards that support multiple interface speeds.

DESCRIPTOR_LENGTH = 18
DESCRIPTOR_TYPE_NUMBER = 1
__post_init__()[source]

Set up our device for execution.

add_configuration(configuration: USBConfiguration)[source]

Adds the provided configuration to this device.

add_descriptor(descriptor: USBDescriptor)[source]

Adds the provided descriptor to this device.

backend: FacedancerUSBApp = None
clear_halt(endpoint_number: int, direction: USBDirection)[source]

Clears a halt condition on the provided non-control endpoint.

Parameters:
  • endpoint_number – The endpoint number

  • direction – The endpoint direction; or OUT if not provided.

configurations: Dict[int, USBConfiguration]
connect(device_speed: DeviceSpeed = DeviceSpeed.FULL)[source]

Connects this device to the host; e.g. turning on our presence-detect pull up.

control_send(endpoint_number: int, in_request: USBControlRequest, data: bytes, *, blocking: bool = False)[source]
Queues sending data on the provided control endpoint in

response to a IN control request.

Parameters:
  • endpoint_number – The endpoint number to send data upon.

  • in_request – The control request being responded to.

  • data – The data to send.

  • blocking – If provided and true, this function will block until the backend indicates the send is complete.

create_request(raw_data: bytes) USBControlRequest[source]
device_class: int = 0
device_revision: int = 0
device_speed: DeviceSpeed = None
device_subclass: int = 0
disconnect()[source]

Disconnects this device from the host.

emulate(*coroutines: Iterable[Coroutine])[source]

Convenience method that runs a full method in a blocking manner. Performs connect, run, and then disconnect.

Parameters:

*coroutines – any asyncio coroutines to be executed concurrently with our emulation

classmethod from_binary_descriptor(data, strings={})[source]

Creates a USBBaseDevice object from its descriptor.

get_configuration_descriptor(index: int) bytes[source]

Returns the configuration descriptor with the given configuration number.

get_descriptor() bytes[source]

Returns a complete descriptor for this device.

get_endpoint(endpoint_number: int, direction: USBDirection) USBEndpoint[source]

Attempts to find a subordinate endpoint matching the given number/direction.

Parameters:
  • endpoint_number – The endpoint number to search for.

  • direction – The endpoint direction to be matched.

Returns:

The matching endpoint; or None if no matching endpoint existed.

get_string_descriptor(index: int) bytes[source]

Returns the string descriptor associated with a given index.

handle_buffer_available(ep_num)[source]

Backend data-buffer-empty handler; for legacy compatibility.

Prefer overriding handle_buffer_available().

handle_buffer_empty(endpoint: USBEndpoint)[source]

Handler called when a given endpoint first has an empty buffer.

Often, an empty buffer indicates an opportunity to queue data for sending (‘prime an endpoint’), but doesn’t necessarily mean that the host is planning on reading the data.

This function is called only once per buffer.

handle_bus_reset()[source]

Event handler for a bus reset.

handle_data_available(ep_num, data)[source]

Backend data-available handler; for legacy compatibility.

Prefer overriding handle_data_received().

handle_data_received(endpoint: USBEndpoint, data: bytes)[source]

Handler for receipt of non-control request data.

Typically, this method will delegate any data received to the appropriate configuration/interface/endpoint. If overridden, the overriding function will receive all data.

Parameters:
  • endpoint_number – The endpoint number on which the data was received.

  • data – The raw bytes received on the relevant endpoint.

handle_data_requested(endpoint: USBEndpoint)[source]

Handler called when the host requests data on a non-control endpoint.

Typically, this method will delegate the request to the appropriate configuration+interface+endpoint. If overridden, the overriding function will receive all events.

Parameters:

endpoint_number – The endpoint number on which the host requested data.

static handle_generic_get_descriptor_request(self: USBDevice | USBInterface, request: USBControlRequest)[source]

Handle GET_DESCRIPTOR requests; per USB2 [9.4.3]

handle_get_supported_languages_descriptor() bytes[source]

Return the special string-descriptor-zero that indicates which languages are supported.

handle_nak(ep_num: int)[source]

Backend data-requested handler; for legacy compatibility.

Prefer overriding handle_data_requested() and handle_unexpected_data_Requested

handle_request(request: USBControlRequest)[source]

Core control request handler.

This function can be overridden by a subclass if desired; but the typical way to handle a specific control request is to the the @control_request_handler decorators.

Parameters:

request – the USBControlRequest object representing the relevant request

handle_unexpected_data_received(endpoint_number: int, data: bytes)[source]

Handler for unexpected data.

Handles any data directed at an unexpected target; e.g. an endpoint that doesn’t exist. Note that even if handle_data_received is overridden, this method can still be called e.g. by configuration.handle_data_received.

Parameters:
  • endpoint_number – The endpoint number on which the data was received.

  • data – The raw bytes received on the relevant endpoint.

handle_unexpected_data_requested(endpoint_number: int)[source]

Handler for unexpected data requests.

Handles any requests directed at an unexpected target; e.g. an endpoint that doesn’t exist. Note that even if handle_data_requested is overridden, this method can still be called e.g. by configuration.handle_data_received.

Parameters:

endpoint_number – The endpoint number on which the data was received.

manufacturer_string: StringRef
max_packet_size_ep0: int = 64
name: str = 'generic device'
print_suggested_additions()[source]

Prints a collection of suggested additions to the stdout.

product_id: int = 18003
product_string: StringRef
protocol_revision_number: int = 0
requestable_descriptors: Dict[tuple[int, int], bytes | callable]
async run()[source]

Runs the actual device emulation.

run_with(*coroutines: Iterable[Coroutine])[source]

Runs the actual device emulation synchronously; running any provided coroutines simultaneously.

send(endpoint_number: int, data: bytes, *, blocking: bool = False)[source]

Queues sending data on the IN endpoint with the provided number.

Parameters:
  • endpoint_number – The endpoint number to send data upon.

  • data – The data to send.

  • blocking – If provided and true, this function will block until the backend indicates the send is complete.

serial_number_string: StringRef
set_address(address: int, defer: bool = False)[source]

Updates the device’s knowledge of its own address.

Parameters:
  • address – The address to apply.

  • defer – If true, the address change should be deferred until the next time a control request ends. Should be set if we’re changing the address before we ack the relevant transaction.

stall(*, endpoint_number: int = 0, direction: USBDirection = USBDirection.OUT)[source]

Stalls the provided endpoint.

For endpoint zero, this indicates that the active (or next) request is not supported. For all other endpoints, this indicates a persistent ‘halt’ condition.

Parameters:

endpoint – The endpoint address; or EP0 if not provided.

supported_languages: tuple = (LanguageIDs.ENGLISH_US,)
usb_spec_version: int = 512
vendor_id: int = 24843
class facedancer.device.USBDevice(*, name: str = 'generic device', device_class: int = 0, device_subclass: int = 0, protocol_revision_number: int = 0, max_packet_size_ep0: int = 64, vendor_id: int = 24843, product_id: int = 18003, manufacturer_string: ~facedancer.descriptor.StringRef = <factory>, product_string: ~facedancer.descriptor.StringRef = <factory>, serial_number_string: ~facedancer.descriptor.StringRef = <factory>, supported_languages: tuple = (LanguageIDs.ENGLISH_US,), device_revision: int = 0, usb_spec_version: int = 512, device_speed: ~facedancer.types.DeviceSpeed = None, requestable_descriptors: ~typing.Dict[tuple[int, int], bytes | callable] = <factory>, configurations: ~typing.Dict[int, ~facedancer.configuration.USBConfiguration] = <factory>, backend: ~facedancer.core.FacedancerUSBApp = None)[source]

Bases: USBBaseDevice

Class representing the behavior of a USB device.

This default implementation provides standard request handlers in order to facilitate creating a host-compatible USB device.

These functions can be overloaded to change their behavior. If you want to dramatically change the behavior of these requests, you can opt to use USBBaseDevice, which lacks standard request handling.

Fields:
device_class/device_subclass/protocol_revision_number –

The USB descriptor fields that select the class, subclass, and protcol.

vendor_id, product_id –

The USB vendor and product ID for this device.

manufacturer_string, product_string, serial_number_string –

Python strings identifying the device to the USB host.

supported_languages –

A tuple containing all of the language IDs supported by the device.

device_revision –

Number indicating the hardware revision of this device. Typically BCD.

usb_spec_revision –

Number indicating the version of the USB specification we adhere to. Typically 0x0200.

configurations: Dict[int, USBConfiguration]
generate_code(name='Device')[source]
handle_clear_feature_request = <ControlRequestHandler wrapping USBDevice.handle_clear_feature_request at 0x7f80aa9ad640
handle_get_configuration_request = <ControlRequestHandler wrapping USBDevice.handle_get_configuration_request at 0x7f80aa9ae630
handle_get_descriptor_request = <ControlRequestHandler wrapping USBDevice.handle_get_descriptor_request at 0x7f80aa9ae0c0
handle_get_status_request = <ControlRequestHandler wrapping USBDevice.handle_get_status_request at 0x7f80aa9acfe0
handle_set_address_request = <ControlRequestHandler wrapping USBDevice.handle_set_address_request at 0x7f80aa9adcd0
handle_set_configuration_request = <ControlRequestHandler wrapping USBDevice.handle_set_configuration_request at 0x7f80aa9ae900
handle_set_descriptor_request = <ControlRequestHandler wrapping USBDevice.handle_set_descriptor_request at 0x7f80aa9ae390
handle_set_feature_request = <ControlRequestHandler wrapping USBDevice.handle_set_feature_request at 0x7f80aa9ad850
handle_synch_frame_request = <ControlRequestHandler wrapping USBDevice.handle_synch_frame_request at 0x7f80aa9aeb70
manufacturer_string: StringRef
product_string: StringRef
requestable_descriptors: Dict[tuple[int, int], bytes | callable]
serial_number_string: StringRef