Skip to content

Commit 2820d7e

Browse files
committed
doc: connectivity: usb: add initial CDC ACM documentation
Add initial documentation for CDC ACM function provided by the new USB device stack. Signed-off-by: Johann Fischer <[email protected]>
1 parent 06daf0e commit 2820d7e

File tree

3 files changed

+168
-66
lines changed

3 files changed

+168
-66
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
.. _usbd_cdc_acm:
2+
3+
USB device CDC ACM
4+
##################
5+
6+
The CDC ACM function provided by the USB device stack only implements Abstract
7+
Control Model Serial Emulation. Its sole purpose is to emulate serial lines, as
8+
its name suggests. Most modern operating systems should provide support
9+
for it out of the box.
10+
11+
The CDC ACM function is represented as a serial interface on both the host and
12+
device sides, while the user or application interface is the :ref:`uart_api`
13+
driver API. This allows applications that already use the UART API to use the
14+
serial interface provided by the CDC ACM function without changing the code
15+
responsible for data communication. Only additional configuration and USB
16+
device stack initialization are required.
17+
18+
CDC ACM UART configuration
19+
==========================
20+
21+
Like the real UART controller, the virtual CDC ACM UART is described in the
22+
device tree. The devicetree compatible property for CDC ACM UART is
23+
:dtcompatible:`zephyr,cdc-acm-uart`.
24+
25+
CDC ACM support is automatically selected when USB device support is enabled
26+
and a compatible node in the devicetree sources is present. If necessary,
27+
CDC ACM support can be explicitly disabled by :kconfig:option:`CONFIG_USBD_CDC_ACM`.
28+
The number of possible CDC ACM instances depends on the number of supported
29+
endpoints on the USB device controller.
30+
The CDC ACM node can use the label property to distinguish different interfaces
31+
on the host side. Below is an example of the devicetree overlay file.
32+
33+
.. code-block:: devicetree
34+
35+
&zephyr_udc0 {
36+
cdc_acm_uart0: cdc_acm_uart0 {
37+
compatible = "zephyr,cdc-acm-uart";
38+
label = "CDC_ACM_0";
39+
};
40+
};
41+
42+
Before the application uses CDC ACM UART, it may want to wait for the DTR
43+
signal. Please refer to :zephyr:code-sample:`usb-cdc-acm` on how to implement
44+
this functionality.
45+
46+
.. note::
47+
To communicate with the host, beside the UART configuration, the application
48+
requires to enable USB device stack, for that please refer to
49+
:ref:`usb_device_next_howto_configure` and read carefully the next chapter. For
50+
users and applications migrating from the legacy stack, this is the only part
51+
they need to adapt.
52+
53+
.. _cdc_acm_uart_as_serial_backend:
54+
55+
CDC ACM UART as serial backend
56+
==============================
57+
58+
With the example from above and ``zephyr,console`` property of the chosen node,
59+
we can describe that CDC ACM UART is to be used with the console.
60+
61+
.. code-block:: devicetree
62+
63+
/ {
64+
chosen {
65+
zephyr,console = &cdc_acm_uart0;
66+
};
67+
};
68+
69+
&zephyr_udc0 {
70+
cdc_acm_uart0: cdc_acm_uart0 {
71+
compatible = "zephyr,cdc-acm-uart";
72+
label = "CDC_ACM_0";
73+
};
74+
};
75+
76+
In the same way that the console in the above example is configured to use the
77+
CDC ACM UART, the chosen property, ``zephyr,shell-uart``, can be used to
78+
configure the shell to use the CDC ACM UART as the serial backend. See
79+
sample :zephyr:code-sample:`shell-module` and :ref:`chosen nodes documentation
80+
<devicetree-chosen-nodes>`.
81+
82+
Since the use case as a serial backend is very common and no configuration is
83+
necessary at runtime for the CDC ACM UART, the stack offers a helper that
84+
performs the steps described in :ref:`usb_device_next_howto_configure`. The
85+
helper is enabled by the :kconfig:option:`CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT`
86+
and initializes the USB device stack with a single CDC ACM instance. Sample
87+
:zephyr:code-sample:`usb-cdc-acm-console` demonstrates how to use it.
88+
89+
:kconfig:option:`CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT` should also be used
90+
by the boards like :zephyr:board:`nrf52840dongle`, which do not have a debug
91+
adapter but a USB device controller, and wants to use CDC ACM UART as default
92+
serial backend for logging and shell.
93+
As the configuration would be identical for any board, there are common
94+
:zephyr_file:`devicetree file <boards/common/usb/cdc_acm_serial.dtsi>` and
95+
:zephyr_file:`Kconfig file <boards/common/usb/Kconfig.cdc_acm_serial.defconfig>`
96+
that must be included in the board's devicetree and Kconfig.defconfig files.
97+
98+
Using CDC ACM UART in the application
99+
=====================================
100+
101+
CDC ACM implements a virtual UART controller and provides Interrupt-driven UART
102+
API and Polling UART API. The ASYNC API is not supported yet. If the
103+
application wants to communicate over CDC ACM UART, the preferable way is to
104+
use Interrupt-driven UART API. It is essential to understand API documentation,
105+
nevertheless, some notes below.
106+
107+
Interrupt-driven UART API
108+
-------------------------
109+
110+
Internally the CDC ACM UART implementation uses two ringbuffers, these take over the
111+
function of the TX/RX FIFOs (TX/RX buffers) from the :ref:`uart_interrupt_api`.
112+
113+
As described in the :ref:`uart_interrupt_api`, the functions
114+
:c:func:`uart_irq_update()`, :c:func:`uart_irq_is_pending`,
115+
:c:func:`uart_irq_rx_ready()`, :c:func:`uart_irq_tx_ready()`
116+
:c:func:`uart_fifo_read()`, and :c:func:`uart_fifo_fill()`
117+
should be called from the interrupt handler, see
118+
:c:func:`uart_irq_callback_user_data_set()`. To prevent undefined behaviour,
119+
the implementation of these functions checks in what context they are called
120+
and fails if it is not an interrupt handler.
121+
122+
Also, as described in the UART API, :c:func:`uart_irq_is_pending`
123+
:c:func:`uart_irq_rx_ready()`, and :c:func:`uart_irq_tx_ready()`
124+
can only be called after :c:func:`uart_irq_update()`.
125+
126+
Simplified, application interrupt handler should look something like:
127+
128+
.. code-block:: c
129+
130+
static void interrupt_handler(const struct device *dev, void *user_data)
131+
{
132+
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
133+
if (uart_irq_rx_ready(dev)) {
134+
int len;
135+
int n;
136+
137+
/* ... */
138+
n = uart_fifo_read(dev, buffer, len);
139+
/* ... */
140+
}
141+
142+
if (uart_irq_tx_ready(dev)) {
143+
int len;
144+
int n;
145+
146+
/* ... */
147+
n = uart_fifo_fill(dev, buffer, len);
148+
/* ... */
149+
}
150+
}
151+
152+
All these functions are not directly dependent on the status of the USB device.
153+
Filling the TX FIFO does not mean that data is being sent to the host. And
154+
successfully reading the RX FIFO does not mean that the device is still
155+
connected to the host. If there is space in the TX FIFO, and the TX interrupt
156+
is enabled, :c:func:`uart_irq_tx_ready()` will succeed. If there is data in the
157+
RX FIFO, and the RX interrupt is enabled, :c:func:`uart_irq_rx_ready()` will
158+
succeed. Function :c:func:`uart_irq_tx_complete()` is not implemented yet.
159+
160+
Polling UART API
161+
----------------
162+
163+
The CDC ACM poll out implementation follows :ref:`uart_polling_api` and
164+
blocks when the TX FIFO is full only if the hw-flow-control property is enabled
165+
and called from a non-ISR context.

