Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
ffb7b90
[nrf fromtree] drivers: udc_dwc2: Suspend if connected to suspended bus
tmon-nordic Sep 27, 2024
765bc6c
[nrf fromtree] drivers: udc_dwc2: Change hibernation exit on bus reset
tmon-nordic Sep 30, 2024
0efe617
[nrf fromtree] uart: update meaning of `uart_irq_tx_ready` return code
JordanYates Apr 1, 2024
a859b30
[nrf fromtree] serial/usb: update `struct ring_buf` users
JordanYates Apr 1, 2024
040b827
[nrf fromtree] serial: async_to_irq: update `tx_ready` return
JordanYates Apr 1, 2024
88dce91
[nrf fromtree] serial: uart_nrfx_*: update `tx_ready` return codes
JordanYates Apr 1, 2024
c3c8636
[nrf fromtree] doc: usb: move "Built-in functions" down and increase…
jfischer-no Jul 4, 2024
9f211a5
[nrf fromtree] usb: device_next: align CDC ACM UART with Interrupt-dr…
jfischer-no Jul 4, 2024
1c0d9f4
[nrf fromtree] doc: usb: describe CDC ACM virtual UART IRQ API behavior
jfischer-no Jul 4, 2024
ed59c81
[nrf fromtree] usb: device_next: align CDC ACM UART poll_out with leg…
jfischer-no Jul 5, 2024
55dd426
[nrf fromtree] usb: device_next: allow fifo_fill and poll_out be used…
jfischer-no Jul 5, 2024
7cf05e5
[nrf fromtree] doc: usb: add note about CDC ACM virtual UART polling …
jfischer-no Jul 5, 2024
d58cd12
[nrf fromtree] usb: device_next: limit CDC ACM OUT transfer size to t…
jfischer-no Sep 9, 2024
4ff2c67
[nrf fromtree] usb: device_next: use delayable work for TX FIFO in CD…
jfischer-no Sep 9, 2024
9a4914b
[nrf fromtree] usb: device_next: USB reset clears remote wakeup permi…
benedekkupper Oct 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 91 additions & 25 deletions doc/connectivity/usb/device_next/usb_device.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,6 @@ 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`.

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}` |
+-----------------------------------+-------------------------+-------------------------+

Samples
=======

Expand Down Expand Up @@ -223,3 +198,94 @@ for this capability.
: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.
4 changes: 4 additions & 0 deletions doc/releases/migration-guide-4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ Sensors
Serial
======

* Users of :c:func:`uart_irq_tx_ready` now need to check for ``ret > 0`` to ensure that the FIFO
can accept data bytes, instead of ``ret == 1``. The function now returns a lower bound on the
number of bytes that can be provided to :c:func:`uart_fifo_fill` without truncation.

Regulator
=========

Expand Down
6 changes: 3 additions & 3 deletions drivers/serial/serial_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ static void irq_tx_disable(const struct device *dev)
static int irq_tx_ready(const struct device *dev)
{
struct serial_vnd_data *data = dev->data;
bool ready = (ring_buf_space_get(data->written) != 0);
int available = ring_buf_space_get(data->written);

LOG_DBG("tx ready: %d", ready);
return ready;
LOG_DBG("tx ready: %d", available);
return available;
}

static void irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
Expand Down
4 changes: 3 additions & 1 deletion drivers/serial/uart_async_to_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,10 @@ void z_uart_async_to_irq_irq_tx_disable(const struct device *dev)
int z_uart_async_to_irq_irq_tx_ready(const struct device *dev)
{
struct uart_async_to_irq_data *data = get_data(dev);
bool ready = (data->flags & A2I_TX_IRQ_ENABLED) && !(data->flags & A2I_TX_BUSY);

return (data->flags & A2I_TX_IRQ_ENABLED) && !(data->flags & A2I_TX_BUSY);
/* async API handles arbitrary sizes */
return ready ? data->tx.len : 0;
}

