USB device stack

USB Vendor and Product identifiers

The USB Vendor ID for the Zephyr project is 0x2FE3. The default USB Product ID for the Zephyr project is 0x100. The USB bcdDevice Device Release Number represents the Zephyr kernel major and minor versions as a binary coded decimal value. When a vendor integrates the Zephyr USB subsystem into a product, the vendor must use the USB Vendor and Product ID assigned to them. A vendor integrating the Zephyr USB subsystem in a product must not use the Vendor ID of the Zephyr project.

The USB maintainer, if one is assigned, or otherwise the Zephyr Technical Steering Committee, may allocate other USB Product IDs based on well-motivated and documented requests.

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.

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.

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 USB loopback application:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
struct usb_loopback_config {
	struct usb_if_descriptor if0;
	struct usb_ep_descriptor if0_out_ep;
	struct usb_ep_descriptor if0_in_ep;
} __packed;

USBD_CLASS_DESCR_DEFINE(primary, 0) struct usb_loopback_config loopback_cfg = {
	/* Interface descriptor 0 */
	.if0 = {
		.bLength = sizeof(struct usb_if_descriptor),
		.bDescriptorType = USB_INTERFACE_DESC,
		.bInterfaceNumber = 0,
		.bAlternateSetting = 0,
		.bNumEndpoints = 2,
		.bInterfaceClass = CUSTOM_CLASS,
		.bInterfaceSubClass = 0,
		.bInterfaceProtocol = 0,
		.iInterface = 0,
	},

	/* Data Endpoint OUT */
	.if0_out_ep = {
		.bLength = sizeof(struct usb_ep_descriptor),
		.bDescriptorType = USB_ENDPOINT_DESC,
		.bEndpointAddress = LOOPBACK_OUT_EP_ADDR,
		.bmAttributes = USB_DC_EP_BULK,
		.wMaxPacketSize = sys_cpu_to_le16(CONFIG_LOOPBACK_BULK_EP_MPS),
		.bInterval = 0x00,
	},

	/* Data Endpoint IN */
	.if0_in_ep = {
		.bLength = sizeof(struct usb_ep_descriptor),
		.bDescriptorType = USB_ENDPOINT_DESC,
		.bEndpointAddress = LOOPBACK_IN_EP_ADDR,
		.bmAttributes = USB_DC_EP_BULK,
		.wMaxPacketSize = sys_cpu_to_le16(CONFIG_LOOPBACK_BULK_EP_MPS),
		.bInterval = 0x00,
	},
};

Endpoint configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
static struct usb_ep_cfg_data ep_cfg[] = {
	{
		.ep_cb = loopback_out_cb,
		.ep_addr = LOOPBACK_OUT_EP_ADDR,
	},
	{
		.ep_cb = loopback_in_cb,
		.ep_addr = LOOPBACK_IN_EP_ADDR,
	},
};

USB Device configuration structure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
USBD_CFG_DATA_DEFINE(loopback) struct usb_cfg_data loopback_config = {
	.usb_device_description = NULL,
	.interface_config = loopback_interface_config,
	.interface_descriptor = &loopback_cfg.if0,
	.cb_usb_status = loopback_status_cb,
	.interface = {
		.class_handler = NULL,
		.custom_handler = NULL,
		.vendor_handler = loopback_vendor_handler,
		.vendor_data = loopback_buf,
		.payload_data = NULL,
	},
	.num_endpoints = ARRAY_SIZE(ep_cfg),
	.endpoint = ep_cfg,
};

For the Composite USB Device configuration is done by composite layer, otherwise:

1
2
3
4
5
	ret = usb_set_config(&loopback_config);
	if (ret < 0) {
		LOG_ERR("Failed to config USB");
		return ret;
	}

To enable the USB device for host/device connection:

1
2
3
4
5
	ret = usb_enable(&loopback_config);
	if (ret < 0) {
		LOG_ERR("Failed to enable USB");
		return ret;
	}

The vendor device requests are forwarded by the USB stack core driver to the class driver through the registered class handler.

For the loopback class driver, loopback_vendor_handler() processes the vendor requests:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static int loopback_vendor_handler(struct usb_setup_packet *setup,
				   s32_t *len, u8_t **data)
{
	LOG_DBG("Class request: bRequest 0x%x bmRequestType 0x%x len %d",
		setup->bRequest, setup->bmRequestType, *len);

	if (REQTYPE_GET_RECIP(setup->bmRequestType) != REQTYPE_RECIP_DEVICE) {
		return -ENOTSUP;
	}

	if (REQTYPE_GET_DIR(setup->bmRequestType) == REQTYPE_DIR_TO_DEVICE &&
	    setup->bRequest == 0x5b) {
		LOG_DBG("Host-to-Device, data %p", *data);
		return 0;
	}

	if ((REQTYPE_GET_DIR(setup->bmRequestType) == REQTYPE_DIR_TO_HOST) &&
	    (setup->bRequest == 0x5c)) {
		if (setup->wLength > sizeof(loopback_buf)) {
			return -ENOTSUP;
		}

		*data = loopback_buf;
		*len = setup->wLength;
		LOG_DBG("Device-to-Host, wLength %d, data %p",
			setup->wLength, *data);
		return 0;
	}

	return -ENOTSUP;
}

