Module Name: src Committed By: mlelstv Date: Sun Apr 16 19:26:20 UTC 2023
Modified Files: src/sys/dev/usb: uaudio.c uaudioreg.h Log Message: Handle packet scheduling for high/super speed. More UAC2 handling for input/output/feature/selector units. Setting sample rate for UAC2 now works, still no support for clock selectors and multipliers. Added sysctl to set debug level. Minor fixes. To generate a diff of this commit: cvs rdiff -u -r1.178 -r1.179 src/sys/dev/usb/uaudio.c cvs rdiff -u -r1.18 -r1.19 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.178 src/sys/dev/usb/uaudio.c:1.179 --- src/sys/dev/usb/uaudio.c:1.178 Mon Apr 10 15:14:50 2023 +++ src/sys/dev/usb/uaudio.c Sun Apr 16 19:26:20 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: uaudio.c,v 1.178 2023/04/10 15:14:50 mlelstv Exp $ */ +/* $NetBSD: uaudio.c,v 1.179 2023/04/16 19:26:20 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.178 2023/04/10 15:14:50 mlelstv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.179 2023/04/16 19:26:20 mlelstv Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" @@ -59,6 +59,7 @@ __KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1 #include <sys/bus.h> #include <sys/cpu.h> #include <sys/atomic.h> +#include <sys/sysctl.h> #include <sys/audioio.h> #include <dev/audio/audio_if.h> @@ -99,8 +100,12 @@ int uaudiodebug = 0; #define DPRINTFN(n,x,y...) #endif -#define UAUDIO_NCHANBUFS 6 /* number of outstanding request */ -#define UAUDIO_NFRAMES 10 /* ms of sound in each request */ +/* number of outstanding requests */ +#define UAUDIO_NCHANBUFS 6 +/* number of USB frames per request, also the number of ms */ +#define UAUDIO_NFRAMES 10 +/* number of microframes per requewst (high, super) */ +#define UAUDIO_NFRAMES_HI (UAUDIO_NFRAMES * USB_UFRAMES_PER_FRAME) #define MIX_MAX_CHAN 8 @@ -133,7 +138,6 @@ struct mixerctl { u_int nranges; u_int delta; u_int mul; - u_int high; uint8_t class; char ctlname[MAX_AUDIO_DEV_LEN]; const char *ctlunit; @@ -147,6 +151,7 @@ struct as_info { uint8_t attributes; /* Copy of bmAttributes of * usb_audio_streaming_endpoint_descriptor */ + uint8_t terminal; /* connected Terminal ID */ struct usbd_interface * ifaceh; const usb_interface_descriptor_t *idesc; const usb_endpoint_descriptor_audio_t *edesc; @@ -177,12 +182,14 @@ struct chan { int altidx; /* currently used altidx */ int curchanbuf; + u_int nframes; /* UAUDIO_NFRAMES or UAUDIO_NFRAMES_HI */ + u_int nchanbufs; /* 1..UAUDIO_NCHANBUFS */ struct chanbuf { struct chan *chan; struct usbd_xfer *xfer; u_char *buffer; - uint16_t sizes[UAUDIO_NFRAMES]; - uint16_t offsets[UAUDIO_NFRAMES]; + uint16_t sizes[UAUDIO_NFRAMES_HI]; + uint16_t offsets[UAUDIO_NFRAMES_HI]; uint16_t size; } chanbufs[UAUDIO_NCHANBUFS]; @@ -223,9 +230,13 @@ struct uaudio_softc { int sc_nratectls; /* V2 sample rates */ int sc_ratectls[AUFMT_MAX_FREQUENCIES]; int sc_ratemode[AUFMT_MAX_FREQUENCIES]; + int sc_playclock; + int sc_recclock; struct audio_format *sc_formats; int sc_nformats; + uint8_t sc_clock[256]; /* map terminals to clocks */ u_int sc_channel_config; + u_int sc_usb_frames_per_second; char sc_dying; struct audio_device sc_adev; }; @@ -244,7 +255,7 @@ struct io_terminal { 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; + const union usb_audio_feature_unit *fu; const struct usb_audio_processing_unit *pu; const struct usb_audio_extension_unit *eu; const struct usb_audio_clksrc_unit *cu; @@ -254,6 +265,7 @@ struct io_terminal { struct terminal_list **inputs; /* list of source input terminals */ struct terminal_list *output; /* list of destination output terminals */ int direct; /* directly connected to an output terminal */ + uint8_t clock; }; #define UAC_OUTPUT 0 @@ -286,7 +298,7 @@ Static const usb_interface_descriptor_t Static void uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *); Static char *uaudio_id_name - (struct uaudio_softc *, const struct io_terminal *, int); + (struct uaudio_softc *, const struct io_terminal *, uint8_t); #ifdef UAUDIO_DEBUG Static void uaudio_dump_cluster (struct uaudio_softc *, const union usb_audio_cluster *); @@ -307,7 +319,7 @@ Static const char *uaudio_get_terminal_n Static int uaudio_determine_class (const struct io_terminal *, struct mixerctl *); Static const char *uaudio_feature_name - (const struct io_terminal *, struct mixerctl *); + (const struct io_terminal *, uint8_t, int); Static void uaudio_add_feature (struct uaudio_softc *, const struct io_terminal *, int); Static void uaudio_add_processing_updown @@ -348,6 +360,7 @@ Static void uaudio_set Static void uaudio_ctl_set (struct uaudio_softc *, int, struct mixerctl *, int, int); +Static usbd_status uaudio_speed(struct uaudio_softc *, int, int, uint8_t *, int); Static usbd_status uaudio_set_speed(struct uaudio_softc *, int, int, u_int); Static usbd_status uaudio_chan_open(struct uaudio_softc *, struct chan *); @@ -357,7 +370,7 @@ Static usbd_status uaudio_chan_alloc_buf (struct uaudio_softc *, struct chan *); Static void uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *); Static void uaudio_chan_init - (struct chan *, int, const struct audio_params *, int); + (struct chan *, int, const struct audio_params *, int, bool); Static void uaudio_chan_set_param(struct chan *, u_char *, u_char *, int); Static void uaudio_chan_ptransfer(struct chan *); Static void uaudio_chan_pintr @@ -414,7 +427,6 @@ static void uaudio_childdet(device_t, de static int uaudio_activate(device_t, enum devact); - CFATTACH_DECL2_NEW(uaudio, sizeof(struct uaudio_softc), uaudio_match, uaudio_attach, uaudio_detach, uaudio_activate, NULL, uaudio_childdet); @@ -510,6 +522,25 @@ uaudio_attach(device_t parent, device_t sc->sc_playchan.altidx = -1; sc->sc_recchan.altidx = -1; + switch (sc->sc_udev->ud_speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + sc->sc_usb_frames_per_second = USB_FRAMES_PER_SECOND; + sc->sc_playchan.nframes = + sc->sc_recchan.nframes = UAUDIO_NFRAMES; + break; + default: /* HIGH, SUPER, SUPER_PLUS, more ? */ + sc->sc_usb_frames_per_second = USB_FRAMES_PER_SECOND * USB_UFRAMES_PER_FRAME; + sc->sc_playchan.nframes = + sc->sc_recchan.nframes = UAUDIO_NFRAMES_HI; + break; + } + sc->sc_playchan.nchanbufs = + sc->sc_recchan.nchanbufs = UAUDIO_NCHANBUFS; + + DPRINTF("usb fps %u, max channel frames %u, max channel buffers %u\n", + sc->sc_usb_frames_per_second, sc->sc_playchan.nframes, sc->sc_playchan.nchanbufs); + if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_FRAC) sc->sc_altflags |= UA_NOFRAC; @@ -785,11 +816,8 @@ uaudio_mixer_add_ctl(struct uaudio_softc if (mc->mul == 0) mc->mul = 1; - mc->delta = res * 255 / mc->mul; - if (mc->delta > 0 && mc->delta < 255) - mc->high = 255 / mc->delta * mc->delta; - else - mc->high = 255; + + mc->delta = (res * 255 + mc->mul - 1) / mc->mul; #ifdef UAUDIO_DEBUG if (uaudiodebug > 2) { @@ -811,11 +839,12 @@ uaudio_mixer_add_ctl(struct uaudio_softc Static char * uaudio_id_name(struct uaudio_softc *sc, - const struct io_terminal *iot, int id) + const struct io_terminal *iot, uint8_t id) { static char tbuf[32]; - snprintf(tbuf, sizeof(tbuf), "i%d", id); + snprintf(tbuf, sizeof(tbuf), "i%u", id); + return tbuf; } @@ -879,6 +908,7 @@ uaudio_get_cluster(struct uaudio_softc * { union usb_audio_cluster r; const uaudio_cs_descriptor_t *dp; + u_int pins; int i; for (i = 0; i < 25; i++) { /* avoid infinite loops */ @@ -891,37 +921,78 @@ uaudio_get_cluster(struct uaudio_softc * 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)); + 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)); + 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: + /* XXX This is not really right */ id = iot[id].d.ot->v1.bSourceId; break; case UDESCSUB_AC_MIXER: - r = *(const union usb_audio_cluster *) - &iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins]; + switch (sc->sc_version) { + case UAUDIO_VERSION1: + pins = iot[id].d.mu->bNrInPins; + r.v1 = *(const struct usb_audio_v1_cluster *) + &iot[id].d.mu->baSourceId[pins]; + break; + case UAUDIO_VERSION2: + pins = iot[id].d.mu->bNrInPins; + r.v2 = *(const struct usb_audio_v2_cluster *) + &iot[id].d.mu->baSourceId[pins]; + break; + } return r; case UDESCSUB_AC_SELECTOR: /* XXX This is not really right */ id = iot[id].d.su->baSourceId[0]; break; case UDESCSUB_AC_FEATURE: - id = iot[id].d.fu->bSourceId; + /* XXX This is not really right */ + switch (sc->sc_version) { + case UAUDIO_VERSION1: + id = iot[id].d.fu->v1.bSourceId; + break; + case UAUDIO_VERSION2: + id = iot[id].d.fu->v2.bSourceId; + break; + } break; case UDESCSUB_AC_PROCESSING: - r = *(const union usb_audio_cluster *) - &iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins]; + switch (sc->sc_version) { + case UAUDIO_VERSION1: + pins = iot[id].d.pu->bNrInPins; + r.v1 = *(const struct usb_audio_v1_cluster *) + &iot[id].d.pu->baSourceId[pins]; + break; + case UAUDIO_VERSION2: + pins = iot[id].d.pu->bNrInPins; + r.v2 = *(const struct usb_audio_v2_cluster *) + &iot[id].d.pu->baSourceId[pins]; + break; + } return r; case UDESCSUB_AC_EXTENSION: - r = *(const union usb_audio_cluster *) - &iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins]; + switch (sc->sc_version) { + case UAUDIO_VERSION1: + pins = iot[id].d.eu->bNrInPins; + r.v1 = *(const struct usb_audio_v1_cluster *) + &iot[id].d.eu->baSourceId[pins]; + break; + case UAUDIO_VERSION2: + pins = iot[id].d.eu->bNrInPins; + r.v2 = *(const struct usb_audio_v2_cluster *) + &iot[id].d.eu->baSourceId[pins]; + break; + } return r; default: goto bad; @@ -951,9 +1022,10 @@ uaudio_add_input(struct uaudio_softc *sc d->v1.iChannelNames, d->v1.iTerminal); #endif /* If USB input terminal, record wChannelConfig */ - if ((UGETW(d->v1.wTerminalType) & 0xff00) != 0x0100) + if ((UGETW(d->v1.wTerminalType) & 0xff00) != UAT_UNDEFINED) return; sc->sc_channel_config = UGETW(d->v1.wChannelConfig); + sc->sc_clock[id] = 0; break; case UAUDIO_VERSION2: #ifdef UAUDIO_DEBUG @@ -965,9 +1037,10 @@ uaudio_add_input(struct uaudio_softc *sc d->v2.iChannelNames, d->v2.bCSourceId, d->v2.iTerminal); #endif /* If USB input terminal, record wChannelConfig */ - if ((UGETW(d->v2.wTerminalType) & 0xff00) != 0x0100) + if ((UGETW(d->v2.wTerminalType) & 0xff00) != UAT_UNDEFINED) return; sc->sc_channel_config = UGETDW(d->v2.bmChannelConfig); + sc->sc_clock[id] = d->v2.bCSourceId; break; } } @@ -986,12 +1059,14 @@ uaudio_add_output(struct uaudio_softc *s "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", d->v1.bTerminalId, UGETW(d->v1.wTerminalType), d->v1.bAssocTerminal, d->v1.bSourceId, d->v1.iTerminal); + sc->sc_clock[id] = 0; 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); + sc->sc_clock[id] = d->v2.bCSourceId; break; } #endif @@ -1002,15 +1077,20 @@ uaudio_add_mixer(struct uaudio_softc *sc { const struct usb_audio_mixer_unit *d; const union usb_audio_mixer_unit_1 *d1; - int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k; + int c, chs, ichs, ochs, nchs, i, o, bno, p, k; + size_t bm_size; const uByte *bm; struct mixerctl mix; d = iot[id].d.mu; + d1 = (const union usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; DPRINTFN(2,"bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins); - d1 = (const union usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; + mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); + uaudio_determine_class(&iot[id], &mix); + mix.type = MIX_SIGNED_16; + mix.ctlunit = AudioNvolume; /* Compute the number of input channels */ /* and the number of output channels */ @@ -1028,60 +1108,55 @@ uaudio_add_mixer(struct uaudio_softc *sc 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; + bm = d1->v2.bmMixerControls; + bm_size = ichs * ochs / 8 + ((ichs * ochs % 8) ? 1 : 0); + /* bmControls */ + if ((bm[bm_size] & UA_MIX_CLUSTER_MASK) != UA_MIX_CLUSTER_RW) + return; 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++) { switch (sc->sc_version) { case UAUDIO_VERSION1: - chs = uaudio_get_cluster(sc, d->baSourceId[i], iot).v1.bNrChannels; + 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; + chs = uaudio_get_cluster(sc, d->baSourceId[i], iot) + .v2.bNrChannels; break; default: - chs = 0; - break; + continue; } - mc = 0; - for (c = 0; c < chs; c++) { - mo = 0; + +#define _BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1) + + nchs = chs < MIX_MAX_CHAN ? chs : MIX_MAX_CHAN; + + k = 0; + for (c = 0; c < nchs; c++) { for (o = 0; o < ochs; o++) { bno = (p + c) * ochs + o; if (_BIT(bno)) - mo++; + mix.wValue[k++] = + MAKE(p+c+1, o+1); } - if (mo == 1) - mc++; - } - if (mc == chs && chs <= MIX_MAX_CHAN) { - k = 0; - for (c = 0; c < chs; c++) - for (o = 0; o < ochs; o++) { - bno = (p + c) * ochs + o; - if (_BIT(bno)) - mix.wValue[k++] = - MAKE(p+c+1, o+1); - } - snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-%s", - d->bUnitId, uaudio_id_name(sc, iot, - d->baSourceId[i])); - mix.nchan = chs; - uaudio_mixer_add_ctl(sc, &mix); - } else { - /* XXX */ } + mix.nchan = nchs; + + snprintf(mix.ctlname, sizeof(mix.ctlname), + "mix%d-%s", d->bUnitId, + uaudio_id_name(sc, iot, d->baSourceId[i]) + ); + uaudio_mixer_add_ctl(sc, &mix); + #undef _BIT + p += chs; } - } Static void @@ -1095,7 +1170,10 @@ uaudio_add_selector(struct uaudio_softc DPRINTFN(2,"bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins); mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); - mix.wValue[0] = MAKE(0, 0); + if (sc->sc_version == UAUDIO_VERSION2) + mix.wValue[0] = MAKE(V2_CUR_SELECTOR, 0); + else + mix.wValue[0] = MAKE(0, 0); uaudio_determine_class(&iot[id], &mix); mix.nchan = 1; mix.type = MIX_SELECTOR; @@ -1106,8 +1184,9 @@ uaudio_add_selector(struct uaudio_softc mix.mul = mix.range0.maxval - mix.range0.minval; wp = snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId); for (i = 1; i <= d->bNrInPins; i++) { - wp += snprintf(mix.ctlname + wp, MAX_AUDIO_DEV_LEN - wp, - "i%d", d->baSourceId[i - 1]); + wp += strlcpy(mix.ctlname + wp, + uaudio_id_name(sc, iot, d->baSourceId[i-1]), + MAX_AUDIO_DEV_LEN - wp); if (wp > MAX_AUDIO_DEV_LEN - 1) break; } @@ -1242,13 +1321,13 @@ uaudio_determine_class(const struct io_t } Static const char * -uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix) +uaudio_feature_name(const struct io_terminal *iot, + uint8_t class, int terminal_type) { - int terminal_type; - terminal_type = uaudio_determine_class(iot, mix); - if (mix->class == UAC_RECORD && terminal_type == 0) + if (class == UAC_RECORD && terminal_type == 0) return AudioNmixerout; + DPRINTF("terminal_type=%s\n", uaudio_get_terminal_name(terminal_type)); switch (terminal_type) { case UAT_STREAM: @@ -1342,123 +1421,229 @@ uaudio_feature_name(const struct io_term return AudioNmaster; } +static void +uaudio_add_feature_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, + int unit, int ctl, struct mixerctl *mc) +{ + const char *mixername, *attr = NULL; + int terminal_type; + + mc->wIndex = MAKE(unit, sc->sc_ac_iface); + terminal_type = uaudio_determine_class(iot, mc); + mixername = uaudio_feature_name(iot, mc->class, terminal_type); + switch (ctl) { + case MUTE_CONTROL: + mc->type = MIX_ON_OFF; + mc->ctlunit = ""; + attr = AudioNmute; + break; + case VOLUME_CONTROL: + mc->type = MIX_SIGNED_16; + mc->ctlunit = AudioNvolume; + attr = NULL; + break; + case BASS_CONTROL: + mc->type = MIX_SIGNED_8; + mc->ctlunit = AudioNbass; + attr = AudioNbass; + break; + case MID_CONTROL: + mc->type = MIX_SIGNED_8; + mc->ctlunit = AudioNmid; + attr = AudioNmid; + break; + case TREBLE_CONTROL: + mc->type = MIX_SIGNED_8; + mc->ctlunit = AudioNtreble; + attr = AudioNtreble; + break; + case GRAPHIC_EQUALIZER_CONTROL: + return; /* XXX don't add anything */ + break; + case AGC_CONTROL: + mc->type = MIX_ON_OFF; + mc->ctlunit = ""; + attr = AudioNagc; + break; + case DELAY_CONTROL: + mc->type = MIX_UNSIGNED_16; + mc->ctlunit = "4 ms"; + attr = AudioNdelay; + break; + case BASS_BOOST_CONTROL: + mc->type = MIX_ON_OFF; + mc->ctlunit = ""; + attr = AudioNbassboost; + break; + case LOUDNESS_CONTROL: + mc->type = MIX_ON_OFF; + mc->ctlunit = ""; + attr = AudioNloudness; + break; + case GAIN_CONTROL: + mc->type = MIX_SIGNED_16; + mc->ctlunit = "gain"; + attr = "gain";; + break; + case GAINPAD_CONTROL: + mc->type = MIX_SIGNED_16; + mc->ctlunit = "gainpad"; + attr = "gainpad";; + break; + case PHASEINV_CONTROL: + mc->type = MIX_ON_OFF; + mc->ctlunit = ""; + attr = "phaseinv";; + break; + case UNDERFLOW_CONTROL: + mc->type = MIX_ON_OFF; + mc->ctlunit = ""; + attr = "underflow";; + break; + case OVERFLOW_CONTROL: + mc->type = MIX_ON_OFF; + mc->ctlunit = ""; + attr = "overflow";; + break; + default: + return; /* XXX don't add anything */ + break; + } + + if (attr != NULL) { + snprintf(mc->ctlname, sizeof(mc->ctlname), + "%s.%s", mixername, attr); + } else { + snprintf(mc->ctlname, sizeof(mc->ctlname), + "%s", mixername); + } + + uaudio_mixer_add_ctl(sc, mc); +} + Static void uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { - const struct usb_audio_feature_unit *d; + const union usb_audio_feature_unit *d; const uByte *ctls; + const uDWord *ctls2; int ctlsize; int nchan; u_int fumask, mmask, cmask; struct mixerctl mix; int chan, ctl, i, unit; - const char *mixername; -#define GET(i) (ctls[(i)*ctlsize] | \ - (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0)) d = iot[id].d.fu; - ctls = d->bmaControls; - ctlsize = d->bControlSize; - if (ctlsize == 0) { - DPRINTF("ignoring feature %d with controlSize of zero\n", id); - return; - } - nchan = (d->bLength - 7) / ctlsize; - mmask = GET(0); - /* Figure out what we can control */ - for (cmask = 0, chan = 1; chan < nchan; chan++) { - DPRINTFN(9,"chan=%d mask=%x\n", - chan, GET(chan)); - cmask |= GET(chan); - } - - DPRINTFN(1,"bUnitId=%d, " - "%d channels, mmask=0x%04x, cmask=0x%04x\n", - d->bUnitId, nchan, mmask, cmask); - - if (nchan > MIX_MAX_CHAN) - nchan = MIX_MAX_CHAN; - unit = d->bUnitId; - mix.wIndex = MAKE(unit, sc->sc_ac_iface); - for (ctl = MUTE_CONTROL; ctl < LOUDNESS_CONTROL; ctl++) { - fumask = FU_MASK(ctl); - DPRINTFN(4,"ctl=%d fumask=0x%04x\n", - ctl, fumask); - if (mmask & fumask) { - mix.nchan = 1; - mix.wValue[0] = MAKE(ctl, 0); - } else if (cmask & fumask) { - mix.nchan = nchan - 1; - for (i = 1; i < nchan; i++) { - if (GET(i) & fumask) - mix.wValue[i-1] = MAKE(ctl, i); - else - mix.wValue[i-1] = -1; + + switch (sc->sc_version) { + case UAUDIO_VERSION1: + +#define GETV1(i) (ctls[(i)*ctlsize] | \ + (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0)) + + ctls = d->v1.bmaControls; + ctlsize = d->v1.bControlSize; + if (ctlsize == 0) { + DPRINTF("ignoring feature %d with controlSize of zero\n", id); + return; + } + + /* offsetof bmaControls + sizeof iFeature == 7 */ + nchan = (d->v1.bLength - 7) / ctlsize; + mmask = GETV1(0); + /* Figure out what we can control */ + for (cmask = 0, chan = 1; chan < nchan; chan++) { + DPRINTFN(9,"chan=%d mask=%x\n", + chan, GETV1(chan)); + cmask |= GETV1(chan); + } + + DPRINTFN(1,"bUnitId=%d, " + "%d channels, mmask=0x%04x, cmask=0x%04x\n", + d->v1.bUnitId, nchan, mmask, cmask); + + if (nchan > MIX_MAX_CHAN) + nchan = MIX_MAX_CHAN; + unit = d->v1.bUnitId; + + for (ctl = MUTE_CONTROL; ctl <= LOUDNESS_CONTROL; ctl++) { + fumask = FU_MASK(ctl); + DPRINTFN(4,"ctl=%d fumask=0x%04x\n", + ctl, fumask); + if (mmask & fumask) { + mix.nchan = 1; + mix.wValue[0] = MAKE(ctl, 0); + } else if (cmask & fumask) { + mix.nchan = nchan - 1; + for (i = 1; i < nchan; i++) { + if (GETV1(i) & fumask) + mix.wValue[i-1] = MAKE(ctl, i); + else + mix.wValue[i-1] = -1; + } + } else { + continue; } - } else { - continue; + + uaudio_add_feature_mixer(sc, &iot[id], unit, ctl, &mix); } -#undef GET - mixername = uaudio_feature_name(&iot[id], &mix); - switch (ctl) { - case MUTE_CONTROL: - mix.type = MIX_ON_OFF; - mix.ctlunit = ""; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "%s.%s", mixername, AudioNmute); - break; - case VOLUME_CONTROL: - mix.type = MIX_SIGNED_16; - mix.ctlunit = AudioNvolume; - strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname)); - break; - case BASS_CONTROL: - mix.type = MIX_SIGNED_8; - mix.ctlunit = AudioNbass; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "%s.%s", mixername, AudioNbass); - break; - case MID_CONTROL: - mix.type = MIX_SIGNED_8; - mix.ctlunit = AudioNmid; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "%s.%s", mixername, AudioNmid); - break; - case TREBLE_CONTROL: - mix.type = MIX_SIGNED_8; - mix.ctlunit = AudioNtreble; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "%s.%s", mixername, AudioNtreble); - break; - case GRAPHIC_EQUALIZER_CONTROL: - continue; /* XXX don't add anything */ - break; - case AGC_CONTROL: - mix.type = MIX_ON_OFF; - mix.ctlunit = ""; - snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", - mixername, AudioNagc); - break; - case DELAY_CONTROL: - mix.type = MIX_UNSIGNED_16; - mix.ctlunit = "4 ms"; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "%s.%s", mixername, AudioNdelay); - break; - case BASS_BOOST_CONTROL: - mix.type = MIX_ON_OFF; - mix.ctlunit = ""; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "%s.%s", mixername, AudioNbassboost); - break; - case LOUDNESS_CONTROL: - mix.type = MIX_ON_OFF; - mix.ctlunit = ""; - snprintf(mix.ctlname, sizeof(mix.ctlname), - "%s.%s", mixername, AudioNloudness); - break; +#undef GETV1 + break; + + case UAUDIO_VERSION2: + +#define GETV2(i) UGETDW(ctls2[(i)]) + + ctls2 = d->v2.bmaControls; + + /* offsetof bmaControls + sizeof iFeature == 6 */ + nchan = (d->v2.bLength - 6) / 4; + if (nchan <= 0) { + DPRINTF("ignoring feature %d with no controls\n", id); + return; } - uaudio_mixer_add_ctl(sc, &mix); + + mmask = GETV2(0); + /* Figure out what we can control */ + for (cmask = 0, chan = 1; chan < nchan; chan++) { + DPRINTFN(9,"chan=%d mask=%x\n", + chan, GETV2(chan)); + cmask |= GETV2(chan); + } + + DPRINTFN(1,"bUnitId=%d, " + "%d channels, mmask=0x%04x, cmask=0x%04x\n", + d->v2.bUnitId, nchan, mmask, cmask); + + if (nchan > MIX_MAX_CHAN) + nchan = MIX_MAX_CHAN; + unit = d->v2.bUnitId; + + for (ctl = MUTE_CONTROL; ctl <= OVERFLOW_CONTROL; ctl++) { + fumask = V2_FU_MASK(ctl); + DPRINTFN(4,"ctl=%d fumask=0x%08x\n", + ctl, fumask); + + if (mmask & fumask) { + mix.nchan = 1; + mix.wValue[0] = MAKE(ctl, 0); + } else if (cmask & fumask) { + mix.nchan = nchan-1; + for (i = 1; i < nchan; ++i) { + if (GETV2(i) & fumask) + mix.wValue[i-1] = MAKE(ctl, i); + else + mix.wValue[i-1] = -1; + } + } else { + continue; + } + + uaudio_add_feature_mixer(sc, &iot[id], unit, ctl, &mix); + } + +#undef GETV2 + break; } } @@ -1750,7 +1935,17 @@ uaudio_io_terminaltype(struct uaudio_sof it->inputs_size = 1; return uaudio_merge_terminal_list(it); case UDESCSUB_AC_FEATURE: - src_id = it->d.fu->bSourceId; + switch (sc->sc_version) { + case UAUDIO_VERSION1: + src_id = it->d.fu->v1.bSourceId; + break; + case UAUDIO_VERSION2: + src_id = it->d.fu->v2.bSourceId; + break; + default: + /* cannot happen */ + return NULL; + } it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); if (it->inputs == NULL) { aprint_error("uaudio_io_terminaltype: no memory\n"); @@ -1891,7 +2086,7 @@ uaudio_process_as(struct uaudio_softc *s const usb_endpoint_descriptor_audio_t *ed; const usb_endpoint_descriptor_audio_t *epdesc1; const struct usb_audio_streaming_endpoint_descriptor *sed; - int format, chan __unused, prec, bps, enc; + int format, chan __unused, prec, bps, enc, terminal; int dir, type, sync, epcount; struct as_info ai; const char *format_str __unused; @@ -1950,7 +2145,9 @@ uaudio_process_as(struct uaudio_softc *s "bInterval=%d bRefresh=%d bSynchAddress=%d\n", ed->bLength, ed->bDescriptorType, ed->bEndpointAddress, ed->bmAttributes, UGETW(ed->wMaxPacketSize), - ed->bInterval, ed->bRefresh, ed->bSynchAddress); + ed->bInterval, + ed->bLength > 7 ? ed->bRefresh : 0, + ed->bLength > 8 ? ed->bSynchAddress : 0); if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS) return USBD_INVAL; break; @@ -1963,9 +2160,10 @@ uaudio_process_as(struct uaudio_softc *s epdesc1->bLength, epdesc1->bDescriptorType, epdesc1->bEndpointAddress, epdesc1->bmAttributes, UGETW(epdesc1->wMaxPacketSize), epdesc1->bInterval, - epdesc1->bRefresh, epdesc1->bSynchAddress); + epdesc1->bLength > 7 ? epdesc1->bRefresh : 0, + epdesc1->bLength > 8 ? epdesc1->bSynchAddress : 0); #if 0 - if (epdesc1->bSynchAddress != 0) { + if (epdesc1->bLength > 8 && epdesc1->bSynchAddress != 0) { aprint_error_dev(sc->sc_dev, "invalid endpoint: bSynchAddress=0\n"); return USBD_INVAL; @@ -1978,7 +2176,7 @@ uaudio_process_as(struct uaudio_softc *s return USBD_INVAL; } #if 0 - if (epdesc1->bEndpointAddress != ed->bSynchAddress) { + if (ed->bLength > 8 && epdesc1->bEndpointAddress != ed->bSynchAddress) { aprint_error_dev(sc->sc_dev, "invalid endpoint addresses: " "ep[0]->bSynchAddress=%#x " @@ -2099,6 +2297,7 @@ leave: enc = AUDIO_ENCODING_NONE; switch (sc->sc_version) { case UAUDIO_VERSION1: + terminal = 0; switch (format) { case UA_FMT_PCM: if (prec == 8) { @@ -2135,6 +2334,7 @@ leave: } break; case UAUDIO_VERSION2: + terminal = asid->v2.bTerminalLink; if (format & UA_V2_FMT_PCM) { if (prec == 8) { sc->sc_altflags |= HAS_8; @@ -2171,11 +2371,11 @@ leave: return USBD_NORMAL_COMPLETION; } #ifdef UAUDIO_DEBUG + aprint_debug_dev(sc->sc_dev, "%s: %dch, %d/%dbit, %s,", + dir == UE_DIR_IN ? "recording" : "playback", + chan, prec, bps * 8, format_str); switch (sc->sc_version) { case UAUDIO_VERSION1: - aprint_debug_dev(sc->sc_dev, "%s: %dch, %d/%dbit, %s,", - dir == UE_DIR_IN ? "recording" : "playback", - chan, prec, asf1d->v1.bSubFrameSize * 8, format_str); if (asf1d->v1.bSamFreqType == UA_SAMP_CONTINUOUS) { aprint_debug(" %d-%dHz\n", UA_SAMP_LO(&asf1d->v1), UA_SAMP_HI(&asf1d->v1)); @@ -2187,7 +2387,7 @@ leave: aprint_debug("Hz\n"); } break; - /* XXX V2 */ + /* UAUDIO_VERSION2 has no frequency information in the format */ } #endif ai.alt = id->bAlternateSetting; @@ -2201,6 +2401,7 @@ leave: ai.nchan = chan; ai.aformat = NULL; ai.ifaceh = NULL; + ai.terminal = terminal; uaudio_add_alt(sc, &ai); #ifdef UAUDIO_DEBUG if (ai.attributes & UA_SED_FREQ_CONTROL) @@ -2390,6 +2591,11 @@ uaudio_build_formats(struct uaudio_softc break; } + DPRINTF("alt[%d] = %d/%d %dch %u[%u,%u,...] alt %u\n", i, + auf->validbits, auf->precision, auf->channels, auf->frequency_type, + auf->frequency[0], auf->frequency[1], + as->idesc->bAlternateSetting); + sc->sc_alts[i].aformat = auf; } } @@ -2557,10 +2763,24 @@ uaudio_identify_ac(struct uaudio_softc * printf("\n"); break; case UDESCSUB_AC_FEATURE: - printf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId); + switch (sc->sc_version) { + case UAUDIO_VERSION1: + printf("AC_FEATURE src=%d\n", iot[i].d.fu->v1.bSourceId); + break; + case UAUDIO_VERSION2: + printf("AC_FEATURE src=%d\n", iot[i].d.fu->v2.bSourceId); + break; + } break; case UDESCSUB_AC_EFFECT: - printf("AC_EFFECT src=%d\n", iot[i].d.fu->bSourceId); + switch (sc->sc_version) { + case UAUDIO_VERSION1: + printf("AC_EFFECT src=%d\n", iot[i].d.fu->v1.bSourceId); + break; + case UAUDIO_VERSION2: + printf("AC_EFFECT src=%d\n", iot[i].d.fu->v2.bSourceId); + break; + } break; case UDESCSUB_AC_PROCESSING: printf("AC_PROCESSING src="); @@ -2687,13 +2907,15 @@ uaudio_identify_ac(struct uaudio_softc * 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)) + 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)) + wi == MAKE(iot[i].d.ot->v2.bCSourceId, sc->sc_ac_iface)) { sc->sc_ratemode[j] |= AUMODE_PLAY; + } break; } } @@ -2905,13 +3127,14 @@ uaudio_round_blocksize(void *addr, int b /* chan.bytes_per_frame can be 0. */ if (mode == AUMODE_PLAY || sc->sc_recchan.bytes_per_frame <= 0) { - b = param->sample_rate * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS; + b = param->sample_rate * sc->sc_recchan.nframes + * sc->sc_recchan.nchanbufs; /* * This does not make accurate value in the case - * of b % USB_FRAMES_PER_SECOND != 0 + * of b % usb_frames_per_second != 0 */ - b /= USB_FRAMES_PER_SECOND; + b /= sc->sc_usb_frames_per_second; b *= param->precision / 8 * param->channels; } else { @@ -2920,7 +3143,7 @@ uaudio_round_blocksize(void *addr, int b * See uaudio_set_format() and uaudio_chan_init() */ b = sc->sc_recchan.bytes_per_frame - * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS; + * sc->sc_recchan.nframes * sc->sc_recchan.nchanbufs; } if (b <= 0) @@ -3073,7 +3296,7 @@ uaudio_set(struct uaudio_softc *sc, int err = usbd_do_request(sc->sc_udev, &req, data); #ifdef UAUDIO_DEBUG if (err) - DPRINTF("err=%d\n", err); + DPRINTF("err=%s\n", usbd_errstr(err)); #endif } @@ -3122,11 +3345,15 @@ uaudio_value2bsd(struct mixerctl *mc, in if (mc->type == MIX_ON_OFF) { val = (val != 0); } else if (mc->type == MIX_SELECTOR) { - if (val < mc->ranges[0].minval || val > mc->ranges[0].maxval) + if (val < mc->ranges[0].minval) val = mc->ranges[0].minval; + if (val > mc->ranges[0].maxval) + val = mc->ranges[0].maxval; + } else if (mc->mul > 0) { + val = ((uaudio_signext(mc->type, val) - mc->ranges[0].minval) + * 255 + mc->mul - 1) / mc->mul; } else - val = (uaudio_signext(mc->type, val) - mc->ranges[0].minval) - * 255 / mc->mul; + val = 0; DPRINTFN_CLEAN(5, "val'=%d\n", val); return val; } @@ -3134,36 +3361,38 @@ uaudio_value2bsd(struct mixerctl *mc, in Static int uaudio_bsd2value(struct mixerctl *mc, int val) { + int i; + DPRINTFN(5,"type=%03x val=%d min=%d max=%d ", mc->type, val, mc->ranges[0].minval, mc->ranges[0].maxval); if (mc->type == MIX_ON_OFF) { val = (val != 0); } else if (mc->type == MIX_SELECTOR) { - if (val < mc->ranges[0].minval || val > mc->ranges[0].maxval) + if (val < mc->ranges[0].minval) val = mc->ranges[0].minval; + if (val > mc->ranges[0].maxval) + val = mc->ranges[0].maxval; } else { if (val < 0) val = 0; - else if (val > mc->high) - val = mc->high; + else if (val > 255) + val = 255; - val = val * mc->mul / mc->high + mc->ranges[0].minval; + val = val * (mc->mul + 1) / 256 + mc->ranges[0].minval; - if (mc->nranges > 0) { - int i; + for (i=0; i<mc->nranges; ++i) { + struct range *r = &mc->ranges[i]; - for (i=0; i<mc->nranges; ++i) { - struct range *r = &mc->ranges[i]; - - if (val > r->maxval) - continue; - if (val < r->minval) - val = r->minval; - val = (val - r->minval + r->resval/2) - / r->resval * r->resval - + r->minval; - break; - } + if (r->resval == 0) + continue; + if (val > r->maxval) + continue; + if (val < r->minval) + val = r->minval; + val = (val - r->minval + r->resval/2) + / r->resval * r->resval + + r->minval; + break; } } DPRINTFN_CLEAN(5, "val'=%d\n", val); @@ -3174,10 +3403,10 @@ Static const char * uaudio_clockname(u_int attr) { static const char *names[] = { - "ext", - "fixed", - "var", - "prog" + "clkext", + "clkfixed", + "clkvar", + "clkprog" }; return names[attr & 3]; @@ -3191,7 +3420,12 @@ uaudio_makename(struct uaudio_softc *sc, tmp = kmem_alloc(USB_MAX_ENCODED_STRING_LEN, KM_SLEEP); err = usbd_get_string0(sc->sc_udev, idx, tmp, true); - count = snprintf(buf, len, "%s%d", err ? defname : tmp, id); + + if (id != 0 || err) + count = snprintf(buf, len, "%s%d", err ? defname : tmp, id); + else + count = snprintf(buf, len, "%s", err ? defname : tmp); + kmem_free(tmp, USB_MAX_ENCODED_STRING_LEN); return count; @@ -3204,7 +3438,7 @@ uaudio_ctl_get(struct uaudio_softc *sc, { int val; - DPRINTFN(5,"which=%d chan=%d\n", which, chan); + DPRINTFN(5,"which=%d chan=%d ctl=%s type=%d\n", which, chan, mc->ctlname, mc->type); mutex_exit(&sc->sc_lock); val = uaudio_get(sc, which, UT_READ_CLASS_INTERFACE, mc->wValue[chan], mc->wIndex, MIX_SIZE(mc->type)); @@ -3217,6 +3451,7 @@ uaudio_ctl_set(struct uaudio_softc *sc, int chan, int val) { + DPRINTFN(5,"which=%d chan=%d ctl=%s type=%d\n", which, chan, mc->ctlname, mc->type); val = uaudio_bsd2value(mc, val); mutex_exit(&sc->sc_lock); uaudio_set(sc, which, UT_WRITE_CLASS_INTERFACE, mc->wValue[chan], @@ -3230,12 +3465,15 @@ uaudio_mixer_get_port(void *addr, mixer_ struct uaudio_softc *sc; struct mixerctl *mc; int i, n, vals[MIX_MAX_CHAN], val; + int req; DPRINTFN(2, "index=%d\n", cp->dev); sc = addr; if (sc->sc_dying) return EIO; + req = sc->sc_version == UAUDIO_VERSION2 ? V2_CUR : GET_CUR; + n = cp->dev - UAC_NCLASSES; if (n < 0 || n >= sc->sc_nctls) return ENXIO; @@ -3244,11 +3482,11 @@ uaudio_mixer_get_port(void *addr, mixer_ if (mc->type == MIX_ON_OFF) { if (cp->type != AUDIO_MIXER_ENUM) return EINVAL; - cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0); + cp->un.ord = uaudio_ctl_get(sc, req, mc, 0); } else if (mc->type == MIX_SELECTOR) { if (cp->type != AUDIO_MIXER_ENUM) return EINVAL; - cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0); + cp->un.ord = uaudio_ctl_get(sc, req, mc, 0); } else { if (cp->type != AUDIO_MIXER_VALUE) return EINVAL; @@ -3256,7 +3494,7 @@ uaudio_mixer_get_port(void *addr, mixer_ cp->un.value.num_channels != mc->nchan) return EINVAL; for (i = 0; i < mc->nchan; i++) - vals[i] = uaudio_ctl_get(sc, GET_CUR, mc, i); + vals[i] = uaudio_ctl_get(sc, req, mc, i); if (cp->un.value.num_channels == 1 && mc->nchan != 1) { for (val = 0, i = 0; i < mc->nchan; i++) val += vals[i]; @@ -3275,12 +3513,15 @@ uaudio_mixer_set_port(void *addr, mixer_ struct uaudio_softc *sc; struct mixerctl *mc; int i, n, vals[MIX_MAX_CHAN]; + int req; DPRINTFN(2, "index = %d\n", cp->dev); sc = addr; if (sc->sc_dying) return EIO; + req = sc->sc_version == UAUDIO_VERSION2 ? V2_CUR : SET_CUR; + n = cp->dev - UAC_NCLASSES; if (n < 0 || n >= sc->sc_nctls) return ENXIO; @@ -3289,11 +3530,11 @@ uaudio_mixer_set_port(void *addr, mixer_ if (mc->type == MIX_ON_OFF) { if (cp->type != AUDIO_MIXER_ENUM) return EINVAL; - uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord); + uaudio_ctl_set(sc, req, mc, 0, cp->un.ord); } else if (mc->type == MIX_SELECTOR) { if (cp->type != AUDIO_MIXER_ENUM) return EINVAL; - uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord); + uaudio_ctl_set(sc, req, mc, 0, cp->un.ord); } else { if (cp->type != AUDIO_MIXER_VALUE) return EINVAL; @@ -3306,7 +3547,7 @@ uaudio_mixer_set_port(void *addr, mixer_ else return EINVAL; for (i = 0; i < mc->nchan; i++) - uaudio_ctl_set(sc, SET_CUR, mc, i, vals[i]); + uaudio_ctl_set(sc, req, mc, i, vals[i]); } return 0; } @@ -3358,7 +3599,7 @@ uaudio_trigger_input(void *addr, void *s * Start as half as many channels for recording as for playback. * This stops playback from stuttering in full-duplex operation. */ - for (i = 0; i < UAUDIO_NCHANBUFS / 2; i++) { + for (i = 0; i < ch->nchanbufs / 2; i++) { uaudio_chan_rtransfer(ch); } @@ -3409,7 +3650,7 @@ uaudio_trigger_output(void *addr, void * ch->intr = intr; ch->arg = arg; - for (i = 0; i < UAUDIO_NCHANBUFS; i++) + for (i = 0; i < ch->nchanbufs; i++) uaudio_chan_ptransfer(ch); mutex_enter(&sc->sc_intr_lock); @@ -3423,14 +3664,14 @@ uaudio_chan_open(struct uaudio_softc *sc { struct as_info *as; usb_device_descriptor_t *ddesc; - int endpt, ifnum; + int endpt, clkid; usbd_status err; as = &sc->sc_alts[ch->altidx]; endpt = as->edesc->bEndpointAddress; - ifnum = as->idesc->bInterfaceNumber; - DPRINTF("endpt=0x%02x, speed=%d, alt=%d\n", - endpt, ch->sample_rate, as->alt); + clkid = sc->sc_clock[as->terminal]; + DPRINTF("endpt=0x%02x, clkid=%d, speed=%d, alt=%d\n", + endpt, clkid, ch->sample_rate, as->alt); /* Set alternate interface corresponding to the mode. */ err = usbd_set_interface(as->ifaceh, as->alt); @@ -3443,7 +3684,7 @@ uaudio_chan_open(struct uaudio_softc *sc ddesc = usbd_get_device_descriptor(sc->sc_udev); if ((UGETW(ddesc->idVendor) != USB_VENDOR_ROLAND) && (UGETW(ddesc->idProduct) != USB_PRODUCT_ROLAND_SD90)) { - err = uaudio_set_speed(sc, ifnum, endpt, ch->sample_rate); + err = uaudio_set_speed(sc, endpt, clkid, ch->sample_rate); if (err) { DPRINTF("set_speed failed err=%s\n", usbd_errstr(err)); } @@ -3507,11 +3748,11 @@ uaudio_chan_alloc_buffers(struct uaudio_ { int i, size; - size = (ch->bytes_per_frame + ch->sample_size) * UAUDIO_NFRAMES; - for (i = 0; i < UAUDIO_NCHANBUFS; i++) { + size = (ch->bytes_per_frame + ch->sample_size) * ch->nframes; + for (i = 0; i < ch->nchanbufs; i++) { struct usbd_xfer *xfer; - int err = usbd_create_xfer(ch->pipe, size, 0, UAUDIO_NFRAMES, + int err = usbd_create_xfer(ch->pipe, size, 0, ch->nframes, &xfer); if (err) goto bad; @@ -3535,34 +3776,35 @@ uaudio_chan_free_buffers(struct uaudio_s { int i; - for (i = 0; i < UAUDIO_NCHANBUFS; i++) + for (i = 0; i < ch->nchanbufs; i++) usbd_destroy_xfer(ch->chanbufs[i].xfer); } Static void uaudio_chan_ptransfer(struct chan *ch) { + struct uaudio_softc *sc = ch->sc; struct chanbuf *cb; int i, n, size, residue, total; - if (ch->sc->sc_dying) + if (sc->sc_dying) return; /* Pick the next channel buffer. */ cb = &ch->chanbufs[ch->curchanbuf]; - if (++ch->curchanbuf >= UAUDIO_NCHANBUFS) + if (++ch->curchanbuf >= ch->nchanbufs) ch->curchanbuf = 0; /* Compute the size of each frame in the next transfer. */ residue = ch->residue; total = 0; - for (i = 0; i < UAUDIO_NFRAMES; i++) { + for (i = 0; i < ch->nframes; i++) { size = ch->bytes_per_frame; residue += ch->fraction; - if (residue >= USB_FRAMES_PER_SECOND) { - if ((ch->sc->sc_altflags & UA_NOFRAC) == 0) + if (residue >= sc->sc_usb_frames_per_second) { + if ((sc->sc_altflags & UA_NOFRAC) == 0) size += ch->sample_size; - residue -= USB_FRAMES_PER_SECOND; + residue -= sc->sc_usb_frames_per_second; } cb->sizes[i] = size; total += size; @@ -3588,7 +3830,7 @@ uaudio_chan_ptransfer(struct chan *ch) #ifdef UAUDIO_DEBUG if (uaudiodebug > 8) { DPRINTF("buffer=%p, residue=0.%03d\n", cb->buffer, ch->residue); - for (i = 0; i < UAUDIO_NFRAMES; i++) { + for (i = 0; i < ch->nframes; i++) { DPRINTF(" [%d] length %d\n", i, cb->sizes[i]); } } @@ -3596,30 +3838,32 @@ uaudio_chan_ptransfer(struct chan *ch) //DPRINTFN(5, "ptransfer xfer=%p\n", cb->xfer); /* Fill the request */ - usbd_setup_isoc_xfer(cb->xfer, cb, cb->sizes, UAUDIO_NFRAMES, 0, + usbd_setup_isoc_xfer(cb->xfer, cb, cb->sizes, ch->nframes, 0, uaudio_chan_pintr); usbd_status err = usbd_transfer(cb->xfer); if (err != USBD_IN_PROGRESS && err != USBD_NORMAL_COMPLETION) - device_printf(ch->sc->sc_dev, "ptransfer error %d\n", err); + device_printf(sc->sc_dev, "ptransfer error %d\n", err); } Static void uaudio_chan_pintr(struct usbd_xfer *xfer, void *priv, usbd_status status) { + struct uaudio_softc *sc; struct chanbuf *cb; struct chan *ch; uint32_t count; cb = priv; ch = cb->chan; + sc = ch->sc; /* Return if we are aborting. */ if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) - device_printf(ch->sc->sc_dev, "pintr error: %s\n", + device_printf(sc->sc_dev, "pintr error: %s\n", usbd_errstr(status)); usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); @@ -3627,13 +3871,13 @@ uaudio_chan_pintr(struct usbd_xfer *xfer count, ch->transferred); #ifdef DIAGNOSTIC if (count != cb->size) { - device_printf(ch->sc->sc_dev, + device_printf(sc->sc_dev, "uaudio_chan_pintr: count(%d) != size(%d), status(%d)\n", count, cb->size, status); } #endif - mutex_enter(&ch->sc->sc_intr_lock); + mutex_enter(&sc->sc_intr_lock); ch->transferred += cb->size; /* Call back to upper layer */ while (ch->transferred >= ch->blksize) { @@ -3641,7 +3885,7 @@ uaudio_chan_pintr(struct usbd_xfer *xfer DPRINTFN(5, "call %p(%p)\n", ch->intr, ch->arg); ch->intr(ch->arg); } - mutex_exit(&ch->sc->sc_intr_lock); + mutex_exit(&sc->sc_intr_lock); /* start next transfer */ uaudio_chan_ptransfer(ch); @@ -3650,22 +3894,31 @@ uaudio_chan_pintr(struct usbd_xfer *xfer Static void uaudio_chan_rtransfer(struct chan *ch) { + struct uaudio_softc *sc = ch->sc; struct chanbuf *cb; int i, size, residue, total; - if (ch->sc->sc_dying) + if (sc->sc_dying) return; /* Pick the next channel buffer. */ cb = &ch->chanbufs[ch->curchanbuf]; - if (++ch->curchanbuf >= UAUDIO_NCHANBUFS) + if (++ch->curchanbuf >= ch->nchanbufs) ch->curchanbuf = 0; /* Compute the size of each frame in the next transfer. */ residue = ch->residue; total = 0; - for (i = 0; i < UAUDIO_NFRAMES; i++) { + for (i = 0; i < ch->nframes; i++) { size = ch->bytes_per_frame; +#if 0 + residue += ch->fraction; + if (residue >= sc->sc_usb_frames_per_second) { + if ((sc->sc_altflags & UA_NOFRAC) == 0) + size += ch->sample_size; + residue -= sc->sc_usb_frames_per_second; + } +#endif cb->sizes[i] = size; cb->offsets[i] = total; total += size; @@ -3676,7 +3929,7 @@ uaudio_chan_rtransfer(struct chan *ch) #ifdef UAUDIO_DEBUG if (uaudiodebug > 8) { DPRINTF("buffer=%p, residue=0.%03d\n", cb->buffer, ch->residue); - for (i = 0; i < UAUDIO_NFRAMES; i++) { + for (i = 0; i < ch->nframes; i++) { DPRINTF(" [%d] length %d\n", i, cb->sizes[i]); } } @@ -3684,18 +3937,19 @@ uaudio_chan_rtransfer(struct chan *ch) DPRINTFN(5, "transfer xfer=%p\n", cb->xfer); /* Fill the request */ - usbd_setup_isoc_xfer(cb->xfer, cb, cb->sizes, UAUDIO_NFRAMES, 0, + usbd_setup_isoc_xfer(cb->xfer, cb, cb->sizes, ch->nframes, 0, uaudio_chan_rintr); usbd_status err = usbd_transfer(cb->xfer); if (err != USBD_IN_PROGRESS && err != USBD_NORMAL_COMPLETION) - device_printf(ch->sc->sc_dev, "rtransfer error %d\n", err); + device_printf(sc->sc_dev, "rtransfer error %d\n", err); } Static void uaudio_chan_rintr(struct usbd_xfer *xfer, void *priv, usbd_status status) { + struct uaudio_softc *sc; struct chanbuf *cb; struct chan *ch; uint32_t count; @@ -3703,12 +3957,13 @@ uaudio_chan_rintr(struct usbd_xfer *xfer cb = priv; ch = cb->chan; + sc = ch->sc; /* Return if we are aborting. */ if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION && status != USBD_SHORT_XFER) - device_printf(ch->sc->sc_dev, "rintr error: %s\n", + device_printf(sc->sc_dev, "rintr error: %s\n", usbd_errstr(status)); usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); @@ -3717,7 +3972,7 @@ uaudio_chan_rintr(struct usbd_xfer *xfer /* count < cb->size is normal for asynchronous source */ #ifdef DIAGNOSTIC if (count > cb->size) { - device_printf(ch->sc->sc_dev, + device_printf(sc->sc_dev, "uaudio_chan_rintr: count(%d) > size(%d) status(%d)\n", count, cb->size, status); } @@ -3727,7 +3982,7 @@ uaudio_chan_rintr(struct usbd_xfer *xfer * Transfer data from channel buffer to upper layer buffer, taking * care of wrapping the upper layer buffer. */ - for (i = 0; i < UAUDIO_NFRAMES; i++) { + for (i = 0; i < ch->nframes; i++) { frsize = cb->sizes[i]; n = uimin(frsize, ch->end - ch->cur); memcpy(ch->cur, cb->buffer + cb->offsets[i], n); @@ -3742,37 +3997,54 @@ uaudio_chan_rintr(struct usbd_xfer *xfer } /* Call back to upper layer */ - mutex_enter(&ch->sc->sc_intr_lock); + mutex_enter(&sc->sc_intr_lock); ch->transferred += count; while (ch->transferred >= ch->blksize) { ch->transferred -= ch->blksize; DPRINTFN(5, "call %p(%p)\n", ch->intr, ch->arg); ch->intr(ch->arg); } - mutex_exit(&ch->sc->sc_intr_lock); + mutex_exit(&sc->sc_intr_lock); /* start next transfer */ uaudio_chan_rtransfer(ch); } Static void -uaudio_chan_init(struct chan *ch, int altidx, const struct audio_params *param, - int maxpktsize) +uaudio_chan_init(struct chan *ch, int altidx, + const struct audio_params *param, int maxpktsize, bool isrecord) { + struct uaudio_softc *sc = ch->sc; int samples_per_frame, sample_size; + DPRINTFN(5, "altidx=%d, %d/%d %dch %dHz ufps %u max %d\n", + altidx, param->validbits, param->precision, param->channels, + param->sample_rate, sc->sc_usb_frames_per_second, maxpktsize); + ch->altidx = altidx; sample_size = param->precision * param->channels / 8; - samples_per_frame = param->sample_rate / USB_FRAMES_PER_SECOND; + + if (isrecord) { + if (maxpktsize >= sample_size) + samples_per_frame = maxpktsize / sample_size; + else + samples_per_frame = param->sample_rate / sc->sc_usb_frames_per_second + + param->channels; + ch->fraction = 0; + } else { + samples_per_frame = param->sample_rate / sc->sc_usb_frames_per_second; + ch->fraction = param->sample_rate % sc->sc_usb_frames_per_second; + } + ch->sample_size = sample_size; ch->sample_rate = param->sample_rate; - if (maxpktsize == 0) { - ch->fraction = param->sample_rate % USB_FRAMES_PER_SECOND; + ch->bytes_per_frame = samples_per_frame * sample_size; + + if (maxpktsize > 0 && ch->bytes_per_frame > maxpktsize) { + samples_per_frame = maxpktsize / sample_size; ch->bytes_per_frame = samples_per_frame * sample_size; - } else { - ch->fraction = 0; - ch->bytes_per_frame = maxpktsize; } + ch->residue = 0; } @@ -3817,14 +4089,15 @@ uaudio_set_format(void *addr, int setmod paltidx = audio_indexof_format(sc->sc_formats, sc->sc_nformats, AUMODE_PLAY, play); /* Transfer should have halted */ - uaudio_chan_init(&sc->sc_playchan, paltidx, play, 0); + uaudio_chan_init(&sc->sc_playchan, paltidx, play, + UGETW(sc->sc_alts[paltidx].edesc->wMaxPacketSize), false); } if ((setmode & AUMODE_RECORD)) { raltidx = audio_indexof_format(sc->sc_formats, sc->sc_nformats, AUMODE_RECORD, rec); /* Transfer should have halted */ uaudio_chan_init(&sc->sc_recchan, raltidx, rec, - UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize)); + UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize), true); } if ((setmode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1) { @@ -3847,43 +4120,81 @@ uaudio_set_format(void *addr, int setmod } Static usbd_status -uaudio_set_speed(struct uaudio_softc *sc, int ifnum, int endpt, u_int speed) +uaudio_speed(struct uaudio_softc *sc, int endpt, int clkid, + uint8_t *data, int set) { usb_device_request_t req; - usbd_status err; - uint8_t data[4]; - - DPRINTFN(5, "endpt=%d speed=%u\n", endpt, speed); switch (sc->sc_version) { case UAUDIO_VERSION1: - req.bmRequestType = UT_WRITE_CLASS_ENDPOINT; - req.bRequest = SET_CUR; + req.bmRequestType = set ? + UT_WRITE_CLASS_ENDPOINT + : UT_READ_CLASS_ENDPOINT; + req.bRequest = set ? + SET_CUR + : GET_CUR; USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0); USETW(req.wIndex, endpt); USETW(req.wLength, 3); - data[0] = speed; - data[1] = speed >> 8; - data[2] = speed >> 16; break; case UAUDIO_VERSION2: - req.bmRequestType = UT_WRITE_CLASS_INTERFACE; + req.bmRequestType = set ? + UT_WRITE_CLASS_INTERFACE + : UT_READ_CLASS_INTERFACE; req.bRequest = V2_CUR; USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0); - USETW(req.wIndex, ifnum); + USETW2(req.wIndex, clkid, sc->sc_ac_iface); USETW(req.wLength, 4); - data[0] = speed; - data[1] = speed >> 8; - data[2] = speed >> 16; - data[3] = speed >> 24; break; } - err = usbd_do_request(sc->sc_udev, &req, data); + return usbd_do_request(sc->sc_udev, &req, data); +} - return err; +Static usbd_status +uaudio_set_speed(struct uaudio_softc *sc, int endpt, int clkid, u_int speed) +{ + uint8_t data[4]; + + DPRINTFN(5, "endpt=%d clkid=%u speed=%u\n", endpt, clkid, speed); + + data[0] = speed; + data[1] = speed >> 8; + data[2] = speed >> 16; + data[3] = speed >> 24; + + return uaudio_speed(sc, endpt, clkid, data, 1); } +#ifdef UAUDIO_DEBUG +SYSCTL_SETUP(sysctl_hw_uaudio_setup, "sysctl hw.uaudio setup") +{ + int err; + const struct sysctlnode *rnode; + const struct sysctlnode *cnode; + + err = sysctl_createv(clog, 0, NULL, &rnode, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "uaudio", + SYSCTL_DESCR("uaudio global controls"), + NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); + + if (err) + goto fail; + + /* control debugging printfs */ + err = sysctl_createv(clog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, + "debug", SYSCTL_DESCR("Enable debugging output"), + NULL, 0, &uaudiodebug, sizeof(uaudiodebug), CTL_CREATE, CTL_EOL); + if (err) + goto fail; + + return; +fail: + aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); +} +#endif + #ifdef _MODULE MODULE(MODULE_CLASS_DRIVER, uaudio, NULL); Index: src/sys/dev/usb/uaudioreg.h diff -u src/sys/dev/usb/uaudioreg.h:1.18 src/sys/dev/usb/uaudioreg.h:1.19 --- src/sys/dev/usb/uaudioreg.h:1.18 Mon Apr 10 15:14:50 2023 +++ src/sys/dev/usb/uaudioreg.h Sun Apr 16 19:26:20 2023 @@ -1,4 +1,4 @@ -/* $NetBSD: uaudioreg.h,v 1.18 2023/04/10 15:14:50 mlelstv Exp $ */ +/* $NetBSD: uaudioreg.h,v 1.19 2023/04/16 19:26:20 mlelstv Exp $ */ /* * Copyright (c) 1999 The NetBSD Foundation, Inc. @@ -66,6 +66,9 @@ typedef struct { * allowed to extend the endpoint descriptor. * Who knows what goes on in the minds of the people in the USB * standardization? :-( + * + * UAC2 no longer uses these extra fields. Check bLength to + * find out if these exist. */ uByte bRefresh; uByte bSynchAddress; @@ -305,9 +308,20 @@ struct usb_audio_mixer_v2_unit_1 { uByte bNrChannels; uDWord bmChannelConfig; uByte iChannelNames; - uByte bmControls[255]; /* [bNrChannels] */ + uByte bmMixerControls[255]; /* [bNrChannels] */ + /*uByte bmControls;*/ /*uByte iMixer;*/ } UPACKED; +#define UA_MIX_CLUSTER_MASK 0x03 +#define UA_MIX_CLUSTER_RO 0x01 +#define UA_MIX_CLUSTER_RW 0x03 +/* UAC2 */ +#define UA_MIX_UNDERFLOW_MASK 0x0c +#define UA_MIX_UNDERFLOW_RO 0x04 +#define UA_MIX_UNDERFLOW_RW 0x0c +#define UA_MIX_OVERFLOW_MASK 0x30 +#define UA_MIX_OVERFLOW_RO 0x10 +#define UA_MIX_OVERFLOW_RW 0x30 union usb_audio_mixer_unit_1 { struct usb_audio_mixer_v1_unit_1 v1; @@ -326,7 +340,7 @@ struct usb_audio_selector_unit { } UPACKED; /* UDESCSUB_AC_FEATURE */ -struct usb_audio_feature_unit { +struct usb_audio_feature_v1_unit { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; @@ -336,6 +350,20 @@ struct usb_audio_feature_unit { uByte bmaControls[255]; /* size for more than enough */ /* uByte iFeature; */ } UPACKED; +struct usb_audio_feature_v2_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bSourceId; + uDWord bmaControls[255]; /* size for more than enough */ + /* uByte iFeature; */ +} UPACKED; + +union usb_audio_feature_unit { + struct usb_audio_feature_v1_unit v1; + struct usb_audio_feature_v2_unit v2; +}; /* UDESCSUB_AC_PROCESSING */ struct usb_audio_processing_unit { @@ -496,11 +524,13 @@ struct usb_audio_clkmult_unit { #define SET_MEM 0x05 #define GET_MEM 0x85 #define GET_STAT 0xff -#define V2_CUR 0x01 -#define V2_RANGES 0x02 + +#define V2_CUR 0x01 +#define V2_RANGES 0x02 #define V2_CUR_CLKFREQ 0x01 #define V2_CUR_CLKSEL 0x01 +#define V2_CUR_SELECTOR 0x01 #define MUTE_CONTROL 0x01 @@ -516,8 +546,12 @@ struct usb_audio_clkmult_unit { #define GAIN_CONTROL 0x0b #define GAINPAD_CONTROL 0x0c #define PHASEINV_CONTROL 0x0d +/* V2 */ +#define UNDERFLOW_CONTROL 0x0e +#define OVERFLOW_CONTROL 0x0f #define FU_MASK(u) (1 << ((u)-1)) +#define V2_FU_MASK(u) (3 << ((u)-1)*2) #define MASTER_CHAN 0