/** Interrupt driven receiver enabling function */
Expand Down
6 changes: 3 additions & 3 deletions drivers/serial/uart_emul.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,18 +231,18 @@ static int uart_emul_fifo_read(const struct device *dev, uint8_t *rx_data, int s

static int uart_emul_irq_tx_ready(const struct device *dev)
{
bool ready = false;
int available = 0;
struct uart_emul_data *data = dev->data;

K_SPINLOCK(&data->tx_lock) {
if (!data->tx_irq_en) {
K_SPINLOCK_BREAK;
}

ready = ring_buf_space_get(data->tx_rb) > 0;
available = ring_buf_space_get(data->tx_rb);
}

return ready;
return available;
}

static int uart_emul_irq_rx_ready(const struct device *dev)
Expand Down
9 changes: 5 additions & 4 deletions drivers/serial/uart_nrfx_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -878,10 +878,11 @@
* called after the TX interrupt is requested to be disabled but before
* the disabling is actually performed (in the IRQ handler).
*/
return nrf_uart_int_enable_check(uart0_addr,
NRF_UART_INT_MASK_TXDRDY) &&
!disable_tx_irq &&
event_txdrdy_check();
bool ready = nrf_uart_int_enable_check(uart0_addr,
NRF_UART_INT_MASK_TXDRDY) &&
!disable_tx_irq &&
event_txdrdy_check();
return ready ? 1 : 0;

Check notice on line 885 in drivers/serial/uart_nrfx_uart.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/serial/uart_nrfx_uart.c:885 - bool ready = nrf_uart_int_enable_check(uart0_addr, - NRF_UART_INT_MASK_TXDRDY) && - !disable_tx_irq && - event_txdrdy_check(); + bool ready = nrf_uart_int_enable_check(uart0_addr, NRF_UART_INT_MASK_TXDRDY) && + !disable_tx_irq && event_txdrdy_check();
}

/** Interrupt driven receiver ready function */
Expand Down
2 changes: 1 addition & 1 deletion drivers/serial/uart_nrfx_uarte.c
Original file line number Diff line number Diff line change
Expand Up @@ -1646,7 +1646,7 @@ static int uarte_nrfx_irq_tx_ready_complete(const struct device *dev)
data->int_driven->fifo_fill_lock = 0;
}

return ready;
return ready ? data->int_driven->tx_buff_size : 0;
}

static int uarte_nrfx_irq_rx_ready(const struct device *dev)
Expand Down
24 changes: 13 additions & 11 deletions drivers/usb/udc/udc_dwc2.c
Original file line number Diff line number Diff line change
Expand Up @@ -904,8 +904,8 @@
}

static void dwc2_restore_essential_registers(const struct device *dev,
bool rwup)
bool rwup, bool bus_reset)
{

Check notice on line 908 in drivers/usb/udc/udc_dwc2.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/usb/udc/udc_dwc2.c:908 -static void dwc2_restore_essential_registers(const struct device *dev, - bool rwup, bool bus_reset) +static void dwc2_restore_essential_registers(const struct device *dev, bool rwup, bool bus_reset)
const struct udc_dwc2_config *const config = dev->config;
struct usb_dwc2_reg *const base = config->base;
struct udc_dwc2_data *const priv = udc_get_private(dev);
Expand All @@ -925,6 +925,10 @@
sys_write32(backup->gusbcfg, (mem_addr_t)&base->gusbcfg);
sys_write32(backup->dcfg, (mem_addr_t)&base->dcfg);

if (bus_reset) {
sys_write32(backup->dcfg, (mem_addr_t)&base->dcfg);
}

if (!rwup) {
pcgcctl |= USB_DWC2_PCGCCTL_RESTOREMODE | USB_DWC2_PCGCCTL_RSTPDWNMODULE;
}
Expand Down Expand Up @@ -1026,8 +1030,9 @@
LOG_DBG("Hibernated");
}

