Skip to content

Commit 5d6f03e

Browse files
tmon-nordicjukkar
authored andcommitted
[nrf fromtree] usb: device_next: uac2: Double buffering on IN data endpoints
Application is expected to call usbd_uac2_send() on each enabled USB Streaming Output Terminal (isochronous IN data endpoint) exactly once every SOF. The class is bookkeeping queued transfers to make it easier to determine component at fault when things go wrong. However, this approach only works fine if the underlying USB device controller buffers the data to be sent on next SOF and reports the transfer completion immediately after data is buffered (e.g. nRF52 USBD). While DWC2 otg also requires the SW to arm endpoint with data for the next SOF, unlike nRF52 USBD the transfer is only considered complete after either the IN token for isochronous endpoint is received or after the Periodic Frame Interval elapses without IN token. This design inevitably requires the application to be able to have at least two buffers for isochronous IN endpoints. Support dual buffering on IN data endpoints to facilitate sending isochronous IN data on every SOF regardless of the underlying USB device controller design. Signed-off-by: Tomasz Moń <[email protected]> (cherry picked from commit c19d34c) (cherry picked from commit 7e05c86)
1 parent 527fbb4 commit 5d6f03e

File tree

1 file changed

+18
-12
lines changed

1 file changed

+18
-12
lines changed

subsys/usb/device_next/class/usbd_uac2.c

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ LOG_MODULE_REGISTER(usbd_uac2, CONFIG_USBD_UAC2_LOG_LEVEL);
2020

2121
#define DT_DRV_COMPAT zephyr_uac2
2222

23-
#define COUNT_UAC2_AS_ENDPOINTS(node) \
23+
#define COUNT_UAC2_AS_ENDPOINT_BUFFERS(node) \
2424
IF_ENABLED(DT_NODE_HAS_COMPAT(node, zephyr_uac2_audio_streaming), ( \
2525
+ AS_HAS_ISOCHRONOUS_DATA_ENDPOINT(node) + \
26+
+ AS_IS_USB_ISO_IN(node) /* ISO IN double buffering */ + \
2627
AS_HAS_EXPLICIT_FEEDBACK_ENDPOINT(node)))
27-
#define COUNT_UAC2_ENDPOINTS(i) \
28+
#define COUNT_UAC2_EP_BUFFERS(i) \
2829
+ DT_PROP(DT_DRV_INST(i), interrupt_endpoint) \
29-
DT_INST_FOREACH_CHILD(i, COUNT_UAC2_AS_ENDPOINTS)
30-
#define UAC2_NUM_ENDPOINTS DT_INST_FOREACH_STATUS_OKAY(COUNT_UAC2_ENDPOINTS)
30+
DT_INST_FOREACH_CHILD(i, COUNT_UAC2_AS_ENDPOINT_BUFFERS)
31+
#define UAC2_NUM_EP_BUFFERS DT_INST_FOREACH_STATUS_OKAY(COUNT_UAC2_EP_BUFFERS)
3132

3233
/* Net buf is used mostly with external data. The main reason behind external
3334
* data is avoiding unnecessary isochronous data copy operations.
@@ -40,7 +41,7 @@ LOG_MODULE_REGISTER(usbd_uac2, CONFIG_USBD_UAC2_LOG_LEVEL);
4041
* "wasted memory" here is likely to be smaller than the memory overhead for
4142
* more complex "only as much as needed" schemes (e.g. heap).
4243
*/
43-
UDC_BUF_POOL_DEFINE(uac2_pool, UAC2_NUM_ENDPOINTS, 6,
44+
UDC_BUF_POOL_DEFINE(uac2_pool, UAC2_NUM_EP_BUFFERS, 6,
4445
sizeof(struct udc_buf_info), NULL);
4546

4647
/* 5.2.2 Control Request Layout */
@@ -80,6 +81,7 @@ struct uac2_ctx {
8081
*/
8182
atomic_t as_active;
8283
atomic_t as_queued;
84+
atomic_t as_double;
8385
uint32_t fb_queued;
8486
};
8587

@@ -247,6 +249,7 @@ int usbd_uac2_send(const struct device *dev, uint8_t terminal,
247249
struct uac2_ctx *ctx = dev->data;
248250
struct net_buf *buf;
249251
const struct usb_ep_descriptor *desc;
252+
atomic_t *queued_bits = &ctx->as_queued;
250253
uint8_t ep = 0;
251254
int as_idx = terminal_to_as_interface(dev, terminal);
252255
int ret;
@@ -267,9 +270,12 @@ int usbd_uac2_send(const struct device *dev, uint8_t terminal,
267270
return 0;
268271
}
269272

270-
if (atomic_test_and_set_bit(&ctx->as_queued, as_idx)) {
271-
LOG_ERR("Previous send not finished yet on 0x%02x", ep);
272-
return -EAGAIN;
273+
if (atomic_test_and_set_bit(queued_bits, as_idx)) {
274+
queued_bits = &ctx->as_double;
275+
if (atomic_test_and_set_bit(queued_bits, as_idx)) {
276+
LOG_DBG("Already double queued on 0x%02x", ep);
277+
return -EAGAIN;
278+
}
273279
}
274280

275281
buf = uac2_buf_alloc(ep, data, size);
@@ -278,7 +284,7 @@ int usbd_uac2_send(const struct device *dev, uint8_t terminal,
278284
* enough, but if it does all we loose is just single packet.
279285
*/
280286
LOG_ERR("No netbuf for send");
281-
atomic_clear_bit(&ctx->as_queued, as_idx);
287+
atomic_clear_bit(queued_bits, as_idx);
282288
ctx->ops->buf_release_cb(dev, terminal, data, ctx->user_data);
283289
return -ENOMEM;
284290
}
@@ -287,7 +293,7 @@ int usbd_uac2_send(const struct device *dev, uint8_t terminal,
287293
if (ret) {
288294
LOG_ERR("Failed to enqueue net_buf for 0x%02x", ep);
289295
net_buf_unref(buf);
290-
atomic_clear_bit(&ctx->as_queued, as_idx);
296+
atomic_clear_bit(queued_bits, as_idx);
291297
ctx->ops->buf_release_cb(dev, terminal, data, ctx->user_data);
292298
}
293299

@@ -761,8 +767,8 @@ static int uac2_request(struct usbd_class_data *const c_data, struct net_buf *bu
761767

762768
if (is_feedback) {
763769
ctx->fb_queued &= ~BIT(as_idx);
764-
} else {
765-
atomic_clear_bit(&ctx->as_queued, as_idx);
770+
} else if (!atomic_test_and_clear_bit(&ctx->as_queued, as_idx) || buf->frags) {
771+
atomic_clear_bit(&ctx->as_double, as_idx);
766772
}
767773

768774
if (USB_EP_DIR_IS_OUT(ep)) {

0 commit comments

Comments
 (0)