The class driver waits for the USB_DC_CONFIGURED device status code before transmitting any data.

Testing USB over USP/IP in native_posix

Virtual USB controller implemented through USB/IP might be used to test USB Device stack. Follow general build procedure to build USB sample for the native_posix configuration.

Run built sample with:

$ make run

In a terminal window, run the following command to list USB devices:

$ usbip list -r localhost
Exportable USB devices
======================
 - 127.0.0.1
        1-1: unknown vendor : unknown product (2fe3:0100)
           : /sys/devices/pci0000:00/0000:00:01.2/usb1/1-1
           : (Defined at Interface level) (00/00/00)
           :  0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/00)

In a terminal window, run the following command to attach USB device:

$ sudo usbip attach -r localhost -b 1-1

The USB device should be connected to your Linux host, and verified with the following commands:

$ sudo usbip port
Imported USB devices
====================
Port 00: <Port in Use> at Full Speed(12Mbps)
       unknown vendor : unknown product (2fe3:0100)
       7-1 -> usbip://localhost:3240/1-1
           -> remote bus/dev 001/002
$ lsusb -d 2fe3:0100
Bus 007 Device 004: ID 2fe3:0100

API Reference

There are two ways to transmit data, using the ‘low’ level read/write API or the ‘high’ level transfer API.

Low level API
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 write. When data is received, the registered endpoint callback is called. usb_read() should be used for retrieving the received data. 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).
High level API
The usb_transfer method can be used to transfer data to/from the host. The transfer API will automatically split the data transmission into one or more USB transaction(s), depending endpoint max packet size. The class driver does not have to implement endpoint callback and should set this callback to the generic usb_transfer_ep_callback.

USB Device Controller

group _usb_device_controller_api

USB Device Controller API.

Typedefs

typedef void (*usb_dc_ep_callback)(u8_t ep, enum usb_dc_ep_cb_status_code cb_status)

Callback function signature for the USB Endpoint status

typedef void (*usb_dc_status_callback)(enum usb_dc_status_code cb_status, const u8_t *param)

Callback function signature for the device

Enums

enum usb_dc_status_code

USB Driver Status Codes.

Status codes reported by the registered device status callback.

Values:

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_INTERFACE

USB interface selected

USB_DC_SET_HALT

Set Feature ENDPOINT_HALT received

USB_DC_CLEAR_HALT

Clear Feature ENDPOINT_HALT received

USB_DC_SOF

Start of Frame received

USB_DC_UNKNOWN

Initial USB connection status

enum usb_dc_ep_cb_status_code

USB Endpoint Callback Status Codes.

Status Codes reported by the registered endpoint callback.

Values:

USB_DC_EP_SETUP

SETUP received

USB_DC_EP_DATA_OUT

Out transaction on this EP, data is available for read

USB_DC_EP_DATA_IN

In transaction done on this EP

enum usb_dc_ep_type

USB Endpoint type.

Values:

USB_DC_EP_CONTROL = 0

Control type endpoint

USB_DC_EP_ISOCHRONOUS

Isochronous type endpoint

USB_DC_EP_BULK

Bulk type endpoint

USB_DC_EP_INTERRUPT

Interrupt type endpoint

Functions

int usb_dc_attach(void)

Attach USB for device connection.

Function to attach 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.

Return
0 on success, negative errno code on fail.

int usb_dc_detach(void)

Detach the USB device.

Function to detach the USB device. Upon success, the USB hardware PLL is powered down and USB communication is disabled.

Return
0 on success, negative errno code on fail.

int usb_dc_reset(void)

Reset the USB device.

This function returns the USB device and firmware back to it’s initial state. N.B. the USB PLL is handled by the usb_detach function

Return
0 on success, negative errno code on fail.

int usb_dc_set_address(const u8_t addr)

Set USB device address.

Return
0 on success, negative errno code on fail.
Parameters
  • addr: Device address

int usb_dc_set_status_callback(const usb_dc_status_callback cb)

Set USB device controller status callback.

Function to set 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.

Return
0 on success, negative errno code on fail.
Parameters
  • cb: Callback function

int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data *const cfg)

check endpoint capabilities

Function to check capabilities of an endpoint. usb_dc_ep_cfg_data structure provides the endpoint configuration parameters: endpoint address, endpoint maximum packet size and endpoint type. The driver should check endpoint capabilities and return 0 if the endpoint configuration is possible.

