Skip to content

Commit 6cb02b2

Browse files
alstrzebonskirlubos
authored andcommitted
applications: nrf_desktop: Remove extra USB thread on nRF54H
Removes the extra USB thread on nRF54H as it's no longer needed. Sets the CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT timeout to allow the usbd_enable() function to return error if the USB cable is not connected. Jira: NCSDK-28000 Jira: NCSDK-28381 Signed-off-by: Aleksander Strzebonski <[email protected]>
1 parent 696f67a commit 6cb02b2

File tree

3 files changed

+58
-165
lines changed

3 files changed

+58
-165
lines changed

applications/nrf_desktop/doc/usb_state.rst

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,15 @@ The module sends the report using :c:struct:`hid_report_event`, that is handled
236236
nRF54H20 support
237237
================
238238

239-
Due to the characteristics of the nRF54H20 USB Device Controller (UDC), several changes have been made in the USB state module to support the nRF54H20 platform:
239+
Due to the characteristics of the nRF54H20 USB Device Controller (UDC), following change has been made in the USB state module to support the nRF54H20 platform:
240240

241-
* The USB state module creates a separate thread to initialize, enable, and disable the USB stack.
242241
* The module disables the USB stack when the USB cable is disconnected and enables the stack when the cable is connected.
243242

244-
These changes are applicable to the nRF54H20 platform only.
245-
They are necessary to ensure proper USB stack operation on the nRF54H20 platform.
243+
This change is applicable to the nRF54H20 platform only.
244+
It is necessary to ensure proper USB stack operation on the nRF54H20 platform.
246245

247-
The USB stack cannot be initialized from the system workqueue thread, because it causes a deadlock.
248-
Because of that, a separate thread is used to initialize the USB stack.
249-
For more details, see the :ref:`CONFIG_DESKTOP_USB_INIT_THREAD <config_desktop_app_options>` Kconfig option.
246+
The :kconfig:option:`CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT` Kconfig option is set to a non-zero value to prevent the :c:func:`usbd_enable` function from blocking the application forever when the USB cable is not connected.
247+
Instead, the function returns an error on timeout.
250248
The UDC is powered down whenever the USB cable is disconnected, failing to trigger the necessary callbacks to the USB stack.
251249
It may cause the USB stack to become non-functional.
252250
The USB stack is disabled upon disconnecting the cable to work around this issue.

applications/nrf_desktop/src/modules/Kconfig.usb_state

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -57,37 +57,6 @@ config DESKTOP_USB_HID_REPORT_SENT_ON_SOF
5757
report pipeline with two sequential reports is required to ensure that
5858
the USB peripheral can provide a HID report on every USB poll.
5959

60-
config DESKTOP_USB_INIT_THREAD
61-
bool
62-
default y if SOC_SERIES_NRF54HX
63-
help
64-
Initialize USB stack in a separate thread instead of doing it in
65-
the context of the system workqueue.
66-
For the nRF54HX SoC use-case, the usbd_init() function blocks while
67-
waiting for a synchronization object that is received after the system
68-
controller sends the USB service event through the NRFS and IPC with
69-
the ICMSG backend to the application core.
70-
The ICMSG backend uses the system workqueue context for both
71-
initialization and on the incoming message handling, so calling the
72-
usbd_init() function from the same context causes deadlock during
73-
application boot.
74-
75-
config DESKTOP_USB_INIT_THREAD_STACK_SIZE
76-
int
77-
default 2048
78-
79-
config DESKTOP_USB_INIT_THREAD_PRIORITY
80-
int
81-
default -1
82-
83-
config DESKTOP_USB_INIT_THREAD_DELAY_MS
84-
int
85-
default 30
86-
help
87-
Due to an issue in the USB stack initialization on the nRF54HX SoC,
88-
the usbd_init() cannot be called too early. The delay is introduced
89-
to delay this call.
90-
9160
choice DESKTOP_USB_STACK
9261
prompt "USB stack"
9362
default DESKTOP_USB_STACK_LEGACY
@@ -206,20 +175,29 @@ config USBD_HID_IN_BUF_COUNT
206175
config DESKTOP_USB_STACK_NEXT_DISABLE_ON_VBUS_REMOVAL
207176
bool
208177
default y if SOC_SERIES_NRF54HX
209-
depends on DESKTOP_USB_INIT_THREAD
210-
select REBOOT
211-
help
212-
Disable USB stack on VBUS removal. This is a workaround for the USB
213-
driver not working correctly on the nRF54HX SoC. After the USB cable
214-
is removed, the USB driver is powered down and doesn't call appropriate
215-
callbacks to the USB stack. It may lead to the USB stack ending in a
216-
broken state. Calling usbd_disable() on VBUS removal workarounds this
217-
issue. When this option is enabled, the usb_init_thread will be used to
218-
call the usbd_enable() and usbd_disable() functions on VBUS events. This
219-
is done because the usbd_enable() function blocks if the USB cable is
220-
not connected. A separate thread allows the application to trigger a
221-
reboot from the workqueue context to prevent blocking the application
222-
forever.
178+
help
179+
Disable the USB stack on VBUS removal and enable it on VBUS ready to
180+
workaround UDC driver limitations of the nRF54HX SoC.
181+
182+
After the USB cable is removed, the USB driver is powered down and
183+
doesn't call appropriate callbacks to the USB stack. It may lead to
184+
the USB stack ending in a broken state. Calling usbd_disable() on VBUS
185+
removal workarounds this issue.
186+
187+
The usbd_enable() function blocks until USB cable is connected. If the
188+
cable is not connected during the predefined wait period
189+
(CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT), the function returns
190+
earlier with a timeout error. The function is called on VBUS ready to
191+
avoid calling usbd_enable() periodically.
192+
193+
config UDC_DWC2_USBHS_VBUS_READY_TIMEOUT
194+
int
195+
depends on SOC_SERIES_NRF54HX
196+
default 100
197+
help
198+
For the nRF54HX SoC the timeout must be set to a non-zero value to
199+
prevent the usbd_enable() function from blocking the application
200+
forever when the USB cable is not connected.
223201