doc/connectivity/usb/device_next/usb_device.rst

Lines changed: 2 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ Samples
4646
``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file
4747
``-DDTC_OVERLAY_FILE="usbd_next_ecm.overlay`` either directly or via ``west``.
4848

49+
.. _usb_device_next_howto_configure:
50+
4951
How to configure and enable USB device support
5052
**********************************************
5153

@@ -217,69 +219,3 @@ instance (``n``) and is used as an argument to the :c:func:`usbd_register_class`
217219
+-----------------------------------+-------------------------+-------------------------+
218220
| USB Video Class (UVC) | Video device | :samp:`uvc_{n}` |
219221
+-----------------------------------+-------------------------+-------------------------+
220-
221-
CDC ACM UART
222-
============
223-
224-
CDC ACM implements a virtual UART controller and provides Interrupt-driven UART
225-
API and Polling UART API.
226-
227-
Interrupt-driven UART API
228-
-------------------------
229-
230-
Internally the implementation uses two ringbuffers, these take over the
231-
function of the TX/RX FIFOs (TX/RX buffers) from the :ref:`uart_interrupt_api`.
232-
233-
As described in the :ref:`uart_interrupt_api`, the functions
234-
:c:func:`uart_irq_update()`, :c:func:`uart_irq_is_pending`,
235-
:c:func:`uart_irq_rx_ready()`, :c:func:`uart_irq_tx_ready()`
236-
:c:func:`uart_fifo_read()`, and :c:func:`uart_fifo_fill()`
237-
should be called from the interrupt handler, see
238-
:c:func:`uart_irq_callback_user_data_set()`. To prevent undefined behaviour,
239-
the implementation of these functions checks in what context they are called
240-
and fails if it is not an interrupt handler.
241-
242-
Also, as described in the UART API, :c:func:`uart_irq_is_pending`
243-
:c:func:`uart_irq_rx_ready()`, and :c:func:`uart_irq_tx_ready()`
244-
can only be called after :c:func:`uart_irq_update()`.
245-
246-
Simplified, the interrupt handler should look something like:
247-
248-
.. code-block:: c
249-
250-
static void interrupt_handler(const struct device *dev, void *user_data)
251-
{
252-
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
253-
if (uart_irq_rx_ready(dev)) {
254-
int len;
255-
int n;
256-
257-
/* ... */
258-
n = uart_fifo_read(dev, buffer, len);
259-
/* ... */
260-
}
261-
262-
if (uart_irq_tx_ready(dev)) {
263-
int len;
264-
int n;
265-
266-
/* ... */
267-
n = uart_fifo_fill(dev, buffer, len);
268-
/* ... */
269-
}
270-
}
271-
272-
All these functions are not directly dependent on the status of the USB device.
273-
Filling the TX FIFO does not mean that data is being sent to the host. And
274-
successfully reading the RX FIFO does not mean that the device is still
275-
connected to the host. If there is space in the TX FIFO, and the TX interrupt
276-
is enabled, :c:func:`uart_irq_tx_ready()` will succeed. If there is data in the
277-
RX FIFO, and the RX interrupt is enabled, :c:func:`uart_irq_rx_ready()` will
278-
succeed. Function :c:func:`uart_irq_tx_complete()` is not implemented yet.
279-
280-
Polling UART API
281-
----------------
282-
283-
The CDC ACM poll out implementation follows :ref:`uart_polling_api` and
284-
blocks when the TX FIFO is full only if the hw-flow-control property is enabled
285-
and called from a non-ISR context.

doc/connectivity/usb/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ USB
1717
:maxdepth: 1
1818

1919
device_next/usb_device.rst
20+
device_next/cdc_acm.rst
2021
device_next/api/index.rst
2122
host/api/index.rst
2223
host/usbip.rst

0 commit comments

Comments
 (0)