Return
0 on success, negative errno code on fail.
Parameters
  • cfg: Endpoint config

int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data *const cfg)

Configure endpoint.

Function to configure an endpoint. usb_dc_ep_cfg_data structure provides the endpoint configuration parameters: endpoint address, endpoint maximum packet size and endpoint type.

Return
0 on success, negative errno code on fail.
Parameters
  • cfg: Endpoint config

int usb_dc_ep_set_stall(const u8_t ep)

Set stall condition for the selected endpoint.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_dc_ep_clear_stall(const u8_t ep)

Clear stall condition for the selected endpoint.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_dc_ep_is_stalled(const u8_t ep, u8_t *const stalled)

Check if the selected endpoint is stalled.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • stalled: Endpoint stall status

int usb_dc_ep_halt(const u8_t ep)

Halt the selected endpoint.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_dc_ep_enable(const u8_t ep)

Enable the selected endpoint.

Function to enable the selected endpoint. Upon success interrupts are enabled for the corresponding endpoint and the endpoint is ready for transmitting/receiving data.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_dc_ep_disable(const u8_t ep)

Disable the selected endpoint.

Function to disable the selected endpoint. Upon success interrupts are disabled for the corresponding endpoint and the endpoint is no longer able for transmitting/receiving data.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_dc_ep_flush(const u8_t ep)

Flush the selected endpoint.

This function flushes the FIFOs for the selected endpoint.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_dc_ep_write(const u8_t ep, const u8_t *const data, const u32_t data_len, u32_t *const ret_bytes)

Write data to the specified endpoint.

This function is called to write data to the specified endpoint. The supplied usb_ep_callback function will be called when data is transmitted out.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • data: Pointer to data to write
  • data_len: Length of the data requested to write. This may be zero for a zero length status packet.
  • ret_bytes: Bytes scheduled for transmission. This value may be NULL if the application expects all bytes to be written

int usb_dc_ep_read(const u8_t ep, u8_t *const data, const u32_t max_data_len, u32_t *const read_bytes)

Read data from the specified endpoint.

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. This function clears the ENDPOINT NAK, if all data in the endpoint FIFO has been read, so as to accept more data from host.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • data: Pointer to data buffer to write to
  • max_data_len: Max length of data to read
  • read_bytes: Number of bytes read. If data is NULL and max_data_len is 0 the number of bytes available for read should be returned.

int usb_dc_ep_set_callback(const u8_t ep, const usb_dc_ep_callback cb)

Set callback function for the specified endpoint.

Function to set callback function for notification of data received and available to application or transmit done on the selected endpoint, NULL if callback not required by application code. The callback status code is described by usb_dc_ep_cb_status_code.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • cb: Callback function

int usb_dc_ep_read_wait(u8_t ep, u8_t *data, u32_t max_data_len, u32_t *read_bytes)

Read data from the specified endpoint.

This is similar to usb_dc_ep_read, the difference being that, it doesn’t clear the endpoint NAKs so that the consumer is not bogged down by further upcalls till he is done with the processing of the data. The caller should reactivate ep by invoking usb_dc_ep_read_continue() do so.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • data: Pointer to data buffer to write to
  • max_data_len: Max length of data to read
  • read_bytes: Number of bytes read. If data is NULL and max_data_len is 0 the number of bytes available for read should be returned.

int usb_dc_ep_read_continue(u8_t ep)

Continue reading data from the endpoint.

Clear the endpoint NAK and enable the endpoint to accept more data from the host. Usually called after usb_dc_ep_read_wait() when the consumer is fine to accept more data. Thus these calls together act as a flow control mechanism.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_dc_ep_mps(u8_t ep)

Get endpoint max packet size.

Return
Enpoint max packet size (mps)
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

struct usb_dc_ep_cfg_data
#include <usb_dc.h>

USB Endpoint Configuration.

Structure containing the USB endpoint configuration.

USB Device Core Layer

group _usb_device_core_api

USB Device Core Layer API.

Defines

USB_TRANS_READ
USB_TRANS_WRITE
USB_TRANS_NO_ZLP

Typedefs

typedef void (*usb_ep_callback)(u8_t ep, enum usb_dc_ep_cb_status_code cb_status)

Callback function signature for the USB Endpoint status.

typedef int (*usb_request_handler)(struct usb_setup_packet *setup, s32_t *transfer_len, u8_t **payload_data)

Callback function signature for class specific requests.

Function which handles Class specific requests corresponding to an interface number specified in the device descriptor table. 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.

typedef void (*usb_interface_config)(struct usb_desc_header *head, u8_t bInterfaceNumber)

Function for interface runtime configuration.

typedef void (*usb_transfer_callback)(u8_t ep, int tsize, void *priv)

Callback function signature for transfer completion.

Functions

int usb_set_config(struct usb_cfg_data *config)

