The Universal Serial Bus (USB) is an industry standard that defines the cables, connectors, and communication protocols used in a bus for connection, communication, and power supply between computers and electronic devices.
The USB device driver provides necessary APIs to support USB Device states and USB data flow, so that the USB Device enumeration, class, and vendor support can be implemented based on it. The driver is asynchronous, which means that all USB data processing is done in callbacks.
To be recognized by a host, a USB device must handle a subset of the USB events. The USB device should build up control communication to report its descriptors to the host and accept host's requests. An application or upper stack that uses the USB device driver should have its descriptors prepared, catch the callbacks, monitor the control requests and handle them correctly. Usually, a USB device application that can be enumerated may use the following sequence:
Initialize
Initialize endpoint 0
On GetDeviceDescriptor request, sends device descriptor
On GetConfigurationDescriptor request, sends configuration descriptors
On SetAddress request sends no data, just goes to status phase
On SetConfigure request initialize and enable other endpoints, sends no data, just goes to status phase
On SetAddress request apply the new address
On SetConfigure request a global variable can be set to indicates that the host driver is loaded and device is ready to work
Enable endpoint 0
Enable
Attach device
To support USB transfer on endpoints, endpoints information is stored by the driver internally, including control request data, endpoint status, callbacks and transfer data pointers. The number of endpoints that the driver can support is defined through configuration. To optimize RAM usage, the number of endpoints the driver needs to support should be minimized.
Initialization/de-initialization
Enabling/disabling
USB device attachment/detachment
USB device address control
USB working speed status
USB device frame number and micro frame number status
Sending remote wakeup to host
Start of Frame (SOF)
VBus change
Reset
Wakeup
Linked Power Management (LPM) Suspend
Suspend
Error
Endpoint initialization/de-initialization
Endpoint enabling/disabling
Control endpoint setup request packet
Data transfer and abort
Endpoint halt state control
Endpoint address
Last transfer result status code
Last error status code
Current transfer state
Transfer size and done count
In case a setup request received on control endpoint
In case transfer finished with/without error
When a buffer is used by a USB endpoint to transfer data, it must be kept unchanged while the transfer is in progress.
After receiving a request that has no data expected, the transfer function must be called with data length zero to complete control status phase.
The USB device capable hardware
48MHz clock for low-speed and full-speed and 480MHz clock for high-speed
DFLL must be used to generate 48MHz clock for USB device with either of the following mode:
Set "DFLL Enable", "Bypass Coarse Lock", "Chill Cycle Disable", "USB Clock Recovery Mode", "Stable DFLL Frequency"
Clear "Wait Lock"
Leave "Operating Mode Selection" to "Closed Loop Mode"
Set "DFLL Enable"
Clear "USB Clock Recovery Mode", "Stable DFLL Frequency"
Select "Closed Loop Mode" of "Operating Mode Selection"
Set "DFLL Multiply Factor" to 1464 or 1465 (48000000/32768)
Set "Generic Clock Generator Enable"
Set "External 32K Oscillator Enable", "Enable 32KHz Output", "Enable XTAL"
Set a right value for "Startup time for the 32K Oscillator", e.g., 1125092 us
Each USB device endpoint number supports two endpoint addresses, corresponding to IN and OUT endpoint. E.g., for endpoint 1, the endpoint IN has address 0x81 and endpoint OUT has address 0x01. Thus, the possible supported endpoint addresses are almost two times of max endpoint number (endpoint 0 must be used as control endpoint instead of dedicated IN and OUT endpoints).
When transferring data through USB device endpoints, buffer pointers can be used to let endpoint get access to the buffer, but there are some limits:
For control endpoint there must always be a buffer available to put received setup packet.
For IN endpoint (transmit to host) the data must in RAM.
For OUT endpoint (receive from host) the data pointer must be aligned, and the data size must be aligned by max endpoint size and not zero.
The driver has option for each endpoint to allocate internal static buffer as cache to buffer input/output data, to remove upper limits. The configuration affects the parameter check of transfer functions, and the RAM usage.
For control endpoints, cache buffer must be enabled to fill setup packet. In addition, to support unaligned OUT packet and IN packet inside flash, the buffer size must be equal to or larger than max endpoint size.
For OUT endpoints, if the cache is allocated, it's possible to pass unaligned buffer address and buffer size to transfer function. Else the transfer function only accepts aligned buffer with it's size multiple of endpoint packet size.
For IN endpoints, if the cache is allocated, it's possible to pass buffer pointer to internal flash or other memory part other than RAM to the transfer function.
To optimize the RAM usage, the configuration of max endpoint number, max number of endpoints supported and the buffer usage of used input and/or output endpoints can be adjusted.