/* $OpenBSD: uaudio.c,v 1.161 2021/05/18 10:02:00 ratchov Exp $ */ /* * Copyright (c) 2018 Alexandre Ratchov * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * The USB Audio Class (UAC) defines what is an audio device and how * to use it. There are two versions of the UAC: v1.0 and v2.0. They * are not compatible with each other but they are close enough to * attempt to have the same driver for both. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef UAUDIO_DEBUG #define DPRINTF(...) \ do { \ if (uaudio_debug) \ printf(__VA_ARGS__); \ } while (0) #else #define DPRINTF(...) do {} while(0) #endif #define DEVNAME(sc) ((sc)->dev.dv_xname) /* * Isochronous endpoint usage (XXX: these belong to dev/usb/usb.h). */ #define UE_ISO_USAGE 0x30 #define UE_ISO_USAGE_DATA 0x00 #define UE_ISO_USAGE_FEEDBACK 0x10 #define UE_ISO_USAGE_IMPL 0x20 #define UE_GET_ISO_USAGE(a) ((a) & UE_ISO_USAGE) /* * Max length of unit names */ #define UAUDIO_NAMEMAX MAX_AUDIO_DEV_LEN /* * USB audio class versions */ #define UAUDIO_V1 0x100 #define UAUDIO_V2 0x200 /* * AC class-specific descriptor interface sub-type */ #define UAUDIO_AC_HEADER 0x1 #define UAUDIO_AC_INPUT 0x2 #define UAUDIO_AC_OUTPUT 0x3 #define UAUDIO_AC_MIXER 0x4 #define UAUDIO_AC_SELECTOR 0x5 #define UAUDIO_AC_FEATURE 0x6 #define UAUDIO_AC_EFFECT 0x7 #define UAUDIO_AC_PROCESSING 0x8 #define UAUDIO_AC_EXTENSION 0x9 #define UAUDIO_AC_CLKSRC 0xa #define UAUDIO_AC_CLKSEL 0xb #define UAUDIO_AC_CLKMULT 0xc #define UAUDIO_AC_RATECONV 0xd /* * AS class-specific interface sub-types */ #define UAUDIO_AS_GENERAL 0x1 #define UAUDIO_AS_FORMAT 0x2 /* * AS class-specific endpoint sub-type */ #define UAUDIO_EP_GENERAL 0x1 /* * UAC v1 formats, wFormatTag is an enum */ #define UAUDIO_V1_FMT_PCM 0x1 #define UAUDIO_V1_FMT_PCM8 0x2 #define UAUDIO_V1_FMT_FLOAT 0x3 #define UAUDIO_V1_FMT_ALAW 0x4 #define UAUDIO_V1_FMT_MULAW 0x5 /* * UAC v2 formats, bmFormats is a bitmap */ #define UAUDIO_V2_FMT_PCM 0x01 #define UAUDIO_V2_FMT_PCM8 0x02 #define UAUDIO_V2_FMT_FLOAT 0x04 #define UAUDIO_V2_FMT_ALAW 0x08 #define UAUDIO_V2_FMT_MULAW 0x10 /* * AC requests */ #define UAUDIO_V1_REQ_SET_CUR 0x01 #define UAUDIO_V1_REQ_SET_MIN 0x02 #define UAUDIO_V1_REQ_SET_MAX 0x03 #define UAUDIO_V1_REQ_SET_RES 0x04 #define UAUDIO_V1_REQ_GET_CUR 0x81 #define UAUDIO_V1_REQ_GET_MIN 0x82 #define UAUDIO_V1_REQ_GET_MAX 0x83 #define UAUDIO_V1_REQ_GET_RES 0x84 #define UAUDIO_V2_REQ_CUR 1 #define UAUDIO_V2_REQ_RANGES 2 /* * AC request "selector control" */ #define UAUDIO_V2_REQSEL_CLKFREQ 1 #define UAUDIO_V2_REQSEL_CLKSEL 1 /* * AS class-specific endpoint attributes */ #define UAUDIO_EP_FREQCTL 0x01 /* * AC feature control selectors (aka wValue in the request) */ #define UAUDIO_REQSEL_MUTE 0x01 #define UAUDIO_REQSEL_VOLUME 0x02 #define UAUDIO_REQSEL_BASS 0x03 #define UAUDIO_REQSEL_MID 0x04 #define UAUDIO_REQSEL_TREBLE 0x05 #define UAUDIO_REQSEL_EQ 0x06 #define UAUDIO_REQSEL_AGC 0x07 #define UAUDIO_REQSEL_DELAY 0x08 #define UAUDIO_REQSEL_BASSBOOST 0x09 #define UAUDIO_REQSEL_LOUDNESS 0x0a #define UAUDIO_REQSEL_GAIN 0x0b #define UAUDIO_REQSEL_GAINPAD 0x0c #define UAUDIO_REQSEL_PHASEINV 0x0d /* * Endpoint (UAC v1) or clock-source unit (UAC v2) sample rate control */ #define UAUDIO_REQSEL_RATE 0x01 /* * Samples-per-frame are fractions. UAC v2.0 requires the denominator to * be multiple of 2^16, as used in the sync pipe. On the othe hand, to * represent sample-per-frame of all rates we support, we need the * denominator to be such that (rate / 1000) can be represented exactly, * 80 works. So we use the least common multiplier of both. */ #define UAUDIO_SPF_DIV 327680 /* * names of DAC and ADC unit names */ #define UAUDIO_NAME_PLAY "dac" #define UAUDIO_NAME_REC "record" /* * read/write pointers for secure sequencial access of binary data, * ex. usb descriptors, tables and alike. Bytes are read using the * read pointer up to the write pointer. */ struct uaudio_blob { unsigned char *rptr, *wptr; }; /* * Ranges of integer values used to represent controls values and * sample frequencies. */ struct uaudio_ranges { unsigned int nval; struct uaudio_ranges_el { struct uaudio_ranges_el *next; int min, max, res; } *el; }; struct uaudio_softc { struct device dev; struct usbd_device *udev; int version; /* * UAC exposes the device as a circuit of units. Input and * output jacks are known as terminal units, others are * processing units. The purpose of this driver is to give * them reasonable names and expose them as mixer(1) * controls. Control names are derived from the type of the * unit and its role in the circuit. * * UAC v2.0 exposes also the clock circuitry using units, so * selecting the sample rate also involves units usage. */ struct uaudio_unit { struct uaudio_unit *unit_next, *src_next, *dst_next; struct uaudio_unit *src_list, *dst_list; char name[UAUDIO_NAMEMAX]; unsigned int nch; int type, id; /* terminal or clock type */ unsigned int term; /* clock source, if a terminal or selector */ struct uaudio_unit *clock; /* sample rates, if this is a clock source */ struct uaudio_ranges rates; /* mixer(4) bits */ #define UAUDIO_CLASS_OUT 0 #define UAUDIO_CLASS_IN 1 #define UAUDIO_CLASS_COUNT 2 int mixer_class; struct uaudio_mixent { struct uaudio_mixent *next; char *fname; #define UAUDIO_MIX_SW 0 #define UAUDIO_MIX_NUM 1 #define UAUDIO_MIX_ENUM 2 int type; int chan; int req_sel; struct uaudio_ranges ranges; } *mixent_list; } *unit_list; /* * Current clock, UAC v2.0 only */ struct uaudio_unit *clock; /* * Number of input and output terminals */ unsigned int nin, nout; /* * When unique names are needed, they are generated using a * base string suffixed with a number. Ex. "spkr5". The * following structure is used to keep track of strings we * allocated. */ struct uaudio_name { struct uaudio_name *next; char *templ; unsigned int unit; } *names; /* * Audio streaming (AS) alternate settings, i.e. stream format * and USB-related parameters to use it. */ struct uaudio_alt { struct uaudio_alt *next; int ifnum, altnum; int mode; /* one of AUMODE_{RECORD,PLAY} */ int data_addr; /* data endpoint address */ int sync_addr; /* feedback endpoint address */ int maxpkt; /* max supported bytes per frame */ int fps; /* USB (micro-)frames per second */ int bps, bits, nch; /* audio encoding */ int v1_rates; /* if UAC 1.0, bitmap of rates */ } *alts; /* * Audio parameters: play and record stream formats usable * together. */ struct uaudio_params { struct uaudio_params *next; struct uaudio_alt *palt, *ralt; int v1_rates; } *params_list, *params; /* * One direction audio stream, aka "DMA" in progress */ struct uaudio_stream { #define UAUDIO_NXFERS_MIN 2 #define UAUDIO_NXFERS_MAX 8 struct uaudio_xfer { struct usbd_xfer *usb_xfer; unsigned char *buf; uint16_t *sizes; unsigned int size; /* bytes requested */ unsigned int nframes; /* frames requested */ } data_xfers[UAUDIO_NXFERS_MAX], sync_xfers[UAUDIO_NXFERS_MAX]; /* * We don't use all the data_xfers[] entries because * we can't schedule too many frames in the usb * controller. */ unsigned int nxfers; unsigned int spf_remain; /* frac sample left */ unsigned int spf; /* avg samples per frame */ unsigned int spf_min, spf_max; /* allowed boundaries */ /* * The max frame size we'll need (which may be lower * than the maxpkt the usb pipe supports). */ unsigned int maxpkt; /* * max number of frames per xfer we'll need */ unsigned int nframes_max; /* * At usb2.0 speed, the number of (micro-)frames per * transfer must correspond to 1ms, which is the usb1.1 * frame duration. This is required by lower level usb * drivers. * * The nframes_mask variable is used to test if the * number of frames per transfer is usable (by checking * that least significant bits are zero). For instance, * nframes_mask will be set to 0x0 on usb1.1 device and * 0x7 on usb2.0 devices running at 8000 fps. */ unsigned int nframes_mask; unsigned int data_nextxfer, sync_nextxfer; struct usbd_pipe *data_pipe; struct usbd_pipe *sync_pipe; void (*intr)(void *); void *arg; /* audio ring extents, passed to trigger() methods */ unsigned char *ring_start, *ring_end; /* pointer to first byte available */ unsigned char *ring_pos; /* audio(9) block size in bytes */ int ring_blksz; /* xfer position relative to block boundary */ int ring_offs; /* * As USB sample-per-frame is not constant, we must * schedule transfers slightly larger that one audio * block. This is the "safe" block size, that ensures * the transfer will cross the audio block boundary. */ int safe_blksz; /* * Number of bytes completed, when it reaches a * block size, we fire an audio(9) interrupt. */ int ring_icnt; /* * USB transfers are used as a FIFO which is the * concatenation of all transfers. This is the write * (read) position of the play (rec) stream */ unsigned int ubuf_xfer; /* xfer index */ unsigned int ubuf_pos; /* offset in bytes */ } pstream, rstream; int ctl_ifnum; /* aka AC interface */ int mode; /* open() mode */ int trigger_mode; /* trigger() mode */ unsigned int rate; /* current sample rate */ unsigned int ufps; /* USB frames per second */ unsigned int sync_pktsz; /* size of sync packet */ unsigned int host_nframes; /* max frames we can schedule */ int diff_nsamp; /* samples play is ahead of rec */ int diff_nframes; /* frames play is ahead of rec */ unsigned int adjspf_age; /* frames since last uaudio_adjspf */ /* * bytes pending to be copied to transfer buffer. This is play * only, as recorded frames are copied as soon they are * received. */ size_t copy_todo; }; int uaudio_match(struct device *, void *, void *); void uaudio_attach(struct device *, struct device *, void *); int uaudio_detach(struct device *, int); int uaudio_open(void *, int); void uaudio_close(void *); int uaudio_set_params(void *, int, int, struct audio_params *, struct audio_params *); unsigned int uaudio_set_blksz(void *, int, struct audio_params *, struct audio_params *, unsigned int); int uaudio_trigger_output(void *, void *, void *, int, void (*)(void *), void *, struct audio_params *); int uaudio_trigger_input(void *, void *, void *, int, void (*)(void *), void *, struct audio_params *); void uaudio_copy_output(void *, size_t); void uaudio_underrun(void *); int uaudio_halt_output(void *); int uaudio_halt_input(void *); int uaudio_query_devinfo(void *, struct mixer_devinfo *); int uaudio_get_port(void *, struct mixer_ctrl *); int uaudio_set_port(void *, struct mixer_ctrl *); int uaudio_get_props(void *); int uaudio_process_unit(struct uaudio_softc *, struct uaudio_unit *, int, struct uaudio_blob, struct uaudio_unit **); void uaudio_pdata_intr(struct usbd_xfer *, void *, usbd_status); void uaudio_rdata_intr(struct usbd_xfer *, void *, usbd_status); void uaudio_psync_intr(struct usbd_xfer *, void *, usbd_status); #ifdef UAUDIO_DEBUG char *uaudio_isoname(int isotype); char *uaudio_modename(int mode); char *uaudio_usagename(int usage); void uaudio_rates_print(int rates); void uaudio_ranges_print(struct uaudio_ranges *r); void uaudio_print_unit(struct uaudio_softc *sc, struct uaudio_unit *u); void uaudio_mixer_print(struct uaudio_softc *sc); void uaudio_conf_print(struct uaudio_softc *sc); /* * 0 - nothing, same as if UAUDIO_DEBUG isn't defined * 1 - initialisations & setup * 2 - audio(4) calls * 3 - transfers */ int uaudio_debug = 1; #endif struct cfdriver uaudio_cd = { NULL, "uaudio", DV_DULL }; const struct cfattach uaudio_ca = { sizeof(struct uaudio_softc), uaudio_match, uaudio_attach, uaudio_detach }; struct audio_hw_if uaudio_hw_if = { uaudio_open, /* open */ uaudio_close, /* close */ uaudio_set_params, /* set_params */ NULL, /* round_blocksize */ NULL, /* commit_settings */ NULL, /* init_output */ NULL, /* init_input */ NULL, /* start_output */ NULL, /* start_input */ uaudio_halt_output, /* halt_output */ uaudio_halt_input, /* halt_input */ NULL, /* speaker_ctl */ NULL, /* setfd */ uaudio_set_port, /* set_port */ uaudio_get_port, /* get_port */ uaudio_query_devinfo, /* query_devinfo */ NULL, /* malloc, we use bounce buffers :'( */ NULL, /* free */ NULL, /* round_buffersize */ uaudio_get_props, /* get_props */ uaudio_trigger_output, /* trigger_output */ uaudio_trigger_input, /* trigger_input */ uaudio_copy_output, /* copy_output */ uaudio_underrun, /* underrun */ uaudio_set_blksz /* set_blksz */ }; /* * To keep things simple, we support only the following rates, we * don't care about continuous sample rates or other "advanced" * features which complicate implementation. */ int uaudio_rates[] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; /* * Convert 8, 16, or 24-bit signed value to an int by expanding the * sign bit. */ int uaudio_sign_expand(unsigned int val, int opsize) { unsigned int s; s = 1 << (8 * opsize - 1); return (val ^ s) - s; } int uaudio_req(struct uaudio_softc *sc, unsigned int type, unsigned int req, unsigned int sel, unsigned int chan, unsigned int ifnum, unsigned int id, unsigned char *buf, size_t size) { struct usb_device_request r; int err; r.bmRequestType = type; r.bRequest = req; USETW(r.wValue, sel << 8 | chan); USETW(r.wIndex, id << 8 | ifnum); USETW(r.wLength, size); DPRINTF("%s: type = 0x%x, req = 0x%x, val = 0x%x, " "index = 0x%x, size = %d\n", __func__, type, req, UGETW(r.wValue), UGETW(r.wIndex), UGETW(r.wLength)); err = usbd_do_request(sc->udev, &r, buf); if (err) { DPRINTF("%s: failed: %s\n", __func__, usbd_errstr(err)); return 0; } return 1; } /* * Read a number of the given size (in bytes) from the given * blob. Return 0 on error. */ int uaudio_getnum(struct uaudio_blob *p, unsigned int size, unsigned int *ret) { unsigned int i, num = 0; if (p->wptr - p->rptr < size) { DPRINTF("%s: %d: too small\n", __func__, size); return 0; } for (i = 0; i < size; i++) num |= *p->rptr++ << (8 * i); if (ret) *ret = num; return 1; } /* * Read a USB descriptor from the given blob. Return 0 on error. */ int uaudio_getdesc(struct uaudio_blob *p, struct uaudio_blob *ret) { unsigned int size; if (!uaudio_getnum(p, 1, &size)) return 0; if (size-- == 0) { DPRINTF("%s: zero sized desc\n", __func__); return 0; } if (p->wptr - p->rptr < size) { DPRINTF("%s: too small\n", __func__); return 0; } ret->rptr = p->rptr; ret->wptr = p->rptr + size; p->rptr += size; return 1; } /* * Find the unit with the given id, return NULL if not found. */ struct uaudio_unit * uaudio_unit_byid(struct uaudio_softc *sc, unsigned int id) { struct uaudio_unit *u; for (u = sc->unit_list; u != NULL; u = u->unit_next) { if (u->id == id) break; } return u; } /* * Return a terminal name for the given terminal type. */ char * uaudio_tname(struct uaudio_softc *sc, unsigned int type, int isout) { unsigned int hi, lo; char *name; hi = type >> 8; lo = type & 0xff; /* usb data stream */ if (hi == 1) return isout ? UAUDIO_NAME_REC : UAUDIO_NAME_PLAY; /* if theres only one input (output) use "input" ("output") */ if (isout) { if (sc->nout == 1) return "output"; } else { if (sc->nin == 1) return "input"; } /* determine name from USB terminal type */ switch (hi) { case 2: /* embedded inputs */ name = isout ? "mic-out" : "mic"; break; case 3: /* embedded outputs, mostly speakers, except 0x302 */ switch (lo) { case 0x02: name = isout ? "hp" : "hp-in"; break; default: name = isout ? "spkr" : "spkr-in"; break; } break; case 4: /* handsets and headset */ name = isout ? "spkr" : "mic"; break; case 5: /* phone line */ name = isout ? "phone-in" : "phone-out"; break; case 6: /* external sources/sinks */ switch (lo) { case 0x02: case 0x05: case 0x06: case 0x07: case 0x09: case 0x0a: name = isout ? "dig-out" : "dig-in"; break; default: name = isout ? "line-out" : "line-in"; break; } break; case 7: /* internal devices */ name = isout ? "int-out" : "int-in"; break; default: name = isout ? "unk-out" : "unk-in"; } return name; } /* * Return a clock name for the given clock type. */ char * uaudio_clkname(unsigned int attr) { static char *names[] = {"ext", "fixed", "var", "prog"}; return names[attr & 3]; } /* * Return an unique name for the given template. */ void uaudio_mkname(struct uaudio_softc *sc, char *templ, char *res) { struct uaudio_name *n; char *sep; /* * if this is not a terminal name (i.e. there's a underscore * in the name, like in "spkr2_mic3"), then use underscore as * separator to avoid concatenating two numbers */ sep = strchr(templ, '_') != NULL ? "_" : ""; n = sc->names; while (1) { if (n == NULL) { n = malloc(sizeof(struct uaudio_name), M_DEVBUF, M_WAITOK); n->templ = templ; n->unit = 0; n->next = sc->names; sc->names = n; } if (strcmp(n->templ, templ) == 0) break; n = n->next; } if (n->unit == 0) snprintf(res, UAUDIO_NAMEMAX, "%s", templ); else snprintf(res, UAUDIO_NAMEMAX, "%s%s%u", templ, sep, n->unit); n->unit++; } /* * Convert UAC v1.0 feature bitmap to UAC v2.0 feature bitmap. */ unsigned int uaudio_feature_fixup(struct uaudio_softc *sc, unsigned int ctl) { int i; unsigned int bits, n; switch (sc->version) { case UAUDIO_V1: n = 0; for (i = 0; i < 16; i++) { bits = (ctl >> i) & 1; if (bits) bits |= 2; n |= bits << (2 * i); } return n; case UAUDIO_V2: break; } return ctl; } /* * Initialize a uaudio_ranges to the empty set */ void uaudio_ranges_init(struct uaudio_ranges *r) { r->el = NULL; r->nval = 0; } /* * Add the given range to the the uaudio_ranges structures. Ranges are * not supposed to overlap (required by USB spec). If they do we just * return. */ void uaudio_ranges_add(struct uaudio_ranges *r, int min, int max, int res) { struct uaudio_ranges_el *e, **pe; if (min > max) { DPRINTF("%s: [%d:%d]/%d: bad range\n", __func__, min, max, res); return; } for (pe = &r->el; (e = *pe) != NULL; pe = &e->next) { if (min <= e->max && max >= e->min) { DPRINTF("%s: overlaping ranges\n", __func__); return; } if (min < e->max) break; } /* XXX: use 'res' here */ r->nval += max - min + 1; e = malloc(sizeof(struct uaudio_ranges_el), M_DEVBUF, M_WAITOK); e->min = min; e->max = max; e->res = res; e->next = *pe; *pe = e; } /* * Free all ranges making the uaudio_ranges the empty set */ void uaudio_ranges_clear(struct uaudio_ranges *r) { struct uaudio_ranges_el *e; while ((e = r->el) != NULL) { r->el = e->next; free(e, M_DEVBUF, sizeof(struct uaudio_ranges_el)); } r->nval = 0; } /* * Convert a value in the given uaudio_ranges, into a 0..255 integer * suitable for mixer usage */ int uaudio_ranges_decode(struct uaudio_ranges *r, int val) { struct uaudio_ranges_el *e; int diff, pos; pos = 0; for (e = r->el; e != NULL; e = e->next) { if (val >= e->min && val <= e->max) { pos += val - e->min; return (r->nval == 1) ? 0 : (pos * 255 + (r->nval - 1) / 2) / (r->nval - 1); } diff = e->max - e->min + 1; pos += diff; } return 0; } /* * Convert a 0..255 to a value in the uaudio_ranges suitable for a USB * request. */ unsigned int uaudio_ranges_encode(struct uaudio_ranges *r, int val) { struct uaudio_ranges_el *e; int diff, pos; pos = (val * (r->nval - 1) + 127) / 255; for (e = r->el; e != NULL; e = e->next) { diff = e->max - e->min + 1; if (pos < diff) return e->min + pos; pos -= diff; } return 0; } /* * Return the bitmap of supported rates included in the given ranges. * This is not a mixer thing, UAC v2.0 uses ranges to report sample * rates. */ int uaudio_ranges_getrates(struct uaudio_ranges *r, unsigned int mult, unsigned int div) { struct uaudio_ranges_el *e; int rates, i, v; rates = 0; for (e = r->el; e != NULL; e = e->next) { for (i = 0; i < nitems(uaudio_rates); i++) { v = (unsigned long long)uaudio_rates[i] * mult / div; if (v < e->min || v > e->max) continue; if (e->res == 0 || v - e->min % e->res == 0) rates |= 1 << i; } } return rates; } /* * Return the index in the uaudio_rates[] array of rate closest to the * given rate in Hz. */ int uaudio_rates_indexof(int mask, int rate) { int i, diff, best_index, best_diff; best_index = -1; best_diff = INT_MAX; for (i = 0; i < nitems(uaudio_rates); i++) { if ((mask & (1 << i)) == 0) continue; diff = uaudio_rates[i] - rate; if (diff < 0) diff = -diff; if (diff < best_diff) { best_index = i; best_diff = diff; } } return best_index; } /* * Do a request that results in a uaudio_ranges. On UAC v1.0, this is * simply a min/max/res triplet. On UAC v2.0, this is an array of * min/max/res triplets. */ int uaudio_req_ranges(struct uaudio_softc *sc, unsigned int opsize, unsigned int sel, unsigned int chan, unsigned int ifnum, unsigned int id, struct uaudio_ranges *r) { unsigned char req_buf[16], *req = NULL; size_t req_size; struct uaudio_blob p; unsigned int count, min, max, res; int i; switch (sc->version) { case UAUDIO_V1: count = 1; req = req_buf; p.rptr = p.wptr = req; if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE, UAUDIO_V1_REQ_GET_MIN, sel, chan, ifnum, id, p.wptr, opsize)) return 0; p.wptr += opsize; if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE, UAUDIO_V1_REQ_GET_MAX, sel, chan, ifnum, id, p.wptr, opsize)) return 0; p.wptr += opsize; if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE, UAUDIO_V1_REQ_GET_RES, sel, chan, ifnum, id, p.wptr, opsize)) return 0; p.wptr += opsize; break; case UAUDIO_V2: /* fetch the ranges count only (first 2 bytes) */ if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE, UAUDIO_V2_REQ_RANGES, sel, chan, ifnum, id, req_buf, 2)) return 0; /* count is at most 65535 */ count = req_buf[0] | req_buf[1] << 8; /* restart the request on a large enough buffer */ req_size = 2 + 3 * opsize * count; if (sizeof(req_buf) >= req_size) req = req_buf; else req = malloc(req_size, M_DEVBUF, M_WAITOK); p.rptr = p.wptr = req; if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE, UAUDIO_V2_REQ_RANGES, sel, chan, ifnum, id, p.wptr, req_size)) return 0; p.wptr += req_size; /* skip initial 2 bytes of count */ p.rptr += 2; break; } for (i = 0; i < count; i++) { if (!uaudio_getnum(&p, opsize, &min)) return 0; if (!uaudio_getnum(&p, opsize, &max)) return 0; if (!uaudio_getnum(&p, opsize, &res)) return 0; uaudio_ranges_add(r, uaudio_sign_expand(min, opsize), uaudio_sign_expand(max, opsize), uaudio_sign_expand(res, opsize)); } if (req != req_buf) free(req, M_DEVBUF, req_size); return 1; } /* * Return the rates bitmap of the given interface alt setting */ int uaudio_alt_getrates(struct uaudio_softc *sc, struct uaudio_alt *p) { struct uaudio_unit *u; unsigned int mult = 1, div = 1; switch (sc->version) { case UAUDIO_V1: return p->v1_rates; case UAUDIO_V2: u = sc->clock; while (1) { switch (u->type) { case UAUDIO_AC_CLKSRC: return uaudio_ranges_getrates(&u->rates, mult, div); case UAUDIO_AC_CLKSEL: u = u->clock; break; case UAUDIO_AC_CLKMULT: case UAUDIO_AC_RATECONV: /* XXX: adjust rate with multiplier */ u = u->src_list; break; default: DPRINTF("%s: no clock\n", __func__); return 0; } } } return 0; } /* * return the clock unit of the given terminal unit (v2 only) */ int uaudio_clock_id(struct uaudio_softc *sc) { struct uaudio_unit *u; u = sc->clock; while (1) { if (u == NULL) { DPRINTF("%s: NULL clock pointer\n", __func__); return -1; } switch (u->type) { case UAUDIO_AC_CLKSRC: return u->id; case UAUDIO_AC_CLKSEL: u = u->clock; break; case UAUDIO_AC_CLKMULT: case UAUDIO_AC_RATECONV: u = u->src_list; break; default: DPRINTF("%s: no clock\n", __func__); return -1; } } } /* * Return the rates bitmap of the given parameters setting */ int uaudio_getrates(struct uaudio_softc *sc, struct uaudio_params *p) { switch (sc->version) { case UAUDIO_V1: return p->v1_rates; case UAUDIO_V2: return uaudio_alt_getrates(sc, p->palt ? p->palt : p->ralt); } return 0; } /* * Add the given feature (aka mixer control) to the given unit. */ void uaudio_feature_addent(struct uaudio_softc *sc, struct uaudio_unit *u, int uac_type, int chan) { static struct { char *name; int mix_type; int req_sel; } features[] = { {"mute", UAUDIO_MIX_SW, UAUDIO_REQSEL_MUTE}, {"level", UAUDIO_MIX_NUM, UAUDIO_REQSEL_VOLUME}, {"bass", UAUDIO_MIX_NUM, UAUDIO_REQSEL_BASS}, {"mid", UAUDIO_MIX_NUM, UAUDIO_REQSEL_MID}, {"treble", UAUDIO_MIX_NUM, UAUDIO_REQSEL_TREBLE}, {"eq", UAUDIO_MIX_NUM, UAUDIO_REQSEL_EQ}, {"agc", UAUDIO_MIX_SW, UAUDIO_REQSEL_AGC}, {NULL, -1, -1}, /* delay */ {"bassboost", UAUDIO_MIX_SW, UAUDIO_REQSEL_BASSBOOST}, {"loud", UAUDIO_MIX_SW, UAUDIO_REQSEL_LOUDNESS}, {"gain", UAUDIO_MIX_NUM, UAUDIO_REQSEL_GAIN}, {"gainpad", UAUDIO_MIX_SW, UAUDIO_REQSEL_GAINPAD}, {"phase", UAUDIO_MIX_SW, UAUDIO_REQSEL_PHASEINV}, {NULL, -1, -1}, /* undeflow */ {NULL, -1, -1} /* overflow */ }; struct uaudio_mixent *m, *i, **pi; int cmp; if (uac_type >= sizeof(features) / sizeof(features[0])) { printf("%s: skipped unknown feature\n", DEVNAME(sc)); return; } m = malloc(sizeof(struct uaudio_mixent), M_DEVBUF, M_WAITOK); m->chan = chan; m->fname = features[uac_type].name; m->type = features[uac_type].mix_type; m->req_sel = features[uac_type].req_sel; uaudio_ranges_init(&m->ranges); if (m->type == UAUDIO_MIX_NUM) { if (!uaudio_req_ranges(sc, 2, m->req_sel, chan < 0 ? 0 : chan + 1, sc->ctl_ifnum, u->id, &m->ranges)) { printf("%s: failed to get ranges for %s control\n", DEVNAME(sc), m->fname); free(m, M_DEVBUF, sizeof(struct uaudio_mixent)); return; } if (m->ranges.el == NULL) { printf("%s: skipped %s control with empty range\n", DEVNAME(sc), m->fname); free(m, M_DEVBUF, sizeof(struct uaudio_mixent)); return; } #ifdef UAUDIO_DEBUG if (uaudio_debug) uaudio_ranges_print(&m->ranges); #endif } /* * Add to unit's mixer controls list, sorting entries by name * and increasing channel number. */ for (pi = &u->mixent_list; (i = *pi) != NULL; pi = &i->next) { cmp = strcmp(i->fname, m->fname); if (cmp == 0) cmp = i->chan - m->chan; if (cmp == 0) { DPRINTF("%02u: %s.%s: duplicate feature for chan %d\n", u->id, u->name, m->fname, m->chan); free(m, M_DEVBUF, sizeof(struct uaudio_mixent)); return; } if (cmp > 0) break; } m->next = *pi; *pi = m; DPRINTF("\t%s[%d]\n", m->fname, m->chan); } /* * For the given unit, parse the list of its sources and recursively * call uaudio_process_unit() for each. */ int uaudio_process_srcs(struct uaudio_softc *sc, struct uaudio_unit *u, struct uaudio_blob units, struct uaudio_blob *p) { struct uaudio_unit *s, **ps; unsigned int i, npin, sid; if (!uaudio_getnum(p, 1, &npin)) return 0; ps = &u->src_list; for (i = 0; i < npin; i++) { if (!uaudio_getnum(p, 1, &sid)) return 0; if (!uaudio_process_unit(sc, u, sid, units, &s)) return 0; s->src_next = NULL; *ps = s; ps = &s->src_next; } return 1; } /* * Parse the number of channels. */ int uaudio_process_nch(struct uaudio_softc *sc, struct uaudio_unit *u, struct uaudio_blob *p) { if (!uaudio_getnum(p, 1, &u->nch)) return 0; /* skip junk */ switch (sc->version) { case UAUDIO_V1: if (!uaudio_getnum(p, 2, NULL)) /* bmChannelConfig */ return 0; break; case UAUDIO_V2: if (!uaudio_getnum(p, 4, NULL)) /* wChannelConfig */ return 0; break; } if (!uaudio_getnum(p, 1, NULL)) /* iChannelNames */ return 0; return 1; } /* * Find the AC class-specific descriptor for this unit id. */ int uaudio_unit_getdesc(struct uaudio_softc *sc, int id, struct uaudio_blob units, struct uaudio_blob *p, unsigned int *rtype) { unsigned int i, type, subtype; /* * Find the usb descriptor for this id. */ while (1) { if (units.rptr == units.wptr) { DPRINTF("%s: %02u: not found\n", __func__, id); return 0; } if (!uaudio_getdesc(&units, p)) return 0; if (!uaudio_getnum(p, 1, &type)) return 0; if (!uaudio_getnum(p, 1, &subtype)) return 0; if (!uaudio_getnum(p, 1, &i)) return 0; if (i == id) break; } *rtype = subtype; return 1; } /* * Parse a unit, possibly calling uaudio_process_unit() for each of * its sources. */ int uaudio_process_unit(struct uaudio_softc *sc, struct uaudio_unit *dest, int id, struct uaudio_blob units, struct uaudio_unit **rchild) { struct uaudio_blob p; struct uaudio_unit *u, *s; unsigned int i, j, size, ctl, type, subtype, assoc, clk; #ifdef UAUDIO_DEBUG unsigned int bit; #endif if (!uaudio_unit_getdesc(sc, id, units, &p, &subtype)) return 0; /* * find this unit on the list as it may be already processed as * the source of another destination */ u = uaudio_unit_byid(sc, id); if (u == NULL) { u = malloc(sizeof(struct uaudio_unit), M_DEVBUF, M_WAITOK); u->id = id; u->type = subtype; u->term = 0; u->src_list = NULL; u->dst_list = NULL; u->clock = NULL; u->mixent_list = NULL; u->nch = 0; u->name[0] = 0; uaudio_ranges_init(&u->rates); u->unit_next = sc->unit_list; sc->unit_list = u; } else { switch (u->type) { case UAUDIO_AC_CLKSRC: case UAUDIO_AC_CLKSEL: case UAUDIO_AC_CLKMULT: case UAUDIO_AC_RATECONV: /* not using 'dest' list */ *rchild = u; return 1; } } if (dest) { dest->dst_next = u->dst_list; u->dst_list = dest; if (dest->dst_next != NULL) { /* already seen */ *rchild = u; return 1; } } switch (u->type) { case UAUDIO_AC_INPUT: if (!uaudio_getnum(&p, 2, &u->term)) return 0; if (!uaudio_getnum(&p, 1, &assoc)) return 0; if (u->term >> 8 != 1) sc->nin++; switch (sc->version) { case UAUDIO_V1: break; case UAUDIO_V2: if (!uaudio_getnum(&p, 1, &clk)) return 0; if (!uaudio_process_unit(sc, NULL, clk, units, &u->clock)) return 0; break; } if (!uaudio_getnum(&p, 1, &u->nch)) return 0; DPRINTF("%02u: " "in, nch = %d, term = 0x%x, assoc = %d\n", u->id, u->nch, u->term, assoc); break; case UAUDIO_AC_OUTPUT: if (!uaudio_getnum(&p, 2, &u->term)) return 0; if (!uaudio_getnum(&p, 1, &assoc)) return 0; if (!uaudio_getnum(&p, 1, &id)) return 0; if (!uaudio_process_unit(sc, u, id, units, &s)) return 0; if (u->term >> 8 != 1) sc->nout++; switch (sc->version) { case UAUDIO_V1: break; case UAUDIO_V2: if (!uaudio_getnum(&p, 1, &clk)) return 0; if (!uaudio_process_unit(sc, NULL, clk, units, &u->clock)) return 0; break; } u->src_list = s; s->src_next = NULL; u->nch = s->nch; DPRINTF("%02u: " "out, id = %d, nch = %d, term = 0x%x, assoc = %d\n", u->id, id, u->nch, u->term, assoc); break; case UAUDIO_AC_MIXER: if (!uaudio_process_srcs(sc, u, units, &p)) return 0; if (!uaudio_process_nch(sc, u, &p)) return 0; DPRINTF("%02u: mixer, nch = %u:\n", u->id, u->nch); #ifdef UAUDIO_DEBUG /* * Print the list of available mixer's unit knobs (a bit * matrix). Matrix mixers are rare because levels are * already controlled by feature units, making the mixer * knobs redundant with the feature's knobs. So, for * now, we don't add clutter to the mixer(4) interface * and ignore all knobs. Other popular OSes doesn't * seem to expose them either. */ bit = 0; for (s = u->src_list; s != NULL; s = s->src_next) { for (i = 0; i < s->nch; i++) { for (j = 0; j < u->nch; j++) { if ((bit++ & 7) == 0) { if (!uaudio_getnum(&p, 1, &ctl)) return 0; } if (ctl & 0x80) DPRINTF("\t%02u[%d] -> [%d]\n", s->id, i, j); ctl <<= 1; } } } #endif break; case UAUDIO_AC_SELECTOR: /* * Selectors are extreamly rare, so not supported yet. */ if (!uaudio_process_srcs(sc, u, units, &p)) return 0; if (u->src_list == NULL) { printf("%s: selector %02u has no sources\n", DEVNAME(sc), u->id); return 0; } u->nch = u->src_list->nch; DPRINTF("%02u: selector, nch = %u\n", u->id, u->nch); break; case UAUDIO_AC_FEATURE: if (!uaudio_getnum(&p, 1, &id)) return 0; if (!uaudio_process_unit(sc, u, id, units, &s)) return 0; s->src_next = u->src_list; u->src_list = s; u->nch = s->nch; switch (sc->version) { case UAUDIO_V1: if (!uaudio_getnum(&p, 1, &size)) return 0; break; case UAUDIO_V2: size = 4; break; } DPRINTF("%02d: feature id = %d, nch = %d, size = %d\n", u->id, id, u->nch, size); if (!uaudio_getnum(&p, size, &ctl)) return 0; ctl = uaudio_feature_fixup(sc, ctl); for (i = 0; i < 16; i++) { if ((ctl & 3) == 3) uaudio_feature_addent(sc, u, i, -1); ctl >>= 2; } /* * certain devices provide no per-channel control descriptors */ if (p.wptr - p.rptr == 1) break; for (j = 0; j < u->nch; j++) { if (!uaudio_getnum(&p, size, &ctl)) return 0; ctl = uaudio_feature_fixup(sc, ctl); for (i = 0; i < 16; i++) { if ((ctl & 3) == 3) uaudio_feature_addent(sc, u, i, j); ctl >>= 2; } } break; case UAUDIO_AC_EFFECT: if (!uaudio_getnum(&p, 2, &type)) return 0; if (!uaudio_getnum(&p, 1, &id)) return 0; if (!uaudio_process_unit(sc, u, id, units, &s)) return 0; s->src_next = u->src_list; u->src_list = s; u->nch = s->nch; DPRINTF("%02d: effect, type = %u, id = %d, nch = %d\n", u->id, type, id, u->nch); break; case UAUDIO_AC_PROCESSING: case UAUDIO_AC_EXTENSION: if (!uaudio_getnum(&p, 2, &type)) return 0; if (!uaudio_process_srcs(sc, u, units, &p)) return 0; if (!uaudio_process_nch(sc, u, &p)) return 0; DPRINTF("%02u: proc/ext, type = 0x%x, nch = %u\n", u->id, type, u->nch); for (s = u->src_list; s != NULL; s = s->src_next) { DPRINTF("%u:\tpin %u:\n", u->id, s->id); } break; case UAUDIO_AC_CLKSRC: if (!uaudio_getnum(&p, 1, &u->term)) return 0; if (!uaudio_getnum(&p, 1, &ctl)) return 0; DPRINTF("%02u: clock source, attr = 0x%x, ctl = 0x%x\n", u->id, u->term, ctl); break; case UAUDIO_AC_CLKSEL: DPRINTF("%02u: clock sel\n", u->id); if (!uaudio_process_srcs(sc, u, units, &p)) return 0; if (u->src_list == NULL) { printf("%s: clock selector %02u with no srcs\n", DEVNAME(sc), u->id); return 0; } break; case UAUDIO_AC_CLKMULT: DPRINTF("%02u: clock mult\n", u->id); /* XXX: fetch multiplier */ printf("%s: clock multiplier not supported\n", DEVNAME(sc)); break; case UAUDIO_AC_RATECONV: DPRINTF("%02u: rate conv\n", u->id); /* XXX: fetch multiplier */ printf("%s: rate converter not supported\n", DEVNAME(sc)); break; } if (rchild) *rchild = u; return 1; } /* * Try to set the unit name to the name of its destination terminal. If * the name is ambigus (already given to another source unit or having * multiple destinations) then return 0. */ int uaudio_setname_dsts(struct uaudio_softc *sc, struct uaudio_unit *u, char *name) { struct uaudio_unit *d = u; while (d != NULL) { if (d->dst_list == NULL || d->dst_list->dst_next != NULL) break; d = d->dst_list; if (d->src_list == NULL || d->src_list->src_next != NULL) break; if (d->name[0] != '\0') { if (name != NULL && strcmp(name, d->name) != 0) break; strlcpy(u->name, d->name, UAUDIO_NAMEMAX); return 1; } } return 0; } /* * Try to set the unit name to the name of its source terminal. If the * name is ambigus (already given to another destination unit or * having multiple sources) then return 0. */ int uaudio_setname_srcs(struct uaudio_softc *sc, struct uaudio_unit *u, char *name) { struct uaudio_unit *s = u; while (s != NULL) { if (s->src_list == NULL || s->src_list->src_next != NULL) break; s = s->src_list; if (s->dst_list == NULL || s->dst_list->dst_next != NULL) break; if (s->name[0] != '\0') { if (name != NULL && strcmp(name, s->name) != 0) break; strlcpy(u->name, s->name, UAUDIO_NAMEMAX); return 1; } } return 0; } /* * Set the name of the given unit by using both its source and * destination units. This is naming scheme is only useful to units * that would have ambigous names if only sources or only destination * were used. */ void uaudio_setname_middle(struct uaudio_softc *sc, struct uaudio_unit *u) { struct uaudio_unit *s, *d; char name[UAUDIO_NAMEMAX]; s = u->src_list; while (1) { if (s == NULL) { DPRINTF("%s: %02u: has no srcs\n", __func__, u->id); return; } if (s->name[0] != '\0') break; s = s->src_list; } d = u->dst_list; while (1) { if (d == NULL) { DPRINTF("%s: %02u: has no dests\n", __func__, u->id); return; } if (d->name[0] != '\0') break; d = d->dst_list; } snprintf(name, UAUDIO_NAMEMAX, "%s_%s", d->name, s->name); uaudio_mkname(sc, name, u->name); } #ifdef UAUDIO_DEBUG /* * Return the synchronization type name, for debug purposes only. */ char * uaudio_isoname(int isotype) { switch (isotype) { case UE_ISO_ASYNC: return "async"; case UE_ISO_ADAPT: return "adapt"; case UE_ISO_SYNC: return "sync"; default: return "unk"; } } /* * Return the name of the given mode, debug only */ char * uaudio_modename(int mode) { switch (mode) { case 0: return "none"; case AUMODE_PLAY: return "play"; case AUMODE_RECORD: return "rec"; case AUMODE_PLAY | AUMODE_RECORD: return "duplex"; default: return "unk"; } } /* * Return UAC v2.0 endpoint usage, debug only */ char * uaudio_usagename(int usage) { switch (usage) { case UE_ISO_USAGE_DATA: return "data"; case UE_ISO_USAGE_FEEDBACK: return "feed"; case UE_ISO_USAGE_IMPL: return "impl"; default: return "unk"; } } /* * Print a bitmap of rates on the console. */ void uaudio_rates_print(int rates) { unsigned int i; for (i = 0; i < nitems(uaudio_rates); i++) { if (rates & (1 << i)) printf(" %d", uaudio_rates[i]); } printf("\n"); } /* * Print uaudio_ranges to console. */ void uaudio_ranges_print(struct uaudio_ranges *r) { struct uaudio_ranges_el *e; int more = 0; for (e = r->el; e != NULL; e = e->next) { if (more) printf(", "); if (e->min == e->max) printf("%d", e->min); else printf("[%d:%d]/%d", e->min, e->max, e->res); more = 1; } printf(" (%d vals)\n", r->nval); } /* * Print unit to the console. */ void uaudio_print_unit(struct uaudio_softc *sc, struct uaudio_unit *u) { struct uaudio_unit *s; switch (u->type) { case UAUDIO_AC_INPUT: printf("%02u: input <%s>, dest = %02u <%s>\n", u->id, u->name, u->dst_list->id, u->dst_list->name); break; case UAUDIO_AC_OUTPUT: printf("%02u: output <%s>, source = %02u <%s>\n", u->id, u->name, u->src_list->id, u->src_list->name); break; case UAUDIO_AC_MIXER: printf("%02u: mixer <%s>:\n", u->id, u->name); for (s = u->src_list; s != NULL; s = s->src_next) printf("%02u:\tsource %u <%s>:\n", u->id, s->id, s->name); break; case UAUDIO_AC_SELECTOR: printf("%02u: selector <%s>:\n", u->id, u->name); for (s = u->src_list; s != NULL; s = s->src_next) printf("%02u:\tsource %u <%s>:\n", u->id, s->id, s->name); break; case UAUDIO_AC_FEATURE: printf("%02u: feature <%s>, " "src = %02u <%s>, dst = %02u <%s>, cls = %d\n", u->id, u->name, u->src_list->id, u->src_list->name, u->dst_list->id, u->dst_list->name, u->mixer_class); break; case UAUDIO_AC_EFFECT: printf("%02u: effect <%s>, " "src = %02u <%s>, dst = %02u <%s>\n", u->id, u->name, u->src_list->id, u->src_list->name, u->dst_list->id, u->dst_list->name); break; case UAUDIO_AC_PROCESSING: case UAUDIO_AC_EXTENSION: printf("%02u: proc/ext <%s>:\n", u->id, u->name); for (s = u->src_list; s != NULL; s = s->src_next) printf("%02u:\tsource %u <%s>:\n", u->id, s->id, s->name); break; case UAUDIO_AC_CLKSRC: printf("%02u: clock source <%s>\n", u->id, u->name); break; case UAUDIO_AC_CLKSEL: printf("%02u: clock sel <%s>\n", u->id, u->name); break; case UAUDIO_AC_CLKMULT: printf("%02u: clock mult\n", u->id); break; case UAUDIO_AC_RATECONV: printf("%02u: rate conv\n", u->id); break; } } /* * Print the full mixer on the console. */ void uaudio_mixer_print(struct uaudio_softc *sc) { struct uaudio_mixent *m; struct uaudio_unit *u; for (u = sc->unit_list; u != NULL; u = u->unit_next) { for (m = u->mixent_list; m != NULL; m = m->next) { printf("%02u:\t%s.%s", u->id, u->name, m->fname); if (m->chan >= 0) printf("[%u]", m->chan); printf("\n"); } } } /* * Print the full device configuration on the console. */ void uaudio_conf_print(struct uaudio_softc *sc) { struct uaudio_alt *a; struct uaudio_params *p; struct mixer_devinfo mi; struct mixer_ctrl ctl; int i, rates; mi.index = 0; while (1) { if (uaudio_query_devinfo(sc, &mi) != 0) break; if (mi.type != AUDIO_MIXER_CLASS) { ctl.dev = mi.index; if (uaudio_get_port(sc, &ctl) != 0) { printf("%02u: failed to get port\n", mi.index); memset(&ctl.un, 0, sizeof(ctl.un)); } } printf("%02u: <%s>, next = %d, prev = %d, class = %d", mi.index, mi.label.name, mi.next, mi.prev, mi.mixer_class); switch (mi.type) { case AUDIO_MIXER_CLASS: break; case AUDIO_MIXER_VALUE: printf(", nch = %d, delta = %d", mi.un.v.num_channels, mi.un.v.delta); printf(", val ="); for (i = 0; i < mi.un.v.num_channels; i++) printf(" %d", ctl.un.value.level[i]); break; case AUDIO_MIXER_ENUM: printf(", members:"); for (i = 0; i != mi.un.e.num_mem; i++) { printf(" %s(=%d)", mi.un.e.member[i].label.name, mi.un.e.member[i].ord); } printf(", val = %d", ctl.un.ord); break; } printf("\n"); mi.index++; } printf("%d controls\n", mi.index); printf("alts:\n"); for (a = sc->alts; a != NULL; a = a->next) { rates = uaudio_alt_getrates(sc, a); printf("mode = %s, ifnum = %d, altnum = %d, " "addr = 0x%x, maxpkt = %d, sync = 0x%x, " "nch = %d, fmt = s%dle%d, rates:", uaudio_modename(a->mode), a->ifnum, a->altnum, a->data_addr, a->maxpkt, a->sync_addr, a->nch, a->bits, a->bps); uaudio_rates_print(rates); } printf("parameters:\n"); for (p = sc->params_list; p != NULL; p = p->next) { switch (sc->version) { case UAUDIO_V1: rates = p->v1_rates; break; case UAUDIO_V2: rates = uaudio_getrates(sc, p); break; } printf("pchan = %d, s%dle%d, rchan = %d, s%dle%d, rates:", p->palt ? p->palt->nch : 0, p->palt ? p->palt->bits : 0, p->palt ? p->palt->bps : 0, p->ralt ? p->ralt->nch : 0, p->ralt ? p->ralt->bits : 0, p->ralt ? p->ralt->bps : 0); uaudio_rates_print(rates); } } #endif /* * Return the number of mixer controls that have the same name but * control different channels of the same stream. */ int uaudio_mixer_nchan(struct uaudio_mixent *m, struct uaudio_mixent **rnext) { char *name; int i; i = 0; name = m->fname; while (m != NULL && strcmp(name, m->fname) == 0) { m = m->next; i++; } if (rnext) *rnext = m; return i; } /* * Skip redundant mixer entries that we don't want to expose to userland. * For instance if there is a mute-all-channels control and per-channel * mute controls, we don't want both (we expose the per-channel mute) */ void uaudio_mixer_skip(struct uaudio_mixent **pm) { struct uaudio_mixent *m = *pm; if (m != NULL && m->chan == -1 && m->next != NULL && strcmp(m->fname, m->next->fname) == 0) m = m->next; *pm = m; } /* * Return pointer to the unit and mixer entry which have the given * index exposed by the mixer(4) API. */ int uaudio_mixer_byindex(struct uaudio_softc *sc, int index, struct uaudio_unit **ru, struct uaudio_mixent **rm) { struct uaudio_unit *u; struct uaudio_mixent *m; char *name; int i; i = UAUDIO_CLASS_COUNT; for (u = sc->unit_list; u != NULL; u = u->unit_next) { m = u->mixent_list; while (1) { uaudio_mixer_skip(&m); if (m == NULL) break; if (index == i) { *ru = u; *rm = m; return 1; } if (m->type == UAUDIO_MIX_NUM) { name = m->fname; while (m != NULL && strcmp(name, m->fname) == 0) m = m->next; } else m = m->next; i++; } } return 0; } /* * Parse AC header descriptor, we use it only to determine UAC * version. Other properties (like wTotalLength) can be determined * using other descriptors, so we try to no rely on them to avoid * inconsistencies and the need for certain quirks. */ int uaudio_process_header(struct uaudio_softc *sc, struct uaudio_blob *p) { struct uaudio_blob ph; unsigned int type, subtype; if (!uaudio_getdesc(p, &ph)) return 0; if (!uaudio_getnum(&ph, 1, &type)) return 0; if (type != UDESC_CS_INTERFACE) { DPRINTF("%s: expected cs iface desc\n", __func__); return 0; } if (!uaudio_getnum(&ph, 1, &subtype)) return 0; if (subtype != UAUDIO_AC_HEADER) { DPRINTF("%s: expected header desc\n", __func__); return 0; } if (!uaudio_getnum(&ph, 2, &sc->version)) return 0; DPRINTF("%s: version 0x%x\n", __func__, sc->version); return 1; } /* * Process AC interrupt endpoint descriptor, this is mainly to skip * the descriptor as we use neither of it's properties. Our mixer * interface doesn't support unsolicitated state changes, so we've no * use of it yet. */ int uaudio_process_ac_ep(struct uaudio_softc *sc, struct uaudio_blob *p) { #ifdef UAUDIO_DEBUG static const char *xfer[] = { "ctl", "iso", "bulk", "intr" }; #endif struct uaudio_blob dp; unsigned int type, addr, attr, maxpkt, ival; unsigned char *savepos; /* * parse optional interrupt endpoint descriptor */ if (p->rptr == p->wptr) return 1; savepos = p->rptr; if (!uaudio_getdesc(p, &dp)) return 0; if (!uaudio_getnum(&dp, 1, &type)) return 0; if (type != UDESC_ENDPOINT) { p->rptr = savepos; return 1; } if (!uaudio_getnum(&dp, 1, &addr)) return 0; if (!uaudio_getnum(&dp, 1, &attr)) return 0; if (!uaudio_getnum(&dp, 2, &maxpkt)) return 0; if (!uaudio_getnum(&dp, 1, &ival)) return 0; DPRINTF("%s: addr = 0x%x, type = %s, maxpkt = %d, ival = %d\n", __func__, addr, xfer[UE_GET_XFERTYPE(attr)], UE_GET_SIZE(maxpkt), ival); return 1; } /* * Process the AC interface descriptors: mainly build the mixer and, * for UAC v2.0, find the clock source. * * The audio device exposes an audio control (AC) interface with a big * set of USB descriptors which expose the complete circuit the * device. The circuit describes how the signal flows between the USB * streaming interfaces to the terminal connectors (jacks, speakers, * mics, ...). The circuit is build of mixers, source selectors, gain * controls, mutters, processors, and alike; each comes with its own * set of controls. Most of the boring driver work is to parse the * circuit and build a human-usable set of controls that could be * exposed through the mixer(4) interface. */ int uaudio_process_ac(struct uaudio_softc *sc, struct uaudio_blob *p, int ifnum) { struct uaudio_blob units, pu; struct uaudio_unit *u, *v; unsigned char *savepos; unsigned int type, subtype, id; char *name, val; DPRINTF("%s: ifnum = %d, %zd bytes to processs\n", __func__, ifnum, p->wptr - p->rptr); sc->ctl_ifnum = ifnum; /* The first AC class-specific descriptor is the AC header */ if (!uaudio_process_header(sc, p)) return 0; /* * Determine the size of the AC descriptors array: scan * descriptors until we get the first non-class-specific * descriptor. This avoids relying on the wTotalLength field. */ savepos = p->rptr; units.rptr = units.wptr = p->rptr; while (p->rptr != p->wptr) { if (!uaudio_getdesc(p, &pu)) return 0; if (!uaudio_getnum(&pu, 1, &type)) return 0; if (type != UDESC_CS_INTERFACE) break; units.wptr = p->rptr; } p->rptr = savepos; /* * Load units, walking from outputs to inputs, as * the usb audio class spec requires. */ while (p->rptr != units.wptr) { if (!uaudio_getdesc(p, &pu)) return 0; if (!uaudio_getnum(&pu, 1, &type)) return 0; if (!uaudio_getnum(&pu, 1, &subtype)) return 0; if (subtype == UAUDIO_AC_OUTPUT) { if (!uaudio_getnum(&pu, 1, &id)) return 0; if (!uaudio_process_unit(sc, NULL, id, units, NULL)) return 0; } } /* * set terminal, effect, and processor unit names */ for (u = sc->unit_list; u != NULL; u = u->unit_next) { switch (u->type) { case UAUDIO_AC_INPUT: uaudio_mkname(sc, uaudio_tname(sc, u->term, 0), u->name); break; case UAUDIO_AC_OUTPUT: uaudio_mkname(sc, uaudio_tname(sc, u->term, 1), u->name); break; case UAUDIO_AC_CLKSRC: uaudio_mkname(sc, uaudio_clkname(u->term), u->name); break; case UAUDIO_AC_CLKSEL: uaudio_mkname(sc, "clksel", u->name); break; case UAUDIO_AC_EFFECT: uaudio_mkname(sc, "fx", u->name); break; case UAUDIO_AC_PROCESSING: uaudio_mkname(sc, "proc", u->name); break; case UAUDIO_AC_EXTENSION: uaudio_mkname(sc, "ext", u->name); break; } } /* * set mixer/selector unit names */ for (u = sc->unit_list; u != NULL; u = u->unit_next) { if (u->type != UAUDIO_AC_MIXER && u->type != UAUDIO_AC_SELECTOR) continue; if (!uaudio_setname_dsts(sc, u, NULL)) { switch (u->type) { case UAUDIO_AC_MIXER: name = "mix"; break; case UAUDIO_AC_SELECTOR: name = "sel"; break; } uaudio_mkname(sc, name, u->name); } } /* * set feature unit names and classes */ for (u = sc->unit_list; u != NULL; u = u->unit_next) { if (u->type != UAUDIO_AC_FEATURE) continue; if (uaudio_setname_dsts(sc, u, UAUDIO_NAME_REC)) { u->mixer_class = UAUDIO_CLASS_IN; continue; } if (uaudio_setname_srcs(sc, u, UAUDIO_NAME_PLAY)) { u->mixer_class = UAUDIO_CLASS_OUT; continue; } if (uaudio_setname_dsts(sc, u, NULL)) { u->mixer_class = UAUDIO_CLASS_OUT; continue; } if (uaudio_setname_srcs(sc, u, NULL)) { u->mixer_class = UAUDIO_CLASS_IN; continue; } uaudio_setname_middle(sc, u); u->mixer_class = UAUDIO_CLASS_IN; } #ifdef UAUDIO_DEBUG if (uaudio_debug) { printf("%s: units list:\n", DEVNAME(sc)); for (u = sc->unit_list; u != NULL; u = u->unit_next) uaudio_print_unit(sc, u); printf("%s: mixer controls:\n", DEVNAME(sc)); uaudio_mixer_print(sc); } #endif /* follows optional interrupt endpoint descriptor */ if (!uaudio_process_ac_ep(sc, p)) return 0; /* fetch clock source rates */ for (u = sc->unit_list; u != NULL; u = u->unit_next) { switch (u->type) { case UAUDIO_AC_CLKSRC: if (!uaudio_req_ranges(sc, 4, UAUDIO_V2_REQSEL_CLKFREQ, 0, /* channel (not used) */ sc->ctl_ifnum, u->id, &u->rates)) { printf("%s: failed to read clock rates\n", DEVNAME(sc)); return 1; } #ifdef UAUDIO_DEBUG if (uaudio_debug) { printf("%02u: clock rates: ", u->id); uaudio_ranges_print(&u->rates); } #endif break; case UAUDIO_AC_CLKSEL: if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE, UAUDIO_V2_REQ_CUR, UAUDIO_V2_REQSEL_CLKSEL, 0, sc->ctl_ifnum, u->id, &val, 1)) { printf("%s: failed to read clock selector\n", DEVNAME(sc)); return 0; } for (v = u->src_list; v != NULL; v = v->src_next) { if (--val == 0) break; } u->clock = v; break; } } 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) */ 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->clock == NULL) { printf("%s: terminal with no 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; } } if (sc->clock == NULL) { printf("%s: no clock found\n", DEVNAME(sc)); return 0; } } return 1; } /* * Parse endpoint descriptor with the following fromat: * * For playback there's a output data endpoint, of the * following types: * * type sync descr * ------------------------------------------------------- * async: Yes the device uses it's own clock but * sends feedback on a (input) sync endpoint * for the host to adjust next packet size * * sync: - data rate is constant, and device * is clocked to the usb bus. * * adapt: - the device adapts to data rate of the * host. If fixed packet size is used, * data rate is equivalent to the usb clock * so this mode is the same as the * sync mode. * * For recording there's and input data endpoint, of * the following types: * * type sync descr * ------------------------------------------------------- * async: - the device uses its own clock and * adjusts packet sizes. * * sync: - the device uses usb clock rate * * adapt: Yes the device uses host's feedback (on * on a dedicated (output) sync endpoint * to adapt to software's desired rate * * * For usb1.1 ival is cardcoded to 1 for isochronous * transfers, which means one transfer every ms. I.e one * transfer every frame period. * * For usb2, ival the poll interval is: * * frame_period * 2^(ival - 1) * * so, if we use this formula, we get something working in all * cases. * * The MaxPacketsOnly attribute is used only by "Type II" encodings, * so we don't care about it. */ int uaudio_process_as_ep(struct uaudio_softc *sc, struct uaudio_blob *p, struct uaudio_alt *a, int nep) { unsigned int addr, attr, maxpkt, isotype, ival; if (!uaudio_getnum(p, 1, &addr)) return 0; if (!uaudio_getnum(p, 1, &attr)) return 0; if (!uaudio_getnum(p, 2, &maxpkt)) return 0; if (!uaudio_getnum(p, 1, &ival)) /* bInterval */ return 0; DPRINTF("%s: addr = 0x%x, %s/%s, " "maxpktsz = %d, ival = %d\n", __func__, addr, uaudio_isoname(UE_GET_ISO_TYPE(attr)), uaudio_usagename(UE_GET_ISO_USAGE(attr)), maxpkt, ival); if (UE_GET_XFERTYPE(attr) != UE_ISOCHRONOUS) { printf("%s: skipped non-isoc endpt.\n", DEVNAME(sc)); return 1; } /* * For each AS interface setting, there's a single data * endpoint and an optional feedback endpoint. The * synchonization type is non-zero and must be set in the data * endpoints. * * However, the isoc sync type field of the attribute can't be * trusted: a lot of devices have it wrong. If the isoc sync * type is set it's necessarily a data endpoint, if it's not, * then if it is the only endpoint, it necessarily the data * endpoint. */ isotype = UE_GET_ISO_TYPE(attr); if (isotype || nep == 1) { /* this is the data endpoint */ if (a->data_addr && addr != a->data_addr) { printf("%s: skipped extra data endpt.\n", DEVNAME(sc)); return 1; } a->mode = (UE_GET_DIR(addr) == UE_DIR_IN) ? AUMODE_RECORD : AUMODE_PLAY; a->data_addr = addr; a->fps = sc->ufps / (1 << (ival - 1)); a->maxpkt = UE_GET_SIZE(maxpkt); } else { /* this is the sync endpoint */ if (a->sync_addr && addr != a->sync_addr) { printf("%s: skipped extra sync endpt.\n", DEVNAME(sc)); return 1; } a->sync_addr = addr; } return 1; } /* * Parse AS general descriptor. Non-PCM interfaces are skipped. UAC * v2.0 report the number of channels. For UAC v1.0 we set the number * of channels to zero, it will be determined later from the format * descriptor. */ int uaudio_process_as_general(struct uaudio_softc *sc, struct uaudio_blob *p, int *rispcm, struct uaudio_alt *a) { unsigned int term, fmt, ctl, fmt_type, fmt_map, nch; if (!uaudio_getnum(p, 1, &term)) return 0; switch (sc->version) { case UAUDIO_V1: if (!uaudio_getnum(p, 1, NULL)) /* bDelay */ return 0; if (!uaudio_getnum(p, 1, &fmt)) return 0; *rispcm = (fmt == UAUDIO_V1_FMT_PCM); break; case UAUDIO_V2: /* XXX: should we check if alt setting control is valid ? */ if (!uaudio_getnum(p, 1, &ctl)) return 0; if (!uaudio_getnum(p, 1, &fmt_type)) return 0; if (!uaudio_getnum(p, 4, &fmt_map)) return 0; if (!uaudio_getnum(p, 1, &nch)) return 0; a->nch = nch; *rispcm = (fmt_type == 1) && (fmt_map & UAUDIO_V2_FMT_PCM); } return 1; } /* * Parse AS format descriptor: we support only "Type 1" formats, aka * PCM. Other formats are not really audio, they are data-only * interfaces that we don't wan't to support: ethernet is much better * for raw data transfers. * * XXX: handle ieee 754 32-bit floating point formats. */ int uaudio_process_as_format(struct uaudio_softc *sc, struct uaudio_blob *p, struct uaudio_alt *a, int *ispcm) { unsigned int type, bps, bits, nch, nrates, rate_min, rate_max, rates; int i, j; switch (sc->version) { case UAUDIO_V1: if (!uaudio_getnum(p, 1, &type)) return 0; if (type != 1) { DPRINTF("%s: class v1: " "skipped unsupported type = %d\n", __func__, type); *ispcm = 0; return 1; } if (!uaudio_getnum(p, 1, &nch)) return 0; if (!uaudio_getnum(p, 1, &bps)) return 0; if (!uaudio_getnum(p, 1, &bits)) return 0; if (!uaudio_getnum(p, 1, &nrates)) return 0; rates = 0; if (nrates == 0) { if (!uaudio_getnum(p, 3, &rate_min)) return 0; if (!uaudio_getnum(p, 3, &rate_max)) return 0; for (i = 0; i < nitems(uaudio_rates); i++) { if (uaudio_rates[i] >= rate_min && uaudio_rates[i] <= rate_max) rates |= 1 << i; } } else { for (j = 0; j < nrates; j++) { if (!uaudio_getnum(p, 3, &rate_min)) return 0; for (i = 0; i < nitems(uaudio_rates); i++) { if (uaudio_rates[i] == rate_min) rates |= 1 << i; } } } a->v1_rates = rates; a->nch = nch; break; case UAUDIO_V2: /* * sample rate ranges are obtained with requests to * the clock source, as defined by the clock source * descriptor * * the number of channels is in the GENERAL descriptor */ if (!uaudio_getnum(p, 1, &type)) return 0; if (type != 1) { DPRINTF("%s: class v2: " "skipped unsupported type = %d\n", __func__, type); *ispcm = 0; return 1; } if (!uaudio_getnum(p, 1, &bps)) return 0; if (!uaudio_getnum(p, 1, &bits)) return 0; /* * nch is in the v2 general desc, rates come from the * clock source, so we're done. */ break; } a->bps = bps; a->bits = bits; *ispcm = 1; return 1; } /* * Parse AS descriptors. * * The audio streaming (AS) interfaces are used to move data between * the host and the device. On the one hand, the device has * analog-to-digital (ADC) and digital-to-analog (DAC) converters * which have their own low-jitter clock source. On other hand, the * USB host runs a bus clock using another clock source. So both * drift. That's why, the device sends feedback to the driver for the * host to adjust continuously its data rate, hence the need for sync * endpoints. */ int uaudio_process_as(struct uaudio_softc *sc, struct uaudio_blob *p, int ifnum, int altnum, int nep) { struct uaudio_alt *a, *anext, **pa; struct uaudio_blob dp; unsigned char *savep; unsigned int type, subtype; int ispcm = 0; a = malloc(sizeof(struct uaudio_alt), M_DEVBUF, M_WAITOK); a->mode = 0; a->nch = 0; a->v1_rates = 0; a->data_addr = 0; a->sync_addr = 0; a->ifnum = ifnum; a->altnum = altnum; while (p->rptr != p->wptr) { savep = p->rptr; if (!uaudio_getdesc(p, &dp)) goto failed; if (!uaudio_getnum(&dp, 1, &type)) goto failed; if (type != UDESC_CS_INTERFACE) { p->rptr = savep; break; } if (!uaudio_getnum(&dp, 1, &subtype)) goto failed; switch (subtype) { case UAUDIO_AS_GENERAL: if (!uaudio_process_as_general(sc, &dp, &ispcm, a)) goto failed; break; case UAUDIO_AS_FORMAT: if (!uaudio_process_as_format(sc, &dp, a, &ispcm)) goto failed; break; default: DPRINTF("%s: unknown desc\n", __func__); continue; } if (!ispcm) { DPRINTF("%s: non-pcm iface\n", __func__); free(a, M_DEVBUF, sizeof(struct uaudio_alt)); return 1; } } while (p->rptr != p->wptr) { savep = p->rptr; if (!uaudio_getdesc(p, &dp)) goto failed; if (!uaudio_getnum(&dp, 1, &type)) goto failed; if (type == UDESC_CS_ENDPOINT) continue; if (type != UDESC_ENDPOINT) { p->rptr = savep; break; } if (!uaudio_process_as_ep(sc, &dp, a, nep)) goto failed; } if (a->mode == 0) { printf("%s: no data endpoints found\n", DEVNAME(sc)); free(a, M_DEVBUF, sizeof(struct uaudio_alt)); return 1; } /* * Append to list of alts, but keep the list sorted by number * of channels, bits and rate. From the most capable to the * less capable. */ pa = &sc->alts; while (1) { if ((anext = *pa) == NULL) break; if (a->nch > anext->nch) break; else if (a->nch == anext->nch) { if (a->bits > anext->bits) break; else if (sc->version == UAUDIO_V1 && a->v1_rates > anext->v1_rates) break; } pa = &anext->next; } a->next = *pa; *pa = a; return 1; failed: free(a, M_DEVBUF, sizeof(struct uaudio_alt)); return 0; } /* * Populate the sc->params_list with combinations of play and rec alt * settings that work together in full-duplex. */ void uaudio_fixup_params(struct uaudio_softc *sc) { struct uaudio_alt *ap, *ar, *a; struct uaudio_params *p, **pp; int rates; /* * Add full-duplex parameter combinations. */ pp = &sc->params_list; for (ap = sc->alts; ap != NULL; ap = ap->next) { if (ap->mode != AUMODE_PLAY) continue; for (ar = sc->alts; ar != NULL; ar = ar->next) { if (ar->mode != AUMODE_RECORD) continue; if (ar->bps != ap->bps || ar->bits != ap->bits) continue; switch (sc->version) { case UAUDIO_V1: rates = ap->v1_rates & ar->v1_rates; if (rates == 0) continue; break; case UAUDIO_V2: /* UAC v2.0 common rates */ rates = 0; break; } p = malloc(sizeof(struct uaudio_params), M_DEVBUF, M_WAITOK); p->palt = ap; p->ralt = ar; p->v1_rates = rates; p->next = NULL; *pp = p; pp = &p->next; } } /* * For unidirectional devices, add play-only and or rec-only * parameters. */ if (sc->params_list == NULL) { for (a = sc->alts; a != NULL; a = a->next) { p = malloc(sizeof(struct uaudio_params), M_DEVBUF, M_WAITOK); if (a->mode == AUMODE_PLAY) { p->palt = a; p->ralt = NULL; } else { p->palt = NULL; p->ralt = a; } p->v1_rates = a->v1_rates; p->next = NULL; *pp = p; pp = &p->next; } } } /* * Parse all descriptors and build configuration of the device. */ int uaudio_process_conf(struct uaudio_softc *sc, struct uaudio_blob *p) { struct uaudio_blob dp; unsigned int type, ifnum, altnum, nep, class, subclass; while (p->rptr != p->wptr) { if (!uaudio_getdesc(p, &dp)) return 0; if (!uaudio_getnum(&dp, 1, &type)) return 0; if (type != UDESC_INTERFACE) continue; if (!uaudio_getnum(&dp, 1, &ifnum)) return 0; if (!uaudio_getnum(&dp, 1, &altnum)) return 0; if (!uaudio_getnum(&dp, 1, &nep)) return 0; if (!uaudio_getnum(&dp, 1, &class)) return 0; if (!uaudio_getnum(&dp, 1, &subclass)) return 0; if (class != UICLASS_AUDIO) { DPRINTF("%s: skipped iface\n", __func__); continue; } switch (subclass) { case UISUBCLASS_AUDIOCONTROL: usbd_claim_iface(sc->udev, ifnum); if (sc->unit_list != NULL) { DPRINTF("%s: >1 AC ifaces\n", __func__); goto done; } if (!uaudio_process_ac(sc, p, ifnum)) return 0; break; case UISUBCLASS_AUDIOSTREAM: usbd_claim_iface(sc->udev, ifnum); if (nep == 0) { DPRINTF("%s: " "stop altnum %d\n", __func__, altnum); break; /* 0 is "stop sound", skip it */ } if (!uaudio_process_as(sc, p, ifnum, altnum, nep)) return 0; } } done: uaudio_fixup_params(sc); return 1; } /* * Allocate a isochronous transfer and its bounce-buffers with the * given maximum framesize and maximum frames per transfer. */ int uaudio_xfer_alloc(struct uaudio_softc *sc, struct uaudio_xfer *xfer, unsigned int framesize, unsigned int count) { xfer->usb_xfer = usbd_alloc_xfer(sc->udev); if (xfer->usb_xfer == NULL) return ENOMEM; xfer->buf = usbd_alloc_buffer(xfer->usb_xfer, framesize * count); if (xfer->buf == NULL) return ENOMEM; xfer->sizes = mallocarray(count, sizeof(xfer->sizes[0]), M_DEVBUF, M_WAITOK); if (xfer->sizes == NULL) return ENOMEM; return 0; } /* * Free a isochronous transfer and its bounce-buffers. */ void uaudio_xfer_free(struct uaudio_softc *sc, struct uaudio_xfer *xfer, unsigned int count) { if (xfer->usb_xfer != NULL) { /* frees request buffer as well */ usbd_free_xfer(xfer->usb_xfer); xfer->usb_xfer = NULL; } if (xfer->sizes != NULL) { free(xfer->sizes, M_DEVBUF, sizeof(xfer->sizes[0]) * count); xfer->sizes = NULL; } } /* * Close a stream and free all associated resources */ void uaudio_stream_close(struct uaudio_softc *sc, int dir) { struct uaudio_stream *s = &sc->pstream; struct uaudio_alt *a = sc->params->palt; struct usbd_interface *iface; int err, i; if (dir == AUMODE_PLAY) { s = &sc->pstream; a = sc->params->palt; } else { s = &sc->rstream; a = sc->params->ralt; } if (s->data_pipe) { usbd_close_pipe(s->data_pipe); s->data_pipe = NULL; } if (s->sync_pipe) { usbd_close_pipe(s->sync_pipe); s->sync_pipe = NULL; } err = usbd_device2interface_handle(sc->udev, a->ifnum, &iface); if (err) printf("%s: can't get iface handle\n", DEVNAME(sc)); else { err = usbd_set_interface(iface, 0); if (err) printf("%s: can't reset interface\n", DEVNAME(sc)); } for (i = 0; i < UAUDIO_NXFERS_MAX; i++) { uaudio_xfer_free(sc, s->data_xfers + i, s->nframes_max); uaudio_xfer_free(sc, s->sync_xfers + i, 1); } } /* * Open a stream with the given buffer settings and set the current * interface alt setting. */ int uaudio_stream_open(struct uaudio_softc *sc, int dir, void *start, void *end, size_t blksz, void (*intr)(void *), void *arg) { struct uaudio_stream *s; struct uaudio_alt *a; struct usbd_interface *iface; unsigned char req_buf[4]; unsigned int bpa, spf_max, min_blksz; int err, clock_id, i; if (dir == AUMODE_PLAY) { s = &sc->pstream; a = sc->params->palt; } else { s = &sc->rstream; a = sc->params->ralt; } for (i = 0; i < UAUDIO_NXFERS_MAX; i++) { s->data_xfers[i].usb_xfer = NULL; s->data_xfers[i].sizes = NULL; s->sync_xfers[i].usb_xfer = NULL; s->sync_xfers[i].sizes = NULL; } s->data_pipe = NULL; s->sync_pipe = NULL; s->nframes_mask = 0; i = a->fps; while (i > 1000) { s->nframes_mask = (s->nframes_mask << 1) | 1; i >>= 1; } /* bytes per audio frame */ bpa = a->bps * a->nch; /* ideal samples per usb frame, fixed-point */ s->spf = (uint64_t)sc->rate * UAUDIO_SPF_DIV / a->fps; /* * UAC2.0 spec allows 1000PPM tolerance in sample frequency, * while USB1.1 requires 1Hz, which is 125PPM at 8kHz. We * accept as much as 1/256, which is 2500PPM. */ s->spf_min = (uint64_t)s->spf * 255 / 256; s->spf_max = (uint64_t)s->spf * 257 / 256; /* max spf can't exceed the device usb packet size */ spf_max = (a->maxpkt / bpa) * UAUDIO_SPF_DIV; if (s->spf > spf_max) { printf("%s: samples per frame too large\n", DEVNAME(sc)); return EIO; } if (s->spf_max > spf_max) s->spf_max = spf_max; /* * Upon transfer completion the device must reach the audio * block boundary, which is propagated to upper layers. In the * worst case, we schedule only frames of spf_max samples, but * the device returns only frames of spf_min samples; in this * case the amount actually transfered is at least: * * min_blksz = blksz / spf_max * spf_min * * As we've UAUDIO_NXFERS outstanding blocks, worst-case * remaining bytes is at most: * * UAUDIO_NXFERS * (blksz - min_blksz) */ min_blksz = (((uint64_t)blksz << 32) / s->spf_max * s->spf_min) >> 32; /* round to sample size */ min_blksz -= min_blksz % bpa; /* finally this is what ensures we cross block boundary */ s->safe_blksz = blksz + UAUDIO_NXFERS_MAX * (blksz - min_blksz); /* max number of (micro-)frames we'll ever use */ s->nframes_max = (uint64_t)(s->safe_blksz / bpa) * UAUDIO_SPF_DIV / s->spf_min + 1; /* round to next usb1.1 frame */ s->nframes_max = (s->nframes_max + s->nframes_mask) & ~s->nframes_mask; /* this is the max packet size we'll ever need */ s->maxpkt = bpa * ((s->spf_max + UAUDIO_SPF_DIV - 1) / UAUDIO_SPF_DIV); /* how many xfers we need to fill sc->host_nframes */ s->nxfers = sc->host_nframes / s->nframes_max; if (s->nxfers > UAUDIO_NXFERS_MAX) s->nxfers = UAUDIO_NXFERS_MAX; DPRINTF("%s: %s: blksz = %zu, rate = %u, fps = %u\n", __func__, dir == AUMODE_PLAY ? "play" : "rec", blksz, sc->rate, a->fps); DPRINTF("%s: spf = 0x%x in [0x%x:0x%x]\n", __func__, s->spf, s->spf_min, s->spf_max); DPRINTF("%s: nframes_max = %u, nframes_mask = %u, maxpkt = %u\n", __func__, s->nframes_max, s->nframes_mask, s->maxpkt); DPRINTF("%s: safe_blksz = %d, nxfers = %d\n", __func__, s->safe_blksz, s->nxfers); if (s->nxfers < UAUDIO_NXFERS_MIN) { printf("%s: block size too large\n", DEVNAME(sc)); return EIO; } /* * Require at least 2ms block size to ensure no * transfer exceeds two blocks. * * XXX: use s->nframes_mask instead of 1000 */ if (1000 * blksz < 2 * sc->rate * bpa) { printf("%s: audio block too small\n", DEVNAME(sc)); return EIO; } for (i = 0; i < s->nxfers; i++) { err = uaudio_xfer_alloc(sc, s->data_xfers + i, s->maxpkt, s->nframes_max); if (err) goto failed; if (a->sync_addr) { err = uaudio_xfer_alloc(sc, s->sync_xfers + i, sc->sync_pktsz, 1); if (err) goto failed; } } err = usbd_device2interface_handle(sc->udev, a->ifnum, &iface); if (err) { printf("%s: can't get iface handle\n", DEVNAME(sc)); goto failed; } err = usbd_set_interface(iface, a->altnum); if (err) { printf("%s: can't set interface\n", DEVNAME(sc)); goto failed; } /* * Set the sample rate. * * Certain devices are able to lock their clock to the data * rate and expose no frequency control. In this case, the * request to set the frequency will fail, but this error is * safe to ignore. * * Such devices expose this capability in the class-specific * endpoint descriptor (UAC v1.0) or in the clock unit * descriptor (UAC v2.0) but we don't want to use them for now * as certain devices have them wrong, missing or misplaced. */ switch (sc->version) { case UAUDIO_V1: req_buf[0] = sc->rate; req_buf[1] = sc->rate >> 8; req_buf[2] = sc->rate >> 16; if (!uaudio_req(sc, UT_WRITE_CLASS_ENDPOINT, UAUDIO_V1_REQ_SET_CUR, UAUDIO_REQSEL_RATE, 0, a->data_addr, 0, req_buf, 3)) { DPRINTF("%s: not setting endpoint rate\n", __func__); } break; case UAUDIO_V2: req_buf[0] = sc->rate; req_buf[1] = sc->rate >> 8; req_buf[2] = sc->rate >> 16; req_buf[3] = sc->rate >> 24; clock_id = uaudio_clock_id(sc); if (clock_id < 0) { printf("%s: can't get clock id\n", DEVNAME(sc)); goto failed; } if (!uaudio_req(sc, UT_WRITE_CLASS_INTERFACE, UAUDIO_V2_REQ_CUR, UAUDIO_REQSEL_RATE, 0, sc->ctl_ifnum, clock_id, req_buf, 4)) { DPRINTF("%s: not setting clock rate\n", __func__); } break; } err = usbd_open_pipe(iface, a->data_addr, 0, &s->data_pipe); if (err) { printf("%s: can't open data pipe\n", DEVNAME(sc)); goto failed; } if (a->sync_addr) { err = usbd_open_pipe(iface, a->sync_addr, 0, &s->sync_pipe); if (err) { printf("%s: can't open sync pipe\n", DEVNAME(sc)); goto failed; } } s->data_nextxfer = 0; s->sync_nextxfer = 0; s->spf_remain = 0; s->intr = intr; s->arg = arg; s->ring_start = start; s->ring_end = end; s->ring_blksz = blksz; s->ring_pos = s->ring_start; s->ring_offs = 0; s->ring_icnt = 0; s->ubuf_xfer = 0; s->ubuf_pos = 0; return 0; failed: uaudio_stream_close(sc, dir); return ENOMEM; } /* * Adjust play samples-per-frame to keep play and rec streams in sync. */ void uaudio_adjspf(struct uaudio_softc *sc) { struct uaudio_stream *s = &sc->pstream; int diff; if (sc->mode != (AUMODE_RECORD | AUMODE_PLAY)) return; if (s->sync_pipe != NULL) return; /* * number of samples play stream is ahead of record stream. */ diff = sc->diff_nsamp; if (sc->diff_nframes > 0) { diff -= (uint64_t)sc->pstream.spf * sc->diff_nframes / UAUDIO_SPF_DIV; } else { diff += (uint64_t)sc->rstream.spf * -sc->diff_nframes / UAUDIO_SPF_DIV; } /* * adjust samples-per-frames to resync within the next second */ s->spf = (uint64_t)(sc->rate - diff) * UAUDIO_SPF_DIV / sc->ufps; if (s->spf > s->spf_max) s->spf = s->spf_max; else if (s->spf < s->spf_min) s->spf = s->spf_min; #ifdef UAUDIO_DEBUG if (uaudio_debug >= 2) printf("%s: diff = %d, spf = 0x%x\n", __func__, diff, s->spf); #endif } /* * Copy one audio block to the xfer buffer. */ void uaudio_pdata_copy(struct uaudio_softc *sc) { struct uaudio_stream *s = &sc->pstream; struct uaudio_xfer *xfer; size_t count, avail; int index; #ifdef UAUDIO_DEBUG struct timeval tv; getmicrotime(&tv); #endif while (sc->copy_todo > 0 && s->ubuf_xfer < s->nxfers) { index = s->data_nextxfer + s->ubuf_xfer; if (index >= s->nxfers) index -= s->nxfers; xfer = s->data_xfers + index; avail = s->ring_end - s->ring_pos; count = xfer->size - s->ubuf_pos; if (count > avail) count = avail; if (count > sc->copy_todo) count = sc->copy_todo; #ifdef UAUDIO_DEBUG if (uaudio_debug >= 2) { printf("%s: %llu.%06lu: %zd..%zd -> %u:%u..%zu\n", __func__, tv.tv_sec, tv.tv_usec, s->ring_pos - s->ring_start, s->ring_pos - s->ring_start + count, s->ubuf_xfer, s->ubuf_pos, s->ubuf_pos + count); } #endif memcpy(xfer->buf + s->ubuf_pos, s->ring_pos, count); sc->copy_todo -= count; s->ring_pos += count; if (s->ring_pos == s->ring_end) { s->ring_pos = s->ring_start; } s->ubuf_pos += count; if (s->ubuf_pos == xfer->size) { usb_syncmem(&xfer->usb_xfer->dmabuf, 0, xfer->size, BUS_DMASYNC_PREWRITE); s->ubuf_pos = 0; #ifdef DIAGNOSTIC if (s->ubuf_xfer == s->nxfers) { printf("%s: overflow\n", __func__); return; } #endif s->ubuf_xfer++; } } } /* * Calculate and fill xfer frames sizes. */ void uaudio_pdata_calcsizes(struct uaudio_softc *sc, struct uaudio_xfer *xfer) { #ifdef UAUDIO_DEBUG struct timeval tv; #endif struct uaudio_stream *s = &sc->pstream; struct uaudio_alt *a = sc->params->palt; unsigned int fsize, bpf; int done; bpf = a->bps * a->nch; done = s->ring_offs; xfer->nframes = 0; while (1) { /* * if we crossed the next block boundary, we're done */ if ((xfer->nframes & s->nframes_mask) == 0 && done > s->safe_blksz) break; /* * this can't happen, debug only */ if (xfer->nframes == s->nframes_max) { printf("%s: too many frames for play xfer: " "done = %u, blksz = %d\n", DEVNAME(sc), done, s->ring_blksz); break; } /* * calculate frame size and adjust state */ s->spf_remain += s->spf; fsize = s->spf_remain / UAUDIO_SPF_DIV * bpf; s->spf_remain %= UAUDIO_SPF_DIV; done += fsize; xfer->sizes[xfer->nframes] = fsize; xfer->nframes++; } xfer->size = done - s->ring_offs; s->ring_offs = done - s->ring_blksz; #ifdef UAUDIO_DEBUG if (uaudio_debug >= 3) { getmicrotime(&tv); printf("%s: size = %d, offs -> %d\n", __func__, xfer->size, s->ring_offs); } #endif memset(xfer->buf, 0, xfer->size); } /* * Submit a play data transfer to the USB driver. */ void uaudio_pdata_xfer(struct uaudio_softc *sc) { #ifdef UAUDIO_DEBUG struct timeval tv; #endif struct uaudio_stream *s = &sc->pstream; struct uaudio_xfer *xfer; int err; xfer = s->data_xfers + s->data_nextxfer; #ifdef UAUDIO_DEBUG if (uaudio_debug >= 3) { getmicrotime(&tv); printf("%s: %llu.%06lu: " "%d bytes, %u frames, remain = 0x%x, offs = %d\n", __func__, tv.tv_sec, tv.tv_usec, xfer->size, xfer->nframes, s->spf_remain, s->ring_offs); } #endif /* this can't happen, debug only */ if (xfer->nframes == 0) { printf("%s: zero frame play xfer\n", DEVNAME(sc)); return; } /* * We accept short transfers because in case of babble/stale frames * the tranfer will be short */ usbd_setup_isoc_xfer(xfer->usb_xfer, s->data_pipe, sc, xfer->sizes, xfer->nframes, USBD_NO_COPY | USBD_SHORT_XFER_OK, uaudio_pdata_intr); err = usbd_transfer(xfer->usb_xfer); if (err != 0 && err != USBD_IN_PROGRESS) printf("%s: play xfer, err = %d\n", DEVNAME(sc), err); if (++s->data_nextxfer == s->nxfers) s->data_nextxfer = 0; } /* * Callback called by the USB driver upon completion of play data transfer. */ void uaudio_pdata_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status) { #ifdef UAUDIO_DEBUG struct timeval tv; #endif struct uaudio_softc *sc = arg; struct uaudio_stream *s = &sc->pstream; struct uaudio_xfer *xfer; uint32_t size; int nintr; if (status != 0 && status != USBD_IOERROR) { DPRINTF("%s: xfer status = %d\n", __func__, status); return; } xfer = s->data_xfers + s->data_nextxfer; if (xfer->usb_xfer != usb_xfer) { DPRINTF("%s: wrong xfer\n", __func__); return; } sc->diff_nsamp += xfer->size / (sc->params->palt->nch * sc->params->palt->bps); sc->diff_nframes += xfer->nframes; #ifdef UAUDIO_DEBUG if (uaudio_debug >= 2) { getmicrotime(&tv); printf("%s: %llu.%06lu: %u: %u bytes\n", __func__, tv.tv_sec, tv.tv_usec, s->data_nextxfer, xfer->size); } #endif usbd_get_xfer_status(usb_xfer, NULL, NULL, &size, NULL); if (size != xfer->size) { DPRINTF("%s: %u bytes out of %u: incomplete play xfer\n", DEVNAME(sc), size, xfer->size); } /* * Upper layer call-back may call uaudio_underrun(), which * needs the current size of this transfer. So, don't * recalculate the sizes and don't schedule the transfer yet. */ s->ring_icnt += xfer->size; nintr = 0; mtx_enter(&audio_lock); while (s->ring_icnt >= s->ring_blksz) { s->intr(s->arg); s->ring_icnt -= s->ring_blksz; nintr++; } mtx_leave(&audio_lock); if (nintr != 1) printf("%s: %d: bad play intr count\n", __func__, nintr); uaudio_pdata_calcsizes(sc, xfer); uaudio_pdata_xfer(sc); #ifdef DIAGNOSTIC if (s->ubuf_xfer == 0) { printf("%s: underflow\n", __func__); return; } #endif s->ubuf_xfer--; uaudio_pdata_copy(sc); } /* * Submit a play sync transfer to the USB driver. */ void uaudio_psync_xfer(struct uaudio_softc *sc) { #ifdef UAUDIO_DEBUG struct timeval tv; #endif struct uaudio_stream *s = &sc->pstream; struct uaudio_xfer *xfer; unsigned int i; int err; xfer = s->sync_xfers + s->sync_nextxfer; xfer->nframes = 1; for (i = 0; i < xfer->nframes; i++) xfer->sizes[i] = sc->sync_pktsz; xfer->size = xfer->nframes * sc->sync_pktsz; #ifdef UAUDIO_DEBUG memset(xfer->buf, 0xd0, sc->sync_pktsz * xfer->nframes); #endif usbd_setup_isoc_xfer(xfer->usb_xfer, s->sync_pipe, sc, xfer->sizes, xfer->nframes, USBD_NO_COPY | USBD_SHORT_XFER_OK, uaudio_psync_intr); err = usbd_transfer(xfer->usb_xfer); if (err != 0 && err != USBD_IN_PROGRESS) printf("%s: sync play xfer, err = %d\n", DEVNAME(sc), err); if (++s->sync_nextxfer == s->nxfers) s->sync_nextxfer = 0; #ifdef UAUDIO_DEBUG if (uaudio_debug >= 3) { getmicrotime(&tv); printf("%s: %llu.%06lu: %dB, %d fr\n", __func__, tv.tv_sec, tv.tv_usec, sc->sync_pktsz, xfer->nframes); } #endif } /* * Callback called by the USB driver upon completion of play sync transfer. */ void uaudio_psync_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status) { #ifdef UAUDIO_DEBUG struct timeval tv; #endif struct uaudio_softc *sc = arg; struct uaudio_stream *s = &sc->pstream; struct uaudio_xfer *xfer; unsigned char *buf; unsigned int i; int32_t val; if (status != 0) { DPRINTF("%s: xfer status = %d\n", __func__, status); return; } xfer = s->sync_xfers + s->sync_nextxfer; if (xfer->usb_xfer != usb_xfer) { DPRINTF("%s: wrong xfer\n", __func__); return; } /* XXX: there's only one frame, the loop is not necessary */ buf = xfer->buf; for (i = 0; i < xfer->nframes; i++) { if (xfer->sizes[i] == sc->sync_pktsz) { val = buf[0] | buf[1] << 8 | buf[2] << 16; if (sc->sync_pktsz == 4) val |= xfer->buf[3] << 24; else val <<= 2; val *= UAUDIO_SPF_DIV / (1 << 16); #ifdef UAUDIO_DEBUG if (uaudio_debug >= 2) { getmicrotime(&tv); printf("%s: %llu.%06lu: spf: %08x\n", __func__, tv.tv_sec, tv.tv_usec, val); } #endif if (val > s->spf_max) s->spf = s->spf_max; else if (val < s->spf_min) s->spf = s->spf_min; else s->spf = val; } buf += sc->sync_pktsz; } uaudio_psync_xfer(sc); } /* * Submit a rec data transfer to the USB driver. */ void uaudio_rdata_xfer(struct uaudio_softc *sc) { #ifdef UAUDIO_DEBUG struct timeval tv; #endif struct uaudio_stream *s = &sc->rstream; struct uaudio_alt *a = sc->params->ralt; struct uaudio_xfer *xfer; unsigned int fsize, bpf; int done; int err; xfer = s->data_xfers + s->data_nextxfer; bpf = a->bps * a->nch; xfer->nframes = 0; done = s->ring_offs; while (1) { /* * if we crossed the next block boundary, we're done */ if ((xfer->nframes & s->nframes_mask) == 0 && done > s->safe_blksz) { done: xfer->size = done - s->ring_offs; s->ring_offs = done - s->ring_blksz; break; } /* * this can't happen, debug only */ if (xfer->nframes == s->nframes_max) { printf("%s: too many frames for rec xfer: " "done = %d, blksz = %d\n", DEVNAME(sc), done, s->ring_blksz); goto done; } /* * estimate next block using s->spf, but allow * transfers up to maxpkt */ s->spf_remain += s->spf; fsize = s->spf_remain / UAUDIO_SPF_DIV * bpf; s->spf_remain %= UAUDIO_SPF_DIV; done += fsize; xfer->sizes[xfer->nframes] = s->maxpkt; xfer->nframes++; } #ifdef UAUDIO_DEBUG if (uaudio_debug >= 3) { getmicrotime(&tv); printf("%s: %llu.%06lu: " "%u fr, %d bytes (max %d), offs = %d\n", __func__, tv.tv_sec, tv.tv_usec, xfer->nframes, xfer->size, s->maxpkt * xfer->nframes, s->ring_offs); } #endif /* this can't happen, debug only */ if (xfer->nframes == 0) { printf("%s: zero frame rec xfer\n", DEVNAME(sc)); return; } #ifdef UAUDIO_DEBUG memset(xfer->buf, 0xd0, s->maxpkt * xfer->nframes); #endif usbd_setup_isoc_xfer(xfer->usb_xfer, s->data_pipe, sc, xfer->sizes, xfer->nframes, USBD_NO_COPY | USBD_SHORT_XFER_OK, uaudio_rdata_intr); err = usbd_transfer(xfer->usb_xfer); if (err != 0 && err != USBD_IN_PROGRESS) printf("%s: rec xfer, err = %d\n", DEVNAME(sc), err); if (++s->data_nextxfer == s->nxfers) s->data_nextxfer = 0; } /* * Callback called by the USB driver upon completion of rec data transfer. */ void uaudio_rdata_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status) { #ifdef UAUDIO_DEBUG struct timeval tv; #endif struct uaudio_softc *sc = arg; struct uaudio_stream *s = &sc->rstream; struct uaudio_alt *a = sc->params->ralt; struct uaudio_xfer *xfer; unsigned char *buf, *framebuf; unsigned int count, fsize, fsize_min, nframes, bpf; unsigned int data_size, null_count; unsigned int nintr; if (status != 0) { DPRINTF("%s: xfer status = %d\n", __func__, status); return; } xfer = s->data_xfers + s->data_nextxfer; if (xfer->usb_xfer != usb_xfer) { DPRINTF("%s: wrong xfer\n", __func__); return; } bpf = a->bps * a->nch; framebuf = xfer->buf; nframes = 0; null_count = 0; data_size = 0; fsize_min = s->spf_min / UAUDIO_SPF_DIV; for (nframes = 0; nframes < xfer->nframes; nframes++) { /* * Device clock may take some time to lock during which * we'd receive empty or incomplete packets for which we * need to generate silence. */ fsize = xfer->sizes[nframes]; if (fsize < fsize_min) { s->spf_remain += s->spf; fsize = s->spf_remain / UAUDIO_SPF_DIV * bpf; s->spf_remain %= UAUDIO_SPF_DIV; memset(framebuf, 0, fsize); null_count++; } data_size += fsize; /* * fill ring from frame buffer, handling * boundary conditions */ buf = framebuf; while (fsize > 0) { count = s->ring_end - s->ring_pos; if (count > fsize) count = fsize; memcpy(s->ring_pos, buf, count); s->ring_pos += count; if (s->ring_pos == s->ring_end) s->ring_pos = s->ring_start; buf += count; fsize -= count; } framebuf += s->maxpkt; } s->ring_offs += data_size - xfer->size; s->ring_icnt += data_size; sc->diff_nsamp -= data_size / (sc->params->ralt->nch * sc->params->ralt->bps); sc->diff_nframes -= xfer->nframes; sc->adjspf_age += xfer->nframes; if (sc->adjspf_age >= sc->ufps / 8) { sc->adjspf_age -= sc->ufps / 8; uaudio_adjspf(sc); } #ifdef UAUDIO_DEBUG if (uaudio_debug >= 2) { getmicrotime(&tv); printf("%s: %llu.%06lu: %u: " "%u bytes of %u, offs -> %d\n", __func__, tv.tv_sec, tv.tv_usec, s->data_nextxfer, data_size, xfer->size, s->ring_offs); } if (null_count > 0) { DPRINTF("%s: %u null frames out of %u: incomplete record xfer\n", DEVNAME(sc), null_count, xfer->nframes); } #endif uaudio_rdata_xfer(sc); nintr = 0; mtx_enter(&audio_lock); while (s->ring_icnt >= s->ring_blksz) { s->intr(s->arg); s->ring_icnt -= s->ring_blksz; nintr++; } mtx_leave(&audio_lock); if (nintr != 1) printf("%s: %u: bad rec intr count\n", DEVNAME(sc), nintr); } /* * Start simultaneously playback and recording, unless trigger_input() * and trigger_output() were not both called yet. */ void uaudio_trigger(struct uaudio_softc *sc) { int i, s; if (sc->mode != sc->trigger_mode) return; DPRINTF("%s: preparing\n", __func__); if (sc->mode & AUMODE_PLAY) { for (i = 0; i < sc->pstream.nxfers; i++) uaudio_pdata_calcsizes(sc, sc->pstream.data_xfers + i); uaudio_pdata_copy(sc); } sc->diff_nsamp = 0; sc->diff_nframes = 0; sc->adjspf_age = 0; DPRINTF("%s: starting\n", __func__); s = splusb(); for (i = 0; i < UAUDIO_NXFERS_MAX; i++) { if ((sc->mode & AUMODE_PLAY) && i < sc->pstream.nxfers) { if (sc->pstream.sync_pipe) uaudio_psync_xfer(sc); uaudio_pdata_xfer(sc); } if ((sc->mode & AUMODE_RECORD) && i < sc->rstream.nxfers) uaudio_rdata_xfer(sc); } splx(s); } void uaudio_print(struct uaudio_softc *sc) { struct uaudio_unit *u; struct uaudio_mixent *m; struct uaudio_params *p; int pchan = 0, rchan = 0, async = 0; int nctl = 0; for (u = sc->unit_list; u != NULL; u = u->unit_next) { m = u->mixent_list; while (1) { uaudio_mixer_skip(&m); if (m == NULL) break; m = m->next; nctl++; } } for (p = sc->params_list; p != NULL; p = p->next) { if (p->palt && p->palt->nch > pchan) pchan = p->palt->nch; if (p->ralt && p->ralt->nch > rchan) rchan = p->ralt->nch; if (p->palt && p->palt->sync_addr) async = 1; if (p->ralt && p->ralt->sync_addr) async = 1; } printf("%s: class v%d, %s, %s, channels: %d play, %d rec, %d ctls\n", DEVNAME(sc), sc->version >> 8, sc->ufps == 1000 ? "full-speed" : "high-speed", async ? "async" : "sync", pchan, rchan, nctl); } int uaudio_match(struct device *parent, void *match, void *aux) { struct usb_attach_arg *arg = aux; struct usb_interface_descriptor *idesc; if (arg->iface == NULL || arg->device == NULL) return UMATCH_NONE; idesc = usbd_get_interface_descriptor(arg->iface); if (idesc == NULL) { DPRINTF("%s: couldn't get idesc\n", __func__); return UMATCH_NONE; } if (idesc->bInterfaceClass != UICLASS_AUDIO || idesc->bInterfaceSubClass != UISUBCLASS_AUDIOSTREAM) return UMATCH_NONE; return UMATCH_VENDOR_PRODUCT_CONF_IFACE; } void uaudio_attach(struct device *parent, struct device *self, void *aux) { struct uaudio_softc *sc = (struct uaudio_softc *)self; struct usb_attach_arg *arg = aux; struct usb_config_descriptor *cdesc; struct uaudio_blob desc; /* * this device has audio AC or AS or MS interface, get the * full config descriptor and attach audio devices */ cdesc = usbd_get_config_descriptor(arg->device); if (cdesc == NULL) return; desc.rptr = (unsigned char *)cdesc; desc.wptr = desc.rptr + UGETW(cdesc->wTotalLength); sc->udev = arg->device; sc->unit_list = NULL; sc->names = NULL; sc->alts = NULL; sc->params_list = NULL; sc->clock = NULL; sc->params = NULL; sc->rate = 0; sc->mode = 0; sc->trigger_mode = 0; sc->copy_todo = 0; /* * Ideally the USB host controller should expose the number of * frames we're allowed to schedule, but there's no such * interface. The uhci(4) driver can buffer up to 128 frames * (or it crashes), ehci(4) starts recording null frames if we * exceed 256 (micro-)frames, ohci(4) works with at most 50 * frames. */ switch (sc->udev->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: sc->ufps = 1000; sc->sync_pktsz = 3; sc->host_nframes = 50; break; case USB_SPEED_HIGH: case USB_SPEED_SUPER: sc->ufps = 8000; sc->sync_pktsz = 4; sc->host_nframes = 240; break; default: printf("%s: unsupported bus speed\n", DEVNAME(sc)); return; } if (!uaudio_process_conf(sc, &desc)) return; #ifdef UAUDIO_DEBUG if (uaudio_debug) uaudio_conf_print(sc); #endif /* print a nice uaudio attach line */ uaudio_print(sc); audio_attach_mi(&uaudio_hw_if, sc, &sc->dev); } int uaudio_detach(struct device *self, int flags) { struct uaudio_softc *sc = (struct uaudio_softc *)self; struct uaudio_unit *unit; struct uaudio_params *params; struct uaudio_alt *alt; struct uaudio_name *name; struct uaudio_mixent *mixent; int rv; rv = config_detach_children(self, flags); usbd_ref_wait(sc->udev); while ((alt = sc->alts) != NULL) { sc->alts = alt->next; free(alt, M_DEVBUF, sizeof(struct uaudio_alt)); } while ((params = sc->params_list) != NULL) { sc->params_list = params->next; free(params, M_DEVBUF, sizeof(struct uaudio_params)); } while ((unit = sc->unit_list) != NULL) { sc->unit_list = unit->unit_next; while ((mixent = unit->mixent_list) != NULL) { unit->mixent_list = mixent->next; uaudio_ranges_clear(&mixent->ranges); free(mixent, M_DEVBUF, sizeof(struct uaudio_mixent)); } uaudio_ranges_clear(&unit->rates); free(unit, M_DEVBUF, sizeof(struct uaudio_unit)); } while ((name = sc->names)) { sc->names = name->next; free(name, M_DEVBUF, sizeof(struct uaudio_name)); } return rv; } int uaudio_open(void *self, int flags) { struct uaudio_softc *sc = self; struct uaudio_params *p; if (usbd_is_dying(sc->udev)) return EIO; usbd_ref_incr(sc->udev); flags &= (FREAD | FWRITE); for (p = sc->params_list; p != NULL; p = p->next) { switch (flags) { case FWRITE: if (!p->palt) break; sc->mode = AUMODE_PLAY; return 0; case FREAD: if (!p->ralt) break; sc->mode = AUMODE_RECORD; return 0; case FREAD | FWRITE: if (!(p->ralt && p->palt)) break; sc->mode = AUMODE_RECORD | AUMODE_PLAY; return 0; } } usbd_ref_decr(sc->udev); return ENXIO; } void uaudio_close(void *self) { struct uaudio_softc *sc = self; sc->mode = 0; usbd_ref_decr(sc->udev); } int uaudio_set_params(void *self, int setmode, int usemode, struct audio_params *ap, struct audio_params *ar) { struct uaudio_softc *sc = (struct uaudio_softc *)self; struct uaudio_params *p, *best_mode, *best_rate, *best_nch; int rate, rateindex; #ifdef DIAGNOSTIC if (setmode != usemode || setmode != sc->mode) { printf("%s: bad call to uaudio_set_params()\n", DEVNAME(sc)); return EINVAL; } if (sc->mode == 0) { printf("%s: uaudio_set_params(): not open\n", DEVNAME(sc)); return EINVAL; } #endif /* * audio(4) layer requests equal play and record rates */ rate = (sc->mode & AUMODE_PLAY) ? ap->sample_rate : ar->sample_rate; rateindex = uaudio_rates_indexof(~0, rate); DPRINTF("%s: rate %d -> %d (index %d)\n", __func__, rate, uaudio_rates[rateindex], rateindex); best_mode = best_rate = best_nch = NULL; for (p = sc->params_list; p != NULL; p = p->next) { /* test if params match the requested mode */ if (sc->mode & AUMODE_PLAY) { if (p->palt == NULL) continue; } if (sc->mode & AUMODE_RECORD) { if (p->ralt == NULL) continue; } if (best_mode == NULL) best_mode = p; /* test if params match the requested rate */ if ((uaudio_getrates(sc, p) & (1 << rateindex)) == 0) continue; if (best_rate == NULL) best_rate = p; /* test if params match the requested channel counts */ if (sc->mode & AUMODE_PLAY) { if (p->palt->nch != ap->channels) continue; } if (sc->mode & AUMODE_RECORD) { if (p->ralt->nch != ar->channels) continue; } if (best_nch == NULL) best_nch = p; /* test if params match the requested precision */ if (sc->mode & AUMODE_PLAY) { if (p->palt->bits != ap->precision) continue; } if (sc->mode & AUMODE_RECORD) { if (p->ralt->bits != ar->precision) continue; } /* everything matched, we're done */ break; } if (p == NULL) { if (best_nch) p = best_nch; else if (best_rate) p = best_rate; else if (best_mode) p = best_mode; else return ENOTTY; } /* * Recalculate rate index, because the choosen parameters * may not support the requested one */ rateindex = uaudio_rates_indexof(uaudio_getrates(sc, p), rate); if (rateindex < 0) return ENOTTY; sc->params = p; sc->rate = uaudio_rates[rateindex]; DPRINTF("%s: rate = %u\n", __func__, sc->rate); if (sc->mode & AUMODE_PLAY) { ap->sample_rate = sc->rate; ap->precision = p->palt->bits; ap->encoding = AUDIO_ENCODING_SLINEAR_LE; ap->bps = p->palt->bps; ap->msb = 1; ap->channels = p->palt->nch; } if (sc->mode & AUMODE_RECORD) { ar->sample_rate = sc->rate; ar->precision = p->ralt->bits; ar->encoding = AUDIO_ENCODING_SLINEAR_LE; ar->bps = p->ralt->bps; ar->msb = 1; ar->channels = p->ralt->nch; } return 0; } unsigned int uaudio_set_blksz(void *self, int mode, struct audio_params *p, struct audio_params *r, unsigned int blksz) { struct uaudio_softc *sc = self; unsigned int fps, fps_min; unsigned int blksz_max, blksz_min; /* * minimum block size is two transfers, see uaudio_stream_open() */ fps_min = sc->ufps; if (mode & AUMODE_PLAY) { fps = sc->params->palt->fps; if (fps_min > fps) fps_min = fps; } if (mode & AUMODE_RECORD) { fps = sc->params->ralt->fps; if (fps_min > fps) fps_min = fps; } blksz_min = (sc->rate * 2 + fps_min - 1) / fps_min; /* * max block size is only limited by the number of frames the * host can schedule */ blksz_max = sc->rate * (sc->host_nframes / UAUDIO_NXFERS_MIN) / sc->ufps * 85 / 100; if (blksz > blksz_max) blksz = blksz_max; else if (blksz < blksz_min) blksz = blksz_min; return blksz; } int uaudio_trigger_output(void *self, void *start, void *end, int blksz, void (*intr)(void *), void *arg, struct audio_params *param) { struct uaudio_softc *sc = self; int err; err = uaudio_stream_open(sc, AUMODE_PLAY, start, end, blksz, intr, arg); if (err) return err; sc->trigger_mode |= AUMODE_PLAY; uaudio_trigger(sc); return 0; } int uaudio_trigger_input(void *self, void *start, void *end, int blksz, void (*intr)(void *), void *arg, struct audio_params *param) { struct uaudio_softc *sc = self; int err; err = uaudio_stream_open(sc, AUMODE_RECORD, start, end, blksz, intr, arg); if (err) return err; sc->trigger_mode |= AUMODE_RECORD; uaudio_trigger(sc); return 0; } void uaudio_copy_output(void *self, size_t todo) { struct uaudio_softc *sc = (struct uaudio_softc *)self; int s; s = splusb(); sc->copy_todo += todo; #ifdef UAUDIO_DEBUG if (uaudio_debug >= 3) { printf("%s: copy_todo -> %zd (+%zd)\n", __func__, sc->copy_todo, todo); } #endif if (sc->mode == sc->trigger_mode) uaudio_pdata_copy(sc); splx(s); } void uaudio_underrun(void *self) { struct uaudio_softc *sc = (struct uaudio_softc *)self; struct uaudio_stream *s = &sc->pstream; sc->copy_todo += s->ring_blksz; #ifdef UAUDIO_DEBUG if (uaudio_debug >= 3) printf("%s: copy_todo -> %zd\n", __func__, sc->copy_todo); #endif /* copy data (actually silence) produced by the audio(4) layer */ uaudio_pdata_copy(sc); } int uaudio_halt_output(void *self) { struct uaudio_softc *sc = (struct uaudio_softc *)self; uaudio_stream_close(sc, AUMODE_PLAY); sc->trigger_mode &= ~AUMODE_PLAY; sc->copy_todo = 0; return 0; } int uaudio_halt_input(void *self) { struct uaudio_softc *sc = (struct uaudio_softc *)self; uaudio_stream_close(sc, AUMODE_RECORD); sc->trigger_mode &= ~AUMODE_RECORD; return 0; } int uaudio_get_props(void *self) { return AUDIO_PROP_FULLDUPLEX; } int uaudio_get_port_do(struct uaudio_softc *sc, struct mixer_ctrl *ctl) { struct uaudio_unit *u; struct uaudio_mixent *m; unsigned char req_buf[4]; struct uaudio_blob p; int i, nch, val, req_num; if (!uaudio_mixer_byindex(sc, ctl->dev, &u, &m)) return ENOENT; switch (sc->version) { case UAUDIO_V1: req_num = UAUDIO_V1_REQ_GET_CUR; break; case UAUDIO_V2: req_num = UAUDIO_V2_REQ_CUR; } switch (m->type) { case UAUDIO_MIX_SW: p.rptr = p.wptr = req_buf; if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE, req_num, m->req_sel, m->chan < 0 ? 0 : m->chan, sc->ctl_ifnum, u->id, req_buf, 1)) return EIO; p.wptr++; if (!uaudio_getnum(&p, 1, &val)) return EIO; ctl->un.ord = !!val; break; case UAUDIO_MIX_NUM: nch = uaudio_mixer_nchan(m, NULL); ctl->un.value.num_channels = nch; for (i = 0; i < nch; i++) { p.rptr = p.wptr = req_buf; if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE, req_num, m->req_sel, m->chan < 0 ? 0 : i + 1, sc->ctl_ifnum, u->id, req_buf, 2)) return EIO; p.wptr += 2; if (!uaudio_getnum(&p, 2, &val)) return EIO; ctl->un.value.level[i] = uaudio_ranges_decode(&m->ranges, uaudio_sign_expand(val, 2)); m = m->next; } break; case UAUDIO_MIX_ENUM: /* XXX: not used yet */ break; } return 0; } int uaudio_set_port_do(struct uaudio_softc *sc, struct mixer_ctrl *ctl) { struct uaudio_unit *u; struct uaudio_mixent *m; unsigned char req_buf[4]; unsigned int val; int i, nch; if (!uaudio_mixer_byindex(sc, ctl->dev, &u, &m)) return ENOENT; switch (m->type) { case UAUDIO_MIX_SW: if (ctl->un.ord < 0 || ctl->un.ord > 1) return EINVAL; req_buf[0] = ctl->un.ord; if (!uaudio_req(sc, UT_WRITE_CLASS_INTERFACE, UAUDIO_V1_REQ_SET_CUR, m->req_sel, m->chan < 0 ? 0 : m->chan, sc->ctl_ifnum, u->id, req_buf, 1)) return EIO; break; case UAUDIO_MIX_NUM: nch = uaudio_mixer_nchan(m, NULL); ctl->un.value.num_channels = nch; for (i = 0; i < nch; i++) { val = uaudio_ranges_encode(&m->ranges, ctl->un.value.level[i]); DPRINTF("%s: ch %d, ctl %d, num val %d\n", __func__, i, ctl->un.value.level[i], val); req_buf[0] = val; req_buf[1] = val >> 8; if (!uaudio_req(sc, UT_WRITE_CLASS_INTERFACE, UAUDIO_V1_REQ_SET_CUR, m->req_sel, m->chan < 0 ? 0 : i + 1, sc->ctl_ifnum, u->id, req_buf, 2)) return EIO; m = m->next; } break; case UAUDIO_MIX_ENUM: /* XXX: not used yet */ break; } return 0; } int uaudio_query_devinfo_do(struct uaudio_softc *sc, struct mixer_devinfo *devinfo) { struct uaudio_unit *u; struct uaudio_mixent *m; devinfo->next = -1; devinfo->prev = -1; switch (devinfo->index) { case UAUDIO_CLASS_IN: strlcpy(devinfo->label.name, AudioCinputs, MAX_AUDIO_DEV_LEN); devinfo->type = AUDIO_MIXER_CLASS; devinfo->mixer_class = -1; return 0; case UAUDIO_CLASS_OUT: strlcpy(devinfo->label.name, AudioCoutputs, MAX_AUDIO_DEV_LEN); devinfo->type = AUDIO_MIXER_CLASS; devinfo->mixer_class = -1; return 0; } /* * find the unit & mixent structure for the given index */ if (!uaudio_mixer_byindex(sc, devinfo->index, &u, &m)) return ENOENT; if (strcmp(m->fname, "level") == 0) { /* * mixer(4) interface doesn't give a names to level * controls */ strlcpy(devinfo->label.name, u->name, MAX_AUDIO_DEV_LEN); } else { if (m->chan == -1) { snprintf(devinfo->label.name, MAX_AUDIO_DEV_LEN, "%s_%s", u->name, m->fname); } else { snprintf(devinfo->label.name, MAX_AUDIO_DEV_LEN, "%s_%s%u", u->name, m->fname, m->chan); } } devinfo->mixer_class = u->mixer_class; switch (m->type) { case UAUDIO_MIX_SW: devinfo->type = AUDIO_MIXER_ENUM; devinfo->un.e.num_mem = 2; devinfo->un.e.member[0].ord = 0; strlcpy(devinfo->un.e.member[0].label.name, "off", MAX_AUDIO_DEV_LEN); devinfo->un.e.member[1].ord = 1; strlcpy(devinfo->un.e.member[1].label.name, "on", MAX_AUDIO_DEV_LEN); break; case UAUDIO_MIX_NUM: devinfo->type = AUDIO_MIXER_VALUE; devinfo->un.v.num_channels = uaudio_mixer_nchan(m, NULL); devinfo->un.v.delta = 1; break; case UAUDIO_MIX_ENUM: /* XXX: not used yet */ devinfo->type = AUDIO_MIXER_ENUM; devinfo->un.e.num_mem = 0; break; } return 0; } int uaudio_get_port(void *arg, struct mixer_ctrl *ctl) { struct uaudio_softc *sc = arg; int rc; usbd_ref_incr(sc->udev); rc = uaudio_get_port_do(sc, ctl); usbd_ref_decr(sc->udev); return rc; } int uaudio_set_port(void *arg, struct mixer_ctrl *ctl) { struct uaudio_softc *sc = arg; int rc; usbd_ref_incr(sc->udev); rc = uaudio_set_port_do(sc, ctl); usbd_ref_decr(sc->udev); return rc; } int uaudio_query_devinfo(void *arg, struct mixer_devinfo *devinfo) { struct uaudio_softc *sc = arg; int rc; usbd_ref_incr(sc->udev); rc = uaudio_query_devinfo_do(sc, devinfo); usbd_ref_decr(sc->udev); return rc; }