summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornsayer <nsayer@FreeBSD.org>2002-07-21 17:28:50 +0000
committernsayer <nsayer@FreeBSD.org>2002-07-21 17:28:50 +0000
commit25022332651db553076ec3b7404f57f158c9967c (patch)
tree2c415cdeffaee4bbbc5bd63b47ecdfb14bbf9b0c
parentb9937742c3d6160f131a6a6ffebc71fdb2e351aa (diff)
downloadFreeBSD-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/files2
-rw-r--r--sys/dev/sound/usb/uaudio.c2912
-rw-r--r--sys/dev/sound/usb/uaudio.h49
-rw-r--r--sys/dev/sound/usb/uaudio_pcm.c375
-rw-r--r--sys/dev/sound/usb/uaudioreg.h384
-rw-r--r--sys/modules/sound/driver/Makefile2
-rw-r--r--sys/modules/sound/driver/uaudio/Makefile9
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>
OpenPOWER on IntegriCloud