Skip to content

Commit 4c6b1e5

Browse files
tmon-nordiccarlescufi
authored andcommitted
usb: device_next: uac2: Support multiple sample rates
Add callbacks for setting and getting the sample rate. The callbacks are optional if all Clock Source entities support only one sample rate. This commit results in working High-Speed operation with Windows UAC2 driver when the Clock Source is host-programmable. Windows UAC2 driver won't work if setting sample rate fails even if Clock Source supports only one sample rate. Signed-off-by: Tomasz Moń <[email protected]>
1 parent 57666d3 commit 4c6b1e5

File tree

2 files changed

+185
-5
lines changed

2 files changed

+185
-5
lines changed

include/zephyr/usb/class/usbd_uac2.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,39 @@ struct uac2_ops {
123123
*/
124124
uint32_t (*feedback_cb)(const struct device *dev, uint8_t terminal,
125125
void *user_data);
126+
/**
127+
* @brief Get active sample rate
128+
*
129+
* USB stack calls this function when the host asks for active sample
130+
* rate if the Clock Source entity supports more than one sample rate.
131+
* This function won't ever be called (should be NULL) if all Clock
132+
* Source entities support only one sample rate.
133+
*
134+
* @param dev USB Audio 2 device
135+
* @param clock_id Clock Source ID whose sample rate should be returned
136+
* @param user_data Opaque user data pointer
137+
*
138+
* @return Active sample rate in Hz
139+
*/
140+
uint32_t (*get_sample_rate)(const struct device *dev, uint8_t clock_id,
141+
void *user_data);
142+
/**
143+
* @brief Set active sample rate
144+
*
145+
* USB stack calls this function when the host sets active sample rate.
146+
* This callback may be NULL if all Clock Source entities have only one
147+
* sample rate. USB stack sanitizes the sample rate to closest valid
148+
* rate for given Clock Source entity.
149+
*
150+
* @param dev USB Audio 2 device
151+
* @param clock_id Clock Source ID whose sample rate should be set
152+
* @param rate Sample rate in Hz
153+
* @param user_data Opaque user data pointer
154+
*
155+
* @return 0 on success, negative value on error
156+
*/
157+
int (*set_sample_rate)(const struct device *dev, uint8_t clock_id,
158+
uint32_t rate, void *user_data);
126159
};
127160

128161
/**

subsys/usb/device_next/class/usbd_uac2.c

Lines changed: 152 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,49 @@ void uac2_update(struct usbd_class_data *const c_data,
464464
}
465465
}
466466

467+
/* 5.2.2 Control Request Layout: "As a general rule, when an attribute value
468+
* is set, a Control will automatically adjust the passed value to the closest
469+
* available valid value."
470+
*
471+
* The values array must be sorted ascending with at least 1 element.
472+
*/
473+
static uint32_t find_closest(const uint32_t input, const uint32_t *values,
474+
const size_t values_count)
475+
{
476+
size_t i;
477+
478+
__ASSERT_NO_MSG(values_count);
479+
480+
for (i = 0; i < values_count; i++) {
481+
if (input == values[i]) {
482+
/* Exact match */
483+
return input;
484+
} else if (input < values[i]) {
485+
break;
486+
}
487+
}
488+
489+
if (i == values_count) {
490+
/* All values are smaller than input, return largest value */
491+
return values[i - 1];
492+
}
493+
494+
if (i == 0) {
495+
/* All values are larger than input, return smallest value */
496+
return values[i];
497+
}
498+
499+
/* At this point values[i] is larger than input and values[i - 1] is
500+
* smaller than input, find and return the one that is closer, favoring
501+
* bigger value if input is exactly in the middle between the two.
502+
*/
503+
if ((values[i] - input) > (input - values[i - 1])) {
504+
return values[i - 1];
505+
} else {
506+
return values[i];
507+
}
508+
}
509+
467510
/* Table 5-6: 4-byte Control CUR Parameter Block */
468511
static void layout3_cur_response(struct net_buf *const buf, uint16_t length,
469512
const uint32_t value)
@@ -475,6 +518,19 @@ static void layout3_cur_response(struct net_buf *const buf, uint16_t length,
475518
net_buf_add_mem(buf, tmp, MIN(length, 4));
476519
}
477520

