|
1 |
| -### USB technology |
| 1 | +### USB Device stack |
2 | 2 |
|
3 |
| -[Insert content about USB technology and architecture.] |
| 3 | +#### Introduction |
| 4 | + |
| 5 | +A functional mbed-os USB device consists of three parts - an implementation of USBPhy, the USBDevice stack and a USB component code. |
| 6 | +- USBPhy - provides raw access to USB in the role of a USB Device |
| 7 | +- USBDevice - the core of mbed-os's USB stack and is responsible for state management and synchronization |
| 8 | +- USB component - is code which inherits from USBDevice and provides the desired USB interfaces |
| 9 | + |
| 10 | +The interaction of these three components can be seen in this diagram: |
| 11 | +TODO - DIAGRAM IS OLD |
| 12 | + |
| 13 | + |
| 14 | +#### Synchronization |
| 15 | + |
| 16 | +The class USBDevice is an interrupt safe class. It uses a critical section to provide thread and interrupt safe locking. This lock can be used from USB components inheriting from USBDevice but is not required. |
| 17 | + |
| 18 | +The recommended model for synchronizing a USB component is to wrap code requiring synchronization in a call to USBDevice::lock and USBDevice::unlock. Functions or callbacks which are synchronized by a caller at a higher level should document this by locking requirement by calling USBDevice::assert_locked in the first line of the function or callback. |
| 19 | + |
| 20 | +Code requiring locking: |
| 21 | +```c |
| 22 | +void USBComponent::do_something() |
| 23 | +{ |
| 24 | + lock(); |
| 25 | + |
| 26 | + // Do do something |
| 27 | + |
| 28 | + unlock(); |
| 29 | +} |
| 30 | +``` |
| 31 | + |
| 32 | +Code where the lock is held externally: |
| 33 | +```c |
| 34 | +void USBComponent::do_something_internal() |
| 35 | +{ |
| 36 | + assert_locked(); |
| 37 | + |
| 38 | + // Do do something |
| 39 | +} |
| 40 | +``` |
| 41 | + |
| 42 | +#### USB Device state |
| 43 | + |
| 44 | +USB defines 5 separate states a device can be in - Attached, Powered, Default, Address, Configured. Each state adds functionality, with the Attached state having the least functionality and the Configured state having the most functionality. |
| 45 | + |
| 46 | +| State | Functionality | |
| 47 | +|:----------:|:----------------------------------------:| |
| 48 | +| Attached | Power events | |
| 49 | +| Powered | Reset events | |
| 50 | +| Default | Control endpoint 0 active | |
| 51 | +| Address | | |
| 52 | +| Configured | All enabled endpoints are functional | |
| 53 | + |
| 54 | + At any time the USB device can enter a state with less functionality. This could be due to a loss of power event or a surprise USB disconnect. When leaving or outside of the Configured state writes to and reads from all endpoints other than endpoint 0 are ignored. |
| 55 | + |
| 56 | +#### USB component callbacks |
| 57 | + |
| 58 | +All callbacks sent by USBDevice to its children are prefixed with callback_*. These callbacks are called with the USB lock is held. One notable callback is `callback_state_change` which can be used generically handle leaving the Configured state. The Configured state is automatically exited by the USB stack on disconnect, power loss or USB reset. |
| 59 | + |
| 60 | +##### Control request state machine |
| 61 | + |
| 62 | +There are four callbacks sent from the USB control state machine. These are shown in the table below. When these callbacks are called they must return a result to continue the control state machine. The result does not need to be returned immediately, giving the USB component time to process the request. **Note that the response must always be sent regardless of any USB device state changes.** |
| 63 | + |
| 64 | +Table of control callbacks and the required response: |
| 65 | + |
| 66 | +| Callback | Response | |
| 67 | +|:---------------------------------------------:|:----------------------------------------:| |
| 68 | +| callback_request(setup_packet) | complete_request(result, data, size) | |
| 69 | +| callback_request_xfer_done(setup_packet) | complete_request_xfer_done(result) | |
| 70 | +| callback_set_configuration(configuration) | complete_set_configuration(result) | |
| 71 | +| callback_set_interface(interface, alternate) | complete_set_interface(result) | |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | +#### IN and OUT state machine |
| 77 | + |
| 78 | +TODO |
| 79 | + |
| 80 | +#### Endpoint Configuration |
| 81 | + |
| 82 | +To ensure a USB component will run on all supported devices the endpoints used by its configuration descriptor must be selected based on the current device. This is because endpoint number and endpoint functionality can differ based on device. The features of a device can be determined by examining its endpoint table. |
| 83 | + |
| 84 | +To simplify the process of selecting endpoints it is recommended that EndpointResolver class is used. It is constructed with an endpoint table and can be called to find an endpoint of the given type and size. After all required endpoints have been found the function EndpointResolver::valid() can be called to check if this configuration is supported by this device. An example of this is shown below: |
| 85 | + |
| 86 | +```c++ |
| 87 | +EndpointResolver resolver(endpoint_table()); |
| 88 | +resolver.endpoint_ctrl(CDC_MAX_PACKET_SIZE); |
| 89 | +bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE); |
| 90 | +bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE); |
| 91 | +int_in = resolver.endpoint_in(USB_EP_TYPE_INT, CDC_MAX_PACKET_SIZE); |
| 92 | +MBED_ASSERT(resolver.valid()); |
| 93 | +``` |
0 commit comments