diff options
author | Hakan Olsson <ho@cvs.openbsd.org> | 1999-11-11 15:55:00 +0000 |
---|---|---|
committer | Hakan Olsson <ho@cvs.openbsd.org> | 1999-11-11 15:55:00 +0000 |
commit | 165618805e42fc37ca9d4bf92a9b474c870145b1 (patch) | |
tree | caf414700de264f8aede20ab27a9edac13e6ef2a /sys/dev | |
parent | 0e4121d9f6ba5b1498bf846906903f701f92689c (diff) |
USB Audio, from NetBSD (augustss@carlstedt.se). Imported by me and niklas@.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/usb/uaudio.c | 2239 | ||||
-rw-r--r-- | sys/dev/usb/uaudioreg.h | 278 |
2 files changed, 2517 insertions, 0 deletions
diff --git a/sys/dev/usb/uaudio.c b/sys/dev/usb/uaudio.c new file mode 100644 index 00000000000..2c186819ca1 --- /dev/null +++ b/sys/dev/usb/uaudio.c @@ -0,0 +1,2239 @@ +/* $OpenBSD: uaudio.c,v 1.1 1999/11/11 15:54:59 ho Exp $ */ +/* $NetBSD: uaudio.c,v 1.5 1999/10/25 10:16:49 augustss Exp $ */ + +/* + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@carlstedt.se) at + * Carlstedt Research & Technology. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * USB audio specs: http://www.teleport.com/~usb/data/Audio10.pdf + * http://www.teleport.com/~usb/data/Frmts10.pdf + * http://www.teleport.com/~usb/data/Termt10.pdf + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/device.h> +#include <sys/poll.h> + +#include <sys/audioio.h> +#include <dev/audio_if.h> +#include <dev/mulaw.h> +#include <dev/auconv.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usb_quirks.h> + +#include <dev/usb/uaudioreg.h> + +#ifdef UAUDIO_DEBUG +#define DPRINTF(x) if (uaudiodebug) printf x +#define DPRINTFN(n,x) if (uaudiodebug>(n)) printf x +int uaudiodebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define UAUDIO_NCHANBUFS 5 /* number of outstanding request */ +#define UAUDIO_NFRAMES 20 /* ms of sound in each request */ + + +#define MIX_MAX_CHAN 8 +struct mixerctl { + u_int16_t wValue[MIX_MAX_CHAN]; /* using nchan */ + u_int16_t wIndex; + u_int8_t nchan; + u_int8_t type; +#define MIX_ON_OFF 1 +#define MIX_SIGNED_16 2 +#define MIX_UNSIGNED_16 3 +#define MIX_SIGNED_8 4 +#define MIX_SIZE(n) ((n) == MIX_SIGNED_16 || (n) == MIX_UNSIGNED_16 ? 2 : 1) +#define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16) + int minval, maxval; + u_int8_t class; + char ctlname[MAX_AUDIO_DEV_LEN]; + char *ctlunit; +}; +#define MAKE(h,l) (((h) << 8) | (l)) + +struct as_info { + u_int8_t alt; + u_int8_t encoding; + usb_interface_descriptor_t *idesc; + usb_endpoint_descriptor_audio_t *edesc; + struct usb_audio_streaming_type1_descriptor *asf1desc; +}; + +struct chan { + int terminal; /* terminal id */ + void (*intr) __P((void *)); /* dma completion intr handler */ + void *arg; /* arg for intr() */ + usbd_pipe_handle pipe; + int dir; /* direction, UE_DIR_XXX */ + + u_int sample_size; + u_int sample_rate; + u_int bytes_per_frame; + u_int fraction; /* fraction/1000 is the extra samples/frame */ + u_int residue; /* accumulates the fractional samples */ + + u_char *start; /* upper layer buffer start */ + u_char *end; /* upper layer buffer end */ + u_char *cur; /* current position in upper layer buffer */ + int blksize; /* chunk size to report up */ + int transferred; /* transferred bytes not reported up */ + + int curchanbuf; + struct chanbuf { + struct chan *chan; + usbd_request_handle reqh; + u_char *buffer; + u_int16_t sizes[UAUDIO_NFRAMES]; + u_int16_t size; + } chanbufs[UAUDIO_NCHANBUFS]; +}; + +struct uaudio_softc { + USBBASEDEVICE sc_dev; /* base device */ + usbd_device_handle sc_udev; /* USB device */ + + int sc_ac_iface; /* Audio Control interface */ + int sc_as_iface; /* Audio Streaming interface */ + usbd_interface_handle sc_ac_ifaceh; + usbd_interface_handle sc_as_ifaceh; + + struct chan sc_chan; + + int sc_curaltidx; + + int sc_nullalt; + + struct as_info *sc_alts; + int sc_nalts; + int sc_props; + + int sc_altflags; +#define HAS_8 0x01 +#define HAS_16 0x02 +#define HAS_8U 0x04 +#define HAS_ALAW 0x08 +#define HAS_MULAW 0x10 + + struct mixerctl *sc_ctls; + int sc_nctls; + + device_ptr_t sc_audiodev; + char sc_dying; +}; + +#define UAC_OUTPUT 0 +#define UAC_INPUT 1 +#define UAC_EQUAL 2 + +usbd_status uaudio_identify_ac __P((struct uaudio_softc *sc, + usb_config_descriptor_t *cdesc)); +usbd_status uaudio_identify_as __P((struct uaudio_softc *sc, + usb_config_descriptor_t *cdesc)); +usbd_status uaudio_process_as __P((struct uaudio_softc *sc, char *buf, + int *offsp, int size, + usb_interface_descriptor_t *id)); + +void uaudio_add_alt __P((struct uaudio_softc *sc, struct as_info *ai)); + +usb_interface_descriptor_t *uaudio_find_iface + __P((char *buf, int size, int *offsp, int subtype)); + +void uaudio_mixer_add_ctl __P((struct uaudio_softc *sc, struct mixerctl *mp)); +char *uaudio_id_name __P((struct uaudio_softc *sc, usb_descriptor_t **dps, + int id)); +struct usb_audio_cluster uaudio_get_cluster __P((int id, + usb_descriptor_t **dps)); +void uaudio_add_input __P((struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps)); +void uaudio_add_output __P((struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps)); +void uaudio_add_mixer __P((struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps)); +void uaudio_add_selector __P((struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps)); +void uaudio_add_feature __P((struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps)); +void uaudio_add_processing __P((struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps)); +void uaudio_add_extension __P((struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps)); +usbd_status uaudio_identify __P((struct uaudio_softc *sc, + usb_config_descriptor_t *cdesc)); + +int uaudio_signext __P((int type, int val)); +int uaudio_value2bsd __P((struct mixerctl *mc, int val)); +int uaudio_bsd2value __P((struct mixerctl *mc, int val)); +int uaudio_get __P((struct uaudio_softc *sc, int type, int which, int wValue, + int wIndex, int len)); +int uaudio_ctl_get __P((struct uaudio_softc *sc, int which, + struct mixerctl *mc, int chan)); +void uaudio_set __P((struct uaudio_softc *sc, int type, int which, int wValue, + int wIndex, int len, int val)); +void uaudio_ctl_set __P((struct uaudio_softc *sc, int which, + struct mixerctl *mc, int chan, int val)); + +usbd_status uaudio_set_speed __P((struct uaudio_softc *, int, u_int)); + +usbd_status uaudio_chan_open __P((struct uaudio_softc *sc, struct chan *ch)); +void uaudio_chan_close __P((struct uaudio_softc *sc, struct chan *ch)); +usbd_status uaudio_chan_alloc_buffers __P((struct uaudio_softc *, struct chan *)); +void uaudio_chan_free_buffers __P((struct uaudio_softc *, struct chan *)); +void uaudio_chan_set_param __P((struct chan *ch, struct audio_params *param, + u_char *start, u_char *end, int blksize)); +void uaudio_chan_ptransfer __P((struct chan *ch)); +void uaudio_chan_pintr __P((usbd_request_handle reqh, + usbd_private_handle priv, usbd_status status)); + +void uaudio_chan_rtransfer __P((struct chan *ch)); +void uaudio_chan_rintr __P((usbd_request_handle reqh, + usbd_private_handle priv, usbd_status status)); + + + +int uaudio_open __P((void *, int)); +void uaudio_close __P((void *)); +int uaudio_drain __P((void *)); +int uaudio_query_encoding __P((void *, struct audio_encoding *)); +int uaudio_set_params __P((void *, int, int, + struct audio_params *, struct audio_params *)); +int uaudio_round_blocksize __P((void *, int)); +int uaudio_trigger_output __P((void *, void *, void *, int, + void (*)(void *), void *, + struct audio_params *)); +int uaudio_trigger_input __P((void *, void *, void *, int, + void (*)(void *), void *, + struct audio_params *)); +int uaudio_halt_in_dma __P((void *)); +int uaudio_halt_out_dma __P((void *)); +int uaudio_getdev __P((void *, struct audio_device *)); +int uaudio_mixer_set_port __P((void *, mixer_ctrl_t *)); +int uaudio_mixer_get_port __P((void *, mixer_ctrl_t *)); +int uaudio_query_devinfo __P((void *, mixer_devinfo_t *)); +int uaudio_get_props __P((void *)); + +struct audio_hw_if uaudio_hw_if = { + uaudio_open, + uaudio_close, + uaudio_drain, + uaudio_query_encoding, + uaudio_set_params, + uaudio_round_blocksize, + NULL, + NULL, + NULL, + NULL, + NULL, + uaudio_halt_out_dma, + uaudio_halt_in_dma, + NULL, + uaudio_getdev, + NULL, + uaudio_mixer_set_port, + uaudio_mixer_get_port, + uaudio_query_devinfo, + NULL, + NULL, + NULL, + NULL, + uaudio_get_props, + uaudio_trigger_output, + uaudio_trigger_input, +}; + +struct audio_device uaudio_device = { + "USB audio", + "", + "uaudio" +}; + +USB_DECLARE_DRIVER(uaudio); + +USB_MATCH(uaudio) +{ + USB_MATCH_START(uaudio, uaa); + usb_interface_descriptor_t *id; + + if (!uaa->iface) + return (UMATCH_NONE); + + id = usbd_get_interface_descriptor(uaa->iface); + /* Trigger on the control interface. */ + if (!id || + id->bInterfaceClass != UCLASS_AUDIO || + id->bInterfaceSubClass != USUBCLASS_AUDIOCONTROL) + return (UMATCH_NONE); + + return (UMATCH_IFACECLASS_IFACESUBCLASS); +} + +USB_ATTACH(uaudio) +{ + USB_ATTACH_START(uaudio, sc, uaa); + usb_interface_descriptor_t *id; + usb_config_descriptor_t *cdesc; + char devinfo[1024]; + usbd_status r; + int i; + + usbd_devinfo(uaa->device, 0, devinfo); + printf(": %s\n", devinfo); + + sc->sc_udev = uaa->device; + + cdesc = usbd_get_config_descriptor(sc->sc_udev); + if (!cdesc) + USB_ATTACH_ERROR_RETURN; + + r = uaudio_identify(sc, cdesc); + if (r != USBD_NORMAL_COMPLETION) { + printf("%s: audio descriptors make no sense, error=%d\n", + USBDEVNAME(sc->sc_dev), r); + USB_ATTACH_ERROR_RETURN; + } + + sc->sc_ac_ifaceh = uaa->iface; + /* Pick up the AS interface. */ + for (i = 0; i < uaa->nifaces; i++) { + if (uaa->ifaces[i]) { + id = usbd_get_interface_descriptor(uaa->ifaces[i]); + if (id->bInterfaceNumber == sc->sc_as_iface) { + sc->sc_as_ifaceh = uaa->ifaces[i]; + uaa->ifaces[i] = 0; + } + } + } + + if (!sc->sc_as_ifaceh) { + printf("%s: missing AS interface(s)\n",USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } + + DPRINTF(("uaudio_attach: doing audio_attach_mi\n")); + /* sc->sc_audiodev = */ audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev); + + USB_ATTACH_SUCCESS_RETURN; +} + +int +uaudio_activate(self, act) + device_ptr_t self; + enum devact act; +{ + struct uaudio_softc *sc = (struct uaudio_softc *)self; + int rv = 0; + + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + break; + + case DVACT_DEACTIVATE: + if (sc->sc_audiodev) + rv = config_deactivate(sc->sc_audiodev); + sc->sc_dying = 1; + break; + } + return (rv); +} + +int +uaudio_detach(self, flags) + device_ptr_t self; + int flags; +{ + struct uaudio_softc *sc = (struct uaudio_softc *)self; + int rv = 0; + + if (sc->sc_audiodev) + rv = config_detach(sc->sc_audiodev, flags); + + return (rv); +} + +int +uaudio_query_encoding(addr, fp) + void *addr; + struct audio_encoding *fp; +{ + struct uaudio_softc *sc = addr; + int flags = sc->sc_altflags; + int idx; + + if (sc->sc_dying) + return (EIO); + + if (sc->sc_nalts == 0 || flags == 0) + return (ENXIO); + + idx = fp->index; + switch (idx) { + case 0: + strcpy(fp->name, AudioEulinear); + fp->encoding = AUDIO_ENCODING_ULINEAR; + fp->precision = 8; + fp->flags = flags&HAS_8U ? 0 : AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 1: + strcpy(fp->name, AudioEmulaw); + fp->encoding = AUDIO_ENCODING_ULAW; + fp->precision = 8; + fp->flags = flags&HAS_MULAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 2: + strcpy(fp->name, AudioEalaw); + fp->encoding = AUDIO_ENCODING_ALAW; + fp->precision = 8; + fp->flags = flags&HAS_ALAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 3: + strcpy(fp->name, AudioEslinear); + fp->encoding = AUDIO_ENCODING_SLINEAR; + fp->precision = 8; + fp->flags = flags&HAS_8 ? 0 : AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 4: + strcpy(fp->name, AudioEslinear_le); + fp->encoding = AUDIO_ENCODING_SLINEAR_LE; + fp->precision = 16; + fp->flags = 0; + return (0); + case 5: + strcpy(fp->name, AudioEulinear_le); + fp->encoding = AUDIO_ENCODING_ULINEAR_LE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 6: + strcpy(fp->name, AudioEslinear_be); + fp->encoding = AUDIO_ENCODING_SLINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 7: + strcpy(fp->name, AudioEulinear_be); + fp->encoding = AUDIO_ENCODING_ULINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + default: + return (EINVAL); + } +} + +usb_interface_descriptor_t * +uaudio_find_iface(buf, size, offsp, subtype) + char *buf; + int size; + int *offsp; + int subtype; +{ + usb_interface_descriptor_t *d; + + while (*offsp < size) { + d = (void *)(buf + *offsp); + *offsp += d->bLength; + if (d->bDescriptorType == UDESC_INTERFACE && + d->bInterfaceClass == UCLASS_AUDIO && + d->bInterfaceSubClass == subtype) + return (d); + } + return (0); +} + +void +uaudio_mixer_add_ctl(sc, mc) + struct uaudio_softc *sc; + struct mixerctl *mc; +{ + if (sc->sc_nctls == 0) + sc->sc_ctls = malloc(sizeof *mc, M_USBDEV, M_NOWAIT); + else +#ifdef __OpenBSD__ + { + void *p; + + p = malloc((sc->sc_nctls+1) * sizeof *mc, M_USBDEV, M_NOWAIT); + if (p != NULL) + bcopy(sc->sc_ctls, p, sc->sc_nctls * sizeof *mc); + free(sc->sc_ctls, M_USBDEV); + sc->sc_ctls = p; + } +#else + sc->sc_ctls = realloc(sc->sc_ctls, + (sc->sc_nctls+1) * sizeof *mc, + M_USBDEV, M_NOWAIT); +#endif + if (sc->sc_ctls == 0) { + printf("uaudio_mixer_add_ctl: no memory\n"); + return; + } + + if (mc->type != MIX_ON_OFF) { + /* Determine min and max values. */ + mc->minval = uaudio_signext(mc->type, + uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE, + mc->wValue[0], mc->wIndex, + MIX_SIZE(mc->type))); + mc->maxval = 1 + uaudio_signext(mc->type, + uaudio_get(sc, GET_MAX, UT_READ_CLASS_INTERFACE, + mc->wValue[0], mc->wIndex, + MIX_SIZE(mc->type))); + } else { + mc->minval = 0; + mc->maxval = 1; + } + + sc->sc_ctls[sc->sc_nctls++] = *mc; + +#ifdef UAUDIO_DEBUG + if (uaudiodebug > 2) { + int i; + DPRINTF(("uaudio_mixer_add_ctl: wValue=%04x",mc->wValue[0])); + for (i = 1; i < mc->nchan; i++) + DPRINTF((",%04x", mc->wValue[i])); + DPRINTF((" wIndex=%04x type=%d name='%s' unit='%s' " + "min=%d max=%d\n", + mc->wIndex, mc->type, mc->ctlname, mc->ctlunit, + mc->minval, mc->maxval)); + } +#endif +} + +char * +uaudio_id_name(sc, dps, id) + struct uaudio_softc *sc; + usb_descriptor_t **dps; + int id; +{ + static char buf[32]; + sprintf(buf, "i%d", id); + return (buf); +} + +struct usb_audio_cluster +uaudio_get_cluster(id, dps) + int id; + usb_descriptor_t **dps; +{ + struct usb_audio_cluster r; + usb_descriptor_t *dp; + int i; + + for (i = 0; i < 25; i++) { /* avoid infinite loops */ + dp = dps[id]; + if (dp == 0) + goto bad; + switch (dp->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: +#define p ((struct usb_audio_input_terminal *)dp) + r.bNrChannels = p->bNrChannels; + USETW(r.wChannelConfig, UGETW(p->wChannelConfig)); + r.iChannelNames = p->iChannelNames; +#undef p + return (r); + case UDESCSUB_AC_OUTPUT: +#define p ((struct usb_audio_output_terminal *)dp) + id = p->bSourceId; +#undef p + break; + case UDESCSUB_AC_MIXER: +#define p ((struct usb_audio_mixer_unit *)dp) + r = *(struct usb_audio_cluster *) + &p->baSourceId[p->bNrInPins]; +#undef p + return (r); + case UDESCSUB_AC_SELECTOR: + /* XXX This is not really right */ +#define p ((struct usb_audio_selector_unit *)dp) + id = p->baSourceId[0]; +#undef p + break; + case UDESCSUB_AC_FEATURE: +#define p ((struct usb_audio_feature_unit *)dp) + id = p->bSourceId; +#undef p + break; + case UDESCSUB_AC_PROCESSING: +#define p ((struct usb_audio_processing_unit *)dp) + r = *(struct usb_audio_cluster *) + &p->baSourceId[p->bNrInPins]; +#undef p + return (r); + case UDESCSUB_AC_EXTENSION: +#define p ((struct usb_audio_extension_unit *)dp) + r = *(struct usb_audio_cluster *) + &p->baSourceId[p->bNrInPins]; +#undef p + return (r); + default: + goto bad; + } + } + bad: + printf("uaudio_get_cluster: bad data\n"); + memset(&r, 0, sizeof r); + return r; + +} + +void +uaudio_add_input(sc, v, dps) + struct uaudio_softc *sc; + usb_descriptor_t *v; + usb_descriptor_t **dps; +{ +#ifdef UAUDIO_DEBUG + struct usb_audio_input_terminal *d = + (struct usb_audio_input_terminal *)v; + + DPRINTFN(2,("uaudio_add_input: bTerminalId=%d wTerminalType=0x%04x " + "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " + "iChannelNames=%d iTerminal=%d\n", + d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, + d->bNrChannels, UGETW(d->wChannelConfig), + d->iChannelNames, d->iTerminal)); +#endif + printf("uaudio_add_input: not implemented\n"); +} + +void +uaudio_add_output(sc, v, dps) + struct uaudio_softc *sc; + usb_descriptor_t *v; + usb_descriptor_t **dps; +{ +#ifdef UAUDIO_DEBUG + struct usb_audio_output_terminal *d = + (struct usb_audio_output_terminal *)v; + + DPRINTFN(2,("uaudio_add_output: bTerminalId=%d wTerminalType=0x%04x " + "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", + d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, + d->bSourceId, d->iTerminal)); +#endif +} + +void +uaudio_add_mixer(sc, v, dps) + struct uaudio_softc *sc; + usb_descriptor_t *v; + usb_descriptor_t **dps; +{ + struct usb_audio_mixer_unit *d = (struct usb_audio_mixer_unit *)v; + struct usb_audio_mixer_unit_1 *d1; + int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k; + uByte *bm; + struct mixerctl mix; + + DPRINTFN(2,("uaudio_add_mixer: bUnitId=%d bNrInPins=%d\n", + d->bUnitId, d->bNrInPins)); + + /* Compute the number of input channels */ + ichs = 0; + for (i = 0; i < d->bNrInPins; i++) + ichs += uaudio_get_cluster(d->baSourceId[i], dps).bNrChannels; + + /* and the number of output channels */ + d1 = (struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; + ochs = d1->bNrChannels; + DPRINTFN(2,("uaudio_add_mixer: ichs=%d ochs=%d\n", ichs, ochs)); + + bm = d1->bmControls; + mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); + mix.class = -1; + mix.type = MIX_SIGNED_16; + mix.ctlunit = AudioNvolume; +#define BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1) + for (p = i = 0; i < d->bNrInPins; i++) { + chs = uaudio_get_cluster(d->baSourceId[i], dps).bNrChannels; + mc = 0; + for (c = 0; c < chs; c++) { + mo = 0; + for (o = 0; o < ochs; o++) { + bno = (p + c) * ochs + o; + if (BIT(bno)) + mo++; + } + if (mo == 1) + mc++; + } + if (mc == chs && chs <= MIX_MAX_CHAN) { + k = 0; + for (c = 0; c < chs; c++) + for (o = 0; o < ochs; o++) { + bno = (p + c) * ochs + o; + if (BIT(bno)) + mix.wValue[k++] = + MAKE(p+c+1, o+1); + } + sprintf(mix.ctlname, "mix%d-%s", d->bUnitId, + uaudio_id_name(sc, dps, d->baSourceId[i])); + mix.nchan = chs; + uaudio_mixer_add_ctl(sc, &mix); + } else { + /* XXX */ + } +#undef BIT + p += chs; + } + +} + +void +uaudio_add_selector(sc, v, dps) + struct uaudio_softc *sc; + usb_descriptor_t *v; + usb_descriptor_t **dps; +{ +#ifdef UAUDIO_DEBUG + struct usb_audio_selector_unit *d = + (struct usb_audio_selector_unit *)v; + + DPRINTFN(2,("uaudio_add_selector: bUnitId=%d bNrInPins=%d\n", + d->bUnitId, d->bNrInPins)); +#endif + printf("uaudio_add_selector: NOT IMPLEMENTED\n"); +} + +void +uaudio_add_feature(sc, v, dps) + struct uaudio_softc *sc; + usb_descriptor_t *v; + usb_descriptor_t **dps; +{ + struct usb_audio_feature_unit *d = (struct usb_audio_feature_unit *)v; + uByte *ctls = d->bmaControls; + int ctlsize = d->bControlSize; + int nchan = (d->bLength - 7) / ctlsize; + int srcId = d->bSourceId; + u_int fumask, mmask, cmask; + struct mixerctl mix; + int chan, ctl, i, unit; + +#define GET(i) (ctls[(i)*ctlsize] | \ + (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0)) + + mmask = GET(0); + /* Figure out what we can control */ + for (cmask = 0, chan = 1; chan < nchan; chan++) { + DPRINTFN(9,("uaudio_add_feature: chan=%d mask=%x\n", + chan, GET(chan))); + cmask |= GET(chan); + } + + DPRINTFN(1,("uaudio_add_feature: bUnitId=%d bSourceId=%d, " + "%d channels, mmask=0x%04x, cmask=0x%04x\n", + d->bUnitId, srcId, nchan, mmask, cmask)); + + if (nchan > MIX_MAX_CHAN) + nchan = MIX_MAX_CHAN; + unit = d->bUnitId; + mix.wIndex = MAKE(unit, sc->sc_ac_iface); + for (ctl = MUTE_CONTROL; ctl < LOUDNESS_CONTROL; ctl++) { + fumask = FU_MASK(ctl); + DPRINTFN(4,("uaudio_add_feature: ctl=%d fumask=0x%04x\n", + ctl, fumask)); + if (mmask & fumask) { + mix.nchan = 1; + mix.wValue[0] = MAKE(ctl, 0); + } else if (cmask & fumask) { + mix.nchan = nchan - 1; + for (i = 1; i < nchan; i++) { + if (GET(i) & fumask) + mix.wValue[i-1] = MAKE(ctl, i); + else + mix.wValue[i-1] = -1; + } + } else { + continue; + } +#undef GET + mix.class = -1; /* XXX */ + switch (ctl) { + case MUTE_CONTROL: + mix.type = MIX_ON_OFF; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNmute); + mix.ctlunit = ""; + break; + case VOLUME_CONTROL: + mix.type = MIX_SIGNED_16; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNmaster); + mix.ctlunit = AudioNvolume; + break; + case BASS_CONTROL: + mix.type = MIX_SIGNED_8; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNbass); + mix.ctlunit = AudioNbass; + break; + case MID_CONTROL: + mix.type = MIX_SIGNED_8; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNmid); + mix.ctlunit = AudioNmid; + break; + case TREBLE_CONTROL: + mix.type = MIX_SIGNED_8; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNtreble); + mix.ctlunit = AudioNtreble; + break; + case GRAPHIC_EQUALIZER_CONTROL: + continue; /* don't add anything */ + break; + case AGC_CONTROL: + mix.type = MIX_ON_OFF; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNagc); + mix.ctlunit = ""; + break; + case DELAY_CONTROL: + mix.type = MIX_UNSIGNED_16; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNdelay); + mix.ctlunit = "4 ms"; + break; + case BASS_BOOST_CONTROL: + mix.type = MIX_ON_OFF; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNbassboost); + mix.ctlunit = ""; + break; + case LOUDNESS_CONTROL: + mix.type = MIX_ON_OFF; + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNloudness); + mix.ctlunit = ""; + break; + } + uaudio_mixer_add_ctl(sc, &mix); + } +} + +void +uaudio_add_processing(sc, v, dps) + struct uaudio_softc *sc; + usb_descriptor_t *v; + usb_descriptor_t **dps; +{ +#ifdef UAUDIO_DEBUG + struct usb_audio_processing_unit *d = + (struct usb_audio_processing_unit *)v; + + DPRINTFN(2,("uaudio_add_processing: bUnitId=%d bNrInPins=%d\n", + d->bUnitId, d->bNrInPins)); + printf("uaudio_add_processing: NOT IMPLEMENTED\n"); +#endif +} + +void +uaudio_add_extension(sc, v, dps) + struct uaudio_softc *sc; + usb_descriptor_t *v; + usb_descriptor_t **dps; +{ + struct usb_audio_extension_unit *d = + (struct usb_audio_extension_unit *)v; + struct usb_audio_extension_unit_1 *d1 = + (struct usb_audio_extension_unit_1 *)&d->baSourceId[d->bNrInPins]; + struct mixerctl mix; + + DPRINTFN(2,("uaudio_add_extension: bUnitId=%d bNrInPins=%d\n", + d->bUnitId, d->bNrInPins)); + + if (d1->bmControls[0] & (1 << UA_EXT_ENABLE)) { + mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); + mix.nchan = 1; + mix.wValue[0] = MAKE(UA_EXT_ENABLE, 0); + mix.class = -1; + mix.type = MIX_ON_OFF; + mix.ctlunit = ""; + sprintf(mix.ctlname, "ext%d-enable", d->bUnitId); + uaudio_mixer_add_ctl(sc, &mix); + } +} + +usbd_status +uaudio_identify(sc, cdesc) + struct uaudio_softc *sc; + usb_config_descriptor_t *cdesc; +{ + usbd_status r; + + r = uaudio_identify_ac(sc, cdesc); + if (r != USBD_NORMAL_COMPLETION) + return (r); + return (uaudio_identify_as(sc, cdesc)); +} + +void +uaudio_add_alt(sc, ai) + struct uaudio_softc *sc; + struct as_info *ai; +{ + if (sc->sc_nalts == 0) + sc->sc_alts = malloc(sizeof *ai, M_USBDEV, M_NOWAIT); + else +#ifdef __OpenBSD__ + { + void *p; + + p = malloc((sc->sc_nalts+1) * sizeof *ai, M_USBDEV, M_NOWAIT); + if (p != NULL) + bcopy(sc->sc_alts, p, sc->sc_nalts * sizeof *ai); + free(sc->sc_alts, M_USBDEV); + sc->sc_alts = p; + } +#else + sc->sc_alts = realloc(sc->sc_alts, + (sc->sc_nalts+1) * sizeof *ai, + M_USBDEV, M_NOWAIT); +#endif + if (sc->sc_alts == 0) { + printf("uaudio_add_alt: no memory\n"); + return; + } + DPRINTFN(2,("uaudio_add_alt: adding alt=%d, enc=%d\n", + ai->alt, ai->encoding)); + sc->sc_alts[sc->sc_nalts++] = *ai; +} + +usbd_status +uaudio_process_as(sc, buf, offsp, size, id) + struct uaudio_softc *sc; + char *buf; + int *offsp; +#define offs (*offsp) + int size; + usb_interface_descriptor_t *id; +{ + struct usb_audio_streaming_interface_descriptor *asid; + struct usb_audio_streaming_type1_descriptor *asf1d; + usb_endpoint_descriptor_audio_t *ed; + struct usb_audio_streaming_endpoint_descriptor *sed; + int format, chan, prec, enc; + int dir, type; + struct as_info ai; + + asid = (void *)(buf + offs); + if (asid->bDescriptorType != UDESC_CS_INTERFACE || + asid->bDescriptorSubtype != AS_GENERAL) + return (USBD_INVAL); + offs += asid->bLength; + if (offs > size) + return (USBD_INVAL); + asf1d = (void *)(buf + offs); + if (asf1d->bDescriptorType != UDESC_CS_INTERFACE || + asf1d->bDescriptorSubtype != FORMAT_TYPE) + return (USBD_INVAL); + offs += asf1d->bLength; + if (offs > size) + return (USBD_INVAL); + + if (asf1d->bFormatType != FORMAT_TYPE_I) { + printf("%s: ignored setting with type %d format\n", + USBDEVNAME(sc->sc_dev), UGETW(asid->wFormatTag)); + return (USBD_NORMAL_COMPLETION); + } + + ed = (void *)(buf + offs); + if (ed->bDescriptorType != UDESC_ENDPOINT) + return (USBD_INVAL); + DPRINTF(("uaudio_process_as: endpoint bLength=%d bDescriptorType=%d " + "bEndpointAddress=%d bmAttributes=0x%x wMaxPacketSize=%d " + "bInterval=%d bRefresh=%d bSynchAddress=%d\n", + ed->bLength, ed->bDescriptorType, ed->bEndpointAddress, + ed->bmAttributes, UGETW(ed->wMaxPacketSize), + ed->bInterval, ed->bRefresh, ed->bSynchAddress)); + offs += ed->bLength; + if (offs > size) + return (USBD_INVAL); + if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS) + return (USBD_INVAL); + + dir = UE_GET_DIR(ed->bEndpointAddress); + type = UE_GET_ISO_TYPE(ed->bmAttributes); + /* We can't handle endpoints that need a sync pipe. */ + if (dir == UE_DIR_IN ? type == UE_ISO_ADAPT : type == UE_ISO_ASYNC) { + printf("%s: ignored %sput endpoint of type 0x%x\n", + USBDEVNAME(sc->sc_dev), + dir == UE_DIR_IN ? "in" : "out", + ed->bmAttributes & UE_ISO_TYPE); + return (USBD_NORMAL_COMPLETION); + } + + sed = (void *)(buf + offs); + if (sed->bDescriptorType != UDESC_CS_ENDPOINT || + sed->bDescriptorSubtype != AS_GENERAL) + return (USBD_INVAL); + offs += sed->bLength; + if (offs > size) + return (USBD_INVAL); + + format = UGETW(asid->wFormatTag); + chan = asf1d->bNrChannels; + prec = asf1d->bBitResolution; + if (prec != 8 && prec != 16) { +#ifdef AUDIO_DEBUG + printf("%s: ignored setting with precision %d\n", + USBDEVNAME(sc->sc_dev), prec); +#endif + return (USBD_NORMAL_COMPLETION); + } + switch (format) { + case UA_FMT_PCM: + sc->sc_altflags |= prec == 8 ? HAS_8 : HAS_16; + enc = AUDIO_ENCODING_SLINEAR_LE; + break; + case UA_FMT_PCM8: + enc = AUDIO_ENCODING_ULINEAR_LE; + sc->sc_altflags |= HAS_8U; + break; + case UA_FMT_ALAW: + enc = AUDIO_ENCODING_ALAW; + sc->sc_altflags |= HAS_ALAW; + break; + case UA_FMT_MULAW: + enc = AUDIO_ENCODING_ULAW; + sc->sc_altflags |= HAS_MULAW; + break; + default: + printf("%s: ignored setting with format %d\n", + USBDEVNAME(sc->sc_dev), format); + return (USBD_NORMAL_COMPLETION); + } + DPRINTFN(1,("uaudio_identify: alt=%d enc=%d chan=%d prec=%d\n", + id->bAlternateSetting, enc, chan, prec)); + ai.alt = id->bAlternateSetting; + ai.encoding = enc; + ai.idesc = id; + ai.edesc = ed; + ai.asf1desc = asf1d; + uaudio_add_alt(sc, &ai); + sc->sc_chan.terminal = asid->bTerminalLink; /* XXX */ + sc->sc_chan.dir = dir; + return (USBD_NORMAL_COMPLETION); +} +#undef offs + +usbd_status +uaudio_identify_as(sc, cdesc) + struct uaudio_softc *sc; + usb_config_descriptor_t *cdesc; +{ + usb_interface_descriptor_t *id; + usbd_status r; + char *buf; + int size, offs; + + size = UGETW(cdesc->wTotalLength); + buf = (char *)cdesc; + + /* Locate the AudioStreaming interface descriptor. */ + offs = 0; + id = uaudio_find_iface(buf, size, &offs, USUBCLASS_AUDIOSTREAM); + if (!id) + return (USBD_INVAL); + sc->sc_as_iface = id->bInterfaceNumber; + DPRINTF(("uaudio_identify_as: AS interface is %d\n", sc->sc_as_iface)); + + sc->sc_chan.terminal = -1; + + /* Loop through all the alternate settings. */ + while (offs <= size) { + switch (id->bNumEndpoints) { + case 0: + DPRINTFN(2, ("uaudio_identify: AS null alt=%d\n", + id->bAlternateSetting)); + sc->sc_nullalt = id->bAlternateSetting; + break; + case 1: + r = uaudio_process_as(sc, buf, &offs, size, id); + break; + default: +#ifdef AUDIO_DEBUG + printf("%s: ignored audio interface with %d " + "endpoints\n", + USBDEVNAME(sc->sc_dev), id->bNumEndpoints); +#endif + break; + } + id = uaudio_find_iface(buf, size, &offs,USUBCLASS_AUDIOSTREAM); + if (!id) + break; + } + if (offs > size) + return (USBD_INVAL); + DPRINTF(("uaudio_identify_as: %d alts available\n", sc->sc_nalts)); + if (sc->sc_chan.terminal < 0) { + printf("%s: no useable endpoint found\n", + USBDEVNAME(sc->sc_dev)); + return (USBD_INVAL); + } + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +uaudio_identify_ac(sc, cdesc) + struct uaudio_softc *sc; + usb_config_descriptor_t *cdesc; +{ + usb_interface_descriptor_t *id; + struct usb_audio_control_descriptor *acdp; + usb_descriptor_t *dp, *dps[256]; + char *buf, *ibuf, *ibufend; + int size, offs, aclen, ndps, i; + + size = UGETW(cdesc->wTotalLength); + buf = (char *)cdesc; + + /* Locate the AudioControl interface descriptor. */ + offs = 0; + id = uaudio_find_iface(buf, size, &offs, USUBCLASS_AUDIOCONTROL); + if (!id) + return (USBD_INVAL); + if (offs + sizeof *acdp > size) + return (USBD_INVAL); + sc->sc_ac_iface = id->bInterfaceNumber; + DPRINTFN(2,("uaudio_identify: AC interface is %d\n", sc->sc_ac_iface)); + + /* A class-specific AC interface header should follow. */ + ibuf = buf + offs; + acdp = (struct usb_audio_control_descriptor *)ibuf; + if (acdp->bDescriptorType != UDESC_CS_INTERFACE || + acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER) + return (USBD_INVAL); + aclen = UGETW(acdp->wTotalLength); + if (offs + aclen > size) + return (USBD_INVAL); + + if (!(usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_BAD_ADC) && + UGETW(acdp->bcdADC) != UAUDIO_VERSION) + return (USBD_INVAL); + + DPRINTFN(2,("uaudio_identify: found AC header, vers=%03x, len=%d\n", + UGETW(acdp->bcdADC), aclen)); + + sc->sc_nullalt = -1; + + /* Scan through all the AC specific descriptors */ + ibufend = ibuf + aclen; + dp = (usb_descriptor_t *)ibuf; + ndps = 0; + memset(dps, 0, sizeof dps); + for (;;) { + ibuf += dp->bLength; + if (ibuf >= ibufend) + break; + dp = (usb_descriptor_t *)ibuf; + if (ibuf + dp->bLength > ibufend) + return (USBD_INVAL); + if (dp->bDescriptorType != UDESC_CS_INTERFACE) { + printf("uaudio_identify: skip desc type=0x%02x\n", + dp->bDescriptorType); + continue; + } + i = ((struct usb_audio_input_terminal *)dp)->bTerminalId; + dps[i] = dp; + if (i > ndps) + ndps = i; + } + ndps++; + + for (i = 0; i < ndps; i++) { + dp = dps[i]; + if (dp == 0) + continue; + DPRINTF(("uaudio_identify: subtype=%d\n", + dp->bDescriptorSubtype)); + switch (dp->bDescriptorSubtype) { + case UDESCSUB_AC_HEADER: + printf("uaudio_identify: unexpected AC header\n"); + break; + case UDESCSUB_AC_INPUT: + uaudio_add_input(sc, dp, dps); + break; + case UDESCSUB_AC_OUTPUT: + uaudio_add_output(sc, dp, dps); + break; + case UDESCSUB_AC_MIXER: + uaudio_add_mixer(sc, dp, dps); + break; + case UDESCSUB_AC_SELECTOR: + uaudio_add_selector(sc, dp, dps); + break; + case UDESCSUB_AC_FEATURE: + uaudio_add_feature(sc, dp, dps); + break; + case UDESCSUB_AC_PROCESSING: + uaudio_add_processing(sc, dp, dps); + break; + case UDESCSUB_AC_EXTENSION: + uaudio_add_extension(sc, dp, dps); + break; + default: + printf("uaudio_identify: bad AC desc subtype=0x%02x\n", + dp->bDescriptorSubtype); + break; + } + } + return (USBD_NORMAL_COMPLETION); +} + +int +uaudio_query_devinfo(addr, mi) + void *addr; + mixer_devinfo_t *mi; +{ + struct uaudio_softc *sc = addr; + struct mixerctl *mc; + int n, nctls; + + DPRINTFN(2,("uaudio_query_devinfo: index=%d\n", mi->index)); + if (sc->sc_dying) + return (EIO); + + n = mi->index; + nctls = sc->sc_nctls; + + if (n < 0 || n >= nctls) { + switch (n - nctls) { + case UAC_OUTPUT: + mi->type = AUDIO_MIXER_CLASS; + mi->mixer_class = nctls + UAC_OUTPUT; + mi->next = mi->prev = AUDIO_MIXER_LAST; + strcpy(mi->label.name, AudioCoutputs); + return (0); + case UAC_INPUT: + mi->type = AUDIO_MIXER_CLASS; + mi->mixer_class = nctls + UAC_INPUT; + mi->next = mi->prev = AUDIO_MIXER_LAST; + strcpy(mi->label.name, AudioCinputs); + return (0); + case UAC_EQUAL: + mi->type = AUDIO_MIXER_CLASS; + mi->mixer_class = nctls + UAC_EQUAL; + mi->next = mi->prev = AUDIO_MIXER_LAST; + strcpy(mi->label.name, AudioCequalization); + return (0); + default: + return (ENXIO); + } + } + mc = &sc->sc_ctls[n]; + strncpy(mi->label.name, mc->ctlname, MAX_AUDIO_DEV_LEN); + mi->mixer_class = mc->class; + mi->next = mi->prev = AUDIO_MIXER_LAST; /* XXX */ + switch (mc->type) { + case MIX_ON_OFF: + mi->type = AUDIO_MIXER_ENUM; + mi->un.e.num_mem = 2; + strcpy(mi->un.e.member[0].label.name, AudioNoff); + mi->un.e.member[0].ord = 0; + strcpy(mi->un.e.member[1].label.name, AudioNon); + mi->un.e.member[1].ord = 1; + break; + default: + mi->type = AUDIO_MIXER_VALUE; + strncpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN); + mi->un.v.num_channels = mc->nchan; + break; + } + return (0); +} + +int +uaudio_open(addr, flags) + void *addr; + int flags; +{ + struct uaudio_softc *sc = addr; + + DPRINTF(("uaudio_open: sc=%p\n", sc)); + if (sc->sc_dying) + return (EIO); + + if (sc->sc_chan.terminal < 0) + return (ENXIO); + + if ((flags & FREAD) && sc->sc_chan.dir != UE_DIR_IN) + return (EACCES); + if ((flags & FWRITE) && sc->sc_chan.dir != UE_DIR_OUT) + return (EACCES); + + sc->sc_chan.intr = 0; + + return (0); +} + +/* + * Close function is called at splaudio(). + */ +void +uaudio_close(addr) + void *addr; +{ + struct uaudio_softc *sc = addr; + + DPRINTF(("uaudio_close: sc=%p\n", sc)); + uaudio_halt_in_dma(sc); + uaudio_halt_out_dma(sc); + + sc->sc_chan.intr = 0; +} + +int +uaudio_drain(addr) + void *addr; +{ + struct uaudio_softc *sc = addr; + + usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); + + return (0); +} + +int +uaudio_halt_out_dma(addr) + void *addr; +{ + struct uaudio_softc *sc = addr; + + DPRINTF(("uaudio_halt_out_dma: enter\n")); + if (sc->sc_chan.pipe) { + uaudio_chan_close(sc, &sc->sc_chan); + sc->sc_chan.pipe = 0; + uaudio_chan_free_buffers(sc, &sc->sc_chan); + } + return (0); +} + +int +uaudio_halt_in_dma(addr) + void *addr; +{ + struct uaudio_softc *sc = addr; + + DPRINTF(("uaudio_halt_in_dma: enter\n")); + if (sc->sc_chan.pipe) { + uaudio_chan_close(sc, &sc->sc_chan); + sc->sc_chan.pipe = 0; + uaudio_chan_free_buffers(sc, &sc->sc_chan); + } + return (0); +} + +int +uaudio_getdev(addr, retp) + void *addr; + struct audio_device *retp; +{ + struct uaudio_softc *sc = addr; + + DPRINTF(("uaudio_mixer_getdev:\n")); + if (sc->sc_dying) + return (EIO); + + *retp = uaudio_device; + return (0); +} + +/* + * Make sure the block size is large enough to hold all outstanding transfers. + */ +int +uaudio_round_blocksize(addr, blk) + void *addr; + int blk; +{ + struct uaudio_softc *sc = addr; + int bpf; + + bpf = sc->sc_chan.bytes_per_frame + sc->sc_chan.sample_size; + /* XXX */ + bpf *= UAUDIO_NFRAMES * UAUDIO_NCHANBUFS; + + bpf = (bpf + 15) &~ 15; + + if (blk < bpf) + blk = bpf; + +#ifdef DIAGNOSTIC + if (blk <= 0) { + printf("uaudio_round_blocksize: blk=%d\n", blk); + blk = 512; + } +#endif + + DPRINTFN(1,("uaudio_round_blocksize: blk=%d\n", blk)); + return (blk); +} + +int +uaudio_get_props(addr) + void *addr; +{ + struct uaudio_softc *sc = addr; + + if (sc->sc_dying) + return (EIO); + + return (sc->sc_props); +} + +int +uaudio_get(sc, which, type, wValue, wIndex, len) + struct uaudio_softc *sc; + int type, which, wValue, wIndex, len; +{ + usb_device_request_t req; + u_int8_t data[4]; + usbd_status r; + int val; + + if (wValue == -1) + return (0); + + req.bmRequestType = type; + req.bRequest = which; + USETW(req.wValue, wValue); + USETW(req.wIndex, wIndex); + USETW(req.wLength, len); + DPRINTFN(2,("uaudio_get: type=0x%02x req=0x%02x wValue=0x%04x " + "wIndex=0x%04x len=%d\n", + type, which, wValue, wIndex, len)); + r = usbd_do_request(sc->sc_udev, &req, &data); + if (r != USBD_NORMAL_COMPLETION) { + DPRINTF(("uaudio_get: r=%d\n", r)); + return (-1); + } + switch (len) { + case 1: + val = data[0]; + break; + case 2: + val = data[0] | (data[1] << 8); + break; + default: + DPRINTF(("uaudio_get: bad length=%d\n", len)); + return (-1); + } + DPRINTFN(2,("uaudio_get: val=%d\n", val)); + return (val); +} + +void +uaudio_set(sc, which, type, wValue, wIndex, len, val) + struct uaudio_softc *sc; + int type, which, wValue, wIndex, len, val; +{ + usb_device_request_t req; + u_int8_t data[4]; + usbd_status r; + + if (wValue == -1) + return; + + req.bmRequestType = type; + req.bRequest = which; + USETW(req.wValue, wValue); + USETW(req.wIndex, wIndex); + USETW(req.wLength, len); + switch (len) { + case 1: + data[0] = val; + break; + case 2: + data[0] = val; + data[1] = val >> 8; + break; + default: + return; + } + DPRINTFN(2,("uaudio_set: type=0x%02x req=0x%02x wValue=0x%04x " + "wIndex=0x%04x len=%d, val=%d\n", + type, which, wValue, wIndex, len, val & 0xffff)); + r = usbd_do_request(sc->sc_udev, &req, &data); +#ifdef UAUDIO_DEBUG + if (r != USBD_NORMAL_COMPLETION) + DPRINTF(("uaudio_set: r=%d\n", r)); +#endif +} + +int +uaudio_signext(type, val) + int type, val; +{ + if (!MIX_UNSIGNED(type)) { + if (MIX_SIZE(type) == 2) + val = (int16_t)val; + else + val = (int8_t)val; + } + return (val); +} + +int +uaudio_value2bsd(mc, val) + struct mixerctl *mc; + int val; +{ + DPRINTFN(5, ("uaudio_value2bsd: type=%03x val=%d min=%d max=%d ", + mc->type, val, mc->minval, mc->maxval)); + if (mc->type == MIX_ON_OFF) + val = val != 0; + else + val = (uaudio_signext(mc->type, val) - mc->minval) * 256 + / (mc->maxval - mc->minval); + DPRINTFN(5, ("val'=%d\n", val)); + return (val); +} + +int +uaudio_bsd2value(mc, val) + struct mixerctl *mc; + int val; +{ + DPRINTFN(5,("uaudio_bsd2value: type=%03x val=%d min=%d max=%d ", + mc->type, val, mc->minval, mc->maxval)); + if (mc->type == MIX_ON_OFF) + val = val != 0; + else + val = val * (mc->maxval - mc->minval) / 256 + mc->minval; + DPRINTFN(5, ("val'=%d\n", val)); + return (val); +} + +int +uaudio_ctl_get(sc, which, mc, chan) + struct uaudio_softc *sc; + int which; + struct mixerctl *mc; + int chan; +{ + int val; + + DPRINTFN(5,("uaudio_ctl_get: which=%d chan=%d\n", which, chan)); + val = uaudio_get(sc, which, UT_READ_CLASS_INTERFACE, mc->wValue[chan], + mc->wIndex, MIX_SIZE(mc->type)); + return (uaudio_value2bsd(mc, val)); +} + +void +uaudio_ctl_set(sc, which, mc, chan, val) + struct uaudio_softc *sc; + int which; + struct mixerctl *mc; + int chan; + int val; +{ + val = uaudio_bsd2value(mc, val); + uaudio_set(sc, which, UT_WRITE_CLASS_INTERFACE, mc->wValue[chan], + mc->wIndex, MIX_SIZE(mc->type), val); +} + +int +uaudio_mixer_get_port(addr, cp) + void *addr; + mixer_ctrl_t *cp; +{ + struct uaudio_softc *sc = addr; + struct mixerctl *mc; + int i, n, vals[MIX_MAX_CHAN], val; + + DPRINTFN(2,("uaudio_mixer_get_port: index=%d\n", cp->dev)); + + if (sc->sc_dying) + return (EIO); + + n = cp->dev; + if (n < 0 || n >= sc->sc_nctls) + return (ENXIO); + mc = &sc->sc_ctls[n]; + + if (mc->type == MIX_ON_OFF) { + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0); + } else { + if (cp->type != AUDIO_MIXER_VALUE) + return (EINVAL); + if (cp->un.value.num_channels != 1 && + cp->un.value.num_channels != mc->nchan) + return (EINVAL); + for (i = 0; i < mc->nchan; i++) + vals[i] = uaudio_ctl_get(sc, GET_CUR, mc, i); + if (cp->un.value.num_channels == 1 && mc->nchan != 1) { + for (val = 0, i = 0; i < mc->nchan; i++) + val += vals[i]; + vals[0] = val / mc->nchan; + } + for (i = 0; i < cp->un.value.num_channels; i++) + cp->un.value.level[i] = vals[i]; + } + + return (0); +} + +int +uaudio_mixer_set_port(addr, cp) + void *addr; + mixer_ctrl_t *cp; +{ + struct uaudio_softc *sc = addr; + struct mixerctl *mc; + int i, n, vals[MIX_MAX_CHAN]; + + DPRINTFN(2,("uaudio_mixer_set_port: index = %d\n", cp->dev)); + if (sc->sc_dying) + return (EIO); + + n = cp->dev; + if (n < 0 || n >= sc->sc_nctls) + return (ENXIO); + mc = &sc->sc_ctls[n]; + + if (mc->type == MIX_ON_OFF) { + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord); + } else { + if (cp->type != AUDIO_MIXER_VALUE) + return (EINVAL); + if (cp->un.value.num_channels == 1) + for (i = 0; i < mc->nchan; i++) + vals[i] = cp->un.value.level[0]; + else if (cp->un.value.num_channels == mc->nchan) + for (i = 0; i < mc->nchan; i++) + vals[i] = cp->un.value.level[i]; + else + return (EINVAL); + for (i = 0; i < mc->nchan; i++) + uaudio_ctl_set(sc, SET_CUR, mc, i, vals[i]); + } + return (0); +} + +int +uaudio_trigger_input(addr, start, end, blksize, intr, arg, param) + void *addr; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +{ + struct uaudio_softc *sc = addr; + struct chan *ch = &sc->sc_chan; + usbd_status r; + int i, s; + + if (sc->sc_dying) + return (EIO); + + DPRINTFN(3,("uaudio_trigger_input: sc=%p start=%p end=%p " + "blksize=%d\n", sc, start, end, blksize)); + + uaudio_chan_set_param(ch, param, start, end, blksize); + DPRINTFN(3,("uaudio_trigger_input: sample_size=%d bytes/frame=%d " + "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame, + ch->fraction)); + + r = uaudio_chan_alloc_buffers(sc, ch); + if (r != USBD_NORMAL_COMPLETION) + return (EIO); + + r = uaudio_chan_open(sc, ch); + if (r != USBD_NORMAL_COMPLETION) { + uaudio_chan_free_buffers(sc, ch); + return (EIO); + } + + sc->sc_chan.intr = intr; + sc->sc_chan.arg = arg; + + s = splusb(); + for (i = 0; i < UAUDIO_NCHANBUFS; i++) + uaudio_chan_rtransfer(ch); + splx(s); + + return (0); +} + +int +uaudio_trigger_output(addr, start, end, blksize, intr, arg, param) + void *addr; + void *start, *end; + int blksize; + void (*intr) __P((void *)); + void *arg; + struct audio_params *param; +{ + struct uaudio_softc *sc = addr; + struct chan *ch = &sc->sc_chan; + usbd_status r; + int i, s; + + if (sc->sc_dying) + return (EIO); + + DPRINTFN(3,("uaudio_trigger_output: sc=%p start=%p end=%p " + "blksize=%d\n", sc, start, end, blksize)); + + uaudio_chan_set_param(ch, param, start, end, blksize); + DPRINTFN(3,("uaudio_trigger_output: sample_size=%d bytes/frame=%d " + "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame, + ch->fraction)); + + r = uaudio_chan_alloc_buffers(sc, ch); + if (r != USBD_NORMAL_COMPLETION) + return (EIO); + + r = uaudio_chan_open(sc, ch); + if (r != USBD_NORMAL_COMPLETION) { + uaudio_chan_free_buffers(sc, ch); + return (EIO); + } + + sc->sc_chan.intr = intr; + sc->sc_chan.arg = arg; + + s = splusb(); + for (i = 0; i < UAUDIO_NCHANBUFS; i++) + uaudio_chan_ptransfer(ch); + splx(s); + + return (0); +} + +/* Set up a pipe for a channel. */ +usbd_status +uaudio_chan_open(sc, ch) + struct uaudio_softc *sc; + struct chan *ch; +{ + struct as_info *as = &sc->sc_alts[sc->sc_curaltidx]; + int endpt = as->edesc->bEndpointAddress; + usbd_status r; + + DPRINTF(("uaudio_open_chan: endpt=0x%02x, speed=%d, alt=%d\n", + endpt, ch->sample_rate, as->alt)); + + /* Set alternate interface corresponding to the mode. */ + r = usbd_set_interface(sc->sc_as_ifaceh, as->alt); + if (r != USBD_NORMAL_COMPLETION) + return (r); + + /* Some devices do not support this request, so ignore errors. */ +#ifdef UAUDIO_DEBUG + r = uaudio_set_speed(sc, endpt, ch->sample_rate); + if (r != USBD_NORMAL_COMPLETION) + DPRINTF(("uaudio_chan_open: set_speed failed r=%s\n", + usbd_errstr(r))); +#else + (void)uaudio_set_speed(sc, endpt, ch->sample_rate); +#endif + + DPRINTF(("uaudio_open_chan: create pipe to 0x%02x\n", endpt)); + r = usbd_open_pipe(sc->sc_as_ifaceh, endpt, 0, &ch->pipe); + return (r); +} + +void +uaudio_chan_close(sc, ch) + struct uaudio_softc *sc; + struct chan *ch; +{ + if (sc->sc_nullalt >= 0) { + DPRINTF(("uaudio_close_chan: set null alt=%d\n", + sc->sc_nullalt)); + usbd_set_interface(sc->sc_as_ifaceh, sc->sc_nullalt); + } + usbd_abort_pipe(ch->pipe); + usbd_close_pipe(ch->pipe); +} + +usbd_status +uaudio_chan_alloc_buffers(sc, ch) + struct uaudio_softc *sc; + struct chan *ch; +{ + usbd_request_handle reqh; + void *buf; + int i, size; + + size = (ch->bytes_per_frame + ch->sample_size) * UAUDIO_NFRAMES; + for (i = 0; i < UAUDIO_NCHANBUFS; i++) { + reqh = usbd_alloc_request(sc->sc_udev); + if (reqh == 0) + goto bad; + ch->chanbufs[i].reqh = reqh; + buf = usbd_alloc_buffer(reqh, size); + if (buf == 0) { + i++; + goto bad; + } + ch->chanbufs[i].buffer = buf; + ch->chanbufs[i].chan = ch; + } + + return (USBD_NORMAL_COMPLETION); + +bad: + while (--i >= 0) + /* implicit buffer free */ + usbd_free_request(ch->chanbufs[i].reqh); + return (USBD_NOMEM); +} + +void +uaudio_chan_free_buffers(sc, ch) + struct uaudio_softc *sc; + struct chan *ch; +{ + int i; + + for (i = 0; i < UAUDIO_NCHANBUFS; i++) + usbd_free_request(ch->chanbufs[i].reqh); +} + +/* Called at splusb() */ +void +uaudio_chan_ptransfer(ch) + struct chan *ch; +{ + struct chanbuf *cb; + int i, n, size, residue, total; + + /* Pick the next channel buffer. */ + cb = &ch->chanbufs[ch->curchanbuf]; + if (++ch->curchanbuf >= UAUDIO_NCHANBUFS) + ch->curchanbuf = 0; + + /* Compute the size of each frame in the next transfer. */ + residue = ch->residue; + total = 0; + for (i = 0; i < UAUDIO_NFRAMES; i++) { + size = ch->bytes_per_frame; + residue += ch->fraction; + if (residue >= USB_FRAMES_PER_SECOND) { + size += ch->sample_size; + residue -= USB_FRAMES_PER_SECOND; + } + cb->sizes[i] = size; + total += size; + } + ch->residue = residue; + cb->size = total; + + /* + * Transfer data from upper layer buffer to channel buffer, taking + * care of wrapping the upper layer buffer. + */ + n = min(total, ch->end - ch->cur); + memcpy(cb->buffer, ch->cur, n); + ch->cur += n; + if (ch->cur >= ch->end) + ch->cur = ch->start; + if (total > n) { + total -= n; + memcpy(cb->buffer + n, ch->cur, total); + ch->cur += total; + } + +#ifdef UAUDIO_DEBUG + if (uaudiodebug > 8) { + DPRINTF(("uaudio_chan_ptransfer: buffer=%p, residue=0.%03d\n", + cb->buffer, ch->residue)); + for (i = 0; i < UAUDIO_NFRAMES; i++) { + DPRINTF((" [%d] length %d\n", i, cb->sizes[i])); + } + } +#endif + + DPRINTFN(5,("uaudio_chan_transfer: ptransfer reqh=%p\n", cb->reqh)); + /* Fill the request */ + usbd_setup_isoc_request(cb->reqh, ch->pipe, cb, cb->sizes, + UAUDIO_NFRAMES, USBD_NO_COPY, + uaudio_chan_pintr); + + (void)usbd_transfer(cb->reqh); +} + +void +uaudio_chan_pintr(reqh, priv, status) + usbd_request_handle reqh; + usbd_private_handle priv; + usbd_status status; +{ + struct chanbuf *cb = priv; + struct chan *ch = cb->chan; + u_int32_t count; + int s; + + /* Return if we are aborting. */ + if (status == USBD_CANCELLED) + return; + + usbd_get_request_status(reqh, 0, 0, &count, 0); + DPRINTFN(5,("uaudio_chan_pintr: count=%d, transferred=%d\n", + count, ch->transferred)); +#ifdef DIAGNOSTIC + if (count != cb->size) { + printf("uaudio_chan_pintr: count(%d) != size(%d)\n", + count, cb->size); + } +#endif + + ch->transferred += cb->size; + s = splaudio(); + /* Call back to upper layer */ + while (ch->transferred >= ch->blksize) { + ch->transferred -= ch->blksize; + DPRINTFN(5,("uaudio_chan_pintr: call %p(%p)\n", + ch->intr, ch->arg)); + ch->intr(ch->arg); + } + splx(s); + + /* start next transfer */ + uaudio_chan_ptransfer(ch); +} + +/* Called at splusb() */ +void +uaudio_chan_rtransfer(ch) + struct chan *ch; +{ + struct chanbuf *cb; + int i, size, residue, total; + + /* Pick the next channel buffer. */ + cb = &ch->chanbufs[ch->curchanbuf]; + if (++ch->curchanbuf >= UAUDIO_NCHANBUFS) + ch->curchanbuf = 0; + + /* Compute the size of each frame in the next transfer. */ + residue = ch->residue; + total = 0; + for (i = 0; i < UAUDIO_NFRAMES; i++) { + size = ch->bytes_per_frame; + residue += ch->fraction; + if (residue >= USB_FRAMES_PER_SECOND) { + size += ch->sample_size; + residue -= USB_FRAMES_PER_SECOND; + } + cb->sizes[i] = size; + total += size; + } + ch->residue = residue; + cb->size = total; + +#ifdef UAUDIO_DEBUG + if (uaudiodebug > 8) { + DPRINTF(("uaudio_chan_rtransfer: buffer=%p, residue=0.%03d\n", + cb->buffer, ch->residue)); + for (i = 0; i < UAUDIO_NFRAMES; i++) { + DPRINTF((" [%d] length %d\n", i, cb->sizes[i])); + } + } +#endif + + DPRINTFN(5,("uaudio_chan_rtransfer: transfer reqh=%p\n", cb->reqh)); + /* Fill the request */ + usbd_setup_isoc_request(cb->reqh, ch->pipe, cb, cb->sizes, + UAUDIO_NFRAMES, USBD_NO_COPY, + uaudio_chan_rintr); + + (void)usbd_transfer(cb->reqh); +} + +void +uaudio_chan_rintr(reqh, priv, status) + usbd_request_handle reqh; + usbd_private_handle priv; + usbd_status status; +{ + struct chanbuf *cb = priv; + struct chan *ch = cb->chan; + u_int32_t count; + int s, n; + + /* Return if we are aborting. */ + if (status == USBD_CANCELLED) + return; + + usbd_get_request_status(reqh, 0, 0, &count, 0); + DPRINTFN(5,("uaudio_chan_rintr: count=%d, transferred=%d\n", + count, ch->transferred)); +#ifdef DIAGNOSTIC + if (count != cb->size) { + printf("uaudio_chan_pintr: count(%d) != size(%d)\n", + count, cb->size); + } +#endif + + /* + * Transfer data from channel buffer to upper layer buffer, taking + * care of wrapping the upper layer buffer. + */ + n = min(count, ch->end - ch->cur); + memcpy(ch->cur, cb->buffer, n); + ch->cur += n; + if (ch->cur >= ch->end) + ch->cur = ch->start; + if (count > n) { + memcpy(ch->cur, cb->buffer + n, count - n); + ch->cur += count - n; + } + + /* Call back to upper layer */ + ch->transferred += cb->size; + s = splaudio(); + while (ch->transferred >= ch->blksize) { + ch->transferred -= ch->blksize; + DPRINTFN(5,("uaudio_chan_pintr: call %p(%p)\n", + ch->intr, ch->arg)); + ch->intr(ch->arg); + } + splx(s); + + /* start next transfer */ + uaudio_chan_rtransfer(ch); +} + +void +uaudio_chan_set_param(ch, param, start, end, blksize) + struct chan *ch; + struct audio_params *param; + u_char *start, *end; + int blksize; +{ + int samples_per_frame, sample_size; + + sample_size = param->precision * param->channels / 8; + samples_per_frame = param->sample_rate / USB_FRAMES_PER_SECOND; + ch->fraction = param->sample_rate % USB_FRAMES_PER_SECOND; + ch->sample_size = sample_size; + ch->sample_rate = param->sample_rate; + ch->bytes_per_frame = samples_per_frame * sample_size; + ch->residue = 0; + + ch->start = start; + ch->end = end; + ch->cur = start; + ch->blksize = blksize; + ch->transferred = 0; + + ch->curchanbuf = 0; +} + +int +uaudio_set_params(addr, setmode, usemode, p, r) + void *addr; + int setmode, usemode; + struct audio_params *p, *r; +{ + struct uaudio_softc *sc = addr; + int flags = sc->sc_altflags; + int enc, i, j; + void (*pswcode) __P((void *, u_char *buf, int cnt)); + void (*rswcode) __P((void *, u_char *buf, int cnt)); + + if (sc->sc_dying) + return (EIO); + + if (sc->sc_chan.pipe) + return (EBUSY); + + pswcode = rswcode = 0; + enc = p->encoding; + switch (p->encoding) { + case AUDIO_ENCODING_SLINEAR_BE: + if (p->precision == 16) { + rswcode = pswcode = swap_bytes; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else if (p->precision == 8 && !(flags & HAS_8)) { + pswcode = rswcode = change_sign8; + enc = AUDIO_ENCODING_ULINEAR_LE; + } + break; + case AUDIO_ENCODING_SLINEAR_LE: + if (p->precision == 8 && !(flags & HAS_8)) { + pswcode = rswcode = change_sign8; + enc = AUDIO_ENCODING_ULINEAR_LE; + } + break; + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision == 16) { + pswcode = swap_bytes_change_sign16; + rswcode = change_sign16_swap_bytes; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else if (p->precision == 8 && !(flags & HAS_8U)) { + pswcode = rswcode = change_sign8; + enc = AUDIO_ENCODING_SLINEAR_LE; + } + break; + case AUDIO_ENCODING_ULINEAR_LE: + if (p->precision == 16) { + pswcode = rswcode = change_sign16; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else if (p->precision == 8 && !(flags & HAS_8U)) { + pswcode = rswcode = change_sign8; + enc = AUDIO_ENCODING_SLINEAR_LE; + } + break; + case AUDIO_ENCODING_ULAW: + if (!(flags & HAS_MULAW)) { + if (flags & HAS_8U) { + pswcode = mulaw_to_ulinear8; + rswcode = ulinear8_to_mulaw; + enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (flags & HAS_8) { + pswcode = mulaw_to_slinear8; + rswcode = slinear8_to_mulaw; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else + return (EINVAL); + } + break; + case AUDIO_ENCODING_ALAW: + if (!(flags & HAS_ALAW)) { + if (flags & HAS_8U) { + pswcode = alaw_to_ulinear8; + rswcode = ulinear8_to_alaw; + enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (flags & HAS_8) { + pswcode = alaw_to_slinear8; + rswcode = slinear8_to_alaw; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else + return (EINVAL); + } + break; + default: + return (EINVAL); + } + /* XXX do some other conversions... */ + + DPRINTF(("uaudio_set_params: chan=%d prec=%d enc=%d rate=%ld\n", + p->channels, p->precision, enc, p->sample_rate)); + + for (i = 0; i < sc->sc_nalts; i++) { + struct usb_audio_streaming_type1_descriptor *a1d = + sc->sc_alts[i].asf1desc; + if (p->channels == a1d->bNrChannels && + p->precision ==a1d->bBitResolution && + enc == sc->sc_alts[i].encoding) { + if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { + if (UA_SAMP_LO(a1d) < p->sample_rate && + p->sample_rate < UA_SAMP_HI(a1d)) + goto found; + } else { + for (j = 0; j < a1d->bSamFreqType; j++) { + /* XXX allow for some slack */ + if (UA_GETSAMP(a1d, j) == + p->sample_rate) + goto found; + } + } + } + } + return (EINVAL); + + found: + p->sw_code = pswcode; + r->sw_code = rswcode; + sc->sc_curaltidx = i; + + DPRINTF(("uaudio_set_params: use altidx=%d, altno=%d\n", + sc->sc_curaltidx, + sc->sc_alts[sc->sc_curaltidx].idesc->bAlternateSetting)); + + return (0); +} + +usbd_status +uaudio_set_speed(sc, endpt, speed) + struct uaudio_softc *sc; + int endpt; + u_int speed; +{ + usb_device_request_t req; + u_int8_t data[3]; + + DPRINTFN(5,("uaudio_set_speed: endpt=%d speed=%u\n", endpt, speed)); + req.bmRequestType = UT_WRITE_CLASS_ENDPOINT; + req.bRequest = SET_CUR; + USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0); + USETW(req.wIndex, endpt); + USETW(req.wLength, 3); + data[0] = speed; + data[1] = speed >> 8; + data[2] = speed >> 16; + + return (usbd_do_request(sc->sc_udev, &req, &data)); +} + diff --git a/sys/dev/usb/uaudioreg.h b/sys/dev/usb/uaudioreg.h new file mode 100644 index 00000000000..113752e6214 --- /dev/null +++ b/sys/dev/usb/uaudioreg.h @@ -0,0 +1,278 @@ +/* $OpenBSD: uaudioreg.h,v 1.1 1999/11/11 15:54:59 ho Exp $ */ +/* $NetBSD: uaudioreg.h,v 1.2 1999/10/13 20:13:29 augustss Exp $ */ + +/* + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Author: Lennart Augustsson <augustss@carlstedt.se> + * Carlstedt Research & Technology + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define UAUDIO_VERSION 0x100 + +#define UDESC_CS_DEVICE 0x21 +#define UDESC_CS_CONFIG 0x22 +#define UDESC_CS_STRING 0x23 +#define UDESC_CS_INTERFACE 0x24 +#define UDESC_CS_ENDPOINT 0x25 + +#define UDESCSUB_AC_HEADER 1 +#define UDESCSUB_AC_INPUT 2 +#define UDESCSUB_AC_OUTPUT 3 +#define UDESCSUB_AC_MIXER 4 +#define UDESCSUB_AC_SELECTOR 5 +#define UDESCSUB_AC_FEATURE 6 +#define UDESCSUB_AC_PROCESSING 7 +#define UDESCSUB_AC_EXTENSION 8 + +/* The first fields are identical to usb_endpoint_descriptor_t */ +typedef struct { + uByte bLength; + uByte bDescriptorType; + uByte bEndpointAddress; + uByte bmAttributes; + uWord wMaxPacketSize; + uByte bInterval; + /* + * The following two entries are only used by the Audio Class. + * And according to the specs the Audio Class is the only one + * allowed to extend the endpoint descriptor. + * Who knows what goes on in the minds of the people in the USB + * standardization? :-( + */ + uByte bRefresh; + uByte bSynchAddress; +} usb_endpoint_descriptor_audio_t; + +struct usb_audio_control_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uWord bcdADC; + uWord wTotalLength; + uByte bInCollection; + uByte baInterfaceNr[1]; +}; + +struct usb_audio_streaming_interface_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalLink; + uByte bDelay; + uWord wFormatTag; +}; + +struct usb_audio_streaming_endpoint_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bmAttributes; + uByte bLockDelayUnits; + uWord wLockDelay; +}; + +struct usb_audio_streaming_type1_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bFormatType; + uByte bNrChannels; + uByte bSubFrameSize; + uByte bBitResolution; + uByte bSamFreqType; +#define UA_SAMP_CONTNUOUS 0 + uByte tSamFreq[3*2]; /* room for low and high */ +#define UA_GETSAMP(p, n) ((p)->tSamFreq[(n)*3+0] | ((p)->tSamFreq[(n)*3+1] << 8) | ((p)->tSamFreq[(n)*3+2] << 16)) +#define UA_SAMP_LO(p) UA_GETSAMP(p, 0) +#define UA_SAMP_HI(p) UA_GETSAMP(p, 1) +}; + +struct usb_audio_cluster { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; +}; + +/* UDESCSUB_AC_INPUT */ +struct usb_audio_input_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte iTerminal; +}; + +/* UDESCSUB_AC_OUTPUT */ +struct usb_audio_output_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bSourceId; + uByte iTerminal; +}; + +/* UDESCSUB_AC_MIXER */ +struct usb_audio_mixer_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[255]; /* length is really bNrInPins */ + /* struct usb_audio_mixer_unit_1 */ +}; +struct usb_audio_mixer_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bmControls[255]; + /*uByte iMixer;*/ +}; + +/* UDESCSUB_AC_SELECTOR */ +struct usb_audio_selector_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[255]; + /* uByte iSelector; */ +}; + +/* UDESCSUB_AC_FEATURE */ +struct usb_audio_feature_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bSourceId; + uByte bControlSize; + uByte bmaControls[255]; /* size for more than enough */ + /* uByte iFeature; */ +}; + +/* UDESCSUB_AC_PROCESSING */ +struct usb_audio_processing_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wProcessType; + uByte bNrInPins; + uByte baSourceId[255]; + /* struct usb_audio_processing_unit_1 */ +}; +struct usb_audio_processing_unit_1{ + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bControlSize; + uByte bmControls[255]; + /*uByte iProcessing;*/ +}; + +/* UDESCSUB_AC_EXTENSION */ +struct usb_audio_extension_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wExtensionCode; + uByte bNrInPins; + uByte baSourceId[255]; + /* struct usb_audio_extension_unit_1 */ +}; +struct usb_audio_extension_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bControlSize; + uByte bmControls[255]; +#define UA_EXT_ENABLE 0 + /*uByte iExtension;*/ +}; + +#define UAT_STREAM 0x0101 + +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define SET_MIN 0x02 +#define GET_MIN 0x82 +#define SET_MAX 0x03 +#define GET_MAX 0x83 +#define SET_RES 0x04 +#define GET_RES 0x84 +#define SET_MEM 0x05 +#define GET_MEM 0x85 +#define GET_STAT 0xff + +#define MUTE_CONTROL 0x01 +#define VOLUME_CONTROL 0x02 +#define BASS_CONTROL 0x03 +#define MID_CONTROL 0x04 +#define TREBLE_CONTROL 0x05 +#define GRAPHIC_EQUALIZER_CONTROL 0x06 +#define AGC_CONTROL 0x07 +#define DELAY_CONTROL 0x08 +#define BASS_BOOST_CONTROL 0x09 +#define LOUDNESS_CONTROL 0x0a + +#define FU_MASK(u) (1 << ((u)-1)) + +#define MASTER_CHAN 0 + +#define AS_GENERAL 1 +#define FORMAT_TYPE 2 +#define FORMAT_SPECIFIC 3 + +#define UA_FMT_PCM 1 +#define UA_FMT_PCM8 2 +#define UA_FMT_IEEE_FLOAT 3 +#define UA_FMT_ALAW 4 +#define UA_FMT_MULAW 5 + +#define SAMPLING_FREQ_CONTROL 0x01 + +#define FORMAT_TYPE_UNDEFINED 0 +#define FORMAT_TYPE_I 1 +#define FORMAT_TYPE_II 2 +#define FORMAT_TYPE_III 3 |