static void dwc2_exit_hibernation(const struct device *dev, bool rwup)
static void dwc2_exit_hibernation(const struct device *dev,
bool rwup, bool bus_reset)
{

Check notice on line 1035 in drivers/usb/udc/udc_dwc2.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/usb/udc/udc_dwc2.c:1035 -static void dwc2_exit_hibernation(const struct device *dev, - bool rwup, bool bus_reset) +static void dwc2_exit_hibernation(const struct device *dev, bool rwup, bool bus_reset)
const struct udc_dwc2_config *const config = dev->config;
struct usb_dwc2_reg *const base = config->base;
struct udc_dwc2_data *const priv = udc_get_private(dev);
Expand Down Expand Up @@ -1066,13 +1071,15 @@
/* Disable PMU interrupt */
sys_clear_bits(gpwrdn_reg, USB_DWC2_GPWRDN_PMUINTSEL);

dwc2_restore_essential_registers(dev, rwup);
dwc2_restore_essential_registers(dev, rwup, bus_reset);

/* Note: in Remote Wakeup case 15 ms max signaling time starts now */

/* Wait for Restore Done Interrupt */
dwc2_wait_for_bit(dev, (mem_addr_t)&base->gintsts, USB_DWC2_GINTSTS_RSTRDONEINT);
sys_write32(0xFFFFFFFFUL, (mem_addr_t)&base->gintsts);
if (!bus_reset) {
sys_write32(0xFFFFFFFFUL, (mem_addr_t)&base->gintsts);
}

/* Disable restore from PMU */
sys_clear_bits(gpwrdn_reg, USB_DWC2_GPWRDN_RESTORE);
Expand Down Expand Up @@ -2027,7 +2034,7 @@
config->irq_disable_func(dev);

if (priv->hibernated) {
dwc2_exit_hibernation(dev, false);
dwc2_exit_hibernation(dev, false, true);
priv->hibernated = 0;
}

Expand Down Expand Up @@ -2727,11 +2734,6 @@
/* Clear USB Suspend interrupt. */
sys_write32(USB_DWC2_GINTSTS_USBSUSP, gintsts_reg);

if (!priv->enumdone) {
/* Ignore stale suspend interrupt */
continue;
}

/* Notify the stack */
udc_set_suspended(dev, true);
udc_submit_event(dev, UDC_EVT_SUSPEND, 0);
Expand All @@ -2749,7 +2751,7 @@
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
struct udc_dwc2_data *const priv = udc_get_private(dev);

dwc2_exit_hibernation(dev, rwup);
dwc2_exit_hibernation(dev, rwup, bus_reset);
dwc2_restore_device_registers(dev, rwup);

priv->hibernated = 0;
Expand Down
10 changes: 6 additions & 4 deletions include/zephyr/drivers/uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -902,9 +902,9 @@ static inline void z_impl_uart_irq_tx_disable(const struct device *dev)
}

/**
* @brief Check if UART TX buffer can accept a new char
* @brief Check if UART TX buffer can accept bytes
*
* @details Check if UART TX buffer can accept at least one character
* @details Check if UART TX buffer can accept more bytes
* for transmission (i.e. uart_fifo_fill() will succeed and return
* non-zero). This function must be called in a UART interrupt
* handler, or its result is undefined. Before calling this function
Expand All @@ -913,9 +913,11 @@ static inline void z_impl_uart_irq_tx_disable(const struct device *dev)
*
* @param dev UART device instance.
*
* @retval 1 If TX interrupt is enabled and at least one char can be
* written to UART.
* @retval 0 If device is not ready to write a new byte.
* @retval >0 Minimum number of bytes that can be written in a single call to
* @ref uart_fifo_fill. It may be possible to write more bytes, but
* the actual number written must be checked in the return code from
* @ref uart_fifo_fill.
* @retval -ENOSYS If this function is not implemented.
* @retval -ENOTSUP If API is not enabled.
*/
Expand Down
2 changes: 1 addition & 1 deletion subsys/usb/device/class/cdc_acm.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ static int cdc_acm_irq_tx_ready(const struct device *dev)
struct cdc_acm_dev_data_t * const dev_data = dev->data;

if (dev_data->tx_irq_ena && dev_data->tx_ready) {
return 1;
return ring_buf_space_get(dev_data->tx_ringbuf);
}

return 0;
Expand Down
Loading
Loading