Configure USB controller.

Function to configure USB controller. Configuration parameters must be valid or an error is returned

Return
0 on success, negative errno code on fail
Parameters
  • config: Pointer to configuration structure

int usb_deconfig(void)

Deconfigure USB controller.

This function returns the USB device to it’s initial state

Return
0 on success, negative errno code on fail

int usb_enable(struct usb_cfg_data *config)

Enable USB for host/device connection.

Function to 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.

Return
0 on success, negative errno code on fail.
Parameters
  • config: Pointer to configuration structure

int usb_disable(void)

Disable the USB device.

Function to disable the USB device. Upon success, the specified USB interface is clock gated in hardware, it is no longer capable of generating interrupts.

Return
0 on success, negative errno code on fail

int usb_write(u8_t ep, const u8_t *data, u32_t data_len, u32_t *bytes_ret)

Write data to the specified endpoint.

Function to write data to the specified endpoint. The supplied usb_ep_callback will be called when transmission is done.

Return
0 on success, negative errno code on fail
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • data: Pointer to data to write
  • data_len: Length of data requested to write. This may be zero for a zero length status packet.
  • bytes_ret: Bytes written to the EP FIFO. This value may be NULL if the application expects all bytes to be written

int usb_read(u8_t ep, u8_t *data, u32_t max_data_len, u32_t *ret_bytes)

Read data from the specified endpoint.

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.

Return
0 on success, negative errno code on fail
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • data: Pointer to data buffer to write to
  • max_data_len: Max length of data to read
  • ret_bytes: Number of bytes read. If data is NULL and max_data_len is 0 the number of bytes available for read is returned.

int usb_ep_set_stall(u8_t ep)

Set STALL condition on the specified endpoint.

This function is called by USB device class handler code to set stall condition on endpoint.

Return
0 on success, negative errno code on fail
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_ep_clear_stall(u8_t ep)

Clears STALL condition on the specified endpoint.

This function is called by USB device class handler code to clear stall condition on endpoint.

Return
0 on success, negative errno code on fail
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

int usb_ep_read_wait(u8_t ep, u8_t *data, u32_t max_data_len, u32_t *read_bytes)

Read data from the specified endpoint.

This is similar to usb_ep_read, the difference being that, it doesn’t clear the endpoint NAKs so that the consumer is not bogged down by further upcalls till he is done with the processing of the data. The caller should reactivate ep by invoking usb_ep_read_continue() do so.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • data: pointer to data buffer to write to
  • max_data_len: max length of data to read
  • read_bytes: Number of bytes read. If data is NULL and max_data_len is 0 the number of bytes available for read should be returned.

int usb_ep_read_continue(u8_t ep)

Continue reading data from the endpoint.

Clear the endpoint NAK and enable the endpoint to accept more data from the host. Usually called after usb_ep_read_wait() when the consumer is fine to accept more data. Thus these calls together acts as flow control mechanism.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

void usb_transfer_ep_callback(u8_t ep, enum usb_dc_ep_cb_status_code)

Transfer management endpoint callback.

If a USB class driver wants to use high-level transfer functions, driver needs to register this callback as usb endpoint callback.

int usb_transfer(u8_t ep, u8_t *data, size_t dlen, unsigned int flags, usb_transfer_callback cb, void *priv)

Start a transfer.

Start a usb transfer to/from the data buffer. This function is asynchronous and can be executed in IRQ context. The provided callback will be called on transfer completion (or error) in thread context.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • data: Pointer to data buffer to write-to/read-from
  • dlen: Size of data buffer
  • flags: Transfer flags (USB_TRANS_READ, USB_TRANS_WRITE…)
  • cb: Function called on transfer completion/failure
  • priv: Data passed back to the transfer completion callback

int usb_transfer_sync(u8_t ep, u8_t *data, size_t dlen, unsigned int flags)

Start a transfer and block-wait for completion.

Synchronous version of usb_transfer, wait for transfer completion before returning.

Return
number of bytes transferred on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table
  • data: Pointer to data buffer to write-to/read-from
  • dlen: Size of data buffer
  • flags: Transfer flags

void usb_cancel_transfer(u8_t ep)

Cancel any ongoing transfer on the specified endpoint.

Return
0 on success, negative errno code on fail.
Parameters
  • ep: Endpoint address corresponding to the one listed in the device configuration table

struct usb_ep_cfg_data
#include <usb_device.h>

USB Endpoint Configuration.

This structure contains configuration for the endpoint.

struct usb_interface_cfg_data
#include <usb_device.h>

USB Interface Configuration.

This structure contains USB interface configuration.

struct usb_cfg_data
#include <usb_device.h>

USB device configuration.

The Application instantiates this with given parameters added using the “usb_set_config” function. Once this function is called changes to this structure will result in undefined behavior. This structure may only be updated after calls to usb_deconfig