Skip to content

Commit 5137439

Browse files
jfischer-nonashif
authored andcommitted
usb: device_next: use slist to store completed transfer requests
USBD_MAX_UDC_MSG configures the number of events coming from the UDC driver that the stack can keep. This can be filled very quickly if there would be multiple bus events for some reason, or function handlers of those events are blocked for long time. As a consequence, subsequent events could be dropped, and completed transfers not handled. To avoid it, we can store completed transfer requests in a slist and check it on every event. Signed-off-by: Johann Fischer <[email protected]>
1 parent 9f20e79 commit 5137439

File tree

2 files changed

+48
-16
lines changed

2 files changed

+48
-16
lines changed

include/zephyr/usb/usbd.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,10 @@ struct usbd_context {
287287
const struct device *dev;
288288
/** Notification message recipient callback */
289289
usbd_msg_cb_t msg_cb;
290+
/** slist to keep endpoint events */
291+
sys_slist_t ep_events;
292+
/** Endpoint event list spinlock */
293+
struct k_spinlock ep_event_lock;
290294
/** Middle layer runtime data */
291295
struct usbd_ch9_data ch9_data;
292296
/** slist to manage descriptors like string, BOS */

subsys/usb/device_next/usbd_core.c

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,29 +34,53 @@ K_MSGQ_DEFINE(usbd_msgq, sizeof(struct udc_event),
3434
static int usbd_event_carrier(const struct device *dev,
3535
const struct udc_event *const event)
3636
{
37+
struct usbd_context *const uds_ctx = (void *)udc_get_event_ctx(dev);
38+
k_spinlock_key_t key;
39+
40+
if (event->type == UDC_EVT_EP_REQUEST) {
41+
/*
42+
* Always add completed transfer requests to the list, so they
43+
* do not get lost.
44+
*/
45+
key = k_spin_lock(&uds_ctx->ep_event_lock);
46+
sys_slist_append(&uds_ctx->ep_events, &event->buf->node);
47+
k_spin_unlock(&uds_ctx->ep_event_lock, key);
48+
}
49+
3750
return k_msgq_put(&usbd_msgq, event, K_NO_WAIT);
3851
}
3952

40-
static int event_handler_ep_request(struct usbd_context *const uds_ctx,
41-
const struct udc_event *const event)
53+
static void event_handler_ep_request(struct usbd_context *const uds_ctx)
4254
{
4355
struct udc_buf_info *bi;
56+
k_spinlock_key_t key;
57+
struct net_buf *buf;
58+
sys_snode_t *node;
4459
int ret;
4560

46-
bi = udc_get_buf_info(event->buf);
61+
while (!sys_slist_is_empty(&uds_ctx->ep_events)) {
62+
key = k_spin_lock(&uds_ctx->ep_event_lock);
63+
node = sys_slist_get(&uds_ctx->ep_events);
64+
k_spin_unlock(&uds_ctx->ep_event_lock, key);
4765

48-
if (USB_EP_GET_IDX(bi->ep) == 0) {
49-
ret = usbd_handle_ctrl_xfer(uds_ctx, event->buf, bi->err);
50-
} else {
51-
ret = usbd_class_handle_xfer(uds_ctx, event->buf, bi->err);
52-
}
66+
buf = SYS_SLIST_CONTAINER(node, buf, node);
67+
if (buf == NULL) {
68+
break;
69+
}
5370

54-
if (ret) {
55-
LOG_ERR("unrecoverable error %d, ep 0x%02x, buf %p",
56-
ret, bi->ep, event->buf);
57-
}
71+
bi = udc_get_buf_info(buf);
72+
if (USB_EP_GET_IDX(bi->ep) == 0) {
73+
ret = usbd_handle_ctrl_xfer(uds_ctx, buf, bi->err);
74+
} else {
75+
ret = usbd_class_handle_xfer(uds_ctx, buf, bi->err);
76+
}
5877

59-
return ret;
78+
if (ret) {
79+
LOG_ERR("Unrecoverable error %d, ep 0x%02x, buf %p",
80+
ret, bi->ep, (void *)buf);
81+
usbd_msg_pub_simple(uds_ctx, USBD_MSG_STACK_ERROR, ret);
82+
}
83+
}
6084
}
6185

6286
static void usbd_class_bcast_event(struct usbd_context *const uds_ctx,
@@ -138,6 +162,13 @@ static ALWAYS_INLINE void usbd_event_handler(struct usbd_context *const uds_ctx,
138162
{
139163
int err = 0;
140164

165+
/* Always check if there is a completed transfer request. */
166+
event_handler_ep_request(uds_ctx);
167+
if (event->type == UDC_EVT_EP_REQUEST) {
168+
/* It has already been handled and cannot be another event type. */
169+
return;
170+
}
171+
141172
switch (event->type) {
142173
case UDC_EVT_VBUS_REMOVED:
143174
LOG_DBG("VBUS remove event");
@@ -167,9 +198,6 @@ static ALWAYS_INLINE void usbd_event_handler(struct usbd_context *const uds_ctx,
167198
err = event_handler_bus_reset(uds_ctx);
168199
usbd_msg_pub_simple(uds_ctx, USBD_MSG_RESET, 0);
169200
break;
170-
case UDC_EVT_EP_REQUEST:
171-
err = event_handler_ep_request(uds_ctx, event);
172-
break;
173201
case UDC_EVT_ERROR:
174202
LOG_ERR("UDC error event");
175203
usbd_msg_pub_simple(uds_ctx, USBD_MSG_UDC_ERROR, event->status);

0 commit comments

Comments
 (0)