224202
choice USBD_LOG_LEVEL_CHOICE
225203
default USBD_LOG_LEVEL_WRN

applications/nrf_desktop/src/modules/usb_state.c

Lines changed: 30 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
#include <zephyr/types.h>
1212
#include <zephyr/sys/byteorder.h>
1313
#include <zephyr/sys/util.h>
14-
#include <zephyr/kernel.h>
15-
#include <zephyr/sys/reboot.h>
1614

1715
#include <zephyr/usb/usbd.h>
1816
#include <zephyr/usb/usbd_msg.h>
@@ -131,26 +129,17 @@ BUILD_ASSERT(!IS_ENABLED(CONFIG_DESKTOP_HID_STATE_ENABLE) ||
131129
IS_ENABLED(CONFIG_DESKTOP_USB_SELECTIVE_REPORT_SUBSCRIPTION) ||
132130
(ARRAY_SIZE(usb_hid_device) <= 1));
133131

132+
#if CONFIG_SOC_SERIES_NRF54HX
133+
BUILD_ASSERT(CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT > 0,
134+
"Timeout must be set to prevent the usbd_enable() function from blocking the "
135+
"application forever when the USB cable is not connected.");
136+
#endif
137+
134138
static struct usbd_context *usbd_ctx;
135139
static bool usb_enabled;
136140

137141
static struct config_channel_transport cfg_chan_transport;
138142

139-
static struct k_thread usb_init_thread;
140-
static K_THREAD_STACK_DEFINE(usb_init_thread_stack, CONFIG_DESKTOP_USB_INIT_THREAD_STACK_SIZE);
141-
142-
#define USB_REQ_TIMEOUT_MS 100
143-
144-
/* USB events */
145-
enum {
146-
USB_REQ_ENABLE = BIT(0),
147-
USB_REQ_DISABLE = BIT(1),
148-
USB_RSP_SUCCESS = BIT(2),
149-
USB_RSP_FAIL = BIT(3),
150-
};
151-
152-
static K_EVENT_DEFINE(usb_event);
153-
154143
static void report_sent(struct usb_hid_device *usb_hid, struct usb_hid_buf *buf, bool error);
155144

156145
static uint8_t usb_hid_buf_get_report_id(struct usb_hid_buf *buf)
@@ -1204,53 +1193,39 @@ static int usb_init_next_hids_init(void)
12041193
return err;
12051194
}
12061195

