|
| 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. |
0 commit comments