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,USBRequestHandlerBase-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
- 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
- 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_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_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_handlerdecorators.- 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.
- max_packet_size_ep0: int = 64
- name: str = 'generic device'
- product_id: int = 18003
- protocol_revision_number: int = 0
- requestable_descriptors: Dict[tuple[int, int], bytes | callable]
- 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.
- 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:
USBBaseDeviceClass 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]
- 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
- requestable_descriptors: Dict[tuple[int, int], bytes | callable]