292 lines
12 KiB
ReStructuredText
292 lines
12 KiB
ReStructuredText
.. _usb_device_stack_next:
|
|
|
|
New USB device support
|
|
######################
|
|
|
|
Overview
|
|
********
|
|
|
|
USB device support consists of the USB device controller (UDC) drivers
|
|
, :ref:`udc_api`, and USB device stack, :ref:`usbd_api`.
|
|
The :ref:`udc_api` provides a generic and vendor independent interface to USB
|
|
device controllers, and although, there a is clear separation between these
|
|
layers, the purpose of :ref:`udc_api` is to serve new Zephyr's USB device stack
|
|
exclusively.
|
|
|
|
The new device stack supports multiple device controllers, meaning that if a
|
|
SoC has multiple controllers, they can be used simultaneously. Full and
|
|
high-speed device controllers are supported. It also provides support for
|
|
registering multiple function or class instances to a configuration at runtime,
|
|
or changing the configuration later. It has built-in support for several USB
|
|
classes and provides an API to implement custom USB functions.
|
|
|
|
The new USB device support is considered experimental and will replace
|
|
:ref:`usb_device_stack`.
|
|
|
|
Samples
|
|
=======
|
|
|
|
* :zephyr:code-sample:`usb-hid-keyboard`
|
|
|
|
* :zephyr:code-sample:`uac2-explicit-feedback`
|
|
|
|
* :zephyr:code-sample:`uac2-implicit-feedback`
|
|
|
|
Samples ported to new USB device support
|
|
----------------------------------------
|
|
|
|
To build a sample that supports both the old and new USB device stack, set the
|
|
configuration ``-DCONF_FILE=usbd_next_prj.conf`` either directly or via
|
|
``west``.
|
|
|
|
* :zephyr:code-sample:`bluetooth_hci_usb`
|
|
|
|
* :zephyr:code-sample:`usb-cdc-acm`
|
|
|
|
* :zephyr:code-sample:`usb-cdc-acm-console`
|
|
|
|
* :zephyr:code-sample:`usb-mass`
|
|
|
|
* :zephyr:code-sample:`usb-hid-mouse`
|
|
|
|
* :zephyr:code-sample:`zperf` To build the sample for the new device support,
|
|
set the configuration overlay file
|
|
``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file
|
|
``-DDTC_OVERLAY_FILE="usbd_next_ecm.overlay`` either directly or via ``west``.
|
|
|
|
How to configure and enable USB device support
|
|
**********************************************
|
|
|
|
For the USB device support samples in the Zephyr project repository, we have a
|
|
common file for instantiation, configuration and initialization,
|
|
:zephyr_file:`samples/subsys/usb/common/sample_usbd_init.c`. The following code
|
|
snippets from this file are used as examples. USB Samples Kconfig options used
|
|
in the USB samples and prefixed with ``SAMPLE_USBD_`` have default values
|
|
specific to the Zephyr project and the scope is limited to the project samples.
|
|
In the examples below, you will need to replace these Kconfig options and other
|
|
defaults with values appropriate for your application or hardware.
|
|
|
|
The USB device stack requires a context structure to manage its properties and
|
|
runtime data. The preferred way to define a device context is to use the
|
|
:c:macro:`USBD_DEVICE_DEFINE` macro. This creates a static
|
|
:c:struct:`usbd_context` variable with a given name. Any number of contexts may
|
|
be instantiated. A USB controller device can be assigned to multiple contexts,
|
|
but only one context can be initialized and used at a time. Context properties
|
|
must not be directly accessed or manipulated by the application.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc device instantiation start
|
|
:end-before: doc device instantiation end
|
|
|
|
Your USB device may have manufacturer, product, and serial number string
|
|
descriptors. To instantiate these string descriptors, the application should
|
|
use the appropriate :c:macro:`USBD_DESC_MANUFACTURER_DEFINE`,
|
|
:c:macro:`USBD_DESC_PRODUCT_DEFINE`, and
|
|
:c:macro:`USBD_DESC_SERIAL_NUMBER_DEFINE` macros. String descriptors also
|
|
require a single instantiation of the language descriptor using the
|
|
:c:macro:`USBD_DESC_LANG_DEFINE` macro.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc string instantiation start
|
|
:end-before: doc string instantiation end
|
|
|
|
String descriptors must be added to the device context at runtime before
|
|
initializing the USB device with :c:func:`usbd_add_descriptor`.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc add string descriptor start
|
|
:end-before: doc add string descriptor end
|
|
|
|
USB device requires at least one configuration instance per supported speed.
|
|
The application should use :c:macro:`USBD_CONFIGURATION_DEFINE` to instantiate
|
|
a configuration. Later, USB device functions are assigned to a configuration.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc configuration instantiation start
|
|
:end-before: doc configuration instantiation end
|
|
|
|
Each configuration instance for a specific speed must be added to the device
|
|
context at runtime before the USB device is initialized using
|
|
:c:func:`usbd_add_configuration`. Note :c:enumerator:`USBD_SPEED_FS` and
|
|
:c:enumerator:`USBD_SPEED_HS`. The first full-speed or high-speed
|
|
configuration will get ``bConfigurationValue`` one, and then further upward.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc configuration register start
|
|
:end-before: doc configuration register end
|
|
|
|
|
|
Although we have already done a lot, this USB device has no function. A device
|
|
can have multiple configurations with different set of functions at different
|
|
speeds. A function or class can be registered on a USB device before
|
|
it is initialized using :c:func:`usbd_register_class`. The desired
|
|
configuration is specified using :c:enumerator:`USBD_SPEED_FS` or
|
|
:c:enumerator:`USBD_SPEED_HS` and the configuration number. For simple cases,
|
|
:c:func:`usbd_register_all_classes` can be used to register all available
|
|
instances.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc functions register start
|
|
:end-before: doc functions register end
|
|
|
|
The last step in the preparation is to initialize the device with
|
|
:c:func:`usbd_init`. After this, the configuration of the device cannot be
|
|
changed. A device can be deinitialized with :c:func:`usbd_shutdown` and all
|
|
instances can be reused, but the previous steps must be repeated. So it is
|
|
possible to shutdown a device, register another type of configuration or
|
|
function, and initialize it again. At the USB controller level,
|
|
:c:func:`usbd_init` does only what is necessary to detect VBUS changes. There
|
|
are controller types where the next step is only possible if a VBUS signal is
|
|
present.
|
|
|
|
A function or class implementation may require its own specific configuration
|
|
steps, which should be performed prior to initializing the USB device.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc device init start
|
|
:end-before: doc device init end
|
|
|
|
The final step to enable the USB device is :c:func:`usbd_enable`, after that,
|
|
if the USB device is connected to a USB host controller, the host can start
|
|
enumerating the device. The application can disable the USB device using
|
|
:c:func:`usbd_disable`.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/hid-keyboard/src/main.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc device enable start
|
|
:end-before: doc device enable end
|
|
|
|
USB Message notifications
|
|
=========================
|
|
|
|
The application can register a callback using :c:func:`usbd_msg_register_cb` to
|
|
receive message notification from the USB device support subsystem. The
|
|
messages are mostly about the common device state changes, and a few specific
|
|
types from the USB CDC ACM implementation.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc device init-and-msg start
|
|
:end-before: doc device init-and-msg end
|
|
|
|
The helper function :c:func:`usbd_msg_type_string()` can be used to convert
|
|
:c:enumerator:`usbd_msg_type` to a human readable form for logging.
|
|
|
|
If the controller supports VBUS state change detection, the battery-powered
|
|
application may want to enable the USB device only when it is connected to a
|
|
host. A generic application should use :c:func:`usbd_can_detect_vbus` to check
|
|
for this capability.
|
|
|
|
.. literalinclude:: ../../../../samples/subsys/usb/hid-keyboard/src/main.c
|
|
:language: c
|
|
:dedent:
|
|
:start-after: doc device msg-cb start
|
|
:end-before: doc device msg-cb end
|
|
|
|
Built-in functions
|
|
******************
|
|
|
|
The USB device stack has built-in USB functions. Some can be used directly in
|
|
the user application through a special API, such as HID or Audio class devices,
|
|
while others use a general Zephyr RTOS driver API, such as MSC and CDC class
|
|
implementations. The *Identification string* identifies a class or function
|
|
instance (``n``) and is used as an argument to the :c:func:`usbd_register_class`.
|
|
|
|
+-----------------------------------+-------------------------+-------------------------+
|
|
| Class or function | User API (if any) | Identification string |
|
|
+===================================+=========================+=========================+
|
|
| USB Audio 2 class | :ref:`uac2_device` | :samp:`uac2_{n}` |
|
|
+-----------------------------------+-------------------------+-------------------------+
|
|
| USB CDC ACM class | :ref:`uart_api` | :samp:`cdc_acm_{n}` |
|
|
+-----------------------------------+-------------------------+-------------------------+
|
|
| USB CDC ECM class | Ethernet device | :samp:`cdc_ecm_{n}` |
|
|
+-----------------------------------+-------------------------+-------------------------+
|
|
| USB Mass Storage Class (MSC) | :ref:`usbd_msc_device` | :samp:`msc_{n}` |
|
|
+-----------------------------------+-------------------------+-------------------------+
|
|
| USB Human Interface Devices (HID) | :ref:`usbd_hid_device` | :samp:`hid_{n}` |
|
|
+-----------------------------------+-------------------------+-------------------------+
|
|
| Bluetooth HCI USB transport layer | :ref:`bt_hci_raw` | :samp:`bt_hci_{n}` |
|
|
+-----------------------------------+-------------------------+-------------------------+
|
|
|
|
CDC ACM UART
|
|
============
|
|
|
|
CDC ACM implements a virtual UART controller and provides Interrupt-driven UART
|
|
API and Polling UART API.
|
|
|
|
Interrupt-driven UART API
|
|
-------------------------
|
|
|
|
Internally the implementation uses two ringbuffers, these take over the
|
|
function of the TX/RX FIFOs (TX/RX buffers) from the :ref:`uart_interrupt_api`.
|
|
|
|
As described in the :ref:`uart_interrupt_api`, the functions
|
|
:c:func:`uart_irq_update()`, :c:func:`uart_irq_is_pending`,
|
|
:c:func:`uart_irq_rx_ready()`, :c:func:`uart_irq_tx_ready()`
|
|
:c:func:`uart_fifo_read()`, and :c:func:`uart_fifo_fill()`
|
|
should be called from the interrupt handler, see
|
|
:c:func:`uart_irq_callback_user_data_set()`. To prevent undefined behaviour,
|
|
the implementation of these functions checks in what context they are called
|
|
and fails if it is not an interrupt handler.
|
|
|
|
Also, as described in the UART API, :c:func:`uart_irq_is_pending`
|
|
:c:func:`uart_irq_rx_ready()`, and :c:func:`uart_irq_tx_ready()`
|
|
can only be called after :c:func:`uart_irq_update()`.
|
|
|
|
Simplified, the interrupt handler should look something like:
|
|
|
|
.. code-block:: c
|
|
|
|
static void interrupt_handler(const struct device *dev, void *user_data)
|
|
{
|
|
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
|
|
if (uart_irq_rx_ready(dev)) {
|
|
int len;
|
|
int n;
|
|
|
|
/* ... */
|
|
n = uart_fifo_read(dev, buffer, len);
|
|
/* ... */
|
|
}
|
|
|
|
if (uart_irq_tx_ready(dev)) {
|
|
int len;
|
|
int n;
|
|
|
|
/* ... */
|
|
n = uart_fifo_fill(dev, buffer, len);
|
|
/* ... */
|
|
}
|
|
}
|
|
|
|
All these functions are not directly dependent on the status of the USB device.
|
|
Filling the TX FIFO does not mean that data is being sent to the host. And
|
|
successfully reading the RX FIFO does not mean that the device is still
|
|
connected to the host. If there is space in the TX FIFO, and the TX interrupt
|
|
is enabled, :c:func:`uart_irq_tx_ready()` will succeed. If there is data in the
|
|
RX FIFO, and the RX interrupt is enabled, :c:func:`uart_irq_rx_ready()` will
|
|
succeed. Function :c:func:`uart_irq_tx_complete()` is not implemented yet.
|
|
|
|
Polling UART API
|
|
----------------
|
|
|
|
The CDC ACM poll out implementation follows :ref:`uart_polling_api` and
|
|
blocks when the TX FIFO is full only if the hw-flow-control property is enabled
|
|
and called from a non-ISR context.
|