521+
static int layout3_cur_request(const struct net_buf *const buf, uint32_t *out)
522+
{
523+
uint8_t tmp[4];
524+
525+
if (buf->len != 4) {
526+
return -EINVAL;
527+
}
528+
529+
memcpy(tmp, buf->data, sizeof(tmp));
530+
*out = sys_get_le32(tmp);
531+
return 0;
532+
}
533+
478534
/* Table 5-7: 4-byte Control RANGE Parameter Block */
479535
static void layout3_range_response(struct net_buf *const buf, uint16_t length,
480536
const uint32_t *min, const uint32_t *max,
@@ -522,7 +578,10 @@ static int get_clock_source_request(struct usbd_class_data *const c_data,
522578
const struct usb_setup_packet *const setup,
523579
struct net_buf *const buf)
524580
{
581+
const struct device *dev = usbd_class_get_private(c_data);
582+
struct uac2_ctx *ctx = dev->data;
525583
const uint32_t *frequencies;
584+
const uint32_t clock_id = CONTROL_ENTITY_ID(setup);
526585
size_t count;
527586

528587
/* Channel Number must be zero */
@@ -533,7 +592,7 @@ static int get_clock_source_request(struct usbd_class_data *const c_data,
533592
return 0;
534593
}
535594

536-
count = clock_frequencies(c_data, CONTROL_ENTITY_ID(setup), &frequencies);
595+
count = clock_frequencies(c_data, clock_id, &frequencies);
537596

538597
if (CONTROL_SELECTOR(setup) == CS_SAM_FREQ_CONTROL) {
539598
if (CONTROL_ATTRIBUTE(setup) == CUR) {
@@ -542,10 +601,15 @@ static int get_clock_source_request(struct usbd_class_data *const c_data,
542601
frequencies[0]);
543602
return 0;
544603
}
545-
/* TODO: If there is more than one frequency supported,
546-
* call registered application API to determine active
547-
* sample rate.
548-
*/
604+
605+
if (ctx->ops->get_sample_rate) {
606+
uint32_t hz;
607+
608+
hz = ctx->ops->get_sample_rate(dev, clock_id,
609+
ctx->user_data);
610+
layout3_cur_response(buf, setup->wLength, hz);
611+
return 0;
612+
}
549613
} else if (CONTROL_ATTRIBUTE(setup) == RANGE) {
550614
layout3_range_response(buf, setup->wLength, frequencies,
551615
frequencies, NULL, count);
@@ -560,6 +624,88 @@ static int get_clock_source_request(struct usbd_class_data *const c_data,
560624
return 0;
561625
}
562626

627+
static int set_clock_source_request(struct usbd_class_data *const c_data,
628+
const struct usb_setup_packet *const setup,
629+
const struct net_buf *const buf)
630+
{
631+
const struct device *dev = usbd_class_get_private(c_data);
632+
struct uac2_ctx *ctx = dev->data;
633+
const uint32_t *frequencies;
634+
const uint32_t clock_id = CONTROL_ENTITY_ID(setup);
635+
size_t count;
636+
637+
/* Channel Number must be zero */
638+
if (CONTROL_CHANNEL_NUMBER(setup) != 0) {
639+
LOG_DBG("Clock source control with channel %d",
640+
CONTROL_CHANNEL_NUMBER(setup));
641+
errno = -EINVAL;
642+
return 0;
643+
}
644+
645+
count = clock_frequencies(c_data, clock_id, &frequencies);
646+
647+
if (CONTROL_SELECTOR(setup) == CS_SAM_FREQ_CONTROL) {
648+
if (CONTROL_ATTRIBUTE(setup) == CUR) {
649+
uint32_t requested, hz;
650+
int err;
651+
652+
err = layout3_cur_request(buf, &requested);
653+
if (err) {
654+
errno = err;
655+
return 0;
656+
}
657+
658+
hz = find_closest(requested, frequencies, count);
659+
660+
if (ctx->ops->set_sample_rate == NULL) {
661+
/* The set_sample_rate() callback is optional
662+
* if there is only one supported sample rate.
663+
*/
664+
if (count > 1) {
665+
errno = -ENOTSUP;
666+
}
667+
return 0;
668+
}
669+
670+
err = ctx->ops->set_sample_rate(dev, clock_id, hz,
671+
ctx->user_data);
672+
if (err) {
673+
errno = err;
674+
}
675+
676+
return 0;
677+
}
678+
} else {
679+
LOG_DBG("Unhandled clock control selector 0x%02x",
680+
CONTROL_SELECTOR(setup));
681+
}
682+
683+
errno = -ENOTSUP;
684+
return 0;
685+
}
686+
687+
static int uac2_control_to_dev(struct usbd_class_data *const c_data,
688+
const struct usb_setup_packet *const setup,
689+
const struct net_buf *const buf)
690+
{
691+
entity_type_t entity_type;
692+
693+
if (CONTROL_ATTRIBUTE(setup) != CUR) {
694+
errno = -ENOTSUP;
695+
return 0;
696+
}
697+
698+
if (setup->bmRequestType == SET_CLASS_REQUEST_TYPE) {
699+
entity_type = id_type(c_data, CONTROL_ENTITY_ID(setup));
700+
if (entity_type == ENTITY_TYPE_CLOCK_SOURCE) {
701+
return set_clock_source_request(c_data, setup, buf);
702+
}
703+
}
704+
705+
errno = -ENOTSUP;
706+
return 0;
707+
}
708+
563709
static int uac2_control_to_host(struct usbd_class_data *const c_data,
564710
const struct usb_setup_packet *const setup,
565711
struct net_buf *const buf)
@@ -720,6 +866,7 @@ static int uac2_init(struct usbd_class_data *const c_data)
720866

721867
struct usbd_class_api uac2_api = {
722868
.update = uac2_update,
869+
.control_to_dev = uac2_control_to_dev,
723870
.control_to_host = uac2_control_to_host,
724871
.request = uac2_request,
725872
.sof = uac2_sof,

0 commit comments

Comments
 (0)