1207-
static int usb_thread_req_send(bool enable)
1208-
{
1209-
uint32_t events;
1210-
int err;
1211-
1212-
__ASSERT_NO_MSG(!k_event_test(&usb_event, USB_REQ_ENABLE | USB_REQ_DISABLE));
1213-
1214-
LOG_DBG("%sabling USB", enable ? "En" : "Dis");
1215-
(void) k_event_set(&usb_event, enable ? USB_REQ_ENABLE : USB_REQ_DISABLE);
1216-
events = k_event_wait(&usb_event,
1217-
USB_RSP_SUCCESS | USB_RSP_FAIL,
1218-
false,
1219-
K_MSEC(USB_REQ_TIMEOUT_MS));
1220-
(void) k_event_clear(&usb_event, events);
1221-
1222-
if (events & USB_RSP_SUCCESS) {
1223-
LOG_DBG("USB %sabled", enable ? "en" : "dis");
1224-
usb_enabled = enable;
1225-
err = 0;
1226-
} else if (events & USB_RSP_FAIL) {
1227-
LOG_ERR("Failed to %sable USB", enable ? "en" : "dis");
1228-
module_set_state(MODULE_STATE_ERROR);
1229-
err = -EIO;
1230-
} else {
1231-
LOG_ERR("Fatal error - USB %sable timeout. Rebooting...", enable ? "en" : "dis");
1232-
LOG_PANIC();
1233-
sys_reboot(SYS_REBOOT_WARM);
1234-
err = -EIO;
1235-
}
1236-
1237-
return err;
1238-
}
1239-
12401196
static int handle_usbd_state_on_status_change(enum usbd_msg_type type)
12411197
{
12421198
int err = 0;
12431199

12441200
switch (type) {
12451201
case USBD_MSG_VBUS_READY:
12461202
if (!usb_enabled) {
1247-
err = usb_thread_req_send(true);
1203+
err = usbd_enable(usbd_ctx);
1204+
if (err == -ETIMEDOUT) {
1205+
/* Probably the USB cable was disconnected before the usbd_enable
1206+
* was executed. Ignoring the error. The USB will be enabled once
1207+
* the cable is connected again.
1208+
*/
1209+
LOG_WRN("usbd_enable timed out");
1210+
err = 0;
1211+
} else if (err) {
1212+
LOG_ERR("usbd_enable failed (err: %d)", err);
1213+
module_set_state(MODULE_STATE_ERROR);
1214+
} else {
1215+
usb_enabled = true;
1216+
}
12481217
}
12491218
break;
12501219

12511220
case USBD_MSG_VBUS_REMOVED:
12521221
if (usb_enabled) {
1253-
err = usb_thread_req_send(false);
1222+
err = usbd_disable(usbd_ctx);
1223+
if (err) {
1224+
LOG_ERR("usbd_disable failed (err: %d)", err);
1225+
module_set_state(MODULE_STATE_ERROR);
1226+
} else {
1227+
usb_enabled = false;
1228+
}
12541229
}
12551230
break;
12561231

@@ -1553,57 +1528,6 @@ static int usb_init(void)
15531528
return err;
15541529
}
15551530

1556-
static void wait_for_usb_requests(void)
1557-
{
1558-
while (true) {
1559-
uint32_t events;
1560-
int err;
1561-
1562-
events = k_event_wait(&usb_event,
1563-
USB_REQ_ENABLE | USB_REQ_DISABLE,
1564-
false,
1565-
K_FOREVER);
1566-
(void) k_event_clear(&usb_event, events);
1567-
__ASSERT_NO_MSG(!(events & USB_REQ_ENABLE) || !(events & USB_REQ_DISABLE));
1568-
1569-
__ASSERT_NO_MSG(usbd_ctx);
1570-
if (events & USB_REQ_ENABLE) {
1571-
err = usbd_enable(usbd_ctx);
1572-
if (err) {
1573-
LOG_ERR("usbd_enable failed (err: %d)", err);
1574-
}
1575-
} else if (events & USB_REQ_DISABLE) {
1576-
err = usbd_disable(usbd_ctx);
1577-
if (err) {
1578-
LOG_ERR("usbd_disable failed (err: %d)", err);
1579-
}
1580-
} else {
1581-
__ASSERT_NO_MSG(false);
1582-
err = -EINVAL;
1583-
}
1584-
1585-
__ASSERT_NO_MSG(!k_event_test(&usb_event, USB_RSP_FAIL | USB_RSP_SUCCESS));
1586-
(void) k_event_post(&usb_event, err ? USB_RSP_FAIL : USB_RSP_SUCCESS);
1587-
}
1588-
}
1589-
1590-
static void usb_init_thread_fn(void *dummy0, void *dummy1, void *dummy2)
1591-
{
1592-
ARG_UNUSED(dummy0);
1593-
ARG_UNUSED(dummy1);
1594-
ARG_UNUSED(dummy2);
1595-
1596-
if (usb_init()) {
1597-
module_set_state(MODULE_STATE_ERROR);
1598-
} else {
1599-
module_set_state(MODULE_STATE_READY);
1600-
}
1601-
1602-
if (IS_ENABLED(CONFIG_DESKTOP_USB_STACK_NEXT_DISABLE_ON_VBUS_REMOVAL)) {
1603-
wait_for_usb_requests();
1604-
}
1605-
}
1606-
16071531
static bool app_event_handler(const struct app_event_header *aeh)
16081532
{
16091533
if (is_hid_report_event(aeh)) {
@@ -1619,17 +1543,10 @@ static bool app_event_handler(const struct app_event_header *aeh)
16191543
__ASSERT_NO_MSG(!initialized);
16201544
initialized = true;
16211545

1622-
if (IS_ENABLED(CONFIG_DESKTOP_USB_INIT_THREAD)) {
1623-
(void) k_thread_create(
1624-
&usb_init_thread,
1625-
usb_init_thread_stack,
1626-
K_THREAD_STACK_SIZEOF(usb_init_thread_stack),
1627-
usb_init_thread_fn,
1628-
NULL, NULL, NULL,
1629-
CONFIG_DESKTOP_USB_INIT_THREAD_PRIORITY, 0,
1630-
K_MSEC(CONFIG_DESKTOP_USB_INIT_THREAD_DELAY_MS));
1546+
if (usb_init()) {
1547+
module_set_state(MODULE_STATE_ERROR);
16311548
} else {
1632-
usb_init_thread_fn(NULL, NULL, NULL);
1549+
module_set_state(MODULE_STATE_READY);
16331550
}
16341551
}
16351552

0 commit comments

Comments
 (0)