diff options
author | nsayer <nsayer@FreeBSD.org> | 2002-07-21 17:28:50 +0000 |
---|---|---|
committer | nsayer <nsayer@FreeBSD.org> | 2002-07-21 17:28:50 +0000 |
commit | 25022332651db553076ec3b7404f57f158c9967c (patch) | |
tree | 2c415cdeffaee4bbbc5bd63b47ecdfb14bbf9b0c | |
parent | b9937742c3d6160f131a6a6ffebc71fdb2e351aa (diff) | |
download | FreeBSD-src-25022332651db553076ec3b7404f57f158c9967c.zip FreeBSD-src-25022332651db553076ec3b7404f57f158c9967c.tar.gz |
Add uaudio -- a USB audio device driver.
This driver actually works slightly better on -stable than on -current
(the system locks on detach on -current), so it should be MFC'd somewhat
sooner.
This driver currently points out a difficulty in the sound device framework.
The PCM unregister routine is allowed to refuse the detach if the device is
in use. In the case of a USB device, however, this unregistration is much more
mandatory in nature, since the device is *actually* gone when this call is
made. The sound subsystem really should not refuse an unregistration and
should take its own steps to reject further I/O. As a result, if you detach
a USB sound device while it is in use, you can expect a panic shortly
thereafter.
This device cannot currently record audio. Some routines are unwritten as
of yet in uaudio.c to support recording.
This device hangs my -current box on detach. I don't know why. This does
not happen on my -stable machine.
Obtained from: Hiroyuki Aizu
MFC after: 2 weeks
-rw-r--r-- | sys/conf/files | 2 | ||||
-rw-r--r-- | sys/dev/sound/usb/uaudio.c | 2912 | ||||
-rw-r--r-- | sys/dev/sound/usb/uaudio.h | 49 | ||||
-rw-r--r-- | sys/dev/sound/usb/uaudio_pcm.c | 375 | ||||
-rw-r--r-- | sys/dev/sound/usb/uaudioreg.h | 384 | ||||
-rw-r--r-- | sys/modules/sound/driver/Makefile | 2 | ||||
-rw-r--r-- | sys/modules/sound/driver/uaudio/Makefile | 9 |
7 files changed, 3732 insertions, 1 deletions
diff --git a/sys/conf/files b/sys/conf/files index f41c071..5e34a0a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -591,6 +591,8 @@ dev/sound/pcm/sndstat.c optional pcm dev/sound/pcm/sound.c optional pcm dev/sound/pcm/vchan.c optional pcm #dev/sound/usb/upcm.c optional pcm usb +dev/sound/usb/uaudio.c optional pcm usb +dev/sound/usb/uaudio_pcm.c optional pcm usb dev/sr/if_sr.c optional sr dev/sr/if_sr_pci.c optional sr pci dev/streams/streams.c optional streams diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c new file mode 100644 index 0000000..b47fb4f --- /dev/null +++ b/sys/dev/sound/usb/uaudio.c @@ -0,0 +1,2912 @@ +/* $NetBSD: uaudio.c,v 1.41 2001/01/23 14:04:13 augustss Exp $ */ +/* $FreeBSD$: */ + +/* + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) 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.usb.org/developers/data/devclass/audio10.pdf + * http://www.usb.org/developers/data/devclass/frmts10.pdf + * http://www.usb.org/developers/data/devclass/termt10.pdf + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/device.h> +#include <sys/ioctl.h> +#endif +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/reboot.h> /* for bootverbose */ +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/device.h> +#elif defined(__FreeBSD__) +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/conf.h> +#endif +#include <sys/poll.h> + +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include <sys/audioio.h> +#include <dev/audio_if.h> +#include <dev/mulaw.h> +#include <dev/auconv.h> +#elif defined(__FreeBSD__) +#include <dev/sound/pcm/sound.h> /* XXXXX */ +#include <dev/sound/chip.h> +#endif + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usb_quirks.h> + +#include <dev/sound/usb/uaudioreg.h> +#include <dev/sound/usb/uaudio.h> + +#ifdef UAUDIO_DEBUG +#define DPRINTF(x) if (uaudiodebug) logprintf x +#define DPRINTFN(n,x) if (uaudiodebug>(n)) logprintf x +int uaudiodebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +#define UAUDIO_NCHANBUFS 6 /* 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_int delta; + u_int mul; +#if defined(__FreeBSD__) /* XXXXX */ + unsigned ctl; +#else + u_int8_t class; + char ctlname[MAX_AUDIO_DEV_LEN]; + char *ctlunit; +#endif +}; +#define MAKE(h,l) (((h) << 8) | (l)) + +struct as_info { + u_int8_t alt; + u_int8_t encoding; + usbd_interface_handle ifaceh; + usb_interface_descriptor_t *idesc; + usb_endpoint_descriptor_audio_t *edesc; + struct usb_audio_streaming_type1_descriptor *asf1desc; +}; + +struct chan { + int terminal; /* terminal id */ +#if defined(__NetBSD__) || defined(__OpenBSD__) + void (*intr)(void *); /* dma completion intr handler */ + void *arg; /* arg for intr() */ +#else + struct pcm_channel *pcm_ch; +#endif + usbd_pipe_handle pipe; + int dir; /* direction */ + + 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 */ + + char nofrac; /* don't do sample rate adjustment */ + + int curchanbuf; + struct chanbuf { + struct chan *chan; + usbd_xfer_handle xfer; + u_char *buffer; + u_int16_t sizes[UAUDIO_NFRAMES]; + u_int16_t size; + } chanbufs[UAUDIO_NCHANBUFS]; + + struct uaudio_softc *sc; /* our softc */ +#if defined(__FreeBSD__) + u_int32_t format; + int precision; + int channels; +#endif +}; + +struct uaudio_softc { + USBBASEDEVICE sc_dev; /* base device */ + usbd_device_handle sc_udev; /* USB device */ + + char sc_dead; /* The device is dead -- kill it */ + + int sc_ac_iface; /* Audio Control interface */ + usbd_interface_handle sc_ac_ifaceh; + + struct chan sc_chan; + + int sc_curaltidx; + + int sc_nullalt; + + int sc_audio_rev; + + 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 + +Static usbd_status uaudio_identify_ac(struct uaudio_softc *sc, + usb_config_descriptor_t *cdesc); +Static usbd_status uaudio_identify_as(struct uaudio_softc *sc, + usb_config_descriptor_t *cdesc); +Static usbd_status uaudio_process_as(struct uaudio_softc *sc, + char *buf, int *offsp, int size, + usb_interface_descriptor_t *id); + +Static void uaudio_add_alt(struct uaudio_softc *sc, + struct as_info *ai); + +Static usb_interface_descriptor_t *uaudio_find_iface(char *buf, + int size, int *offsp, int subtype); + +Static void uaudio_mixer_add_ctl(struct uaudio_softc *sc, + struct mixerctl *mp); + +#if defined(__NetBSD__) || defined(__OpenBSD__) +Static char *uaudio_id_name(struct uaudio_softc *sc, + usb_descriptor_t **dps, int id); +#endif + +Static struct usb_audio_cluster uaudio_get_cluster(int id, + usb_descriptor_t **dps); +Static void uaudio_add_input(struct uaudio_softc *sc, + usb_descriptor_t *v, usb_descriptor_t **dps); +Static void uaudio_add_output(struct uaudio_softc *sc, + usb_descriptor_t *v, usb_descriptor_t **dps); +Static void uaudio_add_mixer(struct uaudio_softc *sc, + usb_descriptor_t *v, usb_descriptor_t **dps); +Static void uaudio_add_selector(struct uaudio_softc *sc, + usb_descriptor_t *v, usb_descriptor_t **dps); +Static void uaudio_add_feature(struct uaudio_softc *sc, + usb_descriptor_t *v, usb_descriptor_t **dps); +Static void uaudio_add_processing_updown(struct uaudio_softc *sc, + usb_descriptor_t *v, usb_descriptor_t **dps); +Static void uaudio_add_processing(struct uaudio_softc *sc, + usb_descriptor_t *v, usb_descriptor_t **dps); +Static void uaudio_add_extension(struct uaudio_softc *sc, + usb_descriptor_t *v, usb_descriptor_t **dps); +Static usbd_status uaudio_identify(struct uaudio_softc *sc, + usb_config_descriptor_t *cdesc); + +Static int uaudio_signext(int type, int val); +Static int uaudio_value2bsd(struct mixerctl *mc, int val); +Static int uaudio_bsd2value(struct mixerctl *mc, int val); +Static int uaudio_get(struct uaudio_softc *sc, int type, + int which, int wValue, int wIndex, int len); +Static int uaudio_ctl_get(struct uaudio_softc *sc, int which, + struct mixerctl *mc, int chan); +Static void uaudio_set(struct uaudio_softc *sc, int type, + int which, int wValue, int wIndex, int l, int v); +Static void uaudio_ctl_set(struct uaudio_softc *sc, int which, + struct mixerctl *mc, int chan, int val); + +Static usbd_status uaudio_set_speed(struct uaudio_softc *, int, u_int); + +Static usbd_status uaudio_chan_open(struct uaudio_softc *sc, + struct chan *ch); +Static void uaudio_chan_close(struct uaudio_softc *sc, + struct chan *ch); +Static usbd_status uaudio_chan_alloc_buffers(struct uaudio_softc *, + struct chan *); +Static void uaudio_chan_free_buffers(struct uaudio_softc *, + struct chan *); + +#if defined(__NetBSD__) || defined(__OpenBSD__) +Static void uaudio_chan_set_param(struct chan *ch, + struct audio_params *param, u_char *start, + u_char *end, int blksize); +#endif + +Static void uaudio_chan_ptransfer(struct chan *ch); +Static void uaudio_chan_pintr(usbd_xfer_handle xfer, + usbd_private_handle priv, usbd_status status); + +Static void uaudio_chan_rtransfer(struct chan *ch); +Static void uaudio_chan_rintr(usbd_xfer_handle xfer, + usbd_private_handle priv, usbd_status status); + +#if defined(__NetBSD__) || defined(__OpenBSD__) +Static int uaudio_open(void *, int); +Static void uaudio_close(void *); +Static int uaudio_drain(void *); +Static int uaudio_query_encoding(void *, struct audio_encoding *); +Static int uaudio_set_params(void *, int, int, + struct audio_params *, struct audio_params *); +Static int uaudio_round_blocksize(void *, int); +Static int uaudio_trigger_output(void *, void *, void *, + int, void (*)(void *), void *, + struct audio_params *); +Static int uaudio_trigger_input (void *, void *, void *, + int, void (*)(void *), void *, + struct audio_params *); +Static int uaudio_halt_in_dma(void *); +Static int uaudio_halt_out_dma(void *); +Static int uaudio_getdev(void *, struct audio_device *); +Static int uaudio_mixer_set_port(void *, mixer_ctrl_t *); +Static int uaudio_mixer_get_port(void *, mixer_ctrl_t *); +Static int uaudio_query_devinfo(void *, mixer_devinfo_t *); +Static int uaudio_get_props(void *); + +Static 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, +}; + +Static struct audio_device uaudio_device = { + "USB audio", + "", + "uaudio" +}; + +#elif defined(__FreeBSD__) +Static int audio_attach_mi(device_t); +Static void uaudio_init_params(struct uaudio_softc * sc, struct chan *ch); + +/* for NetBSD compatibirity */ +#define AUMODE_PLAY 0x01 +#define AUMODE_RECORD 0x02 + +#define AUDIO_PROP_FULLDUPLEX 0x01 + +#define AUDIO_ENCODING_ULAW 1 +#define AUDIO_ENCODING_ALAW 2 +#define AUDIO_ENCODING_SLINEAR_LE 6 +#define AUDIO_ENCODING_SLINEAR_BE 7 +#define AUDIO_ENCODING_ULINEAR_LE 8 +#define AUDIO_ENCODING_ULINEAR_BE 9 + +#endif /* FreeBSD */ + + +#if defined(__NetBSD__) || defined(__OpenBSD__) + +USB_DECLARE_DRIVER(uaudio); + +#elif defined(__FreeBSD__) + +USB_DECLARE_DRIVER_INIT(uaudio, + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(bus_print_child, bus_generic_print_child) + ); +#endif + + +USB_MATCH(uaudio) +{ + USB_MATCH_START(uaudio, uaa); + usb_interface_descriptor_t *id; + + if (uaa->iface == NULL) + return (UMATCH_NONE); + + id = usbd_get_interface_descriptor(uaa->iface); + /* Trigger on the control interface. */ + if (id == NULL || + id->bInterfaceClass != UICLASS_AUDIO || + id->bInterfaceSubClass != UISUBCLASS_AUDIOCONTROL || + (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_AUDIO)) + 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 err; + int i, j, found; + + usbd_devinfo(uaa->device, 0, devinfo); + USB_ATTACH_SETUP; + +#if !defined(__FreeBSD__) + printf(": %s\n", devinfo); +#endif + + sc->sc_udev = uaa->device; + + cdesc = usbd_get_config_descriptor(sc->sc_udev); + if (cdesc == NULL) { + printf("%s: failed to get configuration descriptor\n", + USBDEVNAME(sc->sc_dev)); + USB_ATTACH_ERROR_RETURN; + } + + err = uaudio_identify(sc, cdesc); + if (err) { + printf("%s: audio descriptors make no sense, error=%d\n", + USBDEVNAME(sc->sc_dev), err); + 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] == NULL) + continue; + id = usbd_get_interface_descriptor(uaa->ifaces[i]); + if (id == NULL) + continue; + found = 0; + for (j = 0; j < sc->sc_nalts; j++) { + if (id->bInterfaceNumber == + sc->sc_alts[j].idesc->bInterfaceNumber) { + sc->sc_alts[j].ifaceh = uaa->ifaces[i]; + found = 1; + } + } + if (found) + uaa->ifaces[i] = NULL; + } + + for (j = 0; j < sc->sc_nalts; j++) { + if (sc->sc_alts[j].ifaceh == NULL) { + printf("%s: alt %d missing AS interface(s)\n", + USBDEVNAME(sc->sc_dev), j); + USB_ATTACH_ERROR_RETURN; + } + } + + printf("%s: audio rev %d.%02x\n", USBDEVNAME(sc->sc_dev), + sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff); + + sc->sc_chan.sc = sc; + + if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_FRAC) + sc->sc_chan.nofrac = 1; + +#ifndef UAUDIO_DEBUG + if (bootverbose) +#endif + printf("%s: %d mixer controls\n", USBDEVNAME(sc->sc_dev), + sc->sc_nctls); + +#if !defined(__FreeBSD__) + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + USBDEV(sc->sc_dev)); +#endif + + DPRINTF(("uaudio_attach: doing audio_attach_mi\n")); +#if defined(__OpenBSD__) + audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev); +#elif defined(__NetBSD__) + sc->sc_audiodev = audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev); +#elif defined(__FreeBSD__) + sc->sc_dying = 0; + if (audio_attach_mi(sc->sc_dev)) { + printf("audio_attach_mi failed\n"); + USB_ATTACH_ERROR_RETURN; + } +#endif + + USB_ATTACH_SUCCESS_RETURN; +} + +#if defined(__NetBSD__) || defined(__OpenBSD__) +int +uaudio_activate(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 != NULL) + rv = config_deactivate(sc->sc_audiodev); + sc->sc_dying = 1; + break; + } + return (rv); +} +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +int +uaudio_detach(device_ptr_t self, int flags) +{ + struct uaudio_softc *sc = (struct uaudio_softc *)self; + int rv = 0; + + /* Wait for outstanding requests to complete. */ + usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); + + if (sc->sc_audiodev != NULL) + rv = config_detach(sc->sc_audiodev, flags); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + + return (rv); +} +#elif defined(__FreeBSD__) + +USB_DETACH(uaudio) +{ + USB_DETACH_START(uaudio, sc); + + sc->sc_dying = 1; + +#if 0 /* XXX */ + /* Wait for outstanding requests to complete. */ + usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); +#endif + + /* do nothing ? */ + return bus_generic_detach(sc->sc_dev); +} +#endif + +#if defined(__NetBSD__) || defined(__OpenBSD__) +int +uaudio_query_encoding(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); + } +} +#endif + +usb_interface_descriptor_t * +uaudio_find_iface(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 == UICLASS_AUDIO && + d->bInterfaceSubClass == subtype) + return (d); + } + return (NULL); +} + +void +uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc) +{ + int res; + size_t len = sizeof(*mc) * (sc->sc_nctls + 1); + struct mixerctl *nmc = sc->sc_nctls == 0 ? + malloc(len, M_USBDEV, M_NOWAIT) : + realloc(sc->sc_ctls, len, M_USBDEV, M_NOWAIT); + + if(nmc == NULL){ + printf("uaudio_mixer_add_ctl: no memory\n"); + return; + } + sc->sc_ctls = nmc; + + mc->delta = 0; + 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))); + mc->mul = mc->maxval - mc->minval; + if (mc->mul == 0) + mc->mul = 1; + res = uaudio_get(sc, GET_RES, UT_READ_CLASS_INTERFACE, + mc->wValue[0], mc->wIndex, + MIX_SIZE(mc->type)); + if (res > 0) + mc->delta = (res * 256 + mc->mul/2) / mc->mul; + } 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 +} + +#if defined(__NetBSD__) || defined(__OpenBSD__) +char * +uaudio_id_name(struct uaudio_softc *sc, usb_descriptor_t **dps, int id) +{ + static char buf[32]; + sprintf(buf, "i%d", id); + return (buf); +} +#endif + +struct usb_audio_cluster +uaudio_get_cluster(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(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 +} + +void +uaudio_add_output(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(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); +#if !defined(__FreeBSD__) + mix.class = -1; +#endif + mix.type = MIX_SIGNED_16; +#if !defined(__FreeBSD__) /* XXXXX */ + mix.ctlunit = AudioNvolume; +#endif + +#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); + } +#if !defined(__FreeBSD__) + sprintf(mix.ctlname, "mix%d-%s", d->bUnitId, + uaudio_id_name(sc, dps, d->baSourceId[i])); +#endif + mix.nchan = chs; + uaudio_mixer_add_ctl(sc, &mix); + } else { + /* XXX */ + } +#undef BIT + p += chs; + } + +} + +void +uaudio_add_selector(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(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; +#if !defined(__FreeBSD__) + int srcId = d->bSourceId; +#endif + 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); + } + +#if !defined(__FreeBSD__) + DPRINTFN(1,("uaudio_add_feature: bUnitId=%d bSourceId=%d, " + "%d channels, mmask=0x%04x, cmask=0x%04x\n", + d->bUnitId, srcId, nchan, mmask, cmask)); +#endif + + 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 + +#if !defined(__FreeBSD__) + mix.class = -1; /* XXX */ +#endif + switch (ctl) { + case MUTE_CONTROL: + mix.type = MIX_ON_OFF; +#if defined(__FreeBSD__) + mix.ctl = SOUND_MIXER_NRDEVICES; +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNmute); + mix.ctlunit = ""; +#endif + break; + case VOLUME_CONTROL: + mix.type = MIX_SIGNED_16; +#if defined(__FreeBSD__) + /* mix.ctl = SOUND_MIXER_VOLUME; */ + mix.ctl = SOUND_MIXER_PCM; +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNmaster); + mix.ctlunit = AudioNvolume; +#endif + break; + case BASS_CONTROL: + mix.type = MIX_SIGNED_8; +#if defined(__FreeBSD__) + mix.ctl = SOUND_MIXER_BASS; +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNbass); + mix.ctlunit = AudioNbass; +#endif + break; + case MID_CONTROL: + mix.type = MIX_SIGNED_8; +#if defined(__FreeBSD__) + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNmid); + mix.ctlunit = AudioNmid; +#endif + break; + case TREBLE_CONTROL: + mix.type = MIX_SIGNED_8; +#if defined(__FreeBSD__) + mix.ctl = SOUND_MIXER_TREBLE; +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNtreble); + mix.ctlunit = AudioNtreble; +#endif + break; + case GRAPHIC_EQUALIZER_CONTROL: + continue; /* XXX don't add anything */ + break; + case AGC_CONTROL: + mix.type = MIX_ON_OFF; +#if defined(__FreeBSD__) + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNagc); + mix.ctlunit = ""; +#endif + break; + case DELAY_CONTROL: + mix.type = MIX_UNSIGNED_16; +#if defined(__FreeBSD__) + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNdelay); + mix.ctlunit = "4 ms"; +#endif + break; + case BASS_BOOST_CONTROL: + mix.type = MIX_ON_OFF; +#if defined(__FreeBSD__) + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNbassboost); + mix.ctlunit = ""; +#endif + break; + case LOUDNESS_CONTROL: + mix.type = MIX_ON_OFF; +#if defined(__FreeBSD__) + mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ +#else + sprintf(mix.ctlname, "fea%d-%s-%s", unit, + uaudio_id_name(sc, dps, srcId), + AudioNloudness); + mix.ctlunit = ""; +#endif + break; + } + uaudio_mixer_add_ctl(sc, &mix); + } +} + +void +uaudio_add_processing_updown(struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps) +{ + struct usb_audio_processing_unit *d = + (struct usb_audio_processing_unit *)v; + struct usb_audio_processing_unit_1 *d1 = + (struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins]; + struct usb_audio_processing_unit_updown *ud = + (struct usb_audio_processing_unit_updown *) + &d1->bmControls[d1->bControlSize]; + struct mixerctl mix; + int i; + + DPRINTFN(2,("uaudio_add_processing_updown: bUnitId=%d bNrModes=%d\n", + d->bUnitId, ud->bNrModes)); + + if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) { + DPRINTF(("uaudio_add_processing_updown: no mode select\n")); + return; + } + + mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); + mix.nchan = 1; + mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0); +#if !defined(__FreeBSD__) + mix.class = -1; +#endif + mix.type = MIX_ON_OFF; /* XXX */ +#if !defined(__FreeBSD__) + mix.ctlunit = ""; + sprintf(mix.ctlname, "pro%d-mode", d->bUnitId); +#endif + + for (i = 0; i < ud->bNrModes; i++) { + DPRINTFN(2,("uaudio_add_processing_updown: i=%d bm=0x%x\n", + i, UGETW(ud->waModes[i]))); + /* XXX */ + } + uaudio_mixer_add_ctl(sc, &mix); +} + +void +uaudio_add_processing(struct uaudio_softc *sc, usb_descriptor_t *v, + usb_descriptor_t **dps) +{ + struct usb_audio_processing_unit *d = + (struct usb_audio_processing_unit *)v; + struct usb_audio_processing_unit_1 *d1 = + (struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins]; + int ptype = UGETW(d->wProcessType); + struct mixerctl mix; + + DPRINTFN(2,("uaudio_add_processing: wProcessType=%d bUnitId=%d " + "bNrInPins=%d\n", ptype, d->bUnitId, d->bNrInPins)); + + if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) { + mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); + mix.nchan = 1; + mix.wValue[0] = MAKE(XX_ENABLE_CONTROL, 0); +#if !defined(__FreeBSD__) + mix.class = -1; +#endif + mix.type = MIX_ON_OFF; +#if !defined(__FreeBSD__) + mix.ctlunit = ""; + sprintf(mix.ctlname, "pro%d.%d-enable", d->bUnitId, ptype); +#endif + uaudio_mixer_add_ctl(sc, &mix); + } + + switch(ptype) { + case UPDOWNMIX_PROCESS: + uaudio_add_processing_updown(sc, v, dps); + break; + case DOLBY_PROLOGIC_PROCESS: + case P3D_STEREO_EXTENDER_PROCESS: + case REVERBATION_PROCESS: + case CHORUS_PROCESS: + case DYN_RANGE_COMP_PROCESS: + default: +#ifdef UAUDIO_DEBUG + printf("uaudio_add_processing: unit %d, type=%d not impl.\n", + d->bUnitId, ptype); +#endif + break; + } +} + +void +uaudio_add_extension(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 (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_XU) + return; + + if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) { + mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); + mix.nchan = 1; + mix.wValue[0] = MAKE(UA_EXT_ENABLE, 0); +#if !defined(__FreeBSD__) + mix.class = -1; +#endif + mix.type = MIX_ON_OFF; +#if !defined(__FreeBSD__) + mix.ctlunit = ""; + sprintf(mix.ctlname, "ext%d-enable", d->bUnitId); +#endif + uaudio_mixer_add_ctl(sc, &mix); + } +} + +usbd_status +uaudio_identify(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc) +{ + usbd_status err; + + err = uaudio_identify_ac(sc, cdesc); + if (err) + return (err); + return (uaudio_identify_as(sc, cdesc)); +} + +void +uaudio_add_alt(struct uaudio_softc *sc, struct as_info *ai) +{ + size_t len = sizeof(*ai) * (sc->sc_nalts + 1); + struct as_info *nai = sc->sc_nalts == 0 ? + malloc(len, M_USBDEV, M_NOWAIT) : + realloc(sc->sc_alts, len, M_USBDEV, M_NOWAIT); + + if (nai == NULL) { + printf("uaudio_add_alt: no memory\n"); + return; + } + + sc->sc_alts = nai; + 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(struct uaudio_softc *sc, char *buf, int *offsp, + int size, usb_interface_descriptor_t *id) +#define offs (*offsp) +{ + 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); + if ((usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_INP_ASYNC) && + dir == UE_DIR_IN && type == UE_ISO_ADAPT) + type = UE_ISO_ASYNC; + + /* We can't handle endpoints that need a sync pipe yet. */ + if (dir == UE_DIR_IN ? type == UE_ISO_ADAPT : type == UE_ISO_ASYNC) { + printf("%s: ignored %sput endpoint of type %s\n", + USBDEVNAME(sc->sc_dev), + dir == UE_DIR_IN ? "in" : "out", + dir == UE_DIR_IN ? "adaptive" : "async"); + 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 UAUDIO_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 == UE_DIR_OUT ? AUMODE_PLAY : AUMODE_RECORD; + return (USBD_NORMAL_COMPLETION); +} +#undef offs + +usbd_status +uaudio_identify_as(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc) +{ + usb_interface_descriptor_t *id; + usbd_status err; + 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, UISUBCLASS_AUDIOSTREAM); + if (id == NULL) + return (USBD_INVAL); + + sc->sc_chan.terminal = -1; + sc->sc_chan.dir = 0; + + /* Loop through all the alternate settings. */ + while (offs <= size) { + DPRINTFN(2, ("uaudio_identify: interface %d\n", + id->bInterfaceNumber)); + switch (id->bNumEndpoints) { + case 0: + DPRINTFN(2, ("uaudio_identify: AS null alt=%d\n", + id->bAlternateSetting)); + sc->sc_nullalt = id->bAlternateSetting; + break; + case 1: + err = uaudio_process_as(sc, buf, &offs, size, id); + break; + default: +#ifdef UAUDIO_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,UISUBCLASS_AUDIOSTREAM); + if (id == NULL) + 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); + } + +#ifndef NO_RECORDING + if (sc->sc_chan.dir == (AUMODE_PLAY | AUMODE_RECORD)) + sc->sc_props |= AUDIO_PROP_FULLDUPLEX; +#endif + return (USBD_NORMAL_COMPLETION); +} + +usbd_status +uaudio_identify_ac(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, UISUBCLASS_AUDIOCONTROL); + if (id == NULL) + 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); + + sc->sc_audio_rev = UGETW(acdp->bcdADC); + DPRINTFN(2,("uaudio_identify: found AC header, vers=%03x, len=%d\n", + sc->sc_audio_rev, 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 == NULL) + 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); +} + +#if defined(__NetBSD__) || defined(__OpenBSD__) +int +uaudio_query_devinfo(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; + mi->un.v.delta = mc->delta; + break; + } + return (0); +} + +int +uaudio_open(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 & AUMODE_RECORD)) + return (EACCES); + if ((flags & FWRITE) && !(sc->sc_chan.dir & AUMODE_PLAY)) + return (EACCES); + + sc->sc_chan.intr = 0; + + return (0); +} + +/* + * Close function is called at splaudio(). + */ +void +uaudio_close(void *addr) +{ + struct uaudio_softc *sc = addr; + + if (sc->sc_dying) + return (EIO); + + 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(void *addr) +{ + struct uaudio_softc *sc = addr; + + if (sc->sc_dying) + return (EIO); + + usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); + + return (0); +} + +int +uaudio_halt_out_dma(void *addr) +{ + struct uaudio_softc *sc = addr; + + if (sc->sc_dying) + return (EIO); + + DPRINTF(("uaudio_halt_out_dma: enter\n")); + if (sc->sc_chan.pipe != NULL) { + 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(void *addr) +{ + struct uaudio_softc *sc = addr; + + DPRINTF(("uaudio_halt_in_dma: enter\n")); + if (sc->sc_chan.pipe != NULL) { + 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(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(void *addr, int blk) +{ + struct uaudio_softc *sc = addr; + int bpf; + + if (sc->sc_dying) + return (EIO); + + 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(void *addr) +{ + struct uaudio_softc *sc = addr; + + return (sc->sc_props); +} +#endif /* NetBSD or OpenBSD */ + + +int +uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue, + int wIndex, int len) +{ + usb_device_request_t req; + u_int8_t data[4]; + usbd_status err; + int val; + + if (sc->sc_dying) + return (EIO); + + 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)); + err = usbd_do_request(sc->sc_udev, &req, &data); + if (err) { + DPRINTF(("uaudio_get: err=%s\n", usbd_errstr(err))); + 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(struct uaudio_softc *sc, int which, int type, int wValue, + int wIndex, int len, int val) +{ + usb_device_request_t req; + u_int8_t data[4]; + usbd_status err; + + if (sc->sc_dying) + return; + + 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)); + err = usbd_do_request(sc->sc_udev, &req, &data); +#ifdef UAUDIO_DEBUG + if (err) + DPRINTF(("uaudio_set: err=%d\n", err)); +#endif +} + +int +uaudio_signext(int type, int val) +{ + if (!MIX_UNSIGNED(type)) { + if (MIX_SIZE(type) == 2) + val = (int16_t)val; + else + val = (int8_t)val; + } + return (val); +} + +int +uaudio_value2bsd(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->mul/2) / mc->mul; + DPRINTFN(5, ("val'=%d\n", val)); + return (val); +} + +int +uaudio_bsd2value(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->delta/2) * mc->mul / 256 + mc->minval; + DPRINTFN(5, ("val'=%d\n", val)); + return (val); +} + +int +uaudio_ctl_get(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(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); +} + +#if defined(__NetBSD__) || defined(__OpenBSD__) +int +uaudio_mixer_get_port(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(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(void *addr, void *start, void *end, int blksize, + void (*intr)(void *), void *arg, + struct audio_params *param) +{ + struct uaudio_softc *sc = addr; + struct chan *ch = &sc->sc_chan; + usbd_status err; + 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)); + + err = uaudio_chan_alloc_buffers(sc, ch); + if (err) + return (EIO); + + err = uaudio_chan_open(sc, ch); + if (err) { + 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-1; i++) /* XXX -1 shouldn't be needed */ + uaudio_chan_rtransfer(ch); + splx(s); + + return (0); +} + +int +uaudio_trigger_output(void *addr, void *start, void *end, int blksize, + void (*intr)(void *), void *arg, + struct audio_params *param) +{ + struct uaudio_softc *sc = addr; + struct chan *ch = &sc->sc_chan; + usbd_status err; + 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)); + + err = uaudio_chan_alloc_buffers(sc, ch); + if (err) + return (EIO); + + err = uaudio_chan_open(sc, ch); + if (err) { + 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-1; i++) /* XXX */ + uaudio_chan_ptransfer(ch); + splx(s); + + return (0); +} +#endif /* NetBSD or OpenBSD */ + +/* Set up a pipe for a channel. */ +usbd_status +uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch) +{ + struct as_info *as = &sc->sc_alts[sc->sc_curaltidx]; + int endpt = as->edesc->bEndpointAddress; + usbd_status err; + + if (sc->sc_dying) + return (EIO); + + 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. */ + err = usbd_set_interface(as->ifaceh, as->alt); + if (err) + return (err); + + /* Some devices do not support this request, so ignore errors. */ +#ifdef UAUDIO_DEBUG + err = uaudio_set_speed(sc, endpt, ch->sample_rate); + if (err) + DPRINTF(("uaudio_chan_open: set_speed failed err=%s\n", + usbd_errstr(err))); +#else + (void)uaudio_set_speed(sc, endpt, ch->sample_rate); +#endif + + DPRINTF(("uaudio_open_chan: create pipe to 0x%02x\n", endpt)); + err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->pipe); + return (err); +} + +void +uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch) +{ + struct as_info *as = &sc->sc_alts[sc->sc_curaltidx]; + + if (sc->sc_dying) + return ; + + if (sc->sc_nullalt >= 0) { + DPRINTF(("uaudio_close_chan: set null alt=%d\n", + sc->sc_nullalt)); + usbd_set_interface(as->ifaceh, sc->sc_nullalt); + } + usbd_abort_pipe(ch->pipe); + usbd_close_pipe(ch->pipe); +} + +usbd_status +uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch) +{ + usbd_xfer_handle xfer; + void *buf; + int i, size; + + size = (ch->bytes_per_frame + ch->sample_size) * UAUDIO_NFRAMES; + for (i = 0; i < UAUDIO_NCHANBUFS; i++) { + xfer = usbd_alloc_xfer(sc->sc_udev); + if (xfer == 0) + goto bad; + ch->chanbufs[i].xfer = xfer; + buf = usbd_alloc_buffer(xfer, 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_xfer(ch->chanbufs[i].xfer); + return (USBD_NOMEM); +} + +void +uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch) +{ + int i; + + for (i = 0; i < UAUDIO_NCHANBUFS; i++) + usbd_free_xfer(ch->chanbufs[i].xfer); +} + +/* Called at splusb() */ +void +uaudio_chan_ptransfer(struct chan *ch) +{ + struct chanbuf *cb; + int i, n, size, residue, total; + + if (ch->sc->sc_dying) + return; + + /* 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) { + if (!ch->nofrac) + 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 xfer=%p\n", cb->xfer)); + /* Fill the request */ + usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, + UAUDIO_NFRAMES, USBD_NO_COPY, + uaudio_chan_pintr); + + (void)usbd_transfer(cb->xfer); +} + +void +uaudio_chan_pintr(usbd_xfer_handle xfer, 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_xfer_status(xfer, NULL, NULL, &count, NULL); + 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; +#if defined(__FreeBSD__) + /* s = spltty(); */ + s = splhigh(); + chn_intr(ch->pcm_ch); + splx(s); +#else + 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); +#endif + + /* start next transfer */ + uaudio_chan_ptransfer(ch); +} + +/* Called at splusb() */ +void +uaudio_chan_rtransfer(struct chan *ch) +{ + struct chanbuf *cb; + int i, size, residue, total; + + if (ch->sc->sc_dying) + return; + + /* 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) { + if (!ch->nofrac) + 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 xfer=%p\n", cb->xfer)); + /* Fill the request */ + usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, + UAUDIO_NFRAMES, USBD_NO_COPY, + uaudio_chan_rintr); + + (void)usbd_transfer(cb->xfer); +} + +void +uaudio_chan_rintr(usbd_xfer_handle xfer, 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_xfer_status(xfer, NULL, NULL, &count, NULL); + DPRINTFN(5,("uaudio_chan_rintr: count=%d, transferred=%d\n", + count, ch->transferred)); + + if (count < cb->size) { + /* if the device fails to keep up, copy last byte */ + u_char b = count ? cb->buffer[count-1] : 0; + while (count < cb->size) + cb->buffer[count++] = b; + } + +#ifdef DIAGNOSTIC + if (count != cb->size) { + printf("uaudio_chan_rintr: 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; +#if defined(__FreeBSD__) + s = spltty(); + chn_intr(ch->pcm_ch); + splx(s); +#else + s = splaudio(); + while (ch->transferred >= ch->blksize) { + ch->transferred -= ch->blksize; + DPRINTFN(5,("uaudio_chan_rintr: call %p(%p)\n", + ch->intr, ch->arg)); + ch->intr(ch->arg); + } + splx(s); +#endif + + /* start next transfer */ + uaudio_chan_rtransfer(ch); +} + +#if defined(__NetBSD__) || defined(__OpenBSD__) +void +uaudio_chan_set_param(struct chan *ch, struct audio_params *param, + u_char *start, u_char *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(void *addr, int setmode, int usemode, + struct audio_params *play, struct audio_params *rec) +{ + struct uaudio_softc *sc = addr; + int flags = sc->sc_altflags; + int factor; + int enc, i, j; + void (*swcode)(void *, u_char *buf, int cnt); + struct audio_params *p; + int mode; + + if (sc->sc_dying) + return (EIO); + + if (sc->sc_chan.pipe != NULL) + return (EBUSY); + + for (mode = AUMODE_RECORD; mode != -1; + mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { + if ((setmode & mode) == 0) + continue; + if ((sc->sc_chan.dir & mode) == 0) + continue; + + p = mode == AUMODE_PLAY ? play : rec; + + factor = 1; + swcode = 0; + enc = p->encoding; + switch (enc) { + case AUDIO_ENCODING_SLINEAR_BE: + if (p->precision == 16) { + swcode = swap_bytes; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else if (p->precision == 8 && !(flags & HAS_8)) { + swcode = change_sign8; + enc = AUDIO_ENCODING_ULINEAR_LE; + } + break; + case AUDIO_ENCODING_SLINEAR_LE: + if (p->precision == 8 && !(flags & HAS_8)) { + swcode = change_sign8; + enc = AUDIO_ENCODING_ULINEAR_LE; + } + break; + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision == 16) { + if (mode == AUMODE_PLAY) + swcode = swap_bytes_change_sign16_le; + else + swcode = change_sign16_swap_bytes_le; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else if (p->precision == 8 && !(flags & HAS_8U)) { + swcode = change_sign8; + enc = AUDIO_ENCODING_SLINEAR_LE; + } + break; + case AUDIO_ENCODING_ULINEAR_LE: + if (p->precision == 16) { + swcode = change_sign16_le; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else if (p->precision == 8 && !(flags & HAS_8U)) { + swcode = change_sign8; + enc = AUDIO_ENCODING_SLINEAR_LE; + } + break; + case AUDIO_ENCODING_ULAW: + if (!(flags & HAS_MULAW)) { + if (mode == AUMODE_PLAY && + (flags & HAS_16)) { + swcode = mulaw_to_slinear16_le; + factor = 2; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else if (flags & HAS_8U) { + if (mode == AUMODE_PLAY) + swcode = mulaw_to_ulinear8; + else + swcode = ulinear8_to_mulaw; + enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (flags & HAS_8) { + if (mode == AUMODE_PLAY) + swcode = mulaw_to_slinear8; + else + swcode = slinear8_to_mulaw; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else + return (EINVAL); + } + break; + case AUDIO_ENCODING_ALAW: + if (!(flags & HAS_ALAW)) { + if (mode == AUMODE_PLAY && + (flags & HAS_16)) { + swcode = alaw_to_slinear16_le; + factor = 2; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else if (flags & HAS_8U) { + if (mode == AUMODE_PLAY) + swcode = alaw_to_ulinear8; + else + swcode = ulinear8_to_alaw; + enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (flags & HAS_8) { + if (mode == AUMODE_PLAY) + swcode = alaw_to_slinear8; + else + swcode = 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 && + (mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN) == + UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress)) { + if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { + DPRINTFN(2,("uaudio_set_params: cont %d-%d\n", + UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); + 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++) { + DPRINTFN(2,("uaudio_set_params: disc #" + "%d: %d\n", j, UA_GETSAMP(a1d, j))); + /* XXX allow for some slack */ + if (UA_GETSAMP(a1d, j) == + p->sample_rate) + goto found; + } + } + } + } + return (EINVAL); + + found: + p->sw_code = swcode; + p->factor = factor; + if (usemode == mode) + 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); +} +#endif /* NetBSD or OpenBSD */ + +usbd_status +uaudio_set_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)); +} + + +#if defined(__FreeBSD__) +/************************************************************/ +void +uaudio_init_params(struct uaudio_softc *sc, struct chan *ch) +{ + int i, j, enc; + int samples_per_frame, sample_size; + + switch(ch->format & 0x0000FFFF) { + case AFMT_U8: + enc = AUDIO_ENCODING_ULINEAR_LE; + ch->precision = 8; + break; + case AFMT_S8: + enc = AUDIO_ENCODING_SLINEAR_LE; + ch->precision = 8; + break; + case AFMT_A_LAW: /* ? */ + enc = AUDIO_ENCODING_ALAW; + ch->precision = 8; + break; + case AFMT_MU_LAW: /* ? */ + enc = AUDIO_ENCODING_ULAW; + ch->precision = 8; + break; + case AFMT_S16_LE: + enc = AUDIO_ENCODING_SLINEAR_LE; + ch->precision = 16; + break; + case AFMT_S16_BE: + enc = AUDIO_ENCODING_SLINEAR_BE; + ch->precision = 16; + break; + case AFMT_U16_LE: + enc = AUDIO_ENCODING_ULINEAR_LE; + ch->precision = 16; + break; + case AFMT_U16_BE: + enc = AUDIO_ENCODING_ULINEAR_BE; + ch->precision = 16; + break; + default: + enc = 0; + ch->precision = 16; + printf("Unknown format %x\n", ch->format); + } + + if (ch->format & AFMT_STEREO) { + ch->channels = 2; + } else { + ch->channels = 1; + } + +/* for (mode = ...... */ + for (i = 0; i < sc->sc_nalts; i++) { + struct usb_audio_streaming_type1_descriptor *a1d = + sc->sc_alts[i].asf1desc; + if (ch->channels == a1d->bNrChannels && + ch->precision == a1d->bBitResolution && +#if 1 + enc == sc->sc_alts[i].encoding) { +#else + enc == sc->sc_alts[i].encoding && + (mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN) == + UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress)) { +#endif + if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { + DPRINTFN(2,("uaudio_set_params: cont %d-%d\n", + UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); + if (UA_SAMP_LO(a1d) < ch->sample_rate && + ch->sample_rate < UA_SAMP_HI(a1d)) { + sc->sc_curaltidx = i; + goto found; + } + } else { + for (j = 0; j < a1d->bSamFreqType; j++) { + DPRINTFN(2,("uaudio_set_params: disc #" + "%d: %d\n", j, UA_GETSAMP(a1d, j))); + /* XXX allow for some slack */ + if (UA_GETSAMP(a1d, j) == + ch->sample_rate) { + sc->sc_curaltidx = i; + goto found; + } + } + } + } + } + /* return (EINVAL); */ + + found: +#if 0 /* XXX */ + p->sw_code = swcode; + p->factor = factor; + if (usemode == mode) + sc->sc_curaltidx = i; +#endif +/* } */ + + sample_size = ch->precision * ch->channels / 8; + samples_per_frame = ch->sample_rate / USB_FRAMES_PER_SECOND; + ch->fraction = ch->sample_rate % USB_FRAMES_PER_SECOND; + ch->sample_size = sample_size; + ch->bytes_per_frame = samples_per_frame * sample_size; + ch->residue = 0; + + ch->cur = ch->start; + ch->transferred = 0; + ch->curchanbuf = 0; +} + +void +uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt) +{ + int i, pn=0, rn=0; + int prec, dir; + u_int32_t fmt; + struct uaudio_softc *sc; + + struct usb_audio_streaming_type1_descriptor *a1d; + + sc = device_get_softc(dev); + + for (i = 0; i < sc->sc_nalts; i++) { + fmt = 0; + a1d = sc->sc_alts[i].asf1desc; + prec = a1d->bBitResolution; /* precision */ + + switch (sc->sc_alts[i].encoding) { + case AUDIO_ENCODING_ULINEAR_LE: + if (prec == 8) { + fmt = AFMT_U8; + } else if (prec == 16) { + fmt = AFMT_U16_LE; + } + break; + case AUDIO_ENCODING_SLINEAR_LE: + if (prec == 8) { + fmt = AFMT_S8; + } else if (prec == 16) { + fmt = AFMT_S16_LE; + } + break; + case AUDIO_ENCODING_ULINEAR_BE: + if (prec == 16) { + fmt = AFMT_U16_BE; + } + break; + case AUDIO_ENCODING_SLINEAR_BE: + if (prec == 16) { + fmt = AFMT_S16_BE; + } + break; + case AUDIO_ENCODING_ALAW: + if (prec == 8) { + fmt = AFMT_A_LAW; + } + break; + case AUDIO_ENCODING_ULAW: + if (prec == 8) { + fmt = AFMT_MU_LAW; + } + break; + } + + if (fmt != 0) { + if (a1d->bNrChannels == 2) { /* stereo/mono */ + fmt |= AFMT_STEREO; + } else if (a1d->bNrChannels != 1) { + fmt = 0; + } + } + + if (fmt != 0) { + dir= UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress); + if (dir == UE_DIR_OUT) { + pfmt[pn++] = fmt; + } else if (dir == UE_DIR_IN) { + rfmt[rn++] = fmt; + } + } + + if ((pn > 8*2) || (rn > 8*2)) + break; + } + pfmt[pn] = 0; + rfmt[rn] = 0; + return; +} + +void +uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start, u_char *end, + struct pcm_channel *pc) +{ + struct uaudio_softc *sc; + struct chan *ch; + + sc = device_get_softc(dev); + ch = &sc->sc_chan; + + ch->start = start; + ch->end = end; + + ch->pcm_ch = pc; + + return; +} + +void +uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize) +{ + struct uaudio_softc *sc; + struct chan *ch; + + sc = device_get_softc(dev); + ch = &sc->sc_chan; + + ch->blksize = blocksize; + + return; +} + +void +uaudio_chan_set_param_speed(device_t dev, u_int32_t speed) +{ + struct uaudio_softc *sc; + struct chan *ch; + + sc = device_get_softc(dev); + ch = &sc->sc_chan; + + ch->sample_rate = speed; + + return; +} + +int +uaudio_chan_getptr(device_t dev) +{ + struct uaudio_softc *sc; + struct chan *ch; + int ptr; + + sc = device_get_softc(dev); + ch = &sc->sc_chan; + + ptr = ch->cur - ch->start; + + return ptr; +} + +void +uaudio_chan_set_param_format(device_t dev, u_int32_t format) +{ + struct uaudio_softc *sc; + struct chan *ch; + + sc = device_get_softc(dev); + ch = &sc->sc_chan; + + ch->format = format; + + return; +} + +int +uaudio_halt_out_dma(device_t dev) +{ + struct uaudio_softc *sc; + + sc = device_get_softc(dev); + + DPRINTF(("uaudio_halt_out_dma: enter\n")); + if (sc->sc_chan.pipe != NULL) { + uaudio_chan_close(sc, &sc->sc_chan); + sc->sc_chan.pipe = 0; + uaudio_chan_free_buffers(sc, &sc->sc_chan); + } + return (0); +} + +int +uaudio_trigger_output(device_t dev) +{ + struct uaudio_softc *sc; + struct chan *ch; + usbd_status err; + int i, s; + + sc = device_get_softc(dev); + ch = &sc->sc_chan; + + if (sc->sc_dying) + return (EIO); + + uaudio_init_params(sc, ch); + + err = uaudio_chan_alloc_buffers(sc, ch); + if (err) + return (EIO); + + err = uaudio_chan_open(sc, ch); + if (err) { + uaudio_chan_free_buffers(sc, ch); + return (EIO); + } + + s = splusb(); + for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */ + uaudio_chan_ptransfer(ch); + splx(s); + + return (0); +} + +u_int32_t +uaudio_query_mix_info(device_t dev) +{ + int i; + u_int32_t mask = 0; + struct uaudio_softc *sc; + struct mixerctl *mc; + + sc = device_get_softc(dev); + for (i=0; i < sc->sc_nctls; i++) { + mc = &sc->sc_ctls[i]; + if (mc->ctl != SOUND_MIXER_NRDEVICES) { + /* Set device mask bits. + See /usr/include/machine/soundcard.h */ + mask |= (1 << mc->ctl); + } + } + return mask; +} + +void +uaudio_mixer_set(device_t dev, unsigned type, unsigned left, unsigned right) +{ + int i; + struct uaudio_softc *sc; + struct mixerctl *mc; + + sc = device_get_softc(dev); + for (i=0; i < sc->sc_nctls; i++) { + mc = &sc->sc_ctls[i]; + if (mc->ctl == type) { + if (mc->nchan == 2) { + /* set Right */ + uaudio_ctl_set(sc, SET_CUR, mc, 1, (int)(right*256)/100); + } + /* set Left or Mono */ + uaudio_ctl_set(sc, SET_CUR, mc, 0, (int)(left*256)/100); + } + } + return; +} + +Static int +audio_attach_mi(device_t dev) +{ + device_t child; + struct sndcard_func *func; + + /* Attach the children. */ + /* PCM Audio */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); + if (func == NULL) + return (ENOMEM); + bzero(func, sizeof(*func)); + func->func = SCF_PCM; + child = device_add_child(dev, "pcm", -1); + device_set_ivars(child, func); + + bus_generic_attach(dev); + + return 0; /* XXXXX */ +} + +DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, usbd_driver_load, 0); +MODULE_VERSION(uaudio, 1); + +#endif diff --git a/sys/dev/sound/usb/uaudio.h b/sys/dev/sound/usb/uaudio.h new file mode 100644 index 0000000..8433c81 --- /dev/null +++ b/sys/dev/sound/usb/uaudio.h @@ -0,0 +1,49 @@ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org> + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 NO_RECORDING /* XXX: some routines missing from uaudio.c */ + +/* Defined in uaudio.c, used in uaudio_pcm,c */ + +void uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start, + u_char *end, struct pcm_channel *pc); +int uaudio_trigger_output(device_t dev); +int uaudio_halt_out_dma(device_t dev); +#ifndef NO_RECORDING +int uaudio_trigger_input(device_t dev); +int uaudio_halt_in_dma(device_t dev); +#endif +void uaudio_chan_set_param(device_t, u_char *, u_char *); +void uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize); +void uaudio_chan_set_param_speed(device_t dev, u_int32_t speed); +void uaudio_chan_set_param_format(device_t dev, u_int32_t format); +int uaudio_chan_getptr(device_t dev); +void uaudio_mixer_set(device_t dev, unsigned type, unsigned left, + unsigned right); +u_int32_t uaudio_query_mix_info(device_t dev); +void uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt); + diff --git a/sys/dev/sound/usb/uaudio_pcm.c b/sys/dev/sound/usb/uaudio_pcm.c new file mode 100644 index 0000000..a49ee4a --- /dev/null +++ b/sys/dev/sound/usb/uaudio_pcm.c @@ -0,0 +1,375 @@ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org> + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + + +#include <sys/soundcard.h> +#include <dev/sound/pcm/sound.h> +#include <dev/sound/chip.h> + +#include <dev/sound/usb/uaudio.h> + +#include "mixer_if.h" + +struct ua_info; + +struct ua_chinfo { + struct ua_info *parent; + struct pcm_channel *channel; + struct snd_dbuf *buffer; + int dir, hwch; + u_int32_t fmt, spd, blksz; /* XXXXX */ +}; + +struct ua_info { + device_t sc_dev; + struct ua_chinfo pch, rch; + bus_dma_tag_t parent_dmat; +}; + +static u_int32_t ua_playfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */ + +static struct pcmchan_caps ua_playcaps = {8000, 48000, ua_playfmt, 0}; + +static u_int32_t ua_recfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */ + +static struct pcmchan_caps ua_reccaps = {8000, 48000, ua_recfmt, 0}; + +#define UAUDIO_PCM_BUFF_SIZE 16*1024 + +/************************************************************/ +static void * +ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +{ + device_t pa_dev; + u_char *buf,*end; + + struct ua_info *sc = devinfo; + struct ua_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; + + ch->parent = sc; + ch->channel = c; + ch->buffer = b; + + /* allocate PCM side DMA buffer */ + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, UAUDIO_PCM_BUFF_SIZE) != 0) { + return NULL; + } + + pa_dev = device_get_parent(sc->sc_dev); + buf = end = sndbuf_getbuf(b); + end += sndbuf_getsize(b); + uaudio_chan_set_param_pcm_dma_buff(pa_dev, buf, end, ch->channel); + + /* Create ua_playfmt[] & ua_recfmt[] */ + uaudio_query_formats(pa_dev, (u_int32_t *)&ua_playfmt, (u_int32_t *)&ua_recfmt); + + ch->dir = dir; +#ifndef NO_RECORDING + ch->hwch = 1; + if (dir == PCMDIR_PLAY) + ch->hwch = 2; +#else + ch->hwch = 2; +#endif + + return ch; +} + +static int +ua_chan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + device_t pa_dev; + struct ua_info *ua; + + struct ua_chinfo *ch = data; + + ua = ch->parent; + pa_dev = device_get_parent(ua->sc_dev); + uaudio_chan_set_param_format(pa_dev, format); + + ch->fmt = format; + return 0; +} + +static int +ua_chan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + device_t pa_dev; + struct ua_info *ua; + + struct ua_chinfo *ch = data; + ch->spd = speed; + + ua = ch->parent; + pa_dev = device_get_parent(ua->sc_dev); + uaudio_chan_set_param_speed(pa_dev, speed); + + return ch->spd; +} + +static int +ua_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + device_t pa_dev; + struct ua_info *ua; + struct ua_chinfo *ch = data; + /* ch->blksz = blocksize; */ + if (blocksize) { + ch->blksz = blocksize; + } else { + ch->blksz = UAUDIO_PCM_BUFF_SIZE/2; + } + + /* XXXXX */ + ua = ch->parent; + pa_dev = device_get_parent(ua->sc_dev); + uaudio_chan_set_param_blocksize(pa_dev, blocksize); + + return ch->blksz; +} + +static int +ua_chan_trigger(kobj_t obj, void *data, int go) +{ + device_t pa_dev; + struct ua_info *ua; + struct ua_chinfo *ch = data; + + if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + return 0; + + ua = ch->parent; + pa_dev = device_get_parent(ua->sc_dev); + + /* XXXXX */ + if (ch->dir == PCMDIR_PLAY) { + if (go == PCMTRIG_START) { + uaudio_trigger_output(pa_dev); + } else { + uaudio_halt_out_dma(pa_dev); + } + } else { +#ifndef NO_RECORDING + if (go == PCMTRIG_START) + uaudio_trigger_input(pa_dev); + else + uaudio_halt_in_dma(pa_dev); +#endif + } + + return 0; +} + +static int +ua_chan_getptr(kobj_t obj, void *data) +{ + device_t pa_dev; + struct ua_info *ua; + struct ua_chinfo *ch = data; + + ua = ch->parent; + pa_dev = device_get_parent(ua->sc_dev); + + return uaudio_chan_getptr(pa_dev); +} + +static struct pcmchan_caps * +ua_chan_getcaps(kobj_t obj, void *data) +{ + struct ua_chinfo *ch = data; + + return (ch->dir == PCMDIR_PLAY) ? &ua_playcaps : & ua_reccaps; +} + +static kobj_method_t ua_chan_methods[] = { + KOBJMETHOD(channel_init, ua_chan_init), + KOBJMETHOD(channel_setformat, ua_chan_setformat), + KOBJMETHOD(channel_setspeed, ua_chan_setspeed), + KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize), + KOBJMETHOD(channel_trigger, ua_chan_trigger), + KOBJMETHOD(channel_getptr, ua_chan_getptr), + KOBJMETHOD(channel_getcaps, ua_chan_getcaps), + { 0, 0 } +}; + +CHANNEL_DECLARE(ua_chan); + +/************************************************************/ +static int +ua_mixer_init(struct snd_mixer *m) +{ + u_int32_t mask; + device_t pa_dev; + struct ua_info *ua = mix_getdevinfo(m); + + pa_dev = device_get_parent(ua->sc_dev); + + mask = uaudio_query_mix_info(pa_dev); + mix_setdevs(m, mask); + + return 0; +} + +static int +ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right) +{ + device_t pa_dev; + struct ua_info *ua = mix_getdevinfo(m); + + pa_dev = device_get_parent(ua->sc_dev); + uaudio_mixer_set(pa_dev, type, left, right); + + return left | (right << 8); +} + +static int +ua_mixer_setrecsrc(struct snd_mixer *m, u_int32_t src) +{ + return src; +} + +static kobj_method_t ua_mixer_methods[] = { + KOBJMETHOD(mixer_init, ua_mixer_init), + KOBJMETHOD(mixer_set, ua_mixer_set), + KOBJMETHOD(mixer_setrecsrc, ua_mixer_setrecsrc), + + { 0, 0 } +}; +MIXER_DECLARE(ua_mixer); +/************************************************************/ + + +static int +ua_probe(device_t dev) +{ + char *s; + struct sndcard_func *func; + + /* The parent device has already been probed. */ + + func = device_get_ivars(dev); + if (func == NULL || func->func != SCF_PCM) + return (ENXIO); + + s = "USB Audio"; + + device_set_desc(dev, s); + return 0; +} + +static int +ua_attach(device_t dev) +{ + struct ua_info *ua; + char status[SND_STATUSLEN]; + unsigned int bufsz; + + ua = (struct ua_info *)malloc(sizeof *ua, M_DEVBUF, M_NOWAIT); + if (!ua) + return ENXIO; + bzero(ua, sizeof *ua); + + ua->sc_dev = dev; + + bufsz = pcm_getbuffersize(dev, 4096, UAUDIO_PCM_BUFF_SIZE, 65536); + + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/bufsz, /*nsegments*/1, + /*maxsegz*/0x3fff, /*flags*/0, + &ua->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto bad; + } + + if (mixer_init(dev, &ua_mixer_class, ua)) { + return(ENXIO); + } + + snprintf(status, SND_STATUSLEN, "at addr ?"); + + if (pcm_register(dev, ua, 1, 0)) { + return(ENXIO); + } + + pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua); +#ifndef NO_RECORDING + pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua); +#endif + pcm_setstatus(dev, status); + + return 0; +bad: + if (ua->parent_dmat) + bus_dma_tag_destroy(ua->parent_dmat); + free(ua, M_DEVBUF); + + return ENXIO; +} + +static int +ua_detach(device_t dev) +{ + int r; + struct ua_info *sc; + + r = pcm_unregister(dev); + if (r) + return r; + + sc = pcm_getdevinfo(dev); + bus_dma_tag_destroy(sc->parent_dmat); + free(sc, M_DEVBUF); + + return 0; +} + +/************************************************************/ + +static device_method_t ua_pcm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ua_probe), + DEVMETHOD(device_attach, ua_attach), + DEVMETHOD(device_detach, ua_detach), + + { 0, 0 } +}; + +static driver_t ua_pcm_driver = { + "pcm", + ua_pcm_methods, + PCM_SOFTC_SIZE, +}; + +static devclass_t pcm_devclass; + +DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(ua_pcm, uaudio, 1, 1, 1); +MODULE_DEPEND(ua_pcm, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_VERSION(ua_pcm, 1); diff --git a/sys/dev/sound/usb/uaudioreg.h b/sys/dev/sound/usb/uaudioreg.h new file mode 100644 index 0000000..4d6e834 --- /dev/null +++ b/sys/dev/sound/usb/uaudioreg.h @@ -0,0 +1,384 @@ +/* $NetBSD: uaudioreg.h,v 1.7 2000/12/28 00:29:58 augustss Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (lennart@augustsson.net) 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. + */ + +#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 + +#if defined(__FreeBSD__) && (__FreeBSD_version <= 500014) +#define UPACKED __attribute__ ((packed)) +#endif + +/* 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; +} UPACKED 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]; +} UPACKED; + +struct usb_audio_streaming_interface_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalLink; + uByte bDelay; + uWord wFormatTag; +} UPACKED; + +struct usb_audio_streaming_endpoint_descriptor { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bmAttributes; + uByte bLockDelayUnits; + uWord wLockDelay; +} UPACKED; + +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) +} UPACKED; + +struct usb_audio_cluster { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; +} UPACKED; + +/* 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; +} UPACKED; + +/* UDESCSUB_AC_OUTPUT */ +struct usb_audio_output_terminal { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bTerminalId; + uWord wTerminalType; + uByte bAssocTerminal; + uByte bSourceId; + uByte iTerminal; +} UPACKED; + +/* UDESCSUB_AC_MIXER */ +struct usb_audio_mixer_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[255]; /* [bNrInPins] */ + /* struct usb_audio_mixer_unit_1 */ +} UPACKED; +struct usb_audio_mixer_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bmControls[255]; /* [bNrChannels] */ + /*uByte iMixer;*/ +} UPACKED; + +/* UDESCSUB_AC_SELECTOR */ +struct usb_audio_selector_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uByte bNrInPins; + uByte baSourceId[255]; /* [bNrInPins] */ + /* uByte iSelector; */ +} UPACKED; + +/* 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; */ +} UPACKED; + +/* UDESCSUB_AC_PROCESSING */ +struct usb_audio_processing_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wProcessType; + uByte bNrInPins; + uByte baSourceId[255]; /* [bNrInPins] */ + /* struct usb_audio_processing_unit_1 */ +} UPACKED; +struct usb_audio_processing_unit_1{ + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bControlSize; + uByte bmControls[255]; /* [bControlSize] */ +#define UA_PROC_ENABLE_MASK 1 +} UPACKED; + +struct usb_audio_processing_unit_updown { + uByte iProcessing; + uByte bNrModes; + uWord waModes[255]; /* [bNrModes] */ +} UPACKED; + +/* UDESCSUB_AC_EXTENSION */ +struct usb_audio_extension_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; + uWord wExtensionCode; + uByte bNrInPins; + uByte baSourceId[255]; /* [bNrInPins] */ + /* struct usb_audio_extension_unit_1 */ +} UPACKED; +struct usb_audio_extension_unit_1 { + uByte bNrChannels; + uWord wChannelConfig; + uByte iChannelNames; + uByte bControlSize; + uByte bmControls[255]; /* [bControlSize] */ +#define UA_EXT_ENABLE_MASK 1 +#define UA_EXT_ENABLE 1 + /*uByte iExtension;*/ +} UPACKED; + +/* USB terminal types */ +#define UAT_UNDEFINED 0x0100 +#define UAT_STREAM 0x0101 +#define UAT_VENDOR 0x01ff +/* input terminal types */ +#define UATI_UNDEFINED 0x0200 +#define UATI_MICROPHONE 0x0201 +#define UATI_DESKMICROPHONE 0x0202 +#define UATI_PERSONALMICROPHONE 0x0203 +#define UATI_OMNIMICROPHONE 0x0204 +#define UATI_MICROPHONEARRAY 0x0205 +#define UATI_PROCMICROPHONEARR 0x0206 +/* output terminal types */ +#define UATO_UNDEFINED 0x0300 +#define UATO_SPEAKER 0x0301 +#define UATO_HEADPHONES 0x0302 +#define UATO_DISPLAYAUDIO 0x0303 +#define UATO_DESKTOPSPEAKER 0x0304 +#define UATO_ROOMSPEAKER 0x0305 +#define UATO_COMMSPEAKER 0x0306 +#define UATO_SUBWOOFER 0x0307 +/* bidir terminal types */ +#define UATB_UNDEFINED 0x0400 +#define UATB_HANDSET 0x0401 +#define UATB_HEADSET 0x0402 +#define UATB_SPEAKERPHONE 0x0403 +#define UATB_SPEAKERPHONEESUP 0x0404 +#define UATB_SPEAKERPHONEECANC 0x0405 +/* telephony terminal types */ +#define UATT_UNDEFINED 0x0500 +#define UATT_PHONELINE 0x0501 +#define UATT_TELEPHONE 0x0502 +#define UATT_DOWNLINEPHONE 0x0503 +/* external terminal types */ +#define UATE_UNDEFINED 0x0600 +#define UATE_ANALOGCONN 0x0601 +#define UATE_DIGITALAUIFC 0x0602 +#define UATE_LINECONN 0x0603 +#define UATE_LEGACYCONN 0x0604 +#define UATE_SPDIF 0x0605 +#define UATE_1394DA 0x0606 +#define UATE_1394DV 0x0607 +/* embedded function terminal types */ +#define UATF_UNDEFINED 0x0700 +#define UATF_CALIBNOISE 0x0701 +#define UATF_EQUNOISE 0x0702 +#define UATF_CDPLAYER 0x0703 +#define UATF_DAT 0x0704 +#define UATF_DCC 0x0705 +#define UATF_MINIDISK 0x0706 +#define UATF_ANALOGTAPE 0x0707 +#define UATF_PHONOGRAPH 0x0708 +#define UATF_VCRAUDIO 0x0709 +#define UATF_VIDEODISCAUDIO 0x070a +#define UATF_DVDAUDIO 0x070b +#define UATF_TVTUNERAUDIO 0x070c +#define UATF_SATELLITE 0x070d +#define UATF_CABLETUNER 0x070e +#define UATF_DSS 0x070f +#define UATF_RADIORECV 0x0710 +#define UATF_RADIOXMIT 0x0711 +#define UATF_MULTITRACK 0x0712 +#define UATF_SYNTHESIZER 0x0713 + + +#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 + +#define UA_PROC_MASK(n) (1<< ((n)-1)) +#define PROCESS_UNDEFINED 0 +#define XX_ENABLE_CONTROL 1 +#define UPDOWNMIX_PROCESS 1 +#define UD_ENABLE_CONTROL 1 +#define UD_MODE_SELECT_CONTROL 2 +#define DOLBY_PROLOGIC_PROCESS 2 +#define DP_ENABLE_CONTROL 1 +#define DP_MODE_SELECT_CONTROL 2 +#define P3D_STEREO_EXTENDER_PROCESS 3 +#define P3D_ENABLE_CONTROL 1 +#define P3D_SPACIOUSNESS_CONTROL 2 +#define REVERBATION_PROCESS 4 +#define RV_ENABLE_CONTROL 1 +#define RV_LEVEL_CONTROL 2 +#define RV_TIME_CONTROL 3 +#define RV_FEEDBACK_CONTROL 4 +#define CHORUS_PROCESS 5 +#define CH_ENABLE_CONTROL 1 +#define CH_LEVEL_CONTROL 2 +#define CH_RATE_CONTROL 3 +#define CH_DEPTH_CONTROL 4 +#define DYN_RANGE_COMP_PROCESS 6 +#define DR_ENABLE_CONTROL 1 +#define DR_COMPRESSION_RATE_CONTROL 2 +#define DR_MAXAMPL_CONTROL 3 +#define DR_THRESHOLD_CONTROL 4 +#define DR_ATTACK_TIME_CONTROL 5 +#define DR_RELEASE_TIME_CONTROL 6 + diff --git a/sys/modules/sound/driver/Makefile b/sys/modules/sound/driver/Makefile index 4e71f46..fb748ba 100644 --- a/sys/modules/sound/driver/Makefile +++ b/sys/modules/sound/driver/Makefile @@ -3,6 +3,6 @@ SUBDIR = als4000 ad1816 cmi cs4281 csa ds1 emu10k1 es137x ess SUBDIR += fm801 ich maestro maestro3 mss neomagic sb16 sb8 sbc solo SUBDIR += t4dwave via82c686 vibes -SUBDIR += driver +SUBDIR += driver uaudio .include <bsd.subdir.mk> diff --git a/sys/modules/sound/driver/uaudio/Makefile b/sys/modules/sound/driver/uaudio/Makefile new file mode 100644 index 0000000..f04d883 --- /dev/null +++ b/sys/modules/sound/driver/uaudio/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../../dev/sound/usb + +KMOD= snd_uaudio +SRCS= device_if.h bus_if.h opt_usb.h vnode_if.h isa_if.h +SRCS+= uaudio.c uaudio_pcm.c + +.include <bsd.kmod.mk> |