Module Name: src Committed By: mlelstv Date: Mon Apr 10 15:14:51 UTC 2023
Modified Files: src/sys/dev/usb: uaudio.c uaudioreg.h Log Message: Handle more UAC2 descriptors and add debug output. Fix handling of clock sources (mix.wIndex wasn't set). UAC2 can use separate clock sources (and thus sample rates) for each terminal. That doesn't match the audio(4) model where sample rates are part of an audio format and global. For now, try to match clocks for input and output terminals separately. To generate a diff of this commit: cvs rdiff -u -r1.177 -r1.178 src/sys/dev/usb/uaudio.c cvs rdiff -u -r1.17 -r1.18 src/sys/dev/usb/uaudioreg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/uaudio.c diff -u src/sys/dev/usb/uaudio.c:1.177 src/sys/dev/usb/uaudio.c:1.178 --- src/sys/dev/usb/uaudio.c:1.177 Mon Apr 3 16:00:17 2023 +++ src/sys/dev/usb/uaudio.c Mon Apr 10 15:14:50 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: uaudio.c,v 1.177 2023/04/03 16:00:17 mlelstv Exp $ */ +/* $NetBSD: uaudio.c,v 1.178 2023/04/10 15:14:50 mlelstv Exp $ */ /* * Copyright (c) 1999, 2012 The NetBSD Foundation, Inc. @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.177 2023/04/03 16:00:17 mlelstv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.178 2023/04/10 15:14:50 mlelstv Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -222,6 +222,7 @@ struct uaudio_softc { device_t sc_audiodev; int sc_nratectls; /* V2 sample rates */ int sc_ratectls[AUFMT_MAX_FREQUENCIES]; + int sc_ratemode[AUFMT_MAX_FREQUENCIES]; struct audio_format *sc_formats; int sc_nformats; u_int sc_channel_config; @@ -239,8 +240,8 @@ struct terminal_list { struct io_terminal { union { const uaudio_cs_descriptor_t *desc; - const struct usb_audio_input_terminal *it; - const struct usb_audio_output_terminal *ot; + const union usb_audio_input_terminal *it; + const union usb_audio_output_terminal *ot; const struct usb_audio_mixer_unit *mu; const struct usb_audio_selector_unit *su; const struct usb_audio_feature_unit *fu; @@ -287,10 +288,11 @@ Static void uaudio_mixer_add_ctl(struct Static char *uaudio_id_name (struct uaudio_softc *, const struct io_terminal *, int); #ifdef UAUDIO_DEBUG -Static void uaudio_dump_cluster(const struct usb_audio_cluster *); +Static void uaudio_dump_cluster + (struct uaudio_softc *, const union usb_audio_cluster *); #endif -Static struct usb_audio_cluster uaudio_get_cluster - (int, const struct io_terminal *); +Static union usb_audio_cluster uaudio_get_cluster + (struct uaudio_softc *, int, const struct io_terminal *); Static void uaudio_add_input (struct uaudio_softc *, const struct io_terminal *, int); Static void uaudio_add_output @@ -323,11 +325,11 @@ Static void uaudio_add_clksel Static struct terminal_list *uaudio_merge_terminal_list (const struct io_terminal *); Static struct terminal_list *uaudio_io_terminaltype - (int, struct io_terminal *, int); + (struct uaudio_softc *, int, struct io_terminal *, int); Static usbd_status uaudio_identify (struct uaudio_softc *, const usb_config_descriptor_t *); Static u_int uaudio_get_rates - (struct uaudio_softc *, u_int *, u_int); + (struct uaudio_softc *, int, u_int *, u_int); Static void uaudio_build_formats (struct uaudio_softc *); @@ -643,7 +645,6 @@ uaudio_mixer_add_ctl(struct uaudio_softc DPRINTF("adding %s\n", mc->ctlname); } len = sizeof(*mc) * (sc->sc_nctls + 1); -KASSERT(len > 0); nmc = kmem_alloc(len, KM_SLEEP); /* Copy old data, if there was any */ if (sc->sc_nctls != 0) { @@ -697,8 +698,14 @@ KASSERT(len > 0); mc->mul = r->maxval - r->minval; res = r->resval; } else { /* UAUDIO_VERSION2 */ - count = (uint16_t)uaudio_get(sc, V2_RANGES, UT_READ_CLASS_INTERFACE, - mc->wValue[0], mc->wIndex, 2); + count = (uint16_t)uaudio_get(sc, V2_RANGES, + UT_READ_CLASS_INTERFACE, + mc->wValue[0], mc->wIndex, 2); + + if (count == 0 || count == (uint16_t)-1) { + DPRINTF("invalid range count %zu\n", count); + return; + } if (count > 1) { r = kmem_alloc(sizeof(struct range) * count, @@ -814,19 +821,47 @@ uaudio_id_name(struct uaudio_softc *sc, #ifdef UAUDIO_DEBUG Static void -uaudio_dump_cluster(const struct usb_audio_cluster *cl) +uaudio_dump_cluster(struct uaudio_softc *sc, const union usb_audio_cluster *cl) { - static const char *channel_names[16] = { + static const char *channel_v1_names[16] = { "LEFT", "RIGHT", "CENTER", "LFE", "LEFT_SURROUND", "RIGHT_SURROUND", "LEFT_CENTER", "RIGHT_CENTER", "SURROUND", "LEFT_SIDE", "RIGHT_SIDE", "TOP", "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15", }; - int cc, i, first; + static const char *channel_v2_names[32] = { + "LEFT", "RIGHT", "CENTER", "LFE", + "BACK_LEFT", "BACK_RIGHT", "FLC", "FRC", + "BACK_CENTER", "SIDE_LEFT", "SIDE_RIGHT", "TOP CENTER", + "TFL", "TFC", "TFR", "TBL", "TBC", "TBR", + "TFLC", "TFRC", "LLFE", "RLFE", "TSL", "TSR", + "BC", "BLC", "BRC", + "RESERVED27", "RESERVED28", "RESERVED29", "RESERVED30", + "RAW_DATA" + }; + const char **channel_names; + uint32_t cc; + int i, first, icn; + + switch (sc->sc_version) { + case UAUDIO_VERSION1: + channel_names = channel_v1_names; + cc = UGETW(cl->v1.wChannelConfig); + icn = cl->v1.iChannelNames; + printf("cluster: bNrChannels=%u wChannelConfig=%#.4x", + cl->v1.bNrChannels, cc); + break; + case UAUDIO_VERSION2: + channel_names = channel_v2_names; + cc = UGETDW(cl->v2.bmChannelConfig); + icn = cl->v2.iChannelNames; + printf("cluster: bNrChannels=%u bmChannelConfig=%#.8x", + cl->v2.bNrChannels, cc); + break; + default: + return; + } - cc = UGETW(cl->wChannelConfig); - printf("cluster: bNrChannels=%u wChannelConfig=%#.4x", - cl->bNrChannels, cc); first = TRUE; for (i = 0; cc != 0; i++) { if (cc & 1) { @@ -835,14 +870,14 @@ uaudio_dump_cluster(const struct usb_aud } cc = cc >> 1; } - printf("> iChannelNames=%u", cl->iChannelNames); + printf("> iChannelNames=%u", icn); } #endif -Static struct usb_audio_cluster -uaudio_get_cluster(int id, const struct io_terminal *iot) +Static union usb_audio_cluster +uaudio_get_cluster(struct uaudio_softc *sc, int id, const struct io_terminal *iot) { - struct usb_audio_cluster r; + union usb_audio_cluster r; const uaudio_cs_descriptor_t *dp; int i; @@ -850,17 +885,27 @@ uaudio_get_cluster(int id, const struct dp = iot[id].d.desc; if (dp == 0) goto bad; + switch (dp->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: - r.bNrChannels = iot[id].d.it->bNrChannels; - USETW(r.wChannelConfig, UGETW(iot[id].d.it->wChannelConfig)); - r.iChannelNames = iot[id].d.it->iChannelNames; + switch (sc->sc_version) { + case UAUDIO_VERSION1: + r.v1.bNrChannels = iot[id].d.it->v1.bNrChannels; + USETW(r.v1.wChannelConfig, UGETW(iot[id].d.it->v1.wChannelConfig)); + r.v1.iChannelNames = iot[id].d.it->v1.iChannelNames; + break; + case UAUDIO_VERSION2: + r.v2.bNrChannels = iot[id].d.it->v2.bNrChannels; + USETDW(r.v2.bmChannelConfig, UGETW(iot[id].d.it->v2.bmChannelConfig)); + r.v2.iChannelNames = iot[id].d.it->v2.iChannelNames; + break; + } return r; case UDESCSUB_AC_OUTPUT: - id = iot[id].d.ot->bSourceId; + id = iot[id].d.ot->v1.bSourceId; break; case UDESCSUB_AC_MIXER: - r = *(const struct usb_audio_cluster *) + r = *(const union usb_audio_cluster *) &iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins]; return r; case UDESCSUB_AC_SELECTOR: @@ -871,11 +916,11 @@ uaudio_get_cluster(int id, const struct id = iot[id].d.fu->bSourceId; break; case UDESCSUB_AC_PROCESSING: - r = *(const struct usb_audio_cluster *) + r = *(const union usb_audio_cluster *) &iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins]; return r; case UDESCSUB_AC_EXTENSION: - r = *(const struct usb_audio_cluster *) + r = *(const union usb_audio_cluster *) &iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins]; return r; default: @@ -892,21 +937,39 @@ uaudio_get_cluster(int id, const struct Static void uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { - const struct usb_audio_input_terminal *d; + const union usb_audio_input_terminal *d; d = iot[id].d.it; + switch (sc->sc_version) { + case UAUDIO_VERSION1: #ifdef UAUDIO_DEBUG - DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x " - "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " - "iChannelNames=%d iTerminal=%d\n", - d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, - d->bNrChannels, UGETW(d->wChannelConfig), - d->iChannelNames, d->iTerminal); + DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x " + "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " + "iChannelNames=%d iTerminal=%d\n", + d->v1.bTerminalId, UGETW(d->v1.wTerminalType), d->v1.bAssocTerminal, + d->v1.bNrChannels, UGETW(d->v1.wChannelConfig), + d->v1.iChannelNames, d->v1.iTerminal); #endif - /* If USB input terminal, record wChannelConfig */ - if ((UGETW(d->wTerminalType) & 0xff00) != 0x0100) - return; - sc->sc_channel_config = UGETW(d->wChannelConfig); + /* If USB input terminal, record wChannelConfig */ + if ((UGETW(d->v1.wTerminalType) & 0xff00) != 0x0100) + return; + sc->sc_channel_config = UGETW(d->v1.wChannelConfig); + break; + case UAUDIO_VERSION2: +#ifdef UAUDIO_DEBUG + DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x " + "bAssocTerminal=%d bNrChannels=%d bmChannelConfig=%x " + "iChannelNames=%d bCSourceId=%d iTerminal=%d\n", + d->v2.bTerminalId, UGETW(d->v2.wTerminalType), d->v2.bAssocTerminal, + d->v2.bNrChannels, UGETDW(d->v2.bmChannelConfig), + d->v2.iChannelNames, d->v2.bCSourceId, d->v2.iTerminal); +#endif + /* If USB input terminal, record wChannelConfig */ + if ((UGETW(d->v2.wTerminalType) & 0xff00) != 0x0100) + return; + sc->sc_channel_config = UGETDW(d->v2.bmChannelConfig); + break; + } } Static void @@ -914,13 +977,23 @@ uaudio_add_output(struct uaudio_softc *s const struct io_terminal *iot, int id) { #ifdef UAUDIO_DEBUG - const struct usb_audio_output_terminal *d; + const union usb_audio_output_terminal *d; d = iot[id].d.ot; - DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x " - "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", - d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, - d->bSourceId, d->iTerminal); + switch (sc->sc_version) { + case UAUDIO_VERSION1: + DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x " + "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", + d->v1.bTerminalId, UGETW(d->v1.wTerminalType), d->v1.bAssocTerminal, + d->v1.bSourceId, d->v1.iTerminal); + break; + case UAUDIO_VERSION2: + DPRINTFN(2,"bTerminalId=%d wTerminalType=0x%04x " + "bAssocTerminal=%d bSourceId=%d bCSourceId=%d, iTerminal=%d\n", + d->v2.bTerminalId, UGETW(d->v2.wTerminalType), d->v2.bAssocTerminal, + d->v2.bSourceId, d->v2.bCSourceId, d->v2.iTerminal); + break; + } #endif } @@ -928,7 +1001,7 @@ Static void uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { const struct usb_audio_mixer_unit *d; - const struct usb_audio_mixer_unit_1 *d1; + const union usb_audio_mixer_unit_1 *d1; int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k; const uByte *bm; struct mixerctl mix; @@ -937,24 +1010,46 @@ uaudio_add_mixer(struct uaudio_softc *sc DPRINTFN(2,"bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins); - /* Compute the number of input channels */ - ichs = 0; - for (i = 0; i < d->bNrInPins; i++) - ichs += uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels; + d1 = (const union usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; + /* Compute the number of input channels */ /* and the number of output channels */ - d1 = (const struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; - ochs = d1->bNrChannels; - DPRINTFN(2,"ichs=%d ochs=%d\n", ichs, ochs); - - bm = d1->bmControls; + ichs = 0; + switch (sc->sc_version) { + case UAUDIO_VERSION1: + for (i = 0; i < d->bNrInPins; i++) + ichs += uaudio_get_cluster(sc, d->baSourceId[i], iot).v1.bNrChannels; + ochs = d1->v1.bNrChannels; + DPRINTFN(2,"ichs=%d ochs=%d\n", ichs, ochs); + bm = d1->v1.bmControls; + break; + case UAUDIO_VERSION2: + for (i = 0; i < d->bNrInPins; i++) + ichs += uaudio_get_cluster(sc, d->baSourceId[i], iot).v2.bNrChannels; + ochs = d1->v2.bNrChannels; + DPRINTFN(2,"ichs=%d ochs=%d\n", ichs, ochs); + bm = d1->v2.bmControls; + break; + default: + return; + } mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); uaudio_determine_class(&iot[id], &mix); mix.type = MIX_SIGNED_16; mix.ctlunit = AudioNvolume; #define _BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1) for (p = i = 0; i < d->bNrInPins; i++) { - chs = uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels; + switch (sc->sc_version) { + case UAUDIO_VERSION1: + chs = uaudio_get_cluster(sc, d->baSourceId[i], iot).v1.bNrChannels; + break; + case UAUDIO_VERSION2: + chs = uaudio_get_cluster(sc, d->baSourceId[i], iot).v2.bNrChannels; + break; + default: + chs = 0; + break; + } mc = 0; for (c = 0; c < chs; c++) { mo = 0; @@ -1455,6 +1550,10 @@ uaudio_add_processing(struct uaudio_soft Static void uaudio_add_effect(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { + +#ifdef UAUDIO_DEBUG + aprint_debug("uaudio_add_effect: not impl.\n"); +#endif } Static void @@ -1493,8 +1592,9 @@ uaudio_add_clksrc(struct uaudio_softc *s struct mixerctl mix; d = iot[id].d.cu; - DPRINTFN(2,"bClockId=%d bmAttributes=%d bmControls=%d iClockSource=%d\n", - d->bClockId, d->bmAttributes, d->bmControls, d->iClockSource); + DPRINTFN(2,"bClockId=%d bmAttributes=%d bmControls=%d bAssocTerminal=%d iClockSource=%d\n", + d->bClockId, d->bmAttributes, d->bmControls, d->bAssocTerminal, d->iClockSource); + mix.wIndex = MAKE(d->bClockId, sc->sc_ac_iface); uaudio_determine_class(&iot[id], &mix); mix.nchan = 1; mix.wValue[0] = MAKE(V2_CUR_CLKFREQ, 0); @@ -1574,7 +1674,7 @@ uaudio_merge_terminal_list(const struct } Static struct terminal_list * -uaudio_io_terminaltype(int outtype, struct io_terminal *iot, int id) +uaudio_io_terminaltype(struct uaudio_softc *sc, int outtype, struct io_terminal *iot, int id) { struct terminal_list *tml; struct io_terminal *it; @@ -1633,7 +1733,19 @@ uaudio_io_terminaltype(int outtype, stru return NULL; } it->inputs[0] = tml; - tml->terminals[0] = UGETW(it->d.it->wTerminalType); + switch (sc->sc_version) { + case UAUDIO_VERSION1: + tml->terminals[0] = UGETW(it->d.it->v1.wTerminalType); + break; + case UAUDIO_VERSION2: + tml->terminals[0] = UGETW(it->d.it->v2.wTerminalType); + break; + default: + free(tml, M_TEMP); + free(it->inputs, M_TEMP); + it->inputs = NULL; + return NULL; + } tml->size = 1; it->inputs_size = 1; return uaudio_merge_terminal_list(it); @@ -1642,9 +1754,9 @@ uaudio_io_terminaltype(int outtype, stru it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); if (it->inputs == NULL) { aprint_error("uaudio_io_terminaltype: no memory\n"); - return uaudio_io_terminaltype(outtype, iot, src_id); + return uaudio_io_terminaltype(sc, outtype, iot, src_id); } - it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id); + it->inputs[0] = uaudio_io_terminaltype(sc, outtype, iot, src_id); it->inputs_size = 1; return uaudio_merge_terminal_list(it); case UDESCSUB_AC_OUTPUT: @@ -1653,8 +1765,19 @@ uaudio_io_terminaltype(int outtype, stru aprint_error("uaudio_io_terminaltype: no memory\n"); return NULL; } - src_id = it->d.ot->bSourceId; - it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id); + switch (sc->sc_version) { + case UAUDIO_VERSION1: + src_id = it->d.ot->v1.bSourceId; + break; + case UAUDIO_VERSION2: + src_id = it->d.ot->v2.bSourceId; + break; + default: + free(it->inputs, M_TEMP); + it->inputs = NULL; + return NULL; + } + it->inputs[0] = uaudio_io_terminaltype(sc, outtype, iot, src_id); it->inputs_size = 1; iot[src_id].direct = TRUE; return NULL; @@ -1668,7 +1791,7 @@ uaudio_io_terminaltype(int outtype, stru } for (i = 0; i < it->d.mu->bNrInPins; i++) { src_id = it->d.mu->baSourceId[i]; - it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + it->inputs[i] = uaudio_io_terminaltype(sc, outtype, iot, src_id); it->inputs_size++; } @@ -1683,7 +1806,7 @@ uaudio_io_terminaltype(int outtype, stru } for (i = 0; i < it->d.su->bNrInPins; i++) { src_id = it->d.su->baSourceId[i]; - it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + it->inputs[i] = uaudio_io_terminaltype(sc, outtype, iot, src_id); it->inputs_size++; } @@ -1698,7 +1821,7 @@ uaudio_io_terminaltype(int outtype, stru } for (i = 0; i < it->d.pu->bNrInPins; i++) { src_id = it->d.pu->baSourceId[i]; - it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + it->inputs[i] = uaudio_io_terminaltype(sc, outtype, iot, src_id); it->inputs_size++; } @@ -1713,7 +1836,7 @@ uaudio_io_terminaltype(int outtype, stru } for (i = 0; i < it->d.eu->bNrInPins; i++) { src_id = it->d.eu->baSourceId[i]; - it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + it->inputs[i] = uaudio_io_terminaltype(sc, outtype, iot, src_id); it->inputs_size++; } @@ -2152,7 +2275,7 @@ uaudio_identify_as(struct uaudio_softc * Static u_int -uaudio_get_rates(struct uaudio_softc *sc, u_int *freqs, u_int len) +uaudio_get_rates(struct uaudio_softc *sc, int mode, u_int *freqs, u_int len) { struct mixerctl *mc; u_int n, freq, start, end, step; @@ -2160,6 +2283,14 @@ uaudio_get_rates(struct uaudio_softc *sc n = 0; for (j = 0; j < sc->sc_nratectls; ++j) { + + /* + * skip rates not associated with a terminal + * of the required mode (record/play) + */ + if ((sc->sc_ratemode[j] & mode) == 0) + continue; + mc = &sc->sc_ctls[sc->sc_ratectls[j]]; count = mc->nranges ? mc->nranges : 1; for (k = 0; k < count; ++k) { @@ -2237,7 +2368,7 @@ uaudio_build_formats(struct uaudio_softc auf->channels = as->nchan; #if 0 - auf->frequency_type = uaudio_get_rates(sc, NULL, 0); + auf->frequency_type = uaudio_get_rates(sc, auf->mode, NULL, 0); if (auf->frequency_type >= AUFMT_MAX_FREQUENCIES) { aprint_error("%s: please increase " "AUFMT_MAX_FREQUENCIES to %d\n", @@ -2245,7 +2376,17 @@ uaudio_build_formats(struct uaudio_softc } #endif - auf->frequency_type = uaudio_get_rates(sc, auf->frequency, AUFMT_MAX_FREQUENCIES); + auf->frequency_type = uaudio_get_rates(sc, + auf->mode, auf->frequency, AUFMT_MAX_FREQUENCIES); + + /* + * if rate query failed, guess a rate + */ + if (auf->frequency_type == UA_SAMP_CONTINUOUS) { + auf->frequency[0] = 48000; + auf->frequency[1] = 48000; + } + break; } @@ -2275,7 +2416,7 @@ uaudio_identify_ac(struct uaudio_softc * const usb_interface_descriptor_t *id; const struct usb_audio_control_descriptor *acdp; const uaudio_cs_descriptor_t *dp; - const struct usb_audio_output_terminal *pot; + const union usb_audio_output_terminal *pot; struct terminal_list *tml; const char *tbuf, *ibuf, *ibufend; int size, offs, ndps, i, j; @@ -2339,7 +2480,17 @@ uaudio_identify_ac(struct uaudio_softc * } if (dp->bDescriptorType != UDESC_CS_INTERFACE) break; - i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId; + switch (sc->sc_version) { + case UAUDIO_VERSION1: + i = ((const union usb_audio_input_terminal *)dp)->v1.bTerminalId; + break; + case UAUDIO_VERSION2: + i = ((const union usb_audio_input_terminal *)dp)->v2.bTerminalId; + break; + default: + free(iot, M_TEMP); + return USBD_INVAL; + } iot[i].d.desc = dp; if (i > ndps) ndps = i; @@ -2354,14 +2505,24 @@ uaudio_identify_ac(struct uaudio_softc * if (dp->bDescriptorSubtype != UDESCSUB_AC_OUTPUT) continue; pot = iot[i].d.ot; - tml = uaudio_io_terminaltype(UGETW(pot->wTerminalType), iot, i); + switch (sc->sc_version) { + case UAUDIO_VERSION1: + tml = uaudio_io_terminaltype(sc, UGETW(pot->v1.wTerminalType), iot, i); + break; + case UAUDIO_VERSION2: + tml = uaudio_io_terminaltype(sc, UGETW(pot->v2.wTerminalType), iot, i); + break; + default: + tml = NULL; + break; + } if (tml != NULL) free(tml, M_TEMP); } #ifdef UAUDIO_DEBUG for (i = 0; i < 256; i++) { - struct usb_audio_cluster cluster; + union usb_audio_cluster cluster; if (iot[i].d.desc == NULL) continue; @@ -2369,24 +2530,24 @@ uaudio_identify_ac(struct uaudio_softc * switch (iot[i].d.desc->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: printf("AC_INPUT type=%s\n", uaudio_get_terminal_name - (UGETW(iot[i].d.it->wTerminalType))); + (UGETW(iot[i].d.it->v1.wTerminalType))); printf("\t"); - cluster = uaudio_get_cluster(i, iot); - uaudio_dump_cluster(&cluster); + cluster = uaudio_get_cluster(sc, i, iot); + uaudio_dump_cluster(sc, &cluster); printf("\n"); break; case UDESCSUB_AC_OUTPUT: printf("AC_OUTPUT type=%s ", uaudio_get_terminal_name - (UGETW(iot[i].d.ot->wTerminalType))); - printf("src=%d\n", iot[i].d.ot->bSourceId); + (UGETW(iot[i].d.ot->v1.wTerminalType))); + printf("src=%d\n", iot[i].d.ot->v1.bSourceId); break; case UDESCSUB_AC_MIXER: printf("AC_MIXER src="); for (j = 0; j < iot[i].d.mu->bNrInPins; j++) printf("%d ", iot[i].d.mu->baSourceId[j]); printf("\n\t"); - cluster = uaudio_get_cluster(i, iot); - uaudio_dump_cluster(&cluster); + cluster = uaudio_get_cluster(sc, i, iot); + uaudio_dump_cluster(sc, &cluster); printf("\n"); break; case UDESCSUB_AC_SELECTOR: @@ -2406,8 +2567,8 @@ uaudio_identify_ac(struct uaudio_softc * for (j = 0; j < iot[i].d.pu->bNrInPins; j++) printf("%d ", iot[i].d.pu->baSourceId[j]); printf("\n\t"); - cluster = uaudio_get_cluster(i, iot); - uaudio_dump_cluster(&cluster); + cluster = uaudio_get_cluster(sc, i, iot); + uaudio_dump_cluster(sc, &cluster); printf("\n"); break; case UDESCSUB_AC_EXTENSION: @@ -2415,8 +2576,8 @@ uaudio_identify_ac(struct uaudio_softc * for (j = 0; j < iot[i].d.eu->bNrInPins; j++) printf("%d ", iot[i].d.eu->baSourceId[j]); printf("\n\t"); - cluster = uaudio_get_cluster(i, iot); - uaudio_dump_cluster(&cluster); + cluster = uaudio_get_cluster(sc, i, iot); + uaudio_dump_cluster(sc, &cluster); printf("\n"); break; case UDESCSUB_AC_CLKSRC: @@ -2504,6 +2665,42 @@ uaudio_identify_ac(struct uaudio_softc * } } + switch (sc->sc_version) { + case UAUDIO_VERSION2: + /* + * UAC2 has separate rate controls which effectively creates + * a set of audio_formats per input and output and their + * associated clock sources. + * + * audio(4) can only handle audio_formats per direction. + * - ignore stream terminals + * - mark rates for record or play if associated with an input + * or output terminal respectively. + */ + for (j = 0; j < sc->sc_nratectls; ++j) { + uint16_t wi = sc->sc_ctls[sc->sc_ratectls[j]].wIndex; + sc->sc_ratemode[j] = 0; + for (i = 0; i < ndps; i++) { + dp = iot[i].d.desc; + if (dp == NULL) + continue; + switch (dp->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + if (UGETW(iot[i].d.it->v2.wTerminalType) != UAT_STREAM && + wi == MAKE(iot[i].d.it->v2.bCSourceId, sc->sc_ac_iface)) + sc->sc_ratemode[j] |= AUMODE_RECORD; + break; + case UDESCSUB_AC_OUTPUT: + if (UGETW(iot[i].d.it->v2.wTerminalType) != UAT_STREAM && + wi == MAKE(iot[i].d.ot->v2.bCSourceId, sc->sc_ac_iface)) + sc->sc_ratemode[j] |= AUMODE_PLAY; + break; + } + } + } + break; + } + /* delete io_terminal */ for (i = 0; i < 256; i++) { if (iot[i].d.desc == NULL) @@ -2830,9 +3027,6 @@ uaudio_getbuf(struct uaudio_softc *sc, i usb_device_request_t req; usbd_status err; - if (wValue == -1) - return 0; - req.bmRequestType = type; req.bRequest = which; USETW(req.wValue, wValue); Index: src/sys/dev/usb/uaudioreg.h diff -u src/sys/dev/usb/uaudioreg.h:1.17 src/sys/dev/usb/uaudioreg.h:1.18 --- src/sys/dev/usb/uaudioreg.h:1.17 Sun Apr 2 14:43:35 2023 +++ src/sys/dev/usb/uaudioreg.h Mon Apr 10 15:14:50 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: uaudioreg.h,v 1.17 2023/04/02 14:43:35 mlelstv Exp $ */ +/* $NetBSD: uaudioreg.h,v 1.18 2023/04/10 15:14:50 mlelstv Exp $ */ /* * Copyright (c) 1999 The NetBSD Foundation, Inc. @@ -157,7 +157,7 @@ union usb_audio_streaming_type1_descript struct usb_audio_streaming_type1_v2_descriptor v2; }; -struct usb_audio_cluster { +struct usb_audio_v1_cluster { uByte bNrChannels; uWord wChannelConfig; #define UA_CHANNEL_LEFT 0x0001 @@ -175,6 +175,45 @@ struct usb_audio_cluster { uByte iChannelNames; } UPACKED; +struct usb_audio_v2_cluster { + uByte bNrChannels; + uDWord bmChannelConfig; +/*#define UA_CHANNEL_LEFT 0x00000001 */ +/*#define UA_CHANNEL_RIGHT 0x00000002 */ +/*#define UA_CHANNEL_CENTER 0x00000004 */ +/*#define UA_CHANNEL_LFE 0x00000008 */ +#define UA_CHANNEL_BL 0x00000010 +#define UA_CHANNEL_BR 0x00000020 +#define UA_CHANNEL_FLC 0x00000040 +#define UA_CHANNEL_FRC 0x00000080 +#define UA_CHANNEL_BC 0x00000100 +#define UA_CHANNEL_SL 0x00000200 +#define UA_CHANNEL_SR 0x00000400 +#define UA_CHANNEL_TC 0x00000800 +#define UA_CHANNEL_TFL 0x00001000 +#define UA_CHANNEL_TFC 0x00002000 +#define UA_CHANNEL_TFR 0x00004000 +#define UA_CHANNEL_TBL 0x00008000 +#define UA_CHANNEL_TBC 0x00010000 +#define UA_CHANNEL_TBR 0x00020000 +#define UA_CHANNEL_TFLC 0x00040000 +#define UA_CHANNEL_TFRC 0x00080000 +#define UA_CHANNEL_LLFE 0x00100000 +#define UA_CHANNEL_RLFE 0x00200000 +#define UA_CHANNEL_TSL 0x00400000 +#define UA_CHANNEL_TSR 0x00800000 +#define UA_CHANNEL_BOTTOM 0x01000000 +#define UA_CHANNEL_BOTTOMLC 0x02000000 +#define UA_CHANNEL_BOTTOMRC 0x04000000 +#define UA_CHANNEL_RD 0x80000000 + uByte iChannelNames; +} UPACKED; + +union usb_audio_cluster { + struct usb_audio_v1_cluster v1; + struct usb_audio_v2_cluster v2; +}; + /* Shared by all units and terminals */ struct usb_audio_unit { uByte bLength; @@ -184,7 +223,7 @@ struct usb_audio_unit { }; /* UDESCSUB_AC_INPUT */ -struct usb_audio_input_terminal { +struct usb_audio_input_v1_terminal { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -196,9 +235,28 @@ struct usb_audio_input_terminal { uByte iChannelNames; uByte iTerminal; } UPACKED; +struct usb_audio_input_v2_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bCSourceId; + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iChannelNames; + uWord bmControls; + uByte iTerminal; +} UPACKED; + +union usb_audio_input_terminal { + struct usb_audio_input_v1_terminal v1; + struct usb_audio_input_v2_terminal v2; +}; /* UDESCSUB_AC_OUTPUT */ -struct usb_audio_output_terminal { +struct usb_audio_output_v1_terminal { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -208,6 +266,23 @@ struct usb_audio_output_terminal { uByte bSourceId; uByte iTerminal; } UPACKED; +struct usb_audio_output_v2_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bSourceId; + uByte bCSourceId; + uWord bmControls; + uByte iTerminal; +} UPACKED; + +union usb_audio_output_terminal { + struct usb_audio_output_v1_terminal v1; + struct usb_audio_output_v2_terminal v2; +}; /* UDESCSUB_AC_MIXER */ struct usb_audio_mixer_unit { @@ -217,15 +292,27 @@ struct usb_audio_mixer_unit { uByte bUnitId; uByte bNrInPins; uByte baSourceId[255]; /* [bNrInPins] */ - /* struct usb_audio_mixer_unit_1 */ + /* union usb_audio_mixer_unit_1 */ } UPACKED; -struct usb_audio_mixer_unit_1 { +struct usb_audio_mixer_v1_unit_1 { uByte bNrChannels; uWord wChannelConfig; uByte iChannelNames; uByte bmControls[255]; /* [bNrChannels] */ /*uByte iMixer;*/ } UPACKED; +struct usb_audio_mixer_v2_unit_1 { + uByte bNrChannels; + uDWord bmChannelConfig; + uByte iChannelNames; + uByte bmControls[255]; /* [bNrChannels] */ + /*uByte iMixer;*/ +} UPACKED; + +union usb_audio_mixer_unit_1 { + struct usb_audio_mixer_v1_unit_1 v1; + struct usb_audio_mixer_v2_unit_1 v2; +}; /* UDESCSUB_AC_SELECTOR */ struct usb_audio_selector_unit {