Download raw body.
uaudio: Handle certain devices with multiple clock domains
Hi Alexandre.
Thank you for the diff. All three USB-C devices works here now.
I tested two devices which didn't work before this diff as reported here:
https://marc.info/?t=176298272900002&r=1&w=2
With your diff, they work. Device number one, which didn't work:
uaudio0 at uhub1 port 5 configuration 1 interface 1 "Apple, Inc. USB-C to 3.5mm Headphone Jack Adapter" rev 2.01/26.50 addr 5
uaudio0: class v2, full-speed, sync, channels: 2 play, 1 rec, 7 ctls
audio1 at uaudio0
uhidev0 at uhub1 port 5 configuration 1 interface 3 "Apple, Inc. USB-C to 3.5mm Headphone Jack Adapter" rev 2.01/26.50 addr 5
uhidev0: iclass 3/0, 1 report id
ucc0 at uhidev0 reportid 1: 4 usages, 3 keys, enum
wskbd1 at ucc0 mux 1
wskbd1: connecting to wsdisplay0
uhidev1 at uhub1 port 5 configuration 1 interface 4 "Apple, Inc. USB-C to 3.5mm Headphone Jack Adapter" rev 2.01/26.50 addr 5
uhidev1: iclass 3/0, 2 report ids
uhid0 at uhidev1 reportid 1: input=0, output=63, feature=0
uhid1 at uhidev1 reportid 2: input=63, output=0, feature=0
uaudio0: 0: bad rec intr count
Device number two, which didn't work:
uaudio0 at uhub1 port 5 configuration 1 interface 1 "Apple, Inc. EarPods" rev 2.01/26.81 addr 5
uaudio0: class v2, full-speed, sync, channels: 2 play, 1 rec, 7 ctls
audio1 at uaudio0
uhidev0 at uhub1 port 5 configuration 1 interface 3 "Apple, Inc. EarPods" rev 2.01/26.81 addr 5
uhidev0: iclass 3/0, 1 report id
ucc0 at uhidev0 reportid 1: 4 usages, 3 keys, enum
wskbd1 at ucc0 mux 1
wskbd1: connecting to wsdisplay0
uhidev1 at uhub1 port 5 configuration 1 interface 4 "Apple, Inc. EarPods" rev 2.01/26.81 addr 5
uhidev1: iclass 3/0, 2 report ids
uhid0 at uhidev1 reportid 1: input=0, output=63, feature=0
uhid1 at uhidev1 reportid 2: input=63, output=0, feature=0
uaudio0: 0: bad rec intr count
And device which previously worked, still works:
uaudio0 at uhub1 port 5 configuration 1 interface 1 "Google Pixel USB-C earbuds" rev 2.00/0.20 addr 5
uaudio0: class v1, full-speed, sync, channels: 2 play, 1 rec, 7 ctls
audio1 at uaudio0
uhidev0 at uhub1 port 5 configuration 1 interface 3 "Google Pixel USB-C earbuds" rev 2.00/0.20 addr 5
uhidev0: iclass 3/0, 5 report ids
ucc0 at uhidev0 reportid 1: 3 usages, 3 keys, enum
wskbd1 at ucc0 mux 1
wskbd1: connecting to wsdisplay0
uhid0 at uhidev0 reportid 4: input=0, output=38, feature=0
uhid1 at uhidev0 reportid 5: input=34, output=0, feature=0
Thank you!
On Thu, Nov 13, 2025 at 02:11:18PM +0100, Alexandre Ratchov wrote:
> uaudio(4) doesn't support devices with multiple "clock domains". They
> would be almost multiple independent devices in a single piece of
> hardware.
>
> Physical clocks cost money, so I suspect that most devices have one
> physical clock only, i.e. that the different clocks the device exposes
> are synchronous. If so, uaudio(4) could handle them with the diff
> below.
>
> To test this, first use a know working uaudio(4) device to validate
> your setup (usb host controller, hub etc). Once you feel audio is
> stable, try the device with the multiple clock domains on the same
> port.
>
> I'm insisting on this because there are still usb-related bugs, that
> would prevent understanding the cause of a possible failure.
>
> Index: uaudio.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/uaudio.c,v
> diff -u -p -u -p -r1.180 uaudio.c
> --- uaudio.c 2 Nov 2025 14:35:20 -0000 1.180
> +++ uaudio.c 13 Nov 2025 13:03:24 -0000
> @@ -263,7 +263,7 @@ struct uaudio_softc {
> /*
> * Current clock, UAC v2.0 only
> */
> - struct uaudio_unit *clock;
> + struct uaudio_unit *pclock, *rclock;
>
> /*
> * Number of input and output terminals
> @@ -1023,7 +1023,7 @@ uaudio_alt_getrates(struct uaudio_softc
> case UAUDIO_V1:
> return p->v1_rates;
> case UAUDIO_V2:
> - u = sc->clock;
> + u = p->mode == AUMODE_PLAY ? sc->pclock : sc->rclock;
> while (1) {
> switch (u->type) {
> case UAUDIO_AC_CLKSRC:
> @@ -1050,11 +1050,8 @@ uaudio_alt_getrates(struct uaudio_softc
> * return the clock source unit
> */
> struct uaudio_unit *
> -uaudio_clock(struct uaudio_softc *sc)
> +uaudio_clock(struct uaudio_unit *u)
> {
> - struct uaudio_unit *u;
> -
> - u = sc->clock;
> while (1) {
> if (u == NULL) {
> DPRINTF("%s: NULL clock pointer\n", __func__);
> @@ -1181,6 +1178,54 @@ uaudio_feature_addent(struct uaudio_soft
> }
>
> /*
> + * Compare two clock source units. According to UAC2 each clock
> + * source defines its own clock domain. However there is hardware
> + * exposing multiple clock units that are clocked by the same source
> + * and consequently are equivalent to single domain.
> + */
> +int
> +uaudio_clock_equiv(struct uaudio_softc *sc,
> + struct uaudio_unit *u, struct uaudio_unit *v)
> +{
> + struct uaudio_ranges_el *ur, *vr;
> +
> + if (u == NULL || v == NULL)
> + return 1;
> +
> + u = uaudio_clock(u);
> + if (u == NULL)
> + return 0;
> +
> + v = uaudio_clock(v);
> + if (v == NULL)
> + return 0;
> +
> + if (u->term != v->term) {
> + printf("%s: clock attributes differ\n", DEVNAME(sc));
> + return 0;
> + }
> +
> + ur = u->rates.el;
> + vr = v->rates.el;
> + while (1) {
> + if (ur == NULL && vr == NULL)
> + break;
> + if (ur == NULL ||
> + vr == NULL ||
> + ur->min != vr->min ||
> + ur->max != vr->max ||
> + ur->res != vr->res) {
> + printf("%s: clock rates differ\n", DEVNAME(sc));
> + return 0;
> + }
> + ur = ur->next;
> + vr = vr->next;
> + }
> +
> + DPRINTF("%s: %d, %d: equivalent\n", __func__, u->id, v->id);
> + return 1;
> +}
> +/*
> * For the given unit, parse the list of its sources and recursively
> * call uaudio_process_unit() for each.
> */
> @@ -2264,34 +2309,39 @@ uaudio_process_ac(struct uaudio_softc *s
> }
>
> if (sc->version == UAUDIO_V2) {
> +
> /*
> - * Find common clock unit. We assume all terminals
> - * belong to the same clock domain (ie are connected
> - * to the same source)
> + * Find the clocks for the usb-streaming terminal units
> */
> - sc->clock = NULL;
> +
> for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> - if (u->type != UAUDIO_AC_INPUT &&
> - u->type != UAUDIO_AC_OUTPUT)
> - continue;
> - if (sc->clock == NULL) {
> + if (u->type == UAUDIO_AC_INPUT && (u->term >> 8) == 1) {
> if (u->clock == NULL) {
> - printf("%s: terminal with no clock\n",
> - DEVNAME(sc));
> + printf("%s: no play clock\n", DEVNAME(sc));
> return 0;
> }
> - sc->clock = u->clock;
> - } else if (u->clock != sc->clock) {
> - printf("%s: only one clock domain supported\n",
> - DEVNAME(sc));
> - return 0;
> + sc->pclock = u->clock;
> + break;
> }
> }
>
> - if (sc->clock == NULL) {
> - printf("%s: no clock found\n", DEVNAME(sc));
> - return 0;
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + if (u->type == UAUDIO_AC_OUTPUT && (u->term >> 8) == 1) {
> + if (u->clock == NULL) {
> + printf("%s: no rec clock\n", DEVNAME(sc));
> + return 0;
> + }
> + sc->rclock = u->clock;
> + break;
> + }
> }
> +
> + DPRINTF("%s: pclock = %d, rclock = %d\n", __func__,
> + sc->pclock ? sc->pclock->id : -1,
> + sc->rclock ? sc->rclock->id : -1);
> +
> + if (!uaudio_clock_equiv(sc, sc->pclock, sc->rclock))
> + return 0;
> }
> return 1;
> }
> @@ -3118,7 +3168,7 @@ uaudio_stream_open(struct uaudio_softc *
> req_buf[1] = sc->rate >> 8;
> req_buf[2] = sc->rate >> 16;
> req_buf[3] = sc->rate >> 24;
> - clock = uaudio_clock(sc);
> + clock = uaudio_clock(dir == AUMODE_PLAY ? sc->pclock : sc->rclock);
> if (clock == NULL) {
> printf("%s: can't get clock\n", DEVNAME(sc));
> goto failed;
> @@ -3879,7 +3929,8 @@ uaudio_attach(struct device *parent, str
> sc->names = NULL;
> sc->alts = NULL;
> sc->params_list = NULL;
> - sc->clock = NULL;
> + sc->rclock = NULL;
> + sc->pclock = NULL;
> sc->params = NULL;
> sc->rate = 0;
> sc->mode = 0;
>
--
Regards,
Mikolaj
uaudio: Handle certain devices with multiple clock domains