528 lines
19 KiB
ReStructuredText
528 lines
19 KiB
ReStructuredText
USB device stack
|
|
################
|
|
|
|
The USB device stack is split into three layers:
|
|
* USB Device Controller drivers (hardware dependent)
|
|
* USB device core driver (hardware independent)
|
|
* USB device class drivers (hardware independent)
|
|
|
|
USB device controller drivers
|
|
*****************************
|
|
|
|
The Device Controller Driver Layer implements the low level control routines
|
|
to deal directly with the hardware. All device controller drivers should
|
|
implement the APIs described in file usb_dc.h. This allows the integration of
|
|
new USB device controllers to be done without changing the upper layers.
|
|
For now only Quark SE USB device controller (Designware IP) is supported.
|
|
|
|
Structures
|
|
==========
|
|
|
|
.. code-block:: c
|
|
|
|
struct usb_dc_ep_cfg_data {
|
|
u8_t ep_addr;
|
|
u16_t ep_mps;
|
|
enum usb_dc_ep_type ep_type;
|
|
};
|
|
|
|
Structure containing the USB endpoint configuration.
|
|
* ep_addr: endpoint address, the number associated with the EP in the device
|
|
configuration structure.
|
|
IN EP = 0x80 | <endpoint number>. OUT EP = 0x00 | <endpoint number>
|
|
* ep_mps: Endpoint max packet size.
|
|
* ep_type: Endpoint type, may be Bulk, Interrupt or Control. Isochronous
|
|
endpoints are not supported for now.
|
|
|
|
.. code-block:: c
|
|
|
|
enum usb_dc_status_code {
|
|
USB_DC_ERROR,
|
|
USB_DC_RESET,
|
|
USB_DC_CONNECTED,
|
|
USB_DC_CONFIGURED,
|
|
USB_DC_DISCONNECTED,
|
|
USB_DC_SUSPEND,
|
|
USB_DC_RESUME,
|
|
USB_DC_UNKNOWN
|
|
};
|
|
|
|
Status codes reported by the registered device status callback.
|
|
* USB_DC_ERROR: USB error reported by the controller.
|
|
* USB_DC_RESET: USB reset.
|
|
* USB_DC_CONNECTED: USB connection established - hardware enumeration is completed.
|
|
* USB_DC_CONFIGURED: USB configuration done.
|
|
* USB_DC_DISCONNECTED: USB connection lost.
|
|
* USB_DC_SUSPEND: USB connection suspended by the HOST.
|
|
* USB_DC_RESUME: USB connection resumed by the HOST.
|
|
* USB_DC_UNKNOWN: Initial USB connection status.
|
|
|
|
.. code-block:: c
|
|
|
|
enum usb_dc_ep_cb_status_code {
|
|
USB_DC_EP_SETUP,
|
|
USB_DC_EP_DATA_OUT,
|
|
USB_DC_EP_DATA_IN,
|
|
};
|
|
|
|
Status Codes reported by the registered endpoint callback.
|
|
* USB_DC_EP_SETUP: SETUP packet received.
|
|
* USB_DC_EP_DATA_OUT: Out transaction on this endpoint. Data is available
|
|
for read.
|
|
* USB_DC_EP_DATA_IN: In transaction done on this endpoint.
|
|
|
|
APIs
|
|
====
|
|
|
|
The following APIs are provided by the device controller driver:
|
|
|
|
:c:func:`usb_dc_attach()`
|
|
This function attaches USB for device connection. Upon success, the USB PLL
|
|
is enabled, and the USB device is now capable of transmitting and receiving
|
|
on the USB bus and of generating interrupts.
|
|
|
|
:c:func:`usb_dc_detach()`
|
|
This function detaches the USB device. Upon success the USB hardware PLL is
|
|
powered down and USB communication is disabled.
|
|
|
|
:c:func:`usb_dc_reset()`
|
|
This function returns the USB device to it's initial state.
|
|
|
|
:c:func:`usb_dc_set_address()`
|
|
This function sets USB device address.
|
|
|
|
:c:func:`usb_dc_set_status_callback()`
|
|
This function sets USB device controller status callback. The registered
|
|
callback is used to report changes in the status of the device controller.
|
|
The status code are described by the usb_dc_status_code enumeration.
|
|
|
|
:c:func:`usb_dc_ep_configure()`
|
|
This function configures an endpoint. usb_dc_ep_cfg_data structure provides
|
|
the endpoint configuration parameters: endpoint address, endpoint maximum
|
|
packet size and endpoint type.
|
|
|
|
:c:func:`usb_dc_ep_set_stall()`
|
|
This function sets stall condition for the selected endpoint.
|
|
|
|
:c:func:`usb_dc_ep_clear_stall()`
|
|
This functions clears stall condition for the selected endpoint
|
|
|
|
:c:func:`usb_dc_ep_is_stalled()`
|
|
This function check if selected endpoint is stalled.
|
|
|
|
:c:func:`usb_dc_ep_halt()`
|
|
This function halts the selected endpoint
|
|
|
|
:c:func:`usb_dc_ep_enable()`
|
|
This function enables the selected endpoint. Upon success interrupts are
|
|
enabled for the corresponding endpoint and the endpoint is ready for
|
|
transmitting/receiving data.
|
|
|
|
:c:func:`usb_dc_ep_disable()`
|
|
This function disables the selected endpoint. Upon success interrupts are
|
|
disabled for the corresponding endpoint and the endpoint is no longer able
|
|
for transmitting/receiving data.
|
|
|
|
:c:func:`usb_dc_ep_flush()`
|
|
This function flushes the FIFOs for the selected endpoint.
|
|
|
|
:c:func:`usb_dc_ep_write()`
|
|
This function writes data to the specified endpoint. The supplied
|
|
usb_ep_callback function will be called when data is transmitted out.
|
|
|
|
:c:func:`usb_dc_ep_read()`
|
|
This function is called by the Endpoint handler function, after an OUT
|
|
interrupt has been received for that EP. The application must only call this
|
|
function through the supplied usb_ep_callback function.
|
|
|
|
:c:func:`usb_dc_ep_set_callback()`
|
|
This function sets callback function for notification of data received
|
|
and available to application or transmit done on the selected endpoint.
|
|
The callback status code is described by usb_dc_ep_cb_status_code.
|
|
|
|
USB device core layer
|
|
*********************
|
|
|
|
The USB Device core layer is a hardware independent interface between USB
|
|
device controller driver and USB device class drivers or customer applications.
|
|
It's a port of the LPCUSB device stack. It provides the following
|
|
functionalities:
|
|
|
|
* Responds to standard device requests and returns standard descriptors,
|
|
essentially handling 'Chapter 9' processing, specifically the standard
|
|
device requests in table 9-3 from the universal serial bus specification
|
|
revision 2.0.
|
|
* Provides a programming interface to be used by USB device classes or
|
|
customer applications. The APIs are described in the usb_device.h file.
|
|
* Uses the APIs provided by the device controller drivers to interact with
|
|
the USB device controller.
|
|
|
|
Structures
|
|
==========
|
|
|
|
.. code-block:: c
|
|
|
|
typedef void (*usb_status_callback)(enum usb_dc_status_code status_code);
|
|
|
|
Callback function signature for the device status.
|
|
|
|
.. code-block:: c
|
|
|
|
typedef void (*usb_ep_callback)(u8_t ep,
|
|
enum usb_dc_ep_cb_status_code cb_status);
|
|
|
|
Callback function signature for the USB Endpoint.
|
|
|
|
.. code-block:: c
|
|
|
|
typedef int (*usb_request_handler) (struct usb_setup_packet *setup,
|
|
int *transfer_len, u8_t **payload_data);
|
|
|
|
Callback function signature for class specific requests. For host to device
|
|
direction the 'len' and 'payload_data' contain the length of the received data
|
|
and the pointer to the received data respectively. For device to host class
|
|
requests, 'len' and 'payload_data' should be set by the callback function
|
|
with the length and the address of the data to be transmitted buffer
|
|
respectively.
|
|
|
|
.. code-block:: c
|
|
|
|
struct usb_ep_cfg_data {
|
|
usb_ep_callback ep_cb;
|
|
u8_t ep_addr;
|
|
};
|
|
|
|
This structure contains configuration for a certain endpoint.
|
|
* ep_cb: callback function for notification of data received and available
|
|
to application or transmit done, NULL if callback not required by
|
|
application code.
|
|
* ep_addr: endpoint address. The number associated with the EP in the device
|
|
configuration structure.
|
|
|
|
.. code-block:: c
|
|
|
|
struct usb_interface_cfg_data {
|
|
usb_request_handler class_handler;
|
|
usb_request_handler custom_handler;
|
|
u8_t *payload_data;
|
|
};
|
|
|
|
This structure contains USB interface configuration.
|
|
* class_handler: handler for USB Class specific Control (EP 0)
|
|
communications.
|
|
* custom_handler: the custom request handler gets a first
|
|
chance at handling the request before it is handed over to the
|
|
'chapter 9' request handler.
|
|
* payload_data: this data area, allocated by the application, is used to
|
|
store class specific command data and must be large enough to store the
|
|
largest payload associated with the largest supported Class' command set.
|
|
|
|
.. code-block:: c
|
|
|
|
struct usb_cfg_data {
|
|
const u8_t *usb_device_description;
|
|
usb_status_callback cb_usb_status;
|
|
struct usb_interface_cfg_data interface;
|
|
u8_t num_endpoints;
|
|
struct usb_ep_cfg_data *endpoint;
|
|
};
|
|
|
|
This structure contains USB device configuration.
|
|
* usb_device_description: USB device description, see
|
|
http://www.beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors
|
|
* cb_usb_status: callback to be notified on USB connection status change
|
|
* interface: USB class handlers and storage space.
|
|
* num_endpoints: number of individual endpoints in the device configuration
|
|
* endpoint: pointer to an array of endpoint configuration structures
|
|
(usb_cfg_data) of length equal to the number of EP associated with the
|
|
device description, not including control endpoints.
|
|
|
|
The class drivers instantiates this with given parameters using the
|
|
"usb_set_config" function.
|
|
|
|
APIs
|
|
====
|
|
|
|
:c:func:`usb_set_config()`
|
|
This function configures USB device.
|
|
|
|
:c:func:`usb_deconfig()`
|
|
This function returns the USB device back to it's initial state
|
|
|
|
:c:func:`usb_enable()`
|
|
This function enable USB for host/device connection. Upon success, the USB
|
|
module is no longer clock gated in hardware, it is now capable of
|
|
transmitting and receiving on the USB bus and of generating interrupts.
|
|
|
|
:c:func:`usb_disable()`
|
|
This function disables the USB device. Upon success, the USB module clock
|
|
is gated in hardware and it is no longer capable of generating interrupts.
|
|
|
|
:c:func:`usb_write()`
|
|
write data to the specified endpoint. The supplied usb_ep_callback will be
|
|
called when transmission is done.
|
|
|
|
:c:func:`usb_read()`
|
|
This function is called by the endpoint handler function after an OUT
|
|
interrupt has been received for that EP. The application must only call
|
|
this function through the supplied usb_ep_callback function.
|
|
|
|
|
|
USB device class drivers
|
|
************************
|
|
|
|
To initialize the device class driver instance the USB device class driver
|
|
should call usb_set_config() passing as parameter the instance's configuration
|
|
structure.
|
|
|
|
For example, for CDC_ACM sample application:
|
|
|
|
.. code-block:: c
|
|
|
|
static const u8_t cdc_acm_usb_description[] = {
|
|
/* Device descriptor */
|
|
USB_DEVICE_DESC_SIZE, /* Descriptor size */
|
|
USB_DEVICE_DESC, /* Descriptor type */
|
|
LOW_BYTE(USB_1_1),
|
|
HIGH_BYTE(USB_1_1), /* USB version in BCD format */
|
|
COMMUNICATION_DEVICE_CLASS, /* Class */
|
|
0x00, /* SubClass - Interface specific */
|
|
0x00, /* Protocol - Interface specific */
|
|
MAX_PACKET_SIZE_EP0, /* Max Packet Size */
|
|
LOW_BYTE(VENDOR_ID),
|
|
HIGH_BYTE(VENDOR_ID), /* Vendor Id */
|
|
LOW_BYTE(CDC_PRODUCT_ID),
|
|
HIGH_BYTE(CDC_PRODUCT_ID), /* Product Id */
|
|
LOW_BYTE(BCDDEVICE_RELNUM),
|
|
HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */
|
|
0x01, /* Index of Manufacturer String Descriptor */
|
|
0x02, /* Index of Product String Descriptor */
|
|
0x03, /* Index of Serial Number String Descriptor */
|
|
CDC_NUM_CONF, /* Number of Possible Configuration */
|
|
|
|
/* Configuration descriptor */
|
|
USB_CONFIGURATION_DESC_SIZE, /* Descriptor size */
|
|
USB_CONFIGURATION_DESC, /* Descriptor type */
|
|
LOW_BYTE(CDC_CONF_SIZE),
|
|
HIGH_BYTE(CDC_CONF_SIZE), /* Total length in bytes of data returned */
|
|
CDC_NUM_ITF, /* Number of interfaces */
|
|
0x01, /* Configuration value */
|
|
0x00, /* Index of the Configuration string */
|
|
USB_CONFIGURATION_ATTRIBUTES, /* Attributes */
|
|
MAX_LOW_POWER, /* Max power consumption */
|
|
|
|
/* Interface descriptor */
|
|
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
|
|
USB_INTERFACE_DESC, /* Descriptor type */
|
|
0x00, /* Interface index */
|
|
0x00, /* Alternate setting */
|
|
CDC1_NUM_EP, /* Number of Endpoints */
|
|
COMMUNICATION_DEVICE_CLASS, /* Class */
|
|
ACM_SUBCLASS, /* SubClass */
|
|
V25TER_PROTOCOL, /* Protocol */
|
|
0x00, /* Index of the Interface String Descriptor */
|
|
|
|
/* Header Functional Descriptor */
|
|
USB_HFUNC_DESC_SIZE, /* Descriptor size */
|
|
CS_INTERFACE, /* Descriptor type */
|
|
USB_HFUNC_SUBDESC, /* Descriptor SubType */
|
|
LOW_BYTE(USB_1_1),
|
|
HIGH_BYTE(USB_1_1), /* CDC Device Release Number */
|
|
|
|
/* Call Management Functional Descriptor */
|
|
USB_CMFUNC_DESC_SIZE, /* Descriptor size */
|
|
CS_INTERFACE, /* Descriptor type */
|
|
USB_CMFUNC_SUBDESC, /* Descriptor SubType */
|
|
0x00, /* Capabilities */
|
|
0x01, /* Data Interface */
|
|
|
|
/* ACM Functional Descriptor */
|
|
USB_ACMFUNC_DESC_SIZE, /* Descriptor size */
|
|
CS_INTERFACE, /* Descriptor type */
|
|
USB_ACMFUNC_SUBDESC, /* Descriptor SubType */
|
|
/* Capabilities - Device supports the request combination of:
|
|
* Set_Line_Coding,
|
|
* Set_Control_Line_State,
|
|
* Get_Line_Coding
|
|
* and the notification Serial_State
|
|
*/
|
|
0x02,
|
|
|
|
/* Union Functional Descriptor */
|
|
USB_UFUNC_DESC_SIZE, /* Descriptor size */
|
|
CS_INTERFACE, /* Descriptor type */
|
|
USB_UFUNC_SUBDESC, /* Descriptor SubType */
|
|
0x00, /* Master Interface */
|
|
0x01, /* Slave Interface */
|
|
|
|
/* Endpoint INT */
|
|
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
|
|
USB_ENDPOINT_DESC, /* Descriptor type */
|
|
CDC_ENDP_INT, /* Endpoint address */
|
|
USB_DC_EP_INTERRUPT, /* Attributes */
|
|
LOW_BYTE(CDC_INTERRUPT_EP_MPS),
|
|
HIGH_BYTE(CDC_INTERRUPT_EP_MPS),/* Max packet size */
|
|
0x0A, /* Interval */
|
|
|
|
/* Interface descriptor */
|
|
USB_INTERFACE_DESC_SIZE, /* Descriptor size */
|
|
USB_INTERFACE_DESC, /* Descriptor type */
|
|
0x01, /* Interface index */
|
|
0x00, /* Alternate setting */
|
|
CDC2_NUM_EP, /* Number of Endpoints */
|
|
COMMUNICATION_DEVICE_CLASS_DATA,/* Class */
|
|
0x00, /* SubClass */
|
|
0x00, /* Protocol */
|
|
0x00, /* Index of the Interface String Descriptor */
|
|
|
|
/* First Endpoint IN */
|
|
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
|
|
USB_ENDPOINT_DESC, /* Descriptor type */
|
|
CDC_ENDP_IN, /* Endpoint address */
|
|
USB_DC_EP_BULK, /* Attributes */
|
|
LOW_BYTE(CDC_BULK_EP_MPS),
|
|
HIGH_BYTE(CDC_BULK_EP_MPS), /* Max packet size */
|
|
0x00, /* Interval */
|
|
|
|
/* Second Endpoint OUT */
|
|
USB_ENDPOINT_DESC_SIZE, /* Descriptor size */
|
|
USB_ENDPOINT_DESC, /* Descriptor type */
|
|
CDC_ENDP_OUT, /* Endpoint address */
|
|
USB_DC_EP_BULK, /* Attributes */
|
|
LOW_BYTE(CDC_BULK_EP_MPS),
|
|
HIGH_BYTE(CDC_BULK_EP_MPS), /* Max packet size */
|
|
0x00, /* Interval */
|
|
|
|
/* String descriptor language, only one, so min size 4 bytes.
|
|
* 0x0409 English(US) language code used
|
|
*/
|
|
USB_STRING_DESC_SIZE, /* Descriptor size */
|
|
USB_STRING_DESC, /* Descriptor type */
|
|
0x09,
|
|
0x04,
|
|
/* Manufacturer String Descriptor "Intel" */
|
|
0x0C,
|
|
USB_STRING_DESC,
|
|
'I', 0, 'n', 0, 't', 0, 'e', 0, 'l', 0,
|
|
/* Product String Descriptor "CDC-ACM" */
|
|
0x10,
|
|
USB_STRING_DESC,
|
|
'C', 0, 'D', 0, 'C', 0, '-', 0, 'A', 0, 'C', 0, 'M', 0,
|
|
/* Serial Number String Descriptor "00.01" */
|
|
0x0C,
|
|
USB_STRING_DESC,
|
|
'0', 0, '0', 0, '.', 0, '0', 0, '1', 0,
|
|
};
|
|
|
|
.. code-block:: c
|
|
|
|
static struct usb_ep_cfg_data cdc_acm_ep_data[] = {
|
|
{
|
|
.ep_cb = cdc_acm_int_in,
|
|
.ep_addr = CDC_ENDP_INT
|
|
},
|
|
{
|
|
.ep_cb = cdc_acm_bulk_out,
|
|
.ep_addr = CDC_ENDP_OUT
|
|
},
|
|
{
|
|
.ep_cb = cdc_acm_bulk_in,
|
|
.ep_addr = CDC_ENDP_IN
|
|
}
|
|
};
|
|
|
|
.. code-block:: c
|
|
|
|
static struct usb_cfg_data cdc_acm_config = {
|
|
.usb_device_description = cdc_acm_usb_description,
|
|
.cb_usb_status = cdc_acm_dev_status_cb,
|
|
.interface = {
|
|
.class_handler = cdc_acm_class_handle_req,
|
|
.custom_handler = NULL,
|
|
.payload_data = NULL,
|
|
},
|
|
.num_endpoints = CDC1_NUM_EP + CDC2_NUM_EP,
|
|
.endpoint = cdc_acm_ep_data
|
|
};
|
|
|
|
.. code-block:: c
|
|
|
|
ret = usb_set_config(&cdc_acm_config);
|
|
if (ret < 0) {
|
|
DBG("Failed to config USB\n");
|
|
return ret;
|
|
}
|
|
|
|
To enable the USB device for host/device connection:
|
|
|
|
.. code-block:: c
|
|
|
|
ret = usb_enable(&cdc_acm_config);
|
|
if (ret < 0) {
|
|
DBG("Failed to enable USB\n");
|
|
return ret;
|
|
}
|
|
|
|
The class device requests are forwarded by the USB stack core driver to the
|
|
class driver through the registered class handler.
|
|
For the CDC ACM sample class driver, 'cdc_acm_class_handle_req' processes
|
|
the SET_LINE_CODING, CDC_SET_CONTROL_LINE_STATE and CDC_GET_LINE_CODING
|
|
class requests:
|
|
|
|
.. code-block:: c
|
|
|
|
int cdc_acm_class_handle_req(struct usb_setup_packet *pSetup,
|
|
s32_t *len, u8_t **data)
|
|
{
|
|
struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev);
|
|
|
|
switch (pSetup->bRequest) {
|
|
case CDC_SET_LINE_CODING:
|
|
memcpy(&dev_data->line_coding, *data, sizeof(dev_data->line_coding));
|
|
DBG("\nCDC_SET_LINE_CODING %d %d %d %d\n",
|
|
sys_le32_to_cpu(dev_data->line_coding.dwDTERate),
|
|
dev_data->line_coding.bCharFormat,
|
|
dev_data->line_coding.bParityType,
|
|
dev_data->line_coding.bDataBits);
|
|
break;
|
|
|
|
case CDC_SET_CONTROL_LINE_STATE:
|
|
dev_data->line_state = (u8_t)sys_le16_to_cpu(pSetup->wValue);
|
|
DBG("CDC_SET_CONTROL_LINE_STATE 0x%x\n", dev_data->line_state);
|
|
break;
|
|
|
|
case CDC_GET_LINE_CODING:
|
|
*data = (u8_t *)(&dev_data->line_coding);
|
|
*len = sizeof(dev_data->line_coding);
|
|
DBG("\nCDC_GET_LINE_CODING %d %d %d %d\n",
|
|
sys_le32_to_cpu(dev_data->line_coding.dwDTERate),
|
|
dev_data->line_coding.bCharFormat,
|
|
dev_data->line_coding.bParityType,
|
|
dev_data->line_coding.bDataBits);
|
|
break;
|
|
|
|
default:
|
|
DBG("CDC ACM request 0x%x, value 0x%x\n",
|
|
pSetup->bRequest, pSetup->wValue);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
The class driver should wait for the USB_DC_CONFIGURED device status code
|
|
before transmitting any data.
|
|
|
|
To transmit data to the host, the class driver should call usb_write().
|
|
Upon completion the registered endpoint callback will be called. Before
|
|
sending another packet the class driver should wait for the completion of
|
|
the previous transfer.
|
|
|
|
When data is received, the registered endpoint callback is called.
|
|
usb_read() should be used for retrieving the received data. It must
|
|
always be called through the registered endpoint callback. For CDC ACM
|
|
sample driver this happens via the OUT bulk endpoint handler (cdc_acm_bulk_out)
|
|
mentioned in the endpoint array (cdc_acm_ep_data).
|
|
|
|
Only CDC ACM and DFU class driver examples are provided for now.
|