new USB audio class v2.0 driver

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

new USB audio class v2.0 driver

Alexandre Ratchov-2
Hi,

Here's a new driver for both USB audio class (UAC) v1.0 and v2.0
devices, it would replace the current one. It focuses on reliability
and proper synchronization, including in low-latency configurations.

Our current USB sub-system has limitations that currently allow only
the following combinations:
 - USB v2 devices on ehci(4) only
 - USB v1 devices on uhci(4), ohci(4) or ehci(4) root hub only

If you have an audio device that is class compliant (aka vendor claims
it's "driverless" on MacOS) *and* one of the above host/hub/device
combinations then I'd be very interested in test reports. Especially
I'd like to know about possible regressions.

To test, apply this diff, rebuild the kernel and set:

        sndiod_flags=-f rsnd/1

in /etc/rc.conf.local (assuming your uaudio device shows as uaudio1 in
dmesg). Then do your regular audio work, let me know how it works and
send me output of:

        dmesg
        mixerctl -v -f /dev/mixer1

If something is broken, please check if this is a regression.

Known bugs and limitations:

 - Our USB stack requires at least 3 outstanding isochronous transfers
   to work. So the audio ring buffer size must be at least 3
   blocks. If you're using sndiod's -bz options, ensuire the -b
   argument is at least 3 times the -z argument.

 - Fractional frames are not used on purpose (except on devices using
   a feedback pipe for synchronization). This means that only sample
   rates multiple of 8kHz are reachable. Most azalia(4)'s do so as
   well.

 - UAC v2.0 devices with multiple clock sources, clock multipliers and
   rate converters would use the vendor defaults (by lack of hardware
   to test).

 - Certain mixerctl(1) control names would be too long and are
   truncated. This is hard to fix without changing the mixer(4)
   interface.

 - Controls of selectors or mixers are not exposed in the mixer(4)
   interface, so the device will use vendor defaults there. Such
   controls are very rare, it seems that MacOS & Windows don't expose
   them either.

Thanks in advance.

-- Alexandre

Index: uaudio.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uaudio.c,v
retrieving revision 1.133
diff -u -p -u -p -r1.133 uaudio.c
--- uaudio.c 31 Aug 2018 07:18:18 -0000 1.133
+++ uaudio.c 31 Dec 2018 15:17:23 -0000
@@ -1,3394 +1,4037 @@
-/* $OpenBSD: uaudio.c,v 1.133 2018/08/31 07:18:18 miko Exp $ */
-/* $NetBSD: uaudio.c,v 1.90 2004/10/29 17:12:53 kent Exp $ */
-
+/* $OpenBSD$ */
 /*
- * Copyright (c) 1999 The NetBSD Foundation, Inc.
- * All rights reserved.
+ * Copyright (c) 2018 Alexandre Ratchov <[hidden email]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Lennart Augustsson ([hidden email]) 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.
- *
- * 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.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-
 /*
- * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
- *                  http://www.usb.org/developers/devclass_docs/frmts10.pdf
- *                  http://www.usb.org/developers/devclass_docs/termt10.pdf
+ * The USB Audio Class (UAC) defines what is an audio device and how
+ * to use it. There are two versions of the UAC: v1.0 and v2.0. They
+ * are not compatible with each other but they are close enough to
+ * attempt to have the same driver for both.
+ *
  */
-
 #include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/malloc.h>
+#include <sys/types.h>
 #include <sys/device.h>
-#include <sys/ioctl.h>
-#include <sys/tty.h>
+#include <sys/errno.h>
 #include <sys/fcntl.h>
-#include <sys/selinfo.h>
-#include <sys/poll.h>
-
-#include <machine/bus.h>
-
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/time.h>
 #include <sys/audioio.h>
+#include <machine/bus.h>
 #include <dev/audio_if.h>
-
 #include <dev/usb/usb.h>
-#include <dev/usb/usbdevs.h>
 #include <dev/usb/usbdi.h>
-#include <dev/usb/usbdi_util.h>
 #include <dev/usb/usbdivar.h>
+#include "ehci.h"
+#include "xhci.h"
 
-#include <dev/usb/uaudioreg.h>
-
-/* #define UAUDIO_DEBUG */
 #ifdef UAUDIO_DEBUG
-#define DPRINTF(x) do { if (uaudiodebug) printf x; } while (0)
-#define DPRINTFN(n,x) do { if (uaudiodebug>(n)) printf x; } while (0)
-int uaudiodebug = 0;
+#define DPRINTF(...) \
+ do { \
+ if (uaudio_debug) \
+ printf(__VA_ARGS__); \
+ } while (0)
 #else
-#define DPRINTF(x)
-#define DPRINTFN(n,x)
+#define DPRINTF(...) do {} while(0)
 #endif
 
-#define UAUDIO_NCHANBUFS 3 /* number of outstanding request */
-#define UAUDIO_MIN_FRAMES 2 /* ms of sound in each request */
-#define UAUDIO_MAX_FRAMES 16
-#define UAUDIO_NSYNCBUFS 3 /* number of outstanding sync requests */
-
-#define UAUDIO_MAX_ALTS  32 /* max alt settings allowed by driver */
-
-#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_SELECTOR 5
-#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_int8_t class;
- char ctlname[MAX_AUDIO_DEV_LEN];
- char *ctlunit;
-};
-#define MAKE(h,l) (((h) << 8) | (l))
+#define DEVNAME(sc) ((sc)->dev.dv_xname)
 
-struct as_info {
- u_int8_t alt;
- u_int8_t encoding;
- u_int8_t attributes; /* Copy of bmAttributes of
-     * usb_audio_streaming_endpoint_descriptor
-     */
- struct usbd_interface *ifaceh;
- const usb_interface_descriptor_t *idesc;
- const struct usb_endpoint_descriptor_audio *edesc;
- const struct usb_endpoint_descriptor_audio *edesc1;
- const struct usb_audio_streaming_type1_descriptor *asf1desc;
- int sc_busy; /* currently used */
-};
+/*
+ * Isochronous endpoint usage (XXX: these belong to sys/usb.h).
+ */
+#define UE_ISO_USAGE 0x30
+#define  UE_ISO_USAGE_DATA 0x00
+#define  UE_ISO_USAGE_FEEDBACK 0x10
+#define  UE_ISO_USAGE_IMPL 0x20
+#define UE_GET_ISO_USAGE(a) ((a) & UE_ISO_USAGE)
+
+/*
+ * Max length of unit names
+ */
+#define UAUDIO_NAMEMAX MAX_AUDIO_DEV_LEN
+
+/*
+ * USB audio class versions
+ */
+#define UAUDIO_V1 0x100
+#define UAUDIO_V2 0x200
+
+/*
+ * AC class-specific descriptor interface sub-type
+ */
+#define UAUDIO_AC_HEADER 0x1
+#define UAUDIO_AC_INPUT 0x2
+#define UAUDIO_AC_OUTPUT 0x3
+#define UAUDIO_AC_MIXER 0x4
+#define UAUDIO_AC_SELECTOR 0x5
+#define UAUDIO_AC_FEATURE 0x6
+#define UAUDIO_AC_EFFECT 0x7
+#define UAUDIO_AC_PROCESSING 0x8
+#define UAUDIO_AC_EXTENSION 0x9
+#define UAUDIO_AC_CLKSRC 0xa
+#define UAUDIO_AC_CLKSEL 0xb
+#define UAUDIO_AC_CLKMULT 0xc
+#define UAUDIO_AC_RATECONV 0xd
+
+/*
+ * AS class-specific interface sub-types
+ */
+#define UAUDIO_AS_GENERAL 0x1
+#define UAUDIO_AS_FORMAT 0x2
+
+/*
+ * AS class-specific endpoint sub-type
+ */
+#define UAUDIO_EP_GENERAL 0x1
+
+/*
+ * UAC v1 formats, wFormatTag is an enum
+ */
+#define UAUDIO_V1_FMT_PCM 0x1
+#define UAUDIO_V1_FMT_PCM8 0x2
+#define UAUDIO_V1_FMT_FLOAT 0x3
+#define UAUDIO_V1_FMT_ALAW 0x4
+#define UAUDIO_V1_FMT_MULAW 0x5
+
+/*
+ * UAC v2 formats, bmFormats is a bitmap
+ */
+#define UAUDIO_V2_FMT_PCM 0x01
+#define UAUDIO_V2_FMT_PCM8 0x02
+#define UAUDIO_V2_FMT_FLOAT 0x04
+#define UAUDIO_V2_FMT_ALAW 0x08
+#define UAUDIO_V2_FMT_MULAW 0x10
+
+/*
+ * AC requests
+ */
+#define UAUDIO_V1_REQ_SET_CUR 0x01
+#define UAUDIO_V1_REQ_SET_MIN 0x02
+#define UAUDIO_V1_REQ_SET_MAX 0x03
+#define UAUDIO_V1_REQ_SET_RES 0x04
+#define UAUDIO_V1_REQ_GET_CUR 0x81
+#define UAUDIO_V1_REQ_GET_MIN 0x82
+#define UAUDIO_V1_REQ_GET_MAX 0x83
+#define UAUDIO_V1_REQ_GET_RES 0x84
+#define UAUDIO_V2_REQ_CUR 1
+#define UAUDIO_V2_REQ_RANGES 2
+
+/*
+ * AC request "selector control"
+ */
+#define UAUDIO_V2_REQSEL_CLKFREQ 1
+#define UAUDIO_V2_REQSEL_CLKSEL 1
+
+/*
+ * AS class-specific endpoint attributes
+ */
+#define UAUDIO_EP_FREQCTL 0x01
+
+/*
+ * AC feature control selectors (aka wValue in the request)
+ */
+#define UAUDIO_REQSEL_MUTE 0x01
+#define UAUDIO_REQSEL_VOLUME 0x02
+#define UAUDIO_REQSEL_BASS 0x03
+#define UAUDIO_REQSEL_MID 0x04
+#define UAUDIO_REQSEL_TREBLE 0x05
+#define UAUDIO_REQSEL_EQ 0x06
+#define UAUDIO_REQSEL_AGC 0x07
+#define UAUDIO_REQSEL_DELAY 0x08
+#define UAUDIO_REQSEL_BASSBOOST 0x09
+#define UAUDIO_REQSEL_LOUDNESS 0x0a
+#define UAUDIO_REQSEL_GAIN 0x0b
+#define UAUDIO_REQSEL_GAINPAD 0x0c
+#define UAUDIO_REQSEL_PHASEINV 0x0d
 
-struct chan {
- void (*intr)(void *); /* DMA completion intr handler */
- void *arg; /* arg for intr() */
- struct usbd_pipe *pipe;
- struct usbd_pipe *sync_pipe;
-
- u_int sample_size;
- u_int sample_rate;
- u_int bytes_per_frame;
- u_int max_bytes_per_frame;
- u_int fraction; /* fraction/frac_denom is the extra samples/frame */
- u_int frac_denom; /* denominator for fractional samples */
- u_int residue; /* accumulates the fractional samples */
- u_int nframes; /* # of frames per transfer */
- u_int nsync_frames; /* # of frames per sync transfer */
- u_int usb_fps;
- u_int maxpktsize;
- u_int reqms; /* usb request data duration, in ms */
- u_int hi_speed;
-
- u_char *start; /* upper layer buffer start */
- u_char *end; /* upper layer buffer end */
- u_char *cur; /* current position in upper layer buffer */
- int blksize; /* chunk size to report up */
- int transferred; /* transferred bytes not reported up */
-
- int altidx; /* currently used altidx */
-
- int curchanbuf;
- int cursyncbuf;
-
- struct chanbuf {
- struct chan *chan;
- struct usbd_xfer *xfer;
- u_char *buffer;
- u_int16_t sizes[UAUDIO_MAX_FRAMES];
- u_int16_t offsets[UAUDIO_MAX_FRAMES];
- u_int16_t size;
- } chanbufs[UAUDIO_NCHANBUFS];
-
- struct syncbuf {
- struct chan *chan;
- struct usbd_xfer *xfer;
- u_char *buffer;
- u_int16_t sizes[UAUDIO_MAX_FRAMES];
- u_int16_t offsets[UAUDIO_MAX_FRAMES];
- u_int16_t size;
- } syncbufs[UAUDIO_NSYNCBUFS];
+/*
+ * Endpoint (UAC v1) or clock-source unit (UAC v2) sample rate control
+ */
+#define UAUDIO_REQSEL_RATE 0x01
+
+/*
+ * Number of fixed sample rates we support
+ */
+#define UAUDIO_NRATES (sizeof(uaudio_rates) / sizeof(uaudio_rates[0]))
 
- struct uaudio_softc *sc; /* our softc */
+/*
+ * read/write pointers for secure sequencial access of binary data,
+ * ex. usb descriptors, tables and alike. Bytes are read using the
+ * read pointer up to the write pointer.
+ */
+struct uaudio_blob {
+ unsigned char *rptr, *wptr;
 };
 
-#define UAUDIO_FLAG_BAD_AUDIO 0x0001 /* claims audio class, but isn't */
-#define UAUDIO_FLAG_NO_FRAC 0x0002 /* don't use fractional samples */
-#define UAUDIO_FLAG_NO_XU 0x0004 /* has broken extension unit */
-#define UAUDIO_FLAG_BAD_ADC 0x0008 /* bad audio spec version number */
-#define UAUDIO_FLAG_VENDOR_CLASS 0x0010 /* claims vendor class but works */
-#define UAUDIO_FLAG_DEPENDENT 0x0020 /* play and record params must equal */
-#define UAUDIO_FLAG_EMU0202 0x0040
-#define UAUDIO_FLAG_BAD_ADC_LEN 0x0080 /* bad audio control descriptor size */
-
-struct uaudio_devs {
- struct usb_devno uv_dev;
- int flags;
-} uaudio_devs[] = {
- { { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_UR22 },
- UAUDIO_FLAG_VENDOR_CLASS },
- { { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70 },
- UAUDIO_FLAG_BAD_ADC } ,
- { { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495 },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4_GSM },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4_CDMA },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_6 },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH_2G },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH_3G },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH_4G },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPAD },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPAD2 },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_CREATIVE, USB_PRODUCT_CREATIVE_EMU0202 },
- UAUDIO_FLAG_VENDOR_CLASS | UAUDIO_FLAG_EMU0202 |
- UAUDIO_FLAG_DEPENDENT },
- { { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502 },
- UAUDIO_FLAG_NO_XU | UAUDIO_FLAG_BAD_ADC },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMNBDLX },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRONB },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRO4K },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMZOOM },
- UAUDIO_FLAG_BAD_AUDIO },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC200 },
- UAUDIO_FLAG_BAD_ADC_LEN },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC210 },
- UAUDIO_FLAG_BAD_ADC_LEN },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC250 },
- UAUDIO_FLAG_BAD_ADC_LEN },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC270 },
- UAUDIO_FLAG_BAD_ADC_LEN },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC310 },
- UAUDIO_FLAG_BAD_ADC_LEN },
- { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC500 },
- UAUDIO_FLAG_BAD_ADC_LEN },
- { { USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1 },
- UAUDIO_FLAG_NO_FRAC }
+/*
+ * Ranges of integer values used to represent controls values and
+ * sample frequencies.
+ */
+struct uaudio_ranges {
+ unsigned int nval;
+ struct uaudio_ranges_el {
+ struct uaudio_ranges_el *next;
+ int min, max, res;
+ } *el;
 };
-#define uaudio_lookup(v, p) \
- ((struct uaudio_devs *)usb_lookup(uaudio_devs, v, p))
 
 struct uaudio_softc {
- struct device sc_dev; /* base device */
- struct usbd_device *sc_udev; /* USB device */
- int sc_ac_iface; /* Audio Control interface */
- struct chan sc_playchan; /* play channel */
- struct chan sc_recchan; /* record channel */
- int sc_nullalt;
- int sc_audio_rev;
- struct as_info *sc_alts; /* alternate settings */
- int sc_nalts; /* # of alternate settings */
- int sc_altflags;
-#define HAS_8 0x01
-#define HAS_16 0x02
-#define HAS_8U 0x04
-#define HAS_ALAW 0x08
-#define HAS_MULAW 0x10
-#define UA_NOFRAC 0x20 /* don't do sample rate adjustment */
-#define HAS_24 0x40
- int sc_mode; /* play/record capability */
- struct mixerctl *sc_ctls; /* mixer controls */
- int sc_nctls; /* # of mixer controls */
- int sc_quirks;
-};
+ struct device dev;
+ struct usbd_device *udev;
+ int version;
 
-struct terminal_list {
- int size;
- uint16_t terminals[1];
-};
-#define TERMINAL_LIST_SIZE(N) (offsetof(struct terminal_list, terminals) \
- + sizeof(uint16_t) * (N))
+ /*
+ * UAC exposes the device as a circuit of units. Input and
+ * output jacks are known as terminal units, others are
+ * processing units. The purpose of this driver is to give
+ * them reasonable names and expose them as mixer(1)
+ * controls. Control names are derived from the type of the
+ * unit and its role in the circuit.
+ *
+ * UAC v2.0 exposes also the clock circuitry using units, so
+ * selecting the sample rate also involves units usage.
+ */
+ struct uaudio_unit {
+ struct uaudio_unit *unit_next, *src_next, *dst_next;
+ struct uaudio_unit *src_list, *dst_list;
+ char name[UAUDIO_NAMEMAX];
+ unsigned int nch;
+ int type, id;
+
+ /* clock source, if a terminal or selector */
+ struct uaudio_unit *clock;
+
+ /* sample rates, if this is a clock source */
+ struct uaudio_ranges rates;
+
+ /* mixer(4) bits */
+#define UAUDIO_CLASS_REC 0
+#define UAUDIO_CLASS_OUT 1
+#define UAUDIO_CLASS_IN 2
+#define UAUDIO_CLASS_COUNT 3
+ int mixer_class;
+ struct uaudio_mixent {
+ struct uaudio_mixent *next;
+ char *fname;
+#define UAUDIO_MIX_SW 0
+#define UAUDIO_MIX_NUM 1
+#define UAUDIO_MIX_ENUM 2
+ int type;
+ int chan;
+ int req_sel;
+ struct uaudio_ranges ranges;
+ } *mixent_list;
+ } *unit_list;
+
+ /*
+ * Current clock, UAC v2.0 only
+ */
+ struct uaudio_unit *clock;
+
+ /*
+ * When unique names are needed, they are generated using a
+ * base string suffixed with a number. Ex. "spkr5". The
+ * following structure is used to keep track of strings we
+ * allocated.
+ */
+ struct uaudio_name {
+ struct uaudio_name *next;
+ char *templ;
+ unsigned int unit;
+ } *names;
+
+ /*
+ * Audio streaming (AS) alternate settings, i.e. stream format
+ * and USB-related parameters to use it.
+ */
+ struct uaudio_alt {
+ struct uaudio_alt *next;
+ int ifnum, altnum;
+ int mode; /* one of AUMODE_{RECORD,PLAY} */
+ int sync; /* is sync endpoint used */
+ int data_addr; /* data endpoint address */
+ int sync_addr; /* feedback endpoint address */
+ int maxpkt; /* max supported bytes per frame */
+ int fps; /* USB (micro-)frames per second */
+ int bps, bits, nch; /* audio encoding */
+ int v1_rates; /* if UAC 1.0, bitmap of rates */
+ } *alts;
+
+ /*
+ * Audio parameters: play and record stream formats usable
+ * together.
+ */
+ struct uaudio_params {
+ struct uaudio_params *next;
+ struct uaudio_alt *palt, *ralt;
+ int v1_rates;
+ } *params_list, *params;
+
+ /*
+ * One direction audio stream, aka "DMA" in progress
+ */
+ struct uaudio_stream {
+#define UAUDIO_NXFERS 3
+ struct uaudio_xfer {
+ struct usbd_xfer *usb_xfer;
+ unsigned char *buf;
+ uint16_t *sizes;
+ unsigned int size; /* bytes requested */
+ unsigned int nframes; /* frames requested */
+ } data_xfers[UAUDIO_NXFERS], sync_xfers[UAUDIO_NXFERS];
+
+ unsigned int remain; /* samples 16-bit fixed-point */
+ unsigned int spf; /* avg samples per frame */
+ unsigned int spf_min, spf_max; /* allowed boundaries */
+
+ unsigned int maxpkt;
+
+ unsigned int nframes;
+ unsigned int nframes_min, nframes_max;
+
+ unsigned int data_nextxfer, sync_nextxfer;
+ struct usbd_pipe *data_pipe;
+ struct usbd_pipe *sync_pipe;
+ void (*intr)(void *);
+ void *arg;
+ unsigned char *ring_start, *ring_end, *ring_pos;
+ int ring_offs, ring_blksz, safe_blksz;
 
-struct io_terminal {
- union {
- const usb_descriptor_t *desc;
- const struct usb_audio_input_terminal *it;
- const struct usb_audio_output_terminal *ot;
- const struct usb_audio_mixer_unit *mu;
- const struct usb_audio_selector_unit *su;
- const struct usb_audio_feature_unit *fu;
- const struct usb_audio_processing_unit *pu;
- const struct usb_audio_extension_unit *eu;
- } d;
- int inputs_size;
- struct terminal_list **inputs; /* list of source input terminals */
- struct terminal_list *output; /* list of destination output terminals */
- int direct; /* directly connected to an output terminal */
+ /*
+ * Device clock may take some time to lock during which
+ * we'd receive null packets for which we need to
+ * generate silence. We consider that the device clock
+ * is locked as soon as we receive the first non-null
+ * packet.
+ *
+ * Certain devices (UAC v2 async recording) seem to
+ * generate rare null microframes, but this seems to be
+ * caused bogus^sophisticated fractional sample
+ * calculations, so we shouldn't insert silence in this
+ * case.
+ */
+ int locked;
+ } pstream, rstream;
+
+ int ctl_ifnum; /* aka AC interface */
+
+ int rate; /* current sample rate */
+ int mode; /* open() mode */
+ int trigger_mode; /* trigger() mode */
+
+ unsigned int ufps; /* USB frames per second */
+ unsigned int sync_pktsz; /* size of sync packet */
+ unsigned int nsamp_per_ufr; /* default samples per USB frame */
 };
 
-#define UAC_OUTPUT 0
-#define UAC_INPUT 1
-#define UAC_EQUAL 2
-#define UAC_RECORD 3
-#define UAC_NCLASSES 4
+int uaudio_match(struct device *, void *, void *);
+void uaudio_attach(struct device *, struct device *, void *);
+int uaudio_detach(struct device *, int);
+
+int uaudio_open(void *, int);
+void uaudio_close(void *);
+int uaudio_set_params(void *, int, int, struct audio_params *,
+    struct audio_params *);
+int uaudio_round_blocksize(void *, int);
+int uaudio_trigger_output(void *, void *, void *, int,
+    void (*)(void *), void *, struct audio_params *);
+int uaudio_trigger_input(void *, void *, void *, int,
+    void (*)(void *), void *, struct audio_params *);
+int uaudio_halt_output(void *);
+int uaudio_halt_input(void *);
+int uaudio_query_devinfo(void *, struct mixer_devinfo *);
+int uaudio_get_port(void *, struct mixer_ctrl *);
+int uaudio_set_port(void *, struct mixer_ctrl *);
+int uaudio_get_props(void *);
+
+int uaudio_process_unit(struct uaudio_softc *,
+    struct uaudio_unit *, int,
+    struct uaudio_blob,
+    struct uaudio_unit **);
+
+void uaudio_pdata_intr(struct usbd_xfer *, void *, usbd_status);
+void uaudio_rdata_intr(struct usbd_xfer *, void *, usbd_status);
+void uaudio_psync_intr(struct usbd_xfer *, void *, usbd_status);
+
 #ifdef UAUDIO_DEBUG
-const char *uac_names[] = {
- AudioCoutputs, AudioCinputs, AudioCequalization, AudioCrecord,
-};
+char *uaudio_isoname(int isotype);
+char *uaudio_modename(int mode);
+char *uaudio_usagename(int usage);
+void uaudio_rates_print(int rates);
+void uaudio_ranges_print(struct uaudio_ranges *r);
+void uaudio_print_unit(struct uaudio_softc *sc, struct uaudio_unit *u);
+void uaudio_mixer_print(struct uaudio_softc *sc);
+void uaudio_conf_print(struct uaudio_softc *sc);
+
+/*
+ * 0 - nothing, as if UAUDIO_DEBUG isn't defined
+ * 1 - initialisations & setup
+ * 2 - transfers
+ */
+int uaudio_debug = 1;
 #endif
 
-usbd_status uaudio_identify_ac
- (struct uaudio_softc *, const usb_config_descriptor_t *);
-usbd_status uaudio_identify_as
- (struct uaudio_softc *, const usb_config_descriptor_t *);
-usbd_status uaudio_process_as
- (struct uaudio_softc *, const char *, int *, int,
- const usb_interface_descriptor_t *);
-
-void uaudio_add_alt(struct uaudio_softc *, const struct as_info *);
-
-const usb_interface_descriptor_t *uaudio_find_iface
- (const char *, int, int *, int, int);
-
-void uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *);
-uByte uaudio_get_cluster_nchan
- (int, const struct io_terminal *);
-void uaudio_add_input
- (struct uaudio_softc *, const struct io_terminal *, int);
-void uaudio_add_output
- (struct uaudio_softc *, const struct io_terminal *, int);
-void uaudio_add_mixer
- (struct uaudio_softc *, const struct io_terminal *, int);
-void uaudio_add_selector
- (struct uaudio_softc *, const struct io_terminal *, int);
-#ifdef UAUDIO_DEBUG
-const char *uaudio_get_terminal_name(int);
-#endif
-int uaudio_determine_class
- (const struct io_terminal *, struct mixerctl *);
-const char *uaudio_feature_name
- (const struct io_terminal *, struct mixerctl *);
-void uaudio_add_feature
- (struct uaudio_softc *, const struct io_terminal *, int);
-void uaudio_add_processing_updown
- (struct uaudio_softc *, const struct io_terminal *, int);
-void uaudio_add_processing
- (struct uaudio_softc *, const struct io_terminal *, int);
-void uaudio_add_extension
- (struct uaudio_softc *, const struct io_terminal *, int);
-struct terminal_list *uaudio_merge_terminal_list
- (const struct io_terminal *);
-struct terminal_list *uaudio_io_terminaltype
- (int, struct io_terminal *, int);
-usbd_status uaudio_identify
- (struct uaudio_softc *, const usb_config_descriptor_t *);
-
-int uaudio_signext(int, int);
-int uaudio_unsignext(int, int);
-int uaudio_value2bsd(struct mixerctl *, int);
-int uaudio_bsd2value(struct mixerctl *, int);
-int uaudio_get(struct uaudio_softc *, int, int, int, int, int);
-int uaudio_ctl_get
- (struct uaudio_softc *, int, struct mixerctl *, int);
-void uaudio_set
- (struct uaudio_softc *, int, int, int, int, int, int);
-void uaudio_ctl_set
- (struct uaudio_softc *, int, struct mixerctl *, int, int);
-
-usbd_status uaudio_set_speed(struct uaudio_softc *, int, u_int);
-void uaudio_set_speed_emu0202(struct chan *ch);
-
-usbd_status uaudio_chan_open(struct uaudio_softc *, struct chan *);
-void uaudio_chan_close(struct uaudio_softc *, struct chan *);
-usbd_status uaudio_chan_alloc_buffers
- (struct uaudio_softc *, struct chan *);
-void uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *);
-void uaudio_chan_init
- (struct chan *, int, int, const struct audio_params *);
-void uaudio_chan_set_param(struct chan *, u_char *, u_char *, int);
-void uaudio_chan_ptransfer(struct chan *);
-void uaudio_chan_pintr
- (struct usbd_xfer *, void *, usbd_status);
-void uaudio_chan_psync_transfer(struct chan *);
-void uaudio_chan_psync_intr
- (struct usbd_xfer *, void *, usbd_status);
-
-void uaudio_chan_rtransfer(struct chan *);
-void uaudio_chan_rintr
- (struct usbd_xfer *, void *, usbd_status);
-
-int uaudio_open(void *, int);
-void uaudio_close(void *);
-void uaudio_get_minmax_rates
- (int, const struct as_info *, const struct audio_params *,
- int, int, int, u_long *, u_long *);
-int uaudio_match_alt_rate(void *, int, int);
-int uaudio_match_alt(void *, struct audio_params *, int);
-int uaudio_set_params
- (void *, int, int, struct audio_params *, struct audio_params *);
-int uaudio_round_blocksize(void *, int);
-int uaudio_trigger_output
- (void *, void *, void *, int, void (*)(void *), void *,
- struct audio_params *);
-int uaudio_trigger_input
- (void *, void *, void *, int, void (*)(void *), void *,
- struct audio_params *);
-int uaudio_halt_in_dma(void *);
-int uaudio_halt_out_dma(void *);
-int uaudio_mixer_set_port(void *, mixer_ctrl_t *);
-int uaudio_mixer_get_port(void *, mixer_ctrl_t *);
-int uaudio_query_devinfo(void *, mixer_devinfo_t *);
-int uaudio_get_props(void *);
+extern struct cfdriver ehci_cd;
+extern struct cfdriver xhci_cd;
 
-struct audio_hw_if uaudio_hw_if = {
- uaudio_open,
- uaudio_close,
- uaudio_set_params,
- uaudio_round_blocksize,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- uaudio_halt_out_dma,
- uaudio_halt_in_dma,
- NULL,
- NULL,
- uaudio_mixer_set_port,
- uaudio_mixer_get_port,
- uaudio_query_devinfo,
- NULL,
- NULL,
- NULL,
- uaudio_get_props,
- uaudio_trigger_output,
- uaudio_trigger_input
+struct cfdriver uaudio_cd = {
+ NULL, "uaudio", DV_DULL
 };
 
-int uaudio_match(struct device *, void *, void *);
-void uaudio_attach(struct device *, struct device *, void *);
-int uaudio_detach(struct device *, int);
-
-struct cfdriver uaudio_cd = {
- NULL, "uaudio", DV_DULL
-};
-
 const struct cfattach uaudio_ca = {
  sizeof(struct uaudio_softc), uaudio_match, uaudio_attach, uaudio_detach
 };
 
-int
-uaudio_match(struct device *parent, void *match, void *aux)
-{
- struct usb_attach_arg *uaa = aux;
- usb_interface_descriptor_t *id;
- const usb_interface_descriptor_t *cd_id;
- usb_config_descriptor_t *cdesc;
- struct uaudio_devs *quirk;
- const char *buf;
- int flags = 0, size, offs;
-
- if (uaa->iface == NULL || uaa->device == NULL)
- return (UMATCH_NONE);
-
- quirk = uaudio_lookup(uaa->vendor, uaa->product);
- if (quirk)
- flags = quirk->flags;
-
- if (flags & UAUDIO_FLAG_BAD_AUDIO)
- return (UMATCH_NONE);
-
- id = usbd_get_interface_descriptor(uaa->iface);
- if (id == NULL)
- return (UMATCH_NONE);
-
- if (!(id->bInterfaceClass == UICLASS_AUDIO ||
-    ((flags & UAUDIO_FLAG_VENDOR_CLASS) &&
-    id->bInterfaceClass == UICLASS_VENDOR)))
- return (UMATCH_NONE);
+struct audio_hw_if uaudio_hw_if = {
+ uaudio_open, /* open */
+ uaudio_close, /* close */
+ uaudio_set_params, /* set_params */
+ uaudio_round_blocksize, /* round_blocksize */
+ NULL, /* commit_settings */
+ NULL, /* init_output */
+ NULL, /* init_input */
+ NULL, /* start_output */
+ NULL, /* start_input */
+ uaudio_halt_output, /* halt_output */
+ uaudio_halt_input, /* halt_input */
+ NULL, /* speaker_ctl */
+ NULL, /* setfd */
+ uaudio_set_port, /* set_port */
+ uaudio_get_port, /* get_port */
+ uaudio_query_devinfo, /* query_devinfo */
+ NULL, /* malloc, we use bounce buffers :'( */
+ NULL, /* free */
+ NULL, /* round_buffersize */
+ uaudio_get_props, /* get_props */
+ uaudio_trigger_output, /* trigger_output */
+ uaudio_trigger_input /* trigger_input */
+};
 
- if (id->bInterfaceSubClass != UISUBCLASS_AUDIOCONTROL)
- return (UMATCH_NONE);
+/*
+ * To keep things simple, we support only the following rates, we
+ * don't care about continuous sample rates or other "advanced"
+ * features which complicate implementation.
+ *
+ * Given that USB2.0 frame rate is 8000 fps, if we require a fixed
+ * number of samples per frame, only sample rates multiple of 8000Hz
+ * can be used. To support other rates, we've to use variable number
+ * of samples per frame such that it achives the desired sample rate
+ * over one transfer.
+ */
+int uaudio_rates[] = {
+ 8000,
+#ifdef UAUDIO_USE_FRAC
+ 11025,
+#endif
+ 12000,
+ 16000,
+#ifdef UAUDIO_USE_FRAC
+ 22050,
+#endif
+ 24000,
+ 32000,
+#ifdef UAUDIO_USE_FRAC
+ 44100,
+#endif
+ 48000,
+ 64000,
+#ifdef UAUDIO_USE_FRAC
+ 88200,
+#endif
+ 96000,
+ 128000,
+#ifdef UAUDIO_USE_FRAC
+ 176400,
+#endif
+ 192000
+};
 
- cdesc = usbd_get_config_descriptor(uaa->device);
- if (cdesc == NULL)
- return (UMATCH_NONE);
+/*
+ * Convert 8, 16, or 24-bit signed value to an int by expanding the
+ * sign bit.
+ */
+int
+uaudio_sign_expand(unsigned int val, int opsize)
+{
+ unsigned int s;
 
- size = UGETW(cdesc->wTotalLength);
- buf = (const char *)cdesc;
+ s = 1 << (8 * opsize - 1);
+ return (val ^ s) - s;
+}
 
- offs = 0;
- cd_id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM,
-    flags);
- if (cd_id == NULL)
- return (UMATCH_NONE);
-
- offs = 0;
- cd_id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOCONTROL,
-    flags);
- if (cd_id == NULL)
- return (UMATCH_NONE);
+/*
+ * XXX: there are at least 3 such routines in the kernel, but we're
+ * not supposed to need this. Fix this by using a less stupid audio(9)
+ * round_blocksize() semantics.
+ */
+unsigned int
+uaudio_gcd(unsigned int a, unsigned int b)
+{
+ unsigned int r;
 
- return (UMATCH_VENDOR_PRODUCT_CONF_IFACE);
+ while (b > 0) {
+ r = a % b;
+ a = b;
+ b = r;
+ }
+ return a;
 }
 
-void
-uaudio_attach(struct device *parent, struct device *self, void *aux)
+int
+uaudio_req(struct uaudio_softc *sc,
+    unsigned int type,
+    unsigned int req,
+    unsigned int sel,
+    unsigned int chan,
+    unsigned int ifnum,
+    unsigned int id,
+    unsigned char *buf,
+    size_t size)
 {
- struct uaudio_softc *sc = (struct uaudio_softc *)self;
- struct usb_attach_arg *uaa = aux;
- struct uaudio_devs *quirk;
- usb_interface_descriptor_t *id;
- usb_config_descriptor_t *cdesc;
- usbd_status err;
- int i, j, found;
-
- sc->sc_udev = uaa->device;
-
- quirk = uaudio_lookup(uaa->vendor, uaa->product);
- if (quirk)
- sc->sc_quirks = quirk->flags;
-
- cdesc = usbd_get_config_descriptor(sc->sc_udev);
- if (cdesc == NULL) {
- printf("%s: failed to get configuration descriptor\n",
-       sc->sc_dev.dv_xname);
- return;
- }
+ struct usb_device_request r;
+ int err;
 
- err = uaudio_identify(sc, cdesc);
- if (err) {
- printf("%s: audio descriptors make no sense, error=%d\n",
-       sc->sc_dev.dv_xname, err);
- return;
- }
+ r.bmRequestType = type;
+ r.bRequest = req;
+ USETW(r.wValue, sel << 8 | chan);
+ USETW(r.wIndex, id << 8 | ifnum);
+ USETW(r.wLength, size);
 
- /* Pick up the AS interface. */
- for (i = 0; i < uaa->nifaces; i++) {
- if (usbd_iface_claimed(sc->sc_udev, i))
- 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)
- usbd_claim_iface(sc->sc_udev, i);
- }
+ DPRINTF("req: type = 0x%x, req = 0x%x, val = 0x%x, "
+    "index = 0x%x, size = %d\n",
+    type, req, UGETW(r.wValue), UGETW(r.wIndex), UGETW(r.wLength));
 
- for (j = 0; j < sc->sc_nalts; j++) {
- if (sc->sc_alts[j].ifaceh == NULL) {
- printf("%s: alt %d missing AS interface(s)\n",
-    sc->sc_dev.dv_xname, j);
- return;
- }
+ err = usbd_do_request(sc->udev, &r, buf);
+ if (err) {
+ printf("%s: request failed: %s\n",
+    DEVNAME(sc), usbd_errstr(err));
+ return 0;
  }
+ return 1;
+}
 
- printf("%s: audio rev %d.%02x", sc->sc_dev.dv_xname,
-       sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff);
-
- sc->sc_playchan.sc = sc->sc_recchan.sc = sc;
- sc->sc_playchan.altidx = -1;
- sc->sc_recchan.altidx = -1;
+/*
+ * Read a number of the given size (in bytes) from the given
+ * blob. Return 0 on error.
+ */
+int
+uaudio_getnum(struct uaudio_blob *p, unsigned int size, unsigned int *ret)
+{
+ unsigned int i, num = 0;
 
- if (sc->sc_quirks & UAUDIO_FLAG_NO_FRAC)
- sc->sc_altflags |= UA_NOFRAC;
+ if (p->wptr - p->rptr < size) {
+ DPRINTF("uaudio_getnum: %d: too small\n", size);
+ return 0;
+ }
 
- printf(", %d mixer controls\n", sc->sc_nctls);
+ for (i = 0; i < size; i++)
+ num |= *p->rptr++ << (8 * i);
 
- DPRINTF(("%s: doing audio_attach_mi\n", __func__));
- audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev);
+ if (ret)
+ *ret = num;
+ return 1;
 }
 
+/*
+ * Read a USB descriptor from the given blob. Return 0 on error.
+ */
 int
-uaudio_detach(struct device *self, int flags)
+uaudio_getdesc(struct uaudio_blob *p, struct uaudio_blob *ret)
 {
- struct uaudio_softc *sc = (struct uaudio_softc *)self;
+ unsigned int size;
 
- /*
- * sc_alts may be NULL if uaudio_identify_as() failed, in
- * which case uaudio_attach() didn't finish and there's
- * nothing to detach.
- */
- if (sc->sc_alts == NULL)
- return (0);
- return (config_detach_children(self, flags));
+ if (!uaudio_getnum(p, 1, &size))
+ return 0;
+ if (size-- == 0) {
+ DPRINTF("uaudio_getdesc: zero sized desc\n");
+ return 0;
+ }
+ if (p->wptr - p->rptr < size) {
+ DPRINTF("uaudio_getdesc: too small\n");
+ return 0;
+ }
+ ret->rptr = p->rptr;
+ ret->wptr = p->rptr + size;
+ p->rptr += size;
+ return 1;
 }
 
-const usb_interface_descriptor_t *
-uaudio_find_iface(const char *buf, int size, int *offsp, int subtype, int flags)
+/*
+ * Find the unit with the given id, return NULL if not found.
+ */
+struct uaudio_unit *
+uaudio_unit_byid(struct uaudio_softc *sc, unsigned int id)
 {
- const usb_interface_descriptor_t *d;
+ struct uaudio_unit *u;
 
- while (*offsp < size) {
- d = (const void *)(buf + *offsp);
- *offsp += d->bLength;
- if (d->bDescriptorType == UDESC_INTERFACE &&
-    d->bInterfaceSubClass == subtype &&
-    (d->bInterfaceClass == UICLASS_AUDIO ||
-    (d->bInterfaceClass == UICLASS_VENDOR &&
-    (flags & UAUDIO_FLAG_VENDOR_CLASS))))
- return (d);
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ if (u->id == id)
+ break;
  }
- return (NULL);
+ return u;
 }
 
-void
-uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc)
+/*
+ * Return a terminal name for the given terminal type.
+ */
+char *
+uaudio_tname(unsigned int type, int isout)
 {
- int res, range;
- size_t len;
- struct mixerctl *nmc;
-
- if (mc->class < UAC_NCLASSES) {
- DPRINTF(("%s: adding %s.%s\n",
- __func__, uac_names[mc->class], mc->ctlname));
- } else {
- DPRINTF(("%s: adding %s\n", __func__, mc->ctlname));
- }
+ unsigned int hi, lo;
+ char *name;
 
- nmc = mallocarray(sc->sc_nctls + 1, sizeof(*mc), M_USBDEV, M_NOWAIT);
- if (nmc == NULL) {
- printf("%s: no memory\n", __func__);
- return;
- }
- len = sizeof(*mc) * (sc->sc_nctls + 1);
+ hi = type >> 8;
+ lo = type & 0xff;
 
- /* Copy old data, if there was any */
- if (sc->sc_nctls != 0) {
- memcpy(nmc, sc->sc_ctls, sizeof(*mc) * (sc->sc_nctls));
- free(sc->sc_ctls, M_USBDEV, sc->sc_nctls * sizeof(*mc));
- }
- sc->sc_ctls = nmc;
-
- mc->delta = 0;
- if (mc->type == MIX_ON_OFF) {
- mc->minval = 0;
- mc->maxval = 1;
- } else if (mc->type == MIX_SELECTOR) {
- ;
- } else {
- /* 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 = uaudio_signext(mc->type,
- uaudio_get(sc, GET_MAX, UT_READ_CLASS_INTERFACE,
-   mc->wValue[0], mc->wIndex,
-   MIX_SIZE(mc->type)));
- range = mc->maxval - mc->minval;
- res = uaudio_get(sc, GET_RES, UT_READ_CLASS_INTERFACE,
- mc->wValue[0], mc->wIndex,
- MIX_SIZE(mc->type));
- if (res > 0 && range > 0)
- mc->delta = (res * 255 + res - 1) / range;
- }
-
- sc->sc_ctls[sc->sc_nctls++] = *mc;
-
-#ifdef UAUDIO_DEBUG
- if (uaudiodebug > 2) {
- int i;
- DPRINTF(("%s: wValue=%04x", __func__, 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));
+ switch (hi) {
+ case 1:
+ /* usb data stream */
+ name = isout ? "record" : "play";
+ break;
+ case 2:
+ /* embedded inputs */
+ name = isout ? "mic-out" : "mic";
+ break;
+ case 3:
+ /* embedded outputs, mostly speakers, except 0x302 */
+ switch (lo) {
+ case 0x02:
+ name = isout ? "hp" : "hp-in";
+ break;
+ default:
+ name = isout ? "spkr" : "spkr-in";
+ break;
+ }
+ break;
+ case 4:
+ /* handsets and headset */
+ name = isout ? "spkr" : "mic";
+ break;
+ case 5:
+ /* phone line */
+ name = isout ? "phone-in" : "phone-out";
+ break;
+ case 6:
+ /* external sources/sinks */
+ switch (lo) {
+ case 0x02:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x09:
+ case 0x0a:
+ name = isout ? "dig-out" : "dig-in";
+ break;
+ default:
+ name = isout ? "line-out" : "line-in";
+ break;
+ }
+ break;
+ case 7:
+ /* internal devices */
+ name = isout ? "int-out" : "int-in";
+ break;
+ default:
+ name = isout ? "unk-out" : "unk-in";
  }
-#endif
+ return name;
 }
 
-uByte
-uaudio_get_cluster_nchan(int id, const struct io_terminal *iot)
+/*
+ * Return a clock name for the given clock type.
+ */
+char *
+uaudio_clkname(unsigned int attr)
 {
- struct usb_audio_cluster r;
- const usb_descriptor_t *dp;
- int i;
+ static char *names[] = {"ext", "fixed", "var", "prog"};
 
- for (i = 0; i < 25; i++) { /* avoid infinite loops */
- dp = iot[id].d.desc;
- if (dp == 0)
- goto bad;
- switch (dp->bDescriptorSubtype) {
- case UDESCSUB_AC_INPUT:
- return (iot[id].d.it->bNrChannels);
- case UDESCSUB_AC_OUTPUT:
- id = iot[id].d.ot->bSourceId;
- break;
- case UDESCSUB_AC_MIXER:
- r = *(struct usb_audio_cluster *)
- &iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins];
- return (r.bNrChannels);
- case UDESCSUB_AC_SELECTOR:
- /* XXX This is not really right */
- id = iot[id].d.su->baSourceId[0];
- break;
- case UDESCSUB_AC_FEATURE:
- id = iot[id].d.fu->bSourceId;
- break;
- case UDESCSUB_AC_PROCESSING:
- r = *(struct usb_audio_cluster *)
- &iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins];
- return (r.bNrChannels);
- case UDESCSUB_AC_EXTENSION:
- r = *(struct usb_audio_cluster *)
- &iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins];
- return (r.bNrChannels);
- default:
- goto bad;
- }
- }
-bad:
- printf("%s: bad data\n", __func__);
- return (0);
+ return names[attr & 3];
 }
 
+/*
+ * Return an unique name for the given template.
+ */
 void
-uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+uaudio_mkname(struct uaudio_softc *sc, char *templ, char *res)
 {
-#ifdef UAUDIO_DEBUG
- const struct usb_audio_input_terminal *d = iot[id].d.it;
+ struct uaudio_name *n;
+ char *sep;
 
- DPRINTFN(2,("%s: bTerminalId=%d wTerminalType=0x%04x "
-    "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d "
-    "iChannelNames=%d iTerminal=%d\n",
-    __func__,
-    d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
-    d->bNrChannels, UGETW(d->wChannelConfig),
-    d->iChannelNames, d->iTerminal));
-#endif
+ /*
+ * if this is not a terminal name (i.e. there's a underscore
+ * in the name, like in "spkr2_mic3"), then use underscore as
+ * separator to avoid concatenating two numbers
+ */
+ sep = strchr(templ, '_') != NULL ? "_" : "";
+
+ n = sc->names;
+ while (1) {
+ if (n == NULL) {
+ n = malloc(sizeof(struct uaudio_name),
+    M_DEVBUF, M_WAITOK);
+ n->templ = templ;
+ n->unit = 0;
+ n->next = sc->names;
+ sc->names = n;
+ }
+ if (strcmp(n->templ, templ) == 0)
+ break;
+ n = n->next;
+ }
+ if (n->unit == 0)
+ snprintf(res, UAUDIO_NAMEMAX, "%s", templ);
+ else
+ snprintf(res, UAUDIO_NAMEMAX, "%s%s%u", templ, sep, n->unit);
+ n->unit++;
 }
 
-void
-uaudio_add_output(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+/*
+ * Convert UAC v1.0 feature bitmap to UAC v2.0 feature bitmap.
+ */
+unsigned int
+uaudio_feature_fixup(struct uaudio_softc *sc, unsigned int ctl)
 {
-#ifdef UAUDIO_DEBUG
- const struct usb_audio_output_terminal *d = iot[id].d.ot;
+ int i;
+ unsigned int bits, n;
 
- DPRINTFN(2,("%s: bTerminalId=%d wTerminalType=0x%04x "
-    "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n",
-    __func__,
-    d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
-    d->bSourceId, d->iTerminal));
-#endif
+ switch (sc->version) {
+ case UAUDIO_V1:
+ n = 0;
+ for (i = 0; i < 16; i++) {
+ bits = (ctl >> i) & 1;
+ if (bits)
+ bits |= 2;
+ n |= bits << (2 * i);
+ }
+ return n;
+ case UAUDIO_V2:
+ break;
+ }
+ return ctl;
 }
 
+/*
+ * Initialize a uaudio_ranges to the empty set
+ */
 void
-uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+uaudio_ranges_init(struct uaudio_ranges *r)
 {
- const struct usb_audio_mixer_unit *d = iot[id].d.mu;
- struct usb_audio_mixer_unit_1 *d1;
- int c, chs, ochs, i, o, bno, p, mo, mc, k;
-#ifdef UAUDIO_DEBUG
- int ichs = 0;
-#endif
- uByte *bm;
- struct mixerctl mix;
-
- DPRINTFN(2,("%s: bUnitId=%d bNrInPins=%d\n", __func__,
-    d->bUnitId, d->bNrInPins));
+ r->el = NULL;
+ r->nval = 0;
+}
 
-#ifdef UAUDIO_DEBUG
- /* Compute the number of input channels */
- for (i = 0; i < d->bNrInPins; i++)
- ichs += uaudio_get_cluster_nchan(d->baSourceId[i], iot);
-#endif
+/*
+ * Add the given range to the the uaudio_ranges structures. Ranges are
+ * not supposed to overlap (required by USB spec). If they do we just
+ * return.
+ */
+void
+uaudio_ranges_add(struct uaudio_ranges *r, int min, int max, int res)
+{
+ struct uaudio_ranges_el *e, **pe;
 
- /* and the number of output channels */
- d1 = (struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins];
- ochs = d1->bNrChannels;
- DPRINTFN(2,("%s: ichs=%d ochs=%d\n", __func__, ichs, ochs));
+ if (min > max) {
+ DPRINTF("uaudio_ranges_add: [%d:%d]/%d: bad range\n",
+    min, max, res);
+ return;
+ }
 
- bm = d1->bmControls;
- mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
- uaudio_determine_class(&iot[id], &mix);
- mix.type = MIX_SIGNED_16;
- mix.ctlunit = AudioNvolume;
-#define BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1)
- for (p = i = 0; i < d->bNrInPins; i++) {
- chs = uaudio_get_cluster_nchan(d->baSourceId[i], iot);
- 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);
- }
- snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-i%d",
-    d->bUnitId, d->baSourceId[i]);
- mix.nchan = chs;
- uaudio_mixer_add_ctl(sc, &mix);
- } else {
- /* XXX */
+ for (pe = &r->el; (e = *pe) != NULL; pe = &e->next) {
+ if (min <= e->max && max >= e->min) {
+ DPRINTF("uaudio_ranges_add: overlaping ranges\n");
+ return;
  }
-#undef BIT
- p += chs;
+ if (min < e->max)
+ break;
  }
 
+ /* XXX: use 'res' here */
+ r->nval += max - min + 1;
+
+ e = malloc(sizeof(struct uaudio_ranges_el), M_DEVBUF, M_WAITOK);
+ e->min = min;
+ e->max = max;
+ e->res = res;
+ e->next = *pe;
+ *pe = e;
 }
 
+/*
+ * Free all ranges making the uaudio_ranges the empty set
+ */
 void
-uaudio_add_selector(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
-{
- const struct usb_audio_selector_unit *d = iot[id].d.su;
- struct mixerctl mix;
- int i, wp;
-
- DPRINTFN(2,("%s: bUnitId=%d bNrInPins=%d\n", __func__,
-    d->bUnitId, d->bNrInPins));
- mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
- mix.wValue[0] = MAKE(0, 0);
- uaudio_determine_class(&iot[id], &mix);
- mix.nchan = 1;
- mix.type = MIX_SELECTOR;
- mix.ctlunit = "";
- mix.minval = 1;
- mix.maxval = d->bNrInPins;
- wp = snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId);
- for (i = 1; i <= d->bNrInPins; i++) {
- wp += snprintf(mix.ctlname + wp, MAX_AUDIO_DEV_LEN - wp,
-       "i%d", d->baSourceId[i - 1]);
- if (wp > MAX_AUDIO_DEV_LEN - 1)
- break;
- }
- uaudio_mixer_add_ctl(sc, &mix);
-}
-
-#ifdef UAUDIO_DEBUG
-const char *
-uaudio_get_terminal_name(int terminal_type)
-{
- static char buf[100];
-
- switch (terminal_type) {
- /* USB terminal types */
- case UAT_UNDEFINED: return "UAT_UNDEFINED";
- case UAT_STREAM: return "UAT_STREAM";
- case UAT_VENDOR: return "UAT_VENDOR";
- /* input terminal types */
- case UATI_UNDEFINED: return "UATI_UNDEFINED";
- case UATI_MICROPHONE: return "UATI_MICROPHONE";
- case UATI_DESKMICROPHONE: return "UATI_DESKMICROPHONE";
- case UATI_PERSONALMICROPHONE: return "UATI_PERSONALMICROPHONE";
- case UATI_OMNIMICROPHONE: return "UATI_OMNIMICROPHONE";
- case UATI_MICROPHONEARRAY: return "UATI_MICROPHONEARRAY";
- case UATI_PROCMICROPHONEARR: return "UATI_PROCMICROPHONEARR";
- /* output terminal types */
- case UATO_UNDEFINED: return "UATO_UNDEFINED";
- case UATO_SPEAKER: return "UATO_SPEAKER";
- case UATO_HEADPHONES: return "UATO_HEADPHONES";
- case UATO_DISPLAYAUDIO: return "UATO_DISPLAYAUDIO";
- case UATO_DESKTOPSPEAKER: return "UATO_DESKTOPSPEAKER";
- case UATO_ROOMSPEAKER: return "UATO_ROOMSPEAKER";
- case UATO_COMMSPEAKER: return "UATO_COMMSPEAKER";
- case UATO_SUBWOOFER: return "UATO_SUBWOOFER";
- /* bidir terminal types */
- case UATB_UNDEFINED: return "UATB_UNDEFINED";
- case UATB_HANDSET: return "UATB_HANDSET";
- case UATB_HEADSET: return "UATB_HEADSET";
- case UATB_SPEAKERPHONE: return "UATB_SPEAKERPHONE";
- case UATB_SPEAKERPHONEESUP: return "UATB_SPEAKERPHONEESUP";
- case UATB_SPEAKERPHONEECANC: return "UATB_SPEAKERPHONEECANC";
- /* telephony terminal types */
- case UATT_UNDEFINED: return "UATT_UNDEFINED";
- case UATT_PHONELINE: return "UATT_PHONELINE";
- case UATT_TELEPHONE: return "UATT_TELEPHONE";
- case UATT_DOWNLINEPHONE: return "UATT_DOWNLINEPHONE";
- /* external terminal types */
- case UATE_UNDEFINED: return "UATE_UNDEFINED";
- case UATE_ANALOGCONN: return "UATE_ANALOGCONN";
- case UATE_LINECONN: return "UATE_LINECONN";
- case UATE_LEGACYCONN: return "UATE_LEGACYCONN";
- case UATE_DIGITALAUIFC: return "UATE_DIGITALAUIFC";
- case UATE_SPDIF: return "UATE_SPDIF";
- case UATE_1394DA: return "UATE_1394DA";
- case UATE_1394DV: return "UATE_1394DV";
- /* embedded function terminal types */
- case UATF_UNDEFINED: return "UATF_UNDEFINED";
- case UATF_CALIBNOISE: return "UATF_CALIBNOISE";
- case UATF_EQUNOISE: return "UATF_EQUNOISE";
- case UATF_CDPLAYER: return "UATF_CDPLAYER";
- case UATF_DAT: return "UATF_DAT";
- case UATF_DCC: return "UATF_DCC";
- case UATF_MINIDISK: return "UATF_MINIDISK";
- case UATF_ANALOGTAPE: return "UATF_ANALOGTAPE";
- case UATF_PHONOGRAPH: return "UATF_PHONOGRAPH";
- case UATF_VCRAUDIO: return "UATF_VCRAUDIO";
- case UATF_VIDEODISCAUDIO: return "UATF_VIDEODISCAUDIO";
- case UATF_DVDAUDIO: return "UATF_DVDAUDIO";
- case UATF_TVTUNERAUDIO: return "UATF_TVTUNERAUDIO";
- case UATF_SATELLITE: return "UATF_SATELLITE";
- case UATF_CABLETUNER: return "UATF_CABLETUNER";
- case UATF_DSS: return "UATF_DSS";
- case UATF_RADIORECV: return "UATF_RADIORECV";
- case UATF_RADIOXMIT: return "UATF_RADIOXMIT";
- case UATF_MULTITRACK: return "UATF_MULTITRACK";
- case UATF_SYNTHESIZER: return "UATF_SYNTHESIZER";
- default:
- snprintf(buf, sizeof(buf), "unknown type (0x%.4x)", terminal_type);
- return buf;
+uaudio_ranges_clear(struct uaudio_ranges *r)
+{
+ struct uaudio_ranges_el *e;
+
+ while ((e = r->el) != NULL) {
+ r->el = e->next;
+ free(e, M_DEVBUF, sizeof(struct uaudio_ranges_el));
  }
+ r->nval = 0;
 }
-#endif
 
+/*
+ * Convert a value in the given uaudio_ranges, into a 0..255 integer
+ * suitable for mixer usage
+ */
 int
-uaudio_determine_class(const struct io_terminal *iot, struct mixerctl *mix)
+uaudio_ranges_decode(struct uaudio_ranges *r, int val)
 {
- int terminal_type;
+ struct uaudio_ranges_el *e;
+ int diff, pos;
 
- if (iot == NULL || iot->output == NULL) {
- mix->class = UAC_OUTPUT;
- return 0;
- }
- terminal_type = 0;
- if (iot->output->size == 1)
- terminal_type = iot->output->terminals[0];
- /*
- * If the only output terminal is USB,
- * the class is UAC_RECORD.
- */
- if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
- mix->class = UAC_RECORD;
- if (iot->inputs_size == 1
-    && iot->inputs[0] != NULL
-    && iot->inputs[0]->size == 1)
- return iot->inputs[0]->terminals[0];
- else
- return 0;
- }
- /*
- * If the ultimate destination of the unit is just one output
- * terminal and the unit is connected to the output terminal
- * directly, the class is UAC_OUTPUT.
- */
- if (terminal_type != 0 && iot->direct) {
- mix->class = UAC_OUTPUT;
- return terminal_type;
- }
- /*
- * If the unit is connected to just one input terminal,
- * the class is UAC_INPUT.
- */
- if (iot->inputs_size == 1 && iot->inputs[0] != NULL
-    && iot->inputs[0]->size == 1) {
- mix->class = UAC_INPUT;
- return iot->inputs[0]->terminals[0];
- }
- /*
- * Otherwise, the class is UAC_OUTPUT.
- */
- mix->class = UAC_OUTPUT;
- return terminal_type;
-}
-
-const char *
-uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix)
-{
- int terminal_type;
-
- terminal_type = uaudio_determine_class(iot, mix);
- if (mix->class == UAC_RECORD && terminal_type == 0)
- return AudioNmixerout;
- DPRINTF(("%s: terminal_type=%s\n", __func__,
- uaudio_get_terminal_name(terminal_type)));
- switch (terminal_type) {
- case UAT_STREAM:
- return AudioNdac;
-
- case UATI_MICROPHONE:
- case UATI_DESKMICROPHONE:
- case UATI_PERSONALMICROPHONE:
- case UATI_OMNIMICROPHONE:
- case UATI_MICROPHONEARRAY:
- case UATI_PROCMICROPHONEARR:
- return AudioNmicrophone;
-
- case UATO_SPEAKER:
- case UATO_DESKTOPSPEAKER:
- case UATO_ROOMSPEAKER:
- case UATO_COMMSPEAKER:
- return AudioNspeaker;
-
- case UATO_HEADPHONES:
- return AudioNheadphone;
-
- case UATO_SUBWOOFER:
- return AudioNlfe;
-
- /* telephony terminal types */
- case UATT_UNDEFINED:
- case UATT_PHONELINE:
- case UATT_TELEPHONE:
- case UATT_DOWNLINEPHONE:
- return "phone";
-
- case UATE_ANALOGCONN:
- case UATE_LINECONN:
- case UATE_LEGACYCONN:
- return AudioNline;
-
- case UATE_DIGITALAUIFC:
- case UATE_SPDIF:
- case UATE_1394DA:
- case UATE_1394DV:
- return AudioNaux;
-
- case UATF_CDPLAYER:
- return AudioNcd;
-
- case UATF_SYNTHESIZER:
- return AudioNfmsynth;
-
- case UATF_VIDEODISCAUDIO:
- case UATF_DVDAUDIO:
- case UATF_TVTUNERAUDIO:
- return AudioNvideo;
-
- case UAT_UNDEFINED:
- case UAT_VENDOR:
- case UATI_UNDEFINED:
-/* output terminal types */
- case UATO_UNDEFINED:
- case UATO_DISPLAYAUDIO:
-/* bidir terminal types */
- case UATB_UNDEFINED:
- case UATB_HANDSET:
- case UATB_HEADSET:
- case UATB_SPEAKERPHONE:
- case UATB_SPEAKERPHONEESUP:
- case UATB_SPEAKERPHONEECANC:
-/* external terminal types */
- case UATE_UNDEFINED:
-/* embedded function terminal types */
- case UATF_UNDEFINED:
- case UATF_CALIBNOISE:
- case UATF_EQUNOISE:
- case UATF_DAT:
- case UATF_DCC:
- case UATF_MINIDISK:
- case UATF_ANALOGTAPE:
- case UATF_PHONOGRAPH:
- case UATF_VCRAUDIO:
- case UATF_SATELLITE:
- case UATF_CABLETUNER:
- case UATF_DSS:
- case UATF_RADIORECV:
- case UATF_RADIOXMIT:
- case UATF_MULTITRACK:
- case 0xffff:
- default:
- DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type));
- return AudioNmaster;
+ pos = 0;
+
+ for (e = r->el; e != NULL; e = e->next) {
+ if (val >= e->min && val <= e->max) {
+ pos += val - e->min;
+ return (r->nval == 1) ? 0 :
+    (pos * 255 + (r->nval - 1) / 2) / (r->nval - 1);
+ }
+ diff = e->max - e->min + 1;
+ pos += diff;
  }
+ return 0;
 }
 
-void
-uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+/*
+ * Convert a 0..255 to a value in the uaudio_ranges suitable for a USB
+ * request.
+ */
+unsigned int
+uaudio_ranges_encode(struct uaudio_ranges *r, int val)
 {
- const struct usb_audio_feature_unit *d = iot[id].d.fu;
- uByte *ctls = (uByte *)d->bmaControls;
- int ctlsize = d->bControlSize;
- u_int fumask, mmask, cmask;
- struct mixerctl mix;
- int chan, ctl, i, nchan, unit;
- const char *mixername;
+ struct uaudio_ranges_el *e;
+ int diff, pos;
 
-#define GET(i) (ctls[(i)*ctlsize] | \
- (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0))
+ pos = (val * (r->nval - 1) + 127) / 255;
 
- if (ctlsize == 0) {
- DPRINTF(("ignoring feature %d: bControlSize == 0\n", id));
- return;
+ for (e = r->el; e != NULL; e = e->next) {
+ diff = e->max - e->min + 1;
+ if (pos < diff)
+ return e->min + pos;
+ pos -= diff;
  }
- nchan = (d->bLength - 7) / ctlsize;
- mmask = GET(0);
- /* Figure out what we can control */
- for (cmask = 0, chan = 1; chan < nchan; chan++) {
- DPRINTFN(9,("%s: chan=%d mask=%x\n",
-    __func__, chan, GET(chan)));
- cmask |= GET(chan);
- }
-
- DPRINTFN(1,("%s: bUnitId=%d, "
-    "%d channels, mmask=0x%04x, cmask=0x%04x\n",
-    __func__, d->bUnitId, nchan, mmask, cmask));
-
- if (nchan > MIX_MAX_CHAN)
- nchan = MIX_MAX_CHAN;
- unit = d->bUnitId;
- mix.wIndex = MAKE(unit, sc->sc_ac_iface);
- for (ctl = MUTE_CONTROL; ctl < LOUDNESS_CONTROL; ctl++) {
- fumask = FU_MASK(ctl);
- DPRINTFN(4,("%s: ctl=%d fumask=0x%04x\n",
-    __func__, 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
- mixername = uaudio_feature_name(&iot[id], &mix);
- switch (ctl) {
- case MUTE_CONTROL:
- mix.type = MIX_ON_OFF;
- mix.ctlunit = "";
- snprintf(mix.ctlname, sizeof(mix.ctlname),
- "%s.%s", mixername, AudioNmute);
- break;
- case VOLUME_CONTROL:
- mix.type = MIX_SIGNED_16;
- mix.ctlunit = AudioNvolume;
- strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname));
- break;
- case BASS_CONTROL:
- mix.type = MIX_SIGNED_8;
- mix.ctlunit = AudioNbass;
- snprintf(mix.ctlname, sizeof(mix.ctlname),
- "%s.%s", mixername, AudioNbass);
- break;
- case MID_CONTROL:
- mix.type = MIX_SIGNED_8;
- mix.ctlunit = AudioNmid;
- snprintf(mix.ctlname, sizeof(mix.ctlname),
- "%s.%s", mixername, AudioNmid);
- break;
- case TREBLE_CONTROL:
- mix.type = MIX_SIGNED_8;
- mix.ctlunit = AudioNtreble;
- snprintf(mix.ctlname, sizeof(mix.ctlname),
- "%s.%s", mixername, AudioNtreble);
- break;
- case GRAPHIC_EQUALIZER_CONTROL:
- continue; /* XXX don't add anything */
- break;
- case AGC_CONTROL:
- mix.type = MIX_ON_OFF;
- mix.ctlunit = "";
- snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s",
- mixername, AudioNagc);
- break;
- case DELAY_CONTROL:
- mix.type = MIX_UNSIGNED_16;
- mix.ctlunit = "4 ms";
- snprintf(mix.ctlname, sizeof(mix.ctlname),
- "%s.%s", mixername, AudioNdelay);
- break;
- case BASS_BOOST_CONTROL:
- mix.type = MIX_ON_OFF;
- mix.ctlunit = "";
- snprintf(mix.ctlname, sizeof(mix.ctlname),
- "%s.%s", mixername, AudioNbassboost);
- break;
- case LOUDNESS_CONTROL:
- mix.type = MIX_ON_OFF;
- mix.ctlunit = "";
- snprintf(mix.ctlname, sizeof(mix.ctlname),
- "%s.%s", mixername, AudioNloudness);
- break;
- }
- uaudio_mixer_add_ctl(sc, &mix);
- }
-}
-
-void
-uaudio_add_processing_updown(struct uaudio_softc *sc,
-     const struct io_terminal *iot, int id)
-{
- const struct usb_audio_processing_unit *d = iot[id].d.pu;
- const struct usb_audio_processing_unit_1 *d1 =
-    (const struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins];
- const struct usb_audio_processing_unit_updown *ud =
-    (const struct usb_audio_processing_unit_updown *)
- &d1->bmControls[d1->bControlSize];
- struct mixerctl mix;
- int i;
-
- DPRINTFN(2,("%s: bUnitId=%d bNrModes=%d\n",
-    __func__, d->bUnitId, ud->bNrModes));
+ return 0;
+}
 
- if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) {
- DPRINTF(("%s: no mode select\n", __func__));
- return;
+/*
+ * Return the bitmap of supported rates included in the given ranges.
+ * This is not a mixer thing, UAC v2.0 uses ranges to report sample
+ * rates.
+ */
+int
+uaudio_ranges_getrates(struct uaudio_ranges *r,
+    unsigned int mult, unsigned int div)
+{
+ struct uaudio_ranges_el *e;
+ int rates, i, v;
+
+ rates = 0;
+
+ for (e = r->el; e != NULL; e = e->next) {
+ for (i = 0; i < UAUDIO_NRATES; i++) {
+ v = (unsigned long long)uaudio_rates[i] * mult / div;
+ if (v < e->min || v > e->max)
+ continue;
+ if (e->res == 0 || v - e->min % e->res == 0)
+ rates |= 1 << i;
+ }
  }
 
- mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
- mix.nchan = 1;
- mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0);
- uaudio_determine_class(&iot[id], &mix);
- mix.type = MIX_ON_OFF; /* XXX */
- mix.ctlunit = "";
- snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d-mode", d->bUnitId);
-
- for (i = 0; i < ud->bNrModes; i++) {
- DPRINTFN(2,("%s: i=%d bm=0x%x\n",
-    __func__, i, UGETW(ud->waModes[i])));
- /* XXX */
- }
- uaudio_mixer_add_ctl(sc, &mix);
+ return rates;
 }
 
-void
-uaudio_add_processing(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
-{
- const struct usb_audio_processing_unit *d = iot[id].d.pu;
- const struct usb_audio_processing_unit_1 *d1 =
-    (const struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins];
- int ptype = UGETW(d->wProcessType);
- struct mixerctl mix;
-
- DPRINTFN(2,("%s: wProcessType=%d bUnitId=%d "
-    "bNrInPins=%d\n", __func__, 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);
- uaudio_determine_class(&iot[id], &mix);
- mix.type = MIX_ON_OFF;
- mix.ctlunit = "";
- snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d.%d-enable",
-    d->bUnitId, ptype);
- uaudio_mixer_add_ctl(sc, &mix);
- }
-
- switch(ptype) {
- case UPDOWNMIX_PROCESS:
- uaudio_add_processing_updown(sc, iot, id);
- break;
- case DOLBY_PROLOGIC_PROCESS:
- case P3D_STEREO_EXTENDER_PROCESS:
- case REVERBATION_PROCESS:
- case CHORUS_PROCESS:
- case DYN_RANGE_COMP_PROCESS:
- default:
- DPRINTF(("%s: unit %d, type=%d not impl.\n",
-       __func__, d->bUnitId, ptype));
- break;
+/*
+ * Return the index in the uaudio_rates[] array of rate closest to the
+ * given rate in Hz.
+ */
+int
+uaudio_rates_indexof(int mask, int rate)
+{
+ int i, diff, best_index, best_diff;
+
+ best_index = -1;
+ best_diff = INT_MAX;
+ for (i = 0; i < UAUDIO_NRATES; i++) {
+ if ((mask & (1 << i)) == 0)
+ continue;
+ diff = uaudio_rates[i] - rate;
+ if (diff < 0)
+ diff = -diff;
+ if (diff < best_diff) {
+ best_index = i;
+ best_diff = diff;
+ }
  }
+ return best_index;
 }
 
-void
-uaudio_add_extension(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
-{
- const struct usb_audio_extension_unit *d = iot[id].d.eu;
- const struct usb_audio_extension_unit_1 *d1 =
-    (const struct usb_audio_extension_unit_1 *)&d->baSourceId[d->bNrInPins];
- struct mixerctl mix;
+/*
+ * Do a request that results in a uaudio_ranges. On UAC v1.0, this is
+ * simply a min/max/res triplet. On UAC v2.0, this is an array of
+ * min/max/res triplets.
+ */
+int
+uaudio_req_ranges(struct uaudio_softc *sc,
+    unsigned int opsize,
+    unsigned int sel,
+    unsigned int chan,
+    unsigned int ifnum,
+    unsigned int id,
+    struct uaudio_ranges *r)
+{
+ unsigned char req_buf[16], *req = NULL;
+ size_t req_size;
+ struct uaudio_blob p;
+ unsigned int count, min, max, res;
+ int i;
 
- DPRINTFN(2,("%s: bUnitId=%d bNrInPins=%d\n",
-    __func__, d->bUnitId, d->bNrInPins));
+ switch (sc->version) {
+ case UAUDIO_V1:
+ count = 1;
+ req = req_buf;
+ p.rptr = p.wptr = req;
+ if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
+ UAUDIO_V1_REQ_GET_MIN, sel, chan,
+ ifnum, id, p.wptr, opsize))
+ return 0;
+ p.wptr += opsize;
+ if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
+ UAUDIO_V1_REQ_GET_MAX, sel, chan,
+ ifnum, id, p.wptr, opsize))
+ return 0;
+ p.wptr += opsize;
+ if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
+ UAUDIO_V1_REQ_GET_RES, sel, chan,
+ ifnum, id, p.wptr, opsize))
+ return 0;
+ p.wptr += opsize;
+ break;
+ case UAUDIO_V2:
+ /* fetch the ranges count only (first 2 bytes) */
+ if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
+ UAUDIO_V2_REQ_RANGES, sel, chan,
+ ifnum, id, req_buf, 2))
+ return 0;
+ count = req_buf[0] | req_buf[1] << 8;
 
- if (sc->sc_quirks & UAUDIO_FLAG_NO_XU)
- return;
+ /* restart the request on a large enough buffer */
+ req_size = 2 + 3 * opsize * count;
+ if (sizeof(req_buf) >= req_size)
+ req = req_buf;
+ else
+ req = malloc(req_size, M_DEVBUF, M_WAITOK);
 
- 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);
- uaudio_determine_class(&iot[id], &mix);
- mix.type = MIX_ON_OFF;
- mix.ctlunit = "";
- snprintf(mix.ctlname, sizeof(mix.ctlname), "ext%d-enable",
-    d->bUnitId);
- uaudio_mixer_add_ctl(sc, &mix);
+ p.rptr = p.wptr = req;
+ if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
+ UAUDIO_V2_REQ_RANGES, sel, chan,
+ ifnum, id, p.wptr, req_size))
+ return 0;
+ p.wptr += req_size;
+
+ /* skip initial 2 bytes of count */
+ p.rptr += 2;
+ break;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (!uaudio_getnum(&p, opsize, &min))
+ return 0;
+ if (!uaudio_getnum(&p, opsize, &max))
+ return 0;
+ if (!uaudio_getnum(&p, opsize, &res))
+ return 0;
+ uaudio_ranges_add(r,
+    uaudio_sign_expand(min, opsize),
+    uaudio_sign_expand(max, opsize),
+    uaudio_sign_expand(res, opsize));
  }
+
+ if (req != req_buf)
+ free(req, M_DEVBUF, req_size);
+
+ return 1;
 }
 
-struct terminal_list*
-uaudio_merge_terminal_list(const struct io_terminal *iot)
-{
- struct terminal_list *tml;
- uint16_t *ptm;
- int i, len;
-
- len = 0;
- if (iot->inputs == NULL)
- return NULL;
- for (i = 0; i < iot->inputs_size; i++) {
- if (iot->inputs[i] != NULL)
- len += iot->inputs[i]->size;
- }
- tml = malloc(TERMINAL_LIST_SIZE(len), M_TEMP, M_NOWAIT);
- if (tml == NULL) {
- printf("%s: no memory\n", __func__);
- return NULL;
- }
- tml->size = 0;
- ptm = tml->terminals;
- for (i = 0; i < iot->inputs_size; i++) {
- if (iot->inputs[i] == NULL)
- continue;
- if (iot->inputs[i]->size > len)
- break;
- memcpy(ptm, iot->inputs[i]->terminals,
-       iot->inputs[i]->size * sizeof(uint16_t));
- tml->size += iot->inputs[i]->size;
- ptm += iot->inputs[i]->size;
- len -= iot->inputs[i]->size;
- }
- return tml;
-}
-
-struct terminal_list *
-uaudio_io_terminaltype(int outtype, struct io_terminal *iot, int id)
-{
- struct terminal_list *tml;
- struct io_terminal *it;
- int src_id, i;
-
- it = &iot[id];
- if (it->output != NULL) {
- /* already has outtype? */
- for (i = 0; i < it->output->size; i++)
- if (it->output->terminals[i] == outtype)
- return uaudio_merge_terminal_list(it);
- tml = malloc(TERMINAL_LIST_SIZE(it->output->size + 1),
-    M_TEMP, M_NOWAIT);
- if (tml == NULL) {
- printf("%s: no memory\n", __func__);
- return uaudio_merge_terminal_list(it);
- }
- memcpy(tml, it->output, TERMINAL_LIST_SIZE(it->output->size));
- tml->terminals[it->output->size] = outtype;
- tml->size++;
- free(it->output, M_TEMP, 0);
- it->output = tml;
- if (it->inputs != NULL) {
- for (i = 0; i < it->inputs_size; i++)
- free(it->inputs[i], M_TEMP, 0);
- free(it->inputs, M_TEMP, 0);
- }
- it->inputs_size = 0;
- it->inputs = NULL;
- } else { /* end `iot[id] != NULL' */
- it->inputs_size = 0;
- it->inputs = NULL;
- it->output = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
- if (it->output == NULL) {
- printf("%s: no memory\n", __func__);
- return NULL;
- }
- it->output->terminals[0] = outtype;
- it->output->size = 1;
- it->direct = 0;
- }
-
- switch (it->d.desc->bDescriptorSubtype) {
- case UDESCSUB_AC_INPUT:
- it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
- if (it->inputs == NULL) {
- printf("%s: no memory\n", __func__);
- return NULL;
- }
- tml = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
- if (tml == NULL) {
- printf("%s: no memory\n", __func__);
- free(it->inputs, M_TEMP, 0);
- it->inputs = NULL;
- return NULL;
- }
- it->inputs[0] = tml;
- tml->terminals[0] = UGETW(it->d.it->wTerminalType);
- tml->size = 1;
- it->inputs_size = 1;
- return uaudio_merge_terminal_list(it);
- case UDESCSUB_AC_FEATURE:
- src_id = it->d.fu->bSourceId;
- it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
- if (it->inputs == NULL) {
- printf("%s: no memory\n", __func__);
- return uaudio_io_terminaltype(outtype, iot, src_id);
- }
- it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id);
- it->inputs_size = 1;
- return uaudio_merge_terminal_list(it);
- case UDESCSUB_AC_OUTPUT:
- it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
- if (it->inputs == NULL) {
- printf("%s: no memory\n", __func__);
- return NULL;
- }
- src_id = it->d.ot->bSourceId;
- it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id);
- it->inputs_size = 1;
- iot[src_id].direct = 1;
- return NULL;
- case UDESCSUB_AC_MIXER:
- it->inputs_size = 0;
- it->inputs = mallocarray(it->d.mu->bNrInPins,
-    sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
- if (it->inputs == NULL) {
- printf("%s: no memory\n", __func__);
- return NULL;
- }
- for (i = 0; i < it->d.mu->bNrInPins; i++) {
- src_id = it->d.mu->baSourceId[i];
- it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
-       src_id);
- it->inputs_size++;
- }
- return uaudio_merge_terminal_list(it);
- case UDESCSUB_AC_SELECTOR:
- it->inputs_size = 0;
- it->inputs = mallocarray(it->d.su->bNrInPins,
-    sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
- if (it->inputs == NULL) {
- printf("%s: no memory\n", __func__);
- return NULL;
- }
- for (i = 0; i < it->d.su->bNrInPins; i++) {
- src_id = it->d.su->baSourceId[i];
- it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
-       src_id);
- it->inputs_size++;
- }
- return uaudio_merge_terminal_list(it);
- case UDESCSUB_AC_PROCESSING:
- it->inputs_size = 0;
- it->inputs = mallocarray(it->d.pu->bNrInPins,
-    sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
- if (it->inputs == NULL) {
- printf("%s: no memory\n", __func__);
- return NULL;
- }
- for (i = 0; i < it->d.pu->bNrInPins; i++) {
- src_id = it->d.pu->baSourceId[i];
- it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
-       src_id);
- it->inputs_size++;
- }
- return uaudio_merge_terminal_list(it);
- case UDESCSUB_AC_EXTENSION:
- it->inputs_size = 0;
- it->inputs = mallocarray(it->d.eu->bNrInPins,
-    sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
- if (it->inputs == NULL) {
- printf("%s: no memory\n", __func__);
- return NULL;
- }
- for (i = 0; i < it->d.eu->bNrInPins; i++) {
- src_id = it->d.eu->baSourceId[i];
- it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
-       src_id);
- it->inputs_size++;
+/*
+ * Return the rates bitmap of the given interface alt setting
+ */
+int
+uaudio_alt_getrates(struct uaudio_softc *sc, struct uaudio_alt *p)
+{
+ struct uaudio_unit *u;
+ unsigned int mult = 1, div = 1;
+
+ switch (sc->version) {
+ case UAUDIO_V1:
+ return p->v1_rates;
+ case UAUDIO_V2:
+ u = sc->clock;
+ while (1) {
+ switch (u->type) {
+ case UAUDIO_AC_CLKSRC:
+ return uaudio_ranges_getrates(&u->rates,
+    mult, div);
+ case UAUDIO_AC_CLKSEL:
+ u = u->clock;
+ break;
+ case UAUDIO_AC_CLKMULT:
+ case UAUDIO_AC_RATECONV:
+ /* XXX: adjust rate with multiplier */
+ u = u->src_list;
+ break;
+ default:
+ DPRINTF("uaudio_alt_getrates: no clock\n");
+ return 0;
+ }
  }
- return uaudio_merge_terminal_list(it);
- case UDESCSUB_AC_HEADER:
- default:
- return NULL;
  }
+ return 0;
 }
 
-usbd_status
-uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
+/*
+ * Return the rates bitmap of the given parameters setting
+ */
+int
+uaudio_getrates(struct uaudio_softc *sc, struct uaudio_params *p)
 {
- usbd_status err;
-
- err = uaudio_identify_ac(sc, cdesc);
- if (err)
- return (err);
- return (uaudio_identify_as(sc, cdesc));
+ return uaudio_alt_getrates(sc, p->palt ? p->palt : p->ralt);
 }
 
+/*
+ * Add the given feature (aka mixer control) to the given unit.
+ */
 void
-uaudio_add_alt(struct uaudio_softc *sc, const struct as_info *ai)
+uaudio_feature_addent(struct uaudio_softc *sc,
+    struct uaudio_unit *u, int uac_type, int chan)
 {
- struct as_info *nai;
+ static struct {
+ char *name;
+ int mix_type;
+ int req_sel;
+ } features[] = {
+ {"mute", UAUDIO_MIX_SW, UAUDIO_REQSEL_MUTE},
+ {"level", UAUDIO_MIX_NUM, UAUDIO_REQSEL_VOLUME},
+ {"bass", UAUDIO_MIX_NUM, UAUDIO_REQSEL_BASS},
+ {"mid", UAUDIO_MIX_NUM, UAUDIO_REQSEL_MID},
+ {"treble", UAUDIO_MIX_NUM, UAUDIO_REQSEL_TREBLE},
+ {"eq", UAUDIO_MIX_NUM, UAUDIO_REQSEL_EQ},
+ {"agc", UAUDIO_MIX_SW, UAUDIO_REQSEL_AGC},
+ {NULL, -1, -1}, /* delay */
+ {"bassboost", UAUDIO_MIX_SW, UAUDIO_REQSEL_BASSBOOST},
+ {"loud", UAUDIO_MIX_SW, UAUDIO_REQSEL_LOUDNESS},
+ {"gain", UAUDIO_MIX_NUM, UAUDIO_REQSEL_GAIN},
+ {"gainpad", UAUDIO_MIX_SW, UAUDIO_REQSEL_GAINPAD},
+ {"phase", UAUDIO_MIX_SW, UAUDIO_REQSEL_PHASEINV},
+ {NULL, -1, -1}, /* undeflow */
+ {NULL, -1, -1} /* overflow */
+ };
+ struct uaudio_mixent *m, *i, **pi;
+ int cmp;
 
- nai = mallocarray(sc->sc_nalts + 1, sizeof(*ai), M_USBDEV, M_NOWAIT);
- if (nai == NULL) {
- printf("%s: no memory\n", __func__);
+ if (uac_type >= sizeof(features) / sizeof(features[0])) {
+ printf("%s: skipped unknown feature\n", DEVNAME(sc));
  return;
  }
 
- /* Copy old data, if there was any */
- if (sc->sc_nalts != 0) {
- memcpy(nai, sc->sc_alts, sizeof(*ai) * (sc->sc_nalts));
- free(sc->sc_alts, M_USBDEV, sc->sc_nalts * sizeof(*ai));
- }
- sc->sc_alts = nai;
- DPRINTFN(2,("%s: adding alt=%d, enc=%d\n",
-    __func__, ai->alt, ai->encoding));
- sc->sc_alts[sc->sc_nalts++] = *ai;
-}
-
-usbd_status
-uaudio_process_as(struct uaudio_softc *sc, const char *buf, int *offsp,
-  int size, const usb_interface_descriptor_t *id)
-#define offs (*offsp)
-{
- const struct usb_audio_streaming_interface_descriptor *asid;
- const struct usb_audio_streaming_type1_descriptor *asf1d;
- const struct usb_endpoint_descriptor_audio *ed;
- const struct usb_endpoint_descriptor_audio *sync_ed;
- const struct usb_audio_streaming_endpoint_descriptor *sed;
- int format, chan, prec, enc, bps;
- int dir, type, sync, sync_addr;
- struct as_info ai;
- const char *format_str;
-
- asid = (const void *)(buf + offs);
- if (asid->bDescriptorType != UDESC_CS_INTERFACE ||
-    asid->bDescriptorSubtype != AS_GENERAL)
- return (USBD_INVAL);
- DPRINTF(("%s: asid: bTerminalLink=%d wFormatTag=%d\n", __func__,
- asid->bTerminalLink, UGETW(asid->wFormatTag)));
- offs += asid->bLength;
- if (offs > size)
- return (USBD_INVAL);
-
- asf1d = (const 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",
-       sc->sc_dev.dv_xname, UGETW(asid->wFormatTag));
- return (USBD_NORMAL_COMPLETION);
- }
-
- ed = (const void *)(buf + offs);
- if (ed->bDescriptorType != UDESC_ENDPOINT)
- return (USBD_INVAL);
- DPRINTF(("%s: endpoint[0] bLength=%d bDescriptorType=%d "
- "bEndpointAddress=%d bmAttributes=0x%x wMaxPacketSize=%d "
- "bInterval=%d bRefresh=%d bSynchAddress=%d\n",
- __func__,
- 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);
-
- /* Check for sync endpoint. */
- sync = 0;
- sync_addr = 0;
- if (id->bNumEndpoints > 1 &&
-    ((dir == UE_DIR_IN && type == UE_ISO_ADAPT) ||
-    (dir != UE_DIR_IN && type == UE_ISO_ASYNC)))
- sync = 1;
-
- /* Check whether sync endpoint address is given. */
- if (ed->bLength >= USB_ENDPOINT_DESCRIPTOR_AUDIO_SIZE) {
- /* bSynchAdress set to 0 indicates sync is not used. */
- if (ed->bSynchAddress == 0)
- sync = 0;
- else
- sync_addr = ed->bSynchAddress;
+ m = malloc(sizeof(struct uaudio_mixent), M_DEVBUF, M_WAITOK);
+ m->chan = chan;
+ m->fname = features[uac_type].name;
+ m->type = features[uac_type].mix_type;
+ m->req_sel = features[uac_type].req_sel;
+ uaudio_ranges_init(&m->ranges);
+
+ if (m->type == UAUDIO_MIX_NUM) {
+ if (!uaudio_req_ranges(sc, 2,
+ m->req_sel, chan < 0 ? 0 : chan + 1,
+ sc->ctl_ifnum, u->id,
+ &m->ranges)) {
+ DPRINTF("%s.%s[%d]: failed\n", u->name, m->fname, chan);
+ free(m, M_DEVBUF, sizeof(struct uaudio_mixent));
+ return;
+ }
+ if (m->ranges.el == NULL) {
+ printf("%s: skipped %s control with empty range\n",
+    DEVNAME(sc), m->fname);
+ free(m, M_DEVBUF, sizeof(struct uaudio_mixent));
+ return;
+ }
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug)
+ uaudio_ranges_print(&m->ranges);
+#endif
+ }
+
+ /*
+ * Add to unit's mixer controls list, sorting entries by name
+ * and increasing channel number.
+ */
+ for (pi = &u->mixent_list; (i = *pi) != NULL; pi = &i->next) {
+ cmp = strcmp(i->fname, m->fname);
+ if (cmp == 0)
+ cmp = i->chan - m->chan;
+ if (cmp == 0) {
+ DPRINTF("%02u: %s.%s: duplicate feature for chan %d\n",
+    u->id, u->name, m->fname, m->chan);
+ free(m, M_DEVBUF, sizeof(struct uaudio_mixent));
+ return;
+ }
+ if (cmp > 0)
+ break;
+ }
+ m->next = *pi;
+ *pi = m;
+
+ DPRINTF("\t%s[%d]\n", m->fname, m->chan);
+}
+
+/*
+ * For the given unit, parse the list of its sources and recursively
+ * call uaudio_process_unit() for each.
+ */
+int
+uaudio_process_srcs(struct uaudio_softc *sc,
+ struct uaudio_unit *u, struct uaudio_blob units,
+ struct uaudio_blob *p)
+{
+ struct uaudio_unit *s, **ps;
+ unsigned int i, npin, sid;
+
+ if (!uaudio_getnum(p, 1, &npin))
+ return 0;
+ ps = &u->src_list;
+ for (i = 0; i < npin; i++) {
+ if (!uaudio_getnum(p, 1, &sid))
+ return 0;
+ if (!uaudio_process_unit(sc, u, sid, units, &s))
+ return 0;
+ s->src_next = NULL;
+ *ps = s;
+ ps = &s->src_next;
  }
+ return 1;
+}
 
- sed = (const void *)(buf + offs);
- if (sed->bDescriptorType != UDESC_CS_ENDPOINT ||
-    sed->bDescriptorSubtype != AS_GENERAL)
- return (USBD_INVAL);
- DPRINTF((" streaming_endpoint: offset=%d bLength=%d\n", offs, sed->bLength));
- offs += sed->bLength;
- if (offs > size)
- return (USBD_INVAL);
-
- sync_ed = NULL;
- if (sync == 1) {
- sync_ed = (const void*)(buf + offs);
- if (sync_ed->bDescriptorType != UDESC_ENDPOINT) {
- printf("%s: sync ep descriptor wrong type\n",
-    sc->sc_dev.dv_xname);
- return (USBD_NORMAL_COMPLETION);
- }
- DPRINTF(("%s: endpoint[1] bLength=%d "
- "bDescriptorType=%d bEndpointAddress=%d "
- "bmAttributes=0x%x wMaxPacketSize=%d bInterval=%d "
- "bRefresh=%d bSynchAddress=%d\n",
- __func__,
- sync_ed->bLength, sync_ed->bDescriptorType,
- sync_ed->bEndpointAddress, sync_ed->bmAttributes,
- UGETW(sync_ed->wMaxPacketSize), sync_ed->bInterval,
- sync_ed->bRefresh, sync_ed->bSynchAddress));
- offs += sync_ed->bLength;
- if (offs > size) {
- printf("%s: sync ep descriptor too large\n",
-    sc->sc_dev.dv_xname);
- return (USBD_NORMAL_COMPLETION);
- }
- if (dir == UE_GET_DIR(sync_ed->bEndpointAddress)) {
- printf("%s: sync ep wrong direction\n",
-       sc->sc_dev.dv_xname);
- return (USBD_NORMAL_COMPLETION);
- }
- if (UE_GET_XFERTYPE(sync_ed->bmAttributes) != UE_ISOCHRONOUS) {
- printf("%s: sync ep wrong xfer type\n",
-       sc->sc_dev.dv_xname);
- return (USBD_NORMAL_COMPLETION);
- }
- if (sync_ed->bLength >=
-    USB_ENDPOINT_DESCRIPTOR_AUDIO_SIZE &&
-    sync_ed->bSynchAddress != 0) {
- printf("%s: sync ep bSynchAddress != 0\n",
-       sc->sc_dev.dv_xname);
- return (USBD_NORMAL_COMPLETION);
- }
- if (sync_addr &&
-    UE_GET_ADDR(sync_ed->bEndpointAddress) !=
-    UE_GET_ADDR(sync_addr)) {
- printf("%s: sync ep address mismatch\n",
-       sc->sc_dev.dv_xname);
- return (USBD_NORMAL_COMPLETION);
- }
- }
- if (sync_ed != NULL && dir == UE_DIR_IN) {
- printf("%s: sync pipe for recording not yet implemented\n",
-    sc->sc_dev.dv_xname);
- return (USBD_NORMAL_COMPLETION);
- }
-
- format = UGETW(asid->wFormatTag);
- chan = asf1d->bNrChannels;
- prec = asf1d->bBitResolution;
- bps = asf1d->bSubFrameSize;
- if ((prec != 8 && prec != 16 && prec != 24) || (bps < 1 || bps > 4)) {
- printf("%s: ignored setting with precision %d bps %d\n",
-       sc->sc_dev.dv_xname, prec, bps);
- return (USBD_NORMAL_COMPLETION);
- }
- switch (format) {
- case UA_FMT_PCM:
- if (prec == 8) {
- sc->sc_altflags |= HAS_8;
- } else if (prec == 16) {
- sc->sc_altflags |= HAS_16;
- } else if (prec == 24) {
- sc->sc_altflags |= HAS_24;
- }
- enc = AUDIO_ENCODING_SLINEAR_LE;
- format_str = "pcm";
- break;
- case UA_FMT_PCM8:
- enc = AUDIO_ENCODING_ULINEAR_LE;
- sc->sc_altflags |= HAS_8U;
- format_str = "pcm8";
- break;
- case UA_FMT_ALAW:
- enc = AUDIO_ENCODING_ALAW;
- sc->sc_altflags |= HAS_ALAW;
- format_str = "alaw";
- break;
- case UA_FMT_MULAW:
- enc = AUDIO_ENCODING_ULAW;
- sc->sc_altflags |= HAS_MULAW;
- format_str = "mulaw";
+/*
+ * Parse the number of channels.
+ */
+int
+uaudio_process_nch(struct uaudio_softc *sc,
+ struct uaudio_unit *u, struct uaudio_blob *p)
+{
+ if (!uaudio_getnum(p, 1, &u->nch))
+ return 0;
+ /* skip junk */
+ switch (sc->version) {
+ case UAUDIO_V1:
+ if (!uaudio_getnum(p, 2, NULL)) /* bmChannelConfig */
+ return 0;
+ break;
+ case UAUDIO_V2:
+ if (!uaudio_getnum(p, 4, NULL)) /* wChannelConfig */
+ return 0;
  break;
- case UA_FMT_IEEE_FLOAT:
- default:
- printf("%s: ignored setting with format %d\n",
-       sc->sc_dev.dv_xname, format);
- return (USBD_NORMAL_COMPLETION);
  }
+ if (!uaudio_getnum(p, 1, NULL)) /* iChannelNames */
+ return 0;
+ return 1;
+}
+
+/*
+ * Find the AC class-specific descriptor for this unit id.
+ */
+int
+uaudio_unit_getdesc(struct uaudio_softc *sc, int id,
+ struct uaudio_blob units,
+ struct uaudio_blob *p,
+ unsigned int *rtype)
+{
+ unsigned int i, type, subtype;
+
+ /*
+ * Find the usb descriptor for this id.
+ */
+ while (1) {
+ if (units.rptr == units.wptr) {
+ DPRINTF("uaudio_unit_getdesc: %02u: not found\n", id);
+ return 0;
+ }
+ if (!uaudio_getdesc(&units, p))
+ return 0;
+ if (!uaudio_getnum(p, 1, &type))
+ return 0;
+ if (!uaudio_getnum(p, 1, &subtype))
+ return 0;
+ if (!uaudio_getnum(p, 1, &i))
+ return 0;
+ if (i == id)
+ break;
+ }
+ *rtype = subtype;
+ return 1;
+}
+
+/*
+ * Parse a unit, possibly calling uaudio_process_unit() for each of
+ * its sources.
+ */
+int
+uaudio_process_unit(struct uaudio_softc *sc,
+ struct uaudio_unit *dest, int id,
+ struct uaudio_blob units,
+ struct uaudio_unit **rchild)
+{
+ struct uaudio_blob p;
+ struct uaudio_unit *u, *s;
+ unsigned int i, j, term, size, attr, ctl, type, subtype, assoc, clk;
 #ifdef UAUDIO_DEBUG
- printf("%s: %s: %d-ch %d-bit %d-byte %s,", sc->sc_dev.dv_xname,
-       dir == UE_DIR_IN ? "recording" : "playback",
-       chan, prec, bps, format_str);
- if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
- printf(" %d-%dHz\n", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+ unsigned int bit;
+#endif
+
+ if (!uaudio_unit_getdesc(sc, id, units, &p, &subtype))
+ return 0;
+
+ /*
+ * find this unit on the list as it may be already processed as
+ * the source of another destination
+ */
+ u = uaudio_unit_byid(sc, id);
+ if (u == NULL) {
+ u = malloc(sizeof(struct uaudio_unit), M_DEVBUF, M_WAITOK);
+ u->id = id;
+ u->type = subtype;
+ u->src_list = NULL;
+ u->dst_list = NULL;
+ u->clock = NULL;
+ u->mixent_list = NULL;
+ u->nch = 0;
+ u->name[0] = 0;
+ uaudio_ranges_init(&u->rates);
+ u->unit_next = sc->unit_list;
+ sc->unit_list = u;
  } else {
- int r;
- printf(" %d", UA_GETSAMP(asf1d, 0));
- for (r = 1; r < asf1d->bSamFreqType; r++)
- printf(",%d", UA_GETSAMP(asf1d, r));
- printf("Hz\n");
- }
-#endif
- ai.alt = id->bAlternateSetting;
- ai.encoding = enc;
- ai.attributes = sed->bmAttributes;
- ai.idesc = id;
- ai.edesc = ed;
- ai.edesc1 = sync_ed;
- ai.asf1desc = asf1d;
- ai.sc_busy = 0;
- if (sc->sc_nalts < UAUDIO_MAX_ALTS)
- uaudio_add_alt(sc, &ai);
-#ifdef UAUDIO_DEBUG
- if (ai.attributes & UA_SED_FREQ_CONTROL)
- DPRINTFN(1, ("%s:  FREQ_CONTROL\n", __func__));
- if (ai.attributes & UA_SED_PITCH_CONTROL)
- DPRINTFN(1, ("%s:  PITCH_CONTROL\n", __func__));
-#endif
- sc->sc_mode |= (dir == UE_DIR_OUT) ? AUMODE_PLAY : AUMODE_RECORD;
-
- return (USBD_NORMAL_COMPLETION);
-}
-#undef offs
-
-usbd_status
-uaudio_identify_as(struct uaudio_softc *sc,
-   const usb_config_descriptor_t *cdesc)
-{
- const usb_interface_descriptor_t *id;
- const char *buf;
- int size, offs;
-
- size = UGETW(cdesc->wTotalLength);
- buf = (const char *)cdesc;
-
- /* Locate the AudioStreaming interface descriptor. */
- offs = 0;
- id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM,
-    sc->sc_quirks);
- if (id == NULL)
- return (USBD_INVAL);
-
- /* Loop through all the alternate settings. */
- while (offs <= size) {
- DPRINTFN(2, ("%s: interface=%d offset=%d\n",
-    __func__, id->bInterfaceNumber, offs));
- switch (id->bNumEndpoints) {
- case 0:
- DPRINTFN(2, ("%s: AS null alt=%d\n",
-     __func__, id->bAlternateSetting));
- sc->sc_nullalt = id->bAlternateSetting;
- break;
- case 1:
- case 2:
- uaudio_process_as(sc, buf, &offs, size, id);
- break;
- default:
- printf("%s: ignored audio interface with %d "
-       "endpoints\n",
-       sc->sc_dev.dv_xname, id->bNumEndpoints);
- break;
- }
- id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM,
-    sc->sc_quirks);
- if (id == NULL)
- break;
- }
- if (offs > size)
- return (USBD_INVAL);
- DPRINTF(("%s: %d alts available\n", __func__, sc->sc_nalts));
-
- if (sc->sc_mode == 0) {
- printf("%s: no usable endpoint found\n",
-       sc->sc_dev.dv_xname);
- return (USBD_INVAL);
- }
-
- return (USBD_NORMAL_COMPLETION);
-}
-
-usbd_status
-uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
-{
- struct io_terminal* iot;
- const usb_interface_descriptor_t *id;
- const struct usb_audio_control_descriptor *acdp;
- const usb_descriptor_t *dp;
- const struct usb_audio_output_terminal *pot;
- struct terminal_list *tml;
- const char *buf, *ibuf, *ibufend;
- int size, offs, aclen, ndps, i, j;
-
- size = UGETW(cdesc->wTotalLength);
- buf = (char *)cdesc;
-
- /* Locate the AudioControl interface descriptor. */
- offs = 0;
- id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOCONTROL,
-    sc->sc_quirks);
- if (id == NULL)
- return (USBD_INVAL);
- if (offs + sizeof *acdp > size)
- return (USBD_INVAL);
- sc->sc_ac_iface = id->bInterfaceNumber;
- DPRINTFN(2,("%s: AC interface is %d\n",
-    __func__, sc->sc_ac_iface));
-
- /* A class-specific AC interface header should follow. */
- ibuf = buf + offs;
- acdp = (const 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 (!(sc->sc_quirks & UAUDIO_FLAG_BAD_ADC) &&
-     UGETW(acdp->bcdADC) != UAUDIO_VERSION)
- return (USBD_INVAL);
-
- sc->sc_audio_rev = UGETW(acdp->bcdADC);
- DPRINTFN(2,("%s: found AC header, vers=%03x, len=%d\n",
- __func__, sc->sc_audio_rev, aclen));
-
- /* Some webcams descriptors advertise an off-by-one wTotalLength */
- if (sc->sc_quirks & UAUDIO_FLAG_BAD_ADC_LEN)
- aclen++;
-
- sc->sc_nullalt = -1;
-
- /* Scan through all the AC specific descriptors */
- ibufend = ibuf + aclen;
- dp = (const usb_descriptor_t *)ibuf;
- ndps = 0;
- iot = mallocarray(256, sizeof(struct io_terminal),
-    M_TEMP, M_NOWAIT | M_ZERO);
- if (iot == NULL) {
- printf("%s: no memory\n", __func__);
- return USBD_NOMEM;
- }
- for (;;) {
- ibuf += dp->bLength;
- if (ibuf >= ibufend)
- break;
- dp = (const usb_descriptor_t *)ibuf;
- if (ibuf + dp->bLength > ibufend) {
- free(iot, M_TEMP, 0);
- return (USBD_INVAL);
- }
- if (dp->bDescriptorType != UDESC_CS_INTERFACE) {
- printf("%s: skip desc type=0x%02x\n",
-       __func__, dp->bDescriptorType);
- continue;
+ switch (u->type) {
+ case UAUDIO_AC_CLKSRC:
+ case UAUDIO_AC_CLKSEL:
+ case UAUDIO_AC_CLKMULT:
+ case UAUDIO_AC_RATECONV:
+ /* not using 'dest' list */
+ *rchild = u;
+ return 1;
  }
- i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId;
- iot[i].d.desc = dp;
- if (i > ndps)
- ndps = i;
- }
- ndps++;
-
- /* construct io_terminal */
- for (i = 0; i < ndps; i++) {
- dp = iot[i].d.desc;
- if (dp == NULL)
- continue;
- if (dp->bDescriptorSubtype != UDESCSUB_AC_OUTPUT)
- continue;
- pot = iot[i].d.ot;
- tml = uaudio_io_terminaltype(UGETW(pot->wTerminalType), iot, i);
- free(tml, M_TEMP, 0);
  }
 
-#ifdef UAUDIO_DEBUG
- for (i = 0; i < 256; i++) {
- if (iot[i].d.desc == NULL)
- continue;
- printf("id %d:\t", i);
- switch (iot[i].d.desc->bDescriptorSubtype) {
- case UDESCSUB_AC_INPUT:
- printf("AC_INPUT type=%s\n", uaudio_get_terminal_name
-       (UGETW(iot[i].d.it->wTerminalType)));
- break;
- case UDESCSUB_AC_OUTPUT:
- printf("AC_OUTPUT type=%s ", uaudio_get_terminal_name
-       (UGETW(iot[i].d.ot->wTerminalType)));
- printf("src=%d\n", iot[i].d.ot->bSourceId);
- break;
- case UDESCSUB_AC_MIXER:
- printf("AC_MIXER src=");
- for (j = 0; j < iot[i].d.mu->bNrInPins; j++)
- printf("%d ", iot[i].d.mu->baSourceId[j]);
- printf("\n");
- break;
- case UDESCSUB_AC_SELECTOR:
- printf("AC_SELECTOR src=");
- for (j = 0; j < iot[i].d.su->bNrInPins; j++)
- printf("%d ", iot[i].d.su->baSourceId[j]);
- printf("\n");
+ if (dest) {
+ dest->dst_next = u->dst_list;
+ u->dst_list = dest;
+ if (dest->dst_next != NULL) {
+ /* already seen */
+ *rchild = u;
+ return 1;
+ }
+ }
+
+ switch (u->type) {
+ case UAUDIO_AC_INPUT:
+ if (!uaudio_getnum(&p, 2, &term))
+ return 0;
+ if (!uaudio_getnum(&p, 1, &assoc))
+ return 0;
+ switch (sc->version) {
+ case UAUDIO_V1:
  break;
- case UDESCSUB_AC_FEATURE:
- printf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId);
+ case UAUDIO_V2:
+ if (!uaudio_getnum(&p, 1, &clk))
+ return 0;
+ if (!uaudio_process_unit(sc, NULL,
+ clk, units, &u->clock))
+ return 0;
  break;
- case UDESCSUB_AC_PROCESSING:
- printf("AC_PROCESSING src=");
- for (j = 0; j < iot[i].d.pu->bNrInPins; j++)
- printf("%d ", iot[i].d.pu->baSourceId[j]);
- printf("\n");
+ }
+ if (!uaudio_getnum(&p, 1, &u->nch))
+ return 0;
+ uaudio_mkname(sc, uaudio_tname(term, 0), u->name);
+ DPRINTF("%02u: "
+    "in, nch = %d, term = 0x%x, assoc = %d\n",
+    u->id, u->nch, term, assoc);
+ break;
+ case UAUDIO_AC_OUTPUT:
+ if (!uaudio_getnum(&p, 2, &term))
+ return 0;
+ if (!uaudio_getnum(&p, 1, &assoc))
+ return 0;
+ if (!uaudio_getnum(&p, 1, &id))
+ return 0;
+ if (!uaudio_process_unit(sc, u, id, units, &s))
+ return 0;
+ switch (sc->version) {
+ case UAUDIO_V1:
  break;
- case UDESCSUB_AC_EXTENSION:
- printf("AC_EXTENSION src=");
- for (j = 0; j < iot[i].d.eu->bNrInPins; j++)
- printf("%d ", iot[i].d.eu->baSourceId[j]);
- printf("\n");
+ case UAUDIO_V2:
+ if (!uaudio_getnum(&p, 1, &clk))
+ return 0;
+ if (!uaudio_process_unit(sc, NULL,
+ clk, units, &u->clock))
+ return 0;
+ break;
+ }
+ u->src_list = s;
+ s->src_next = NULL;
+ u->nch = s->nch;
+ uaudio_mkname(sc, uaudio_tname(term, 1), u->name);
+ DPRINTF("%02u: "
+    "out, id = %d, nch = %d, term = 0x%x, assoc = %d\n",
+    u->id, id, u->nch, term, assoc);
+ break;
+ case UAUDIO_AC_MIXER:
+ if (!uaudio_process_srcs(sc, u, units, &p))
+ return 0;
+ if (!uaudio_process_nch(sc, u, &p))
+ return 0;
+ DPRINTF("%02u: mixer, nch = %u:\n", u->id, u->nch);
+
+#ifdef UAUDIO_DEBUG
+ /*
+ * Print the list of available mixer's unit knobs (a bit
+ * matrix). Matrix mixers are rare because levels are
+ * already controlled by feature units, making the mixer
+ * knobs redundant with the feature's knobs. So, for
+ * now, we don't add clutter to the mixer(4) interface
+ * and ignore all knobs. Other popular OSes doesn't
+ * seem to expose them either.
+ */
+ bit = 0;
+ for (s = u->src_list; s != NULL; s = s->src_next) {
+ for (i = 0; i < s->nch; i++) {
+ for (j = 0; j < u->nch; j++) {
+ if ((bit++ & 7) == 0) {
+ if (!uaudio_getnum(&p, 1, &ctl))
+ return 0;
+ }
+ if (ctl & 0x80)
+ DPRINTF("\t%02u[%d] -> [%d]\n",
+    s->id, i, j);
+ ctl <<= 1;
+ }
+ }
+ }
+#endif
+ break;
+ case UAUDIO_AC_SELECTOR:
+ /*
+ * Selectors are extreamly rare, so not supported yet.
+ */
+ if (!uaudio_process_srcs(sc, u, units, &p))
+ return 0;
+ if (u->src_list == NULL) {
+ printf("%s: selector %02u has no sources\n",
+    DEVNAME(sc), u->id);
+ return 0;
+ }
+ u->nch = u->src_list->nch;
+ DPRINTF("%02u: selector, nch = %u\n", u->id, u->nch);
+ break;
+ case UAUDIO_AC_FEATURE:
+ if (!uaudio_getnum(&p, 1, &id))
+ return 0;
+ if (!uaudio_process_unit(sc, u, id, units, &s))
+ return 0;
+ s->src_next = u->src_list;
+ u->src_list = s;
+ u->nch = s->nch;
+ switch (sc->version) {
+ case UAUDIO_V1:
+ if (!uaudio_getnum(&p, 1, &size))
+ return 0;
+ break;
+ case UAUDIO_V2:
+ size = 4;
  break;
- default:
- printf("unknown audio control (subtype=%d)\n",
-       iot[i].d.desc->bDescriptorSubtype);
  }
- for (j = 0; j < iot[i].inputs_size; j++) {
- int k;
- printf("\tinput%d: ", j);
- tml = iot[i].inputs[j];
- if (tml == NULL) {
- printf("NULL\n");
- continue;
+ DPRINTF("%02d: feature id = %d, nch = %d, size = %d\n",
+    u->id, id, u->nch, size);
+ if (!uaudio_getnum(&p, size, &ctl))
+ return 0;
+ ctl = uaudio_feature_fixup(sc, ctl);
+ for (i = 0; i < 16; i++) {
+ if ((ctl & 3) == 3)
+ uaudio_feature_addent(sc, u, i, -1);
+ ctl >>= 2;
+ }
+ for (j = 0; j < u->nch; j++) {
+ if (!uaudio_getnum(&p, size, &ctl))
+ return 0;
+ ctl = uaudio_feature_fixup(sc, ctl);
+ for (i = 0; i < 16; i++) {
+ if ((ctl & 3) == 3)
+ uaudio_feature_addent(sc, u, i, j);
+ ctl >>= 2;
  }
- for (k = 0; k < tml->size; k++)
- printf("%s ", uaudio_get_terminal_name
-       (tml->terminals[k]));
- printf("\n");
  }
- printf("\toutput: ");
- tml = iot[i].output;
- for (j = 0; j < tml->size; j++)
- printf("%s ", uaudio_get_terminal_name(tml->terminals[j]));
- printf("\n");
+ break;
+ case UAUDIO_AC_EFFECT:
+ if (!uaudio_getnum(&p, 2, &type))
+ return 0;
+ if (!uaudio_getnum(&p, 1, &id))
+ return 0;
+ if (!uaudio_process_unit(sc, u, id, units, &s))
+ return 0;
+ s->src_next = u->src_list;
+ u->src_list = s;
+ u->nch = s->nch;
+ DPRINTF("%02d: effect, type = %u, id = %d, nch = %d\n",
+    u->id, type, id, u->nch);
+ break;
+ case UAUDIO_AC_PROCESSING:
+ case UAUDIO_AC_EXTENSION:
+ if (!uaudio_getnum(&p, 2, &type))
+ return 0;
+ if (!uaudio_process_srcs(sc, u, units, &p))
+ return 0;
+ if (!uaudio_process_nch(sc, u, &p))
+ return 0;
+ DPRINTF("%02u: proc/ext, type = 0x%x, nch = %u\n",
+    u->id, type, u->nch);
+ for (s = u->src_list; s != NULL; s = s->src_next) {
+ DPRINTF("%u:\tpin %u:\n", u->id, s->id);
+ }
+ break;
+ case UAUDIO_AC_CLKSRC:
+ if (!uaudio_getnum(&p, 1, &attr))
+ return 0;
+ if (!uaudio_getnum(&p, 1, &ctl))
+ return 0;
+ DPRINTF("%02u: clock source, attr = 0x%x, ctl = 0x%x\n",
+    u->id, attr, ctl);
+ uaudio_mkname(sc, uaudio_clkname(attr), u->name);
+ break;
+ case UAUDIO_AC_CLKSEL:
+ DPRINTF("%02u: clock sel\n", u->id);
+ if (!uaudio_process_srcs(sc, u, units, &p))
+ return 0;
+ if (u->src_list == NULL) {
+ printf("%s: clock selector %02u with no srcs\n",
+    DEVNAME(sc), u->id);
+ return 0;
+ }
+ uaudio_mkname(sc, "clksel", u->name);
+ break;
+ case UAUDIO_AC_CLKMULT:
+ DPRINTF("%02u: clock mult\n", u->id);
+
+ /* XXX: fetch multiplier */
+ printf("%s: clock multiplier not supported\n", DEVNAME(sc));
+ break;
+ case UAUDIO_AC_RATECONV:
+ DPRINTF("%02u: rate conv\n", u->id);
+
+ /* XXX: fetch multiplier */
+ printf("%s: rate converter not supported\n", DEVNAME(sc));
+ break;
  }
-#endif
+ if (rchild)
+ *rchild = u;
+ return 1;
+}
 
- for (i = 0; i < ndps; i++) {
- dp = iot[i].d.desc;
- if (dp == NULL)
- continue;
- DPRINTF(("%s: id=%d subtype=%d\n",
- __func__, i, dp->bDescriptorSubtype));
- switch (dp->bDescriptorSubtype) {
- case UDESCSUB_AC_HEADER:
- printf("%s: unexpected AC header\n", __func__);
- break;
- case UDESCSUB_AC_INPUT:
- uaudio_add_input(sc, iot, i);
- break;
- case UDESCSUB_AC_OUTPUT:
- uaudio_add_output(sc, iot, i);
+/*
+ * Try to set the unit name to the name of its destination terminal. If
+ * the name is ambigus (already given to another source unit or having
+ * multiple destinations) then return 0.
+ */
+int
+uaudio_setname_dsts(struct uaudio_softc *sc, struct uaudio_unit *u, char *name)
+{
+ struct uaudio_unit *d = u;
+
+ while (d != NULL) {
+ if (d->dst_list == NULL || d->dst_list->dst_next != NULL)
  break;
- case UDESCSUB_AC_MIXER:
- uaudio_add_mixer(sc, iot, i);
+ d = d->dst_list;
+ if (d->src_list == NULL || d->src_list->src_next != NULL)
  break;
- case UDESCSUB_AC_SELECTOR:
- uaudio_add_selector(sc, iot, i);
+ if (d->name[0] != '\0') {
+ if (name != NULL && strcmp(name, d->name) != 0)
+ break;
+ strlcpy(u->name, d->name, UAUDIO_NAMEMAX);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Try to set the unit name to the name of its source terminal. If the
+ * name is ambigus (already given to another destination unit or
+ * having multiple sources) then return 0.
+ */
+int
+uaudio_setname_srcs(struct uaudio_softc *sc, struct uaudio_unit *u, char *name)
+{
+ struct uaudio_unit *s = u;
+
+ while (s != NULL) {
+ if (s->src_list == NULL || s->src_list->src_next != NULL)
  break;
- case UDESCSUB_AC_FEATURE:
- uaudio_add_feature(sc, iot, i);
+ s = s->src_list;
+ if (s->dst_list == NULL || s->dst_list->dst_next != NULL)
  break;
- case UDESCSUB_AC_PROCESSING:
- uaudio_add_processing(sc, iot, i);
+ if (s->name[0] != '\0') {
+ if (name != NULL && strcmp(name, s->name) != 0)
+ break;
+ strlcpy(u->name, s->name, UAUDIO_NAMEMAX);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Set the name of the given unit by using both its source and
+ * destination units. This is naming scheme is only useful to units
+ * that would have ambigous names if only sources or only destination
+ * were used.
+ */
+void
+uaudio_setname_middle(struct uaudio_softc *sc, struct uaudio_unit *u)
+{
+ struct uaudio_unit *s, *d;
+ char name[UAUDIO_NAMEMAX];
+
+ s = u->src_list;
+ while (1) {
+ if (s == NULL) {
+ DPRINTF("uaudio_setname_middle: %02u: has no srcs\n",
+  u->id);
+ return;
+ }
+ if (s->name[0] != '\0')
  break;
- case UDESCSUB_AC_EXTENSION:
- uaudio_add_extension(sc, iot, i);
+ s = s->src_list;
+ }
+
+ d = u->dst_list;
+ while (1) {
+ if (d == NULL) {
+ DPRINTF("uaudio_setname_middle: %02u: has no dests\n",
+  u->id);
+ return;
+ }
+ if (d->name[0] != '\0')
  break;
- default:
- printf("%s: bad AC desc subtype=0x%02x\n",
-       __func__, dp->bDescriptorSubtype);
+ d = d->dst_list;
+ }
+
+ snprintf(name, UAUDIO_NAMEMAX, "%s_%s", d->name, s->name);
+ uaudio_mkname(sc, name, u->name);
+}
+
+#ifdef UAUDIO_DEBUG
+/*
+ * Return the synchronization type name, for debug purposes only.
+ */
+char *
+uaudio_isoname(int isotype)
+{
+ switch (isotype) {
+ case UE_ISO_ASYNC:
+ return "async";
+ case UE_ISO_ADAPT:
+ return "adapt";
+ case UE_ISO_SYNC:
+ return "sync";
+ default:
+ return "unk";
+ }
+}
+
+/*
+ * Return the name of the given mode, debug only
+ */
+char *
+uaudio_modename(int mode)
+{
+ switch (mode) {
+ case 0:
+ return "none";
+ case AUMODE_PLAY:
+ return "play";
+ case AUMODE_RECORD:
+ return "rec";
+ case AUMODE_PLAY | AUMODE_RECORD:
+ return "duplex";
+ default:
+ return "unk";
+ }
+}
+
+/*
+ * Return UAC v2.0 endpoint usage, debug only
+ */
+char *
+uaudio_usagename(int usage)
+{
+ switch (usage) {
+ case UE_ISO_USAGE_DATA:
+ return "data";
+ case UE_ISO_USAGE_FEEDBACK:
+ return "feed";
+ case UE_ISO_USAGE_IMPL:
+ return "impl";
+ default:
+ return "unk";
+ }
+}
+
+/*
+ * Print a bitmap of rates on the console.
+ */
+void
+uaudio_rates_print(int rates)
+{
+ unsigned int i;
+
+ for (i = 0; i < UAUDIO_NRATES; i++) {
+ if (rates & (1 << i))
+ printf(" %d", uaudio_rates[i]);
+ }
+ printf("\n");
+}
+
+
+/*
+ * Print uaudio_ranges to console.
+ */
+void
+uaudio_ranges_print(struct uaudio_ranges *r)
+{
+ struct uaudio_ranges_el *e;
+ int more = 0;
+
+ for (e = r->el; e != NULL; e = e->next) {
+ if (more)
+ printf(", ");
+ if (e->min == e->max)
+ printf("%d", e->min);
+ else
+ printf("[%d:%d]/%d", e->min, e->max, e->res);
+ more = 1;
+ }
+ printf(" (%d vals)\n", r->nval);
+}
+
+/*
+ * Print unit to the console.
+ */
+void
+uaudio_print_unit(struct uaudio_softc *sc, struct uaudio_unit *u)
+{
+ struct uaudio_unit *s;
+
+ switch (u->type) {
+ case UAUDIO_AC_INPUT:
+ printf("%02u: input <%s>, dest = %02u <%s>\n",
+    u->id, u->name, u->dst_list->id, u->dst_list->name);
+ break;
+ case UAUDIO_AC_OUTPUT:
+ printf("%02u: output <%s>, source = %02u <%s>\n",
+    u->id, u->name, u->src_list->id, u->src_list->name);
+ break;
+ case UAUDIO_AC_MIXER:
+ printf("%02u: mixer <%s>:\n", u->id, u->name);
+ for (s = u->src_list; s != NULL; s = s->src_next)
+ printf("%02u:\tsource %u <%s>:\n",
+    u->id, s->id, s->name);
+ break;
+ case UAUDIO_AC_SELECTOR:
+ printf("%02u: selector <%s>:\n", u->id, u->name);
+ for (s = u->src_list; s != NULL; s = s->src_next)
+ printf("%02u:\tsource %u <%s>:\n",
+    u->id, s->id, s->name);
+ break;
+ case UAUDIO_AC_FEATURE:
+ printf("%02u: feature <%s>, "
+    "src = %02u <%s>, dst = %02u <%s>, cls = %d\n",
+    u->id, u->name,
+    u->src_list->id, u->src_list->name,
+    u->dst_list->id, u->dst_list->name, u->mixer_class);
+ break;
+ case UAUDIO_AC_EFFECT:
+ printf("%02u: effect <%s>, "
+    "src = %02u <%s>, dst = %02u <%s>\n",
+    u->id, u->name,
+    u->src_list->id, u->src_list->name,
+    u->dst_list->id, u->dst_list->name);
+ break;
+ case UAUDIO_AC_PROCESSING:
+ case UAUDIO_AC_EXTENSION:
+ printf("%02u: proc/ext <%s>:\n", u->id, u->name);
+ for (s = u->src_list; s != NULL; s = s->src_next)
+ printf("%02u:\tsource %u <%s>:\n",
+    u->id, s->id, s->name);
+ break;
+ case UAUDIO_AC_CLKSRC:
+ printf("%02u: clock source <%s>\n", u->id, u->name);
+ break;
+ case UAUDIO_AC_CLKSEL:
+ printf("%02u: clock sel <%s>\n", u->id, u->name);
+ break;
+ case UAUDIO_AC_CLKMULT:
+ printf("%02u: clock mult\n", u->id);
+ break;
+ case UAUDIO_AC_RATECONV:
+ printf("%02u: rate conv\n", u->id);
+ break;
+ }
+}
+
+/*
+ * Print the full mixer on the console.
+ */
+void
+uaudio_mixer_print(struct uaudio_softc *sc)
+{
+ struct uaudio_mixent *m;
+ struct uaudio_unit *u;
+
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ for (m = u->mixent_list; m != NULL; m = m->next) {
+ printf("%02u:\t%s.%s",
+    u->id, u->name, m->fname);
+ if (m->chan >= 0)
+ printf("[%u]", m->chan);
+ printf("\n");
+ }
+ }
+}
+
+/*
+ * Print the full device configuration on the console.
+ */
+void
+uaudio_conf_print(struct uaudio_softc *sc)
+{
+ struct uaudio_alt *a;
+ struct uaudio_params *p;
+ struct mixer_devinfo mi;
+ struct mixer_ctrl ctl;
+ int i, rates;
+
+ mi.index = 0;
+ while (1) {
+ if (uaudio_query_devinfo(sc, &mi) != 0)
+ break;
+
+ if (mi.type != AUDIO_MIXER_CLASS) {
+ ctl.dev = mi.index;
+ if (uaudio_get_port(sc, &ctl) != 0) {
+ printf("%02u: failed to get port\n", mi.index);
+ memset(&ctl.un, 0, sizeof(ctl.un));
+ }
+ }
+
+ printf("%02u: <%s>, next = %d, prev = %d, class = %d",
+    mi.index, mi.label.name, mi.next, mi.prev, mi.mixer_class);
+
+ switch (mi.type) {
+ case AUDIO_MIXER_CLASS:
+ break;
+ case AUDIO_MIXER_VALUE:
+ printf(", nch = %d, delta = %d",
+    mi.un.v.num_channels, mi.un.v.delta);
+ printf(", val =");
+ for (i = 0; i < mi.un.v.num_channels; i++)
+ printf(" %d", ctl.un.value.level[i]);
+ break;
+ case AUDIO_MIXER_ENUM:
+ printf(", members:");
+ for (i = 0; i != mi.un.e.num_mem; i++) {
+ printf(" %s(=%d)",
+    mi.un.e.member[i].label.name,
+    mi.un.e.member[i].ord);
+ }
+ printf(", val = %d", ctl.un.ord);
+ break;
+ }
+
+ printf("\n");
+ mi.index++;
+ }
+
+ printf("%d controls\n", mi.index);
+
+ printf("alts:\n");
+ for (a = sc->alts; a != NULL; a = a->next) {
+ rates = uaudio_alt_getrates(sc, a);
+ printf("mode = %s, ifnum = %d, altnum = %d, "
+    "addr = 0x%x, maxpkt = %d, sync = 0x%x, "
+    "nch = %d, fmt = s%dle%d, rates:",
+    uaudio_modename(a->mode),
+    a->ifnum, a->altnum,
+    a->data_addr, a->maxpkt,
+    a->sync_addr,
+    a->nch, a->bits, a->bps);
+ uaudio_rates_print(rates);
+ }
+
+ printf("parameters:\n");
+ for (p = sc->params_list; p != NULL; p = p->next) {
+ switch (sc->version) {
+ case UAUDIO_V1:
+ rates = p->v1_rates;
+ break;
+ case UAUDIO_V2:
+ rates = uaudio_getrates(sc, p);
+ break;
+ }
+ printf("pchan = %d, s%dle%d, rchan = %d, s%dle%d, rates:",
+    p->palt ? p->palt->nch : 0,
+    p->palt ? p->palt->bits : 0,
+    p->palt ? p->palt->bps : 0,
+    p->ralt ? p->ralt->nch : 0,
+    p->ralt ? p->ralt->bits : 0,
+    p->ralt ? p->ralt->bps : 0);
+ uaudio_rates_print(rates);
+ }
+}
+#endif
+
+/*
+ * Return the number of mixer controls that have the same name but
+ * control different channels of the same stream.
+ */
+int
+uaudio_mixer_nchan(struct uaudio_mixent *m, struct uaudio_mixent **rnext)
+{
+ char *name;
+ int i;
+
+ i = 0;
+ name = m->fname;
+ while (m != NULL && strcmp(name, m->fname) == 0) {
+ m = m->next;
+ i++;
+ }
+ if (rnext)
+ *rnext = m;
+ return i;
+}
+
+/*
+ * Return pointer to the unit and mixer entry which have the given
+ * index exposed by the mixer(4) API.
+ */
+int
+uaudio_mixer_byindex(struct uaudio_softc *sc, int index,
+    struct uaudio_unit **ru, struct uaudio_mixent **rm)
+{
+ struct uaudio_unit *u;
+ struct uaudio_mixent *m;
+ char *name;
+ int i;
+
+ i = UAUDIO_CLASS_COUNT;
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ m = u->mixent_list;
+ while (1) {
+ if (m == NULL)
+ break;
+ if (index == i) {
+ *ru = u;
+ *rm = m;
+ return 1;
+ }
+ if (m->type == UAUDIO_MIX_NUM) {
+ name = m->fname;
+ while (m != NULL &&
+    strcmp(name, m->fname) == 0)
+ m = m->next;
+ } else
+ m = m->next;
+ i++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Parse AC header descriptor, we use it only to determine UAC
+ * version. Other properties (like wTotalLength) can be determined
+ * using other descriptors, so we try to no rely on them to avoid
+ * inconsistencies and the need for certain quirks.
+ */
+int
+uaudio_process_header(struct uaudio_softc *sc, struct uaudio_blob *p)
+{
+ struct uaudio_blob ph;
+ unsigned int type, subtype;
+
+ if (!uaudio_getdesc(p, &ph))
+ return 0;
+ if (!uaudio_getnum(&ph, 1, &type))
+ return 0;
+ if (type != UDESC_CS_INTERFACE) {
+ DPRINTF("uaudio_process_header: expected cs iface desc\n");
+ return 0;
+ }
+ if (!uaudio_getnum(&ph, 1, &subtype))
+ return 0;
+ if (subtype != UAUDIO_AC_HEADER) {
+ DPRINTF("uaudio_process_header: expected header desc\n");
+ return 0;
+ }
+ if (!uaudio_getnum(&ph, 2, &sc->version))
+ return 0;
+
+ DPRINTF("uaudio_process_header: version 0x%x\n", sc->version);
+ return 1;
+}
+
+/*
+ * Process AC interrupt endpoint descriptor, this is mainly to skip
+ * the descriptor as we use neither of it's properties. Our mixer
+ * interface doesn't support unsolicitated state changes, so we've no
+ * use of it yet.
+ */
+int
+uaudio_process_ac_ep(struct uaudio_softc *sc, struct uaudio_blob *p)
+{
+#ifdef UAUDIO_DEBUG
+ static const char *xfer[] = {
+ "ctl", "iso", "bulk", "intr"
+ };
+#endif
+ struct uaudio_blob dp;
+ unsigned int type, addr, attr, maxpkt, ival;
+ unsigned char *savepos;
+
+ /*
+ * parse optional interrupt endpoint descriptor
+ */
+ if (p->rptr == p->wptr)
+ return 1;
+ savepos = p->rptr;
+ if (!uaudio_getdesc(p, &dp))
+ return 0;
+ if (!uaudio_getnum(&dp, 1, &type))
+ return 0;
+ if (type != UDESC_ENDPOINT) {
+ p->rptr = savepos;
+ return 1;
+ }
+
+ if (!uaudio_getnum(&dp, 1, &addr))
+ return 0;
+ if (!uaudio_getnum(&dp, 1, &attr))
+ return 0;
+ if (!uaudio_getnum(&dp, 2, &maxpkt))
+ return 0;
+ if (!uaudio_getnum(&dp, 1, &ival))
+ return 0;
+
+ DPRINTF("uaudio_process_ac_ep: "
+    "addr = 0x%x, type = %s, maxpkt = %d, ival = %d\n",
+    addr, xfer[UE_GET_XFERTYPE(attr)], UE_GET_SIZE(maxpkt), ival);
+
+ return 1;
+}
+
+/*
+ * Process the AC interface descriptors: mainly build the mixer and,
+ * for UAC v2.0, find the clock source.
+ *
+ * The audio device exposes an audio control (AC) interface with a big
+ * set of USB descriptors which expose the complete circuit the
+ * device. The circuit describes how the signal flows between the USB
+ * streaming interfaces to the terminal connectors (jacks, speakers,
+ * mics, ...). The circuit is build of mixers, source selectors, gain
+ * controls, mutters, processors, and alike; each comes with its own
+ * set of controls. Most of the boring driver work is to parse the
+ * circuit and build a human-usable set of controls that could be
+ * exposed through the mixer(4) interface.
+ */
+int
+uaudio_process_ac(struct uaudio_softc *sc, struct uaudio_blob *p, int ifnum)
+{
+ struct uaudio_blob units, pu;
+ struct uaudio_unit *u, *v;
+ unsigned char *savepos;
+ unsigned int type, subtype, id;
+ char *name, val;
+
+ DPRINTF("uaudio_process_ac: ifnum = %d, %zd bytes to processs\n",
+    ifnum, p->wptr - p->rptr);
+
+ sc->ctl_ifnum = ifnum;
+
+ /* The first AC class-specific descriptor is the AC header */
+ if (!uaudio_process_header(sc, p))
+ return 0;
+
+ /*
+ * Determine the size of the AC descriptors array: scan
+ * descriptors until we get the first non-class-specific
+ * descriptor. This avoids relying on the wTotalLength field.
+ */
+ savepos = p->rptr;
+ units.rptr = p->rptr;
+ while (p->rptr != p->wptr) {
+ if (!uaudio_getdesc(p, &pu))
+ return 0;
+ if (!uaudio_getnum(&pu, 1, &type))
+ return 0;
+ if (type != UDESC_CS_INTERFACE)
+ break;
+ units.wptr = p->rptr;
+ }
+ p->rptr = savepos;
+
+ /*
+ * Load units, walking from outputs to inputs, as
+ * the usb audio class spec requires.
+ */
+ while (p->rptr != units.wptr) {
+ if (!uaudio_getdesc(p, &pu))
+ return 0;
+ if (!uaudio_getnum(&pu, 1, &type))
+ return 0;
+ if (!uaudio_getnum(&pu, 1, &subtype))
+ return 0;
+ if (subtype == UAUDIO_AC_OUTPUT) {
+ if (!uaudio_getnum(&pu, 1, &id))
+ return 0;
+ if (!uaudio_process_unit(sc, NULL, id, units, NULL))
+ return 0;
+ }
+ }
+
+ /*
+ * set effect and processor unit names
+ */
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ switch (u->type) {
+ case UAUDIO_AC_EFFECT:
+ uaudio_mkname(sc, "fx", u->name);
+ break;
+ case UAUDIO_AC_PROCESSING:
+ uaudio_mkname(sc, "proc", u->name);
+ break;
+ case UAUDIO_AC_EXTENSION:
+ uaudio_mkname(sc, "ext", u->name);
+ break;
+ }
+ }
+
+ /*
+ * set mixer/selector unit names
+ */
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ if (u->type != UAUDIO_AC_MIXER &&
+    u->type != UAUDIO_AC_SELECTOR)
+ continue;
+ if (!uaudio_setname_dsts(sc, u, NULL)) {
+ switch (u->type) {
+ case UAUDIO_AC_MIXER:
+ name = "mix";
+ break;
+ case UAUDIO_AC_SELECTOR:
+ name = "sel";
+ break;
+ }
+ uaudio_mkname(sc, name, u->name);
+ }
+ }
+
+ /*
+ * set feature unit names and classes
+ */
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ if (u->type != UAUDIO_AC_FEATURE)
+ continue;
+ if (uaudio_setname_dsts(sc, u, "record")) {
+ u->mixer_class = UAUDIO_CLASS_REC;
+ continue;
+ }
+ if (uaudio_setname_srcs(sc, u, "play")) {
+ u->mixer_class = UAUDIO_CLASS_OUT;
+ continue;
+ }
+ if (uaudio_setname_dsts(sc, u, NULL)) {
+ u->mixer_class = UAUDIO_CLASS_OUT;
+ continue;
+ }
+ if (uaudio_setname_srcs(sc, u, NULL)) {
+ u->mixer_class = UAUDIO_CLASS_IN;
+ continue;
+ }
+ uaudio_setname_middle(sc, u);
+ u->mixer_class = UAUDIO_CLASS_IN;
+ }
+
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug) {
+ printf("%s: units list:\n", DEVNAME(sc));
+ for (u = sc->unit_list; u != NULL; u = u->unit_next)
+ uaudio_print_unit(sc, u);
+
+ printf("%s: mixer controls:\n", DEVNAME(sc));
+ uaudio_mixer_print(sc);
+ }
+#endif
+
+ /* follows optional interrupt endpoint descriptor */
+ if (!uaudio_process_ac_ep(sc, p))
+ return 0;
+
+ /* fetch clock source rates */
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ switch (u->type) {
+ case UAUDIO_AC_CLKSRC:
+ if (!uaudio_req_ranges(sc, 4,
+ UAUDIO_V2_REQSEL_CLKFREQ,
+ 0, /* channel (not used) */
+ sc->ctl_ifnum,
+ u->id,
+ &u->rates))
+ return 1;
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug) {
+ printf("%02u: clock rates: ", u->id);
+ uaudio_ranges_print(&u->rates);
+ }
+#endif
+ break;
+ case UAUDIO_AC_CLKSEL:
+ if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
+ UAUDIO_V2_REQ_CUR,
+ UAUDIO_V2_REQSEL_CLKSEL, 0,
+ sc->ctl_ifnum, u->id,
+ &val, 1))
+ return 0;
+ for (v = u->src_list; v != NULL; v = v->src_next) {
+ if (--val == 0)
+ break;
+ }
+ u->clock = v;
+ break;
+ }
+ }
+
+ if (sc->version == UAUDIO_V2) {
+ /*
+ * Find common clock unit. We assume all terminals
+ * belong to the same clock domain (ie are connected
+ * to the same source)
+ */
+ sc->clock = NULL;
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ if (u->type != UAUDIO_AC_INPUT &&
+    u->type != UAUDIO_AC_OUTPUT)
+ continue;
+ if (sc->clock == NULL) {
+ if (u->clock == NULL) {
+ printf("%s: terminal with no clock\n",
+    DEVNAME(sc));
+ return 0;
+ }
+ sc->clock = u->clock;
+ } else if (u->clock != sc->clock) {
+ printf("%s: only one clock domain supported\n",
+    DEVNAME(sc));
+ return 0;
+ }
+ }
+
+ if (sc->clock == NULL) {
+ printf("%s: no clock found\n", DEVNAME(sc));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Parse endpoint descriptor with the following fromat:
+ *
+ * For playback there's a output data endpoint, of the
+ * following types:
+ *
+ *  type sync descr
+ *  -------------------------------------------------------
+ *  async: Yes the device uses it's own clock but
+ * sends feedback on a (input) sync endpoint
+ * for the host to adjust next packet size
+ *
+ *  sync: - data rate is constant, and device
+ * is clocked to the usb bus.
+ *
+ *  adapt: - the device adapts to data rate of the
+ * host. If fixed packet size is used,
+ * data rate is equivalent to the usb clock
+ * so this mode is the same as the
+ * sync mode.
+ *
+ * For recording there's and input data endpoint, of
+ * the following types:
+ *
+ *  type sync descr
+ *  -------------------------------------------------------
+ *  async: - the device uses its own clock and
+ * adjusts packet sizes.
+ *
+ *  sync: - the device uses usb clock rate
+ *
+ *  adapt: Yes the device uses host's feedback (on
+ * on a dedicated (output) sync endpoint
+ * to adapt to software's desired rate
+ *
+ *
+ * For usb1.1 ival is cardcoded to 1 for isochronous
+ * transfers, which means one transfer every ms. I.e one
+ * transfer every frame period.
+ *
+ * For usb2, ival the poll interval is:
+ *
+ * frame_period * 2^(ival - 1)
+ *
+ * so, if we use this formula, we get something working in all
+ * cases.
+ *
+ * The MaxPacketsOnly attribute is used only by "Type II" encodings,
+ * so we don't care about it.
+ */
+int
+uaudio_process_as_ep(struct uaudio_softc *sc,
+ struct uaudio_blob *p, struct uaudio_alt *a)
+{
+ unsigned int addr, attr, maxpkt, isotype, ival;
+
+ if (!uaudio_getnum(p, 1, &addr))
+ return 0;
+ if (!uaudio_getnum(p, 1, &attr))
+ return 0;
+ if (!uaudio_getnum(p, 2, &maxpkt))
+ return 0;
+ if (!uaudio_getnum(p, 1, &ival)) /* bInterval */
+ return 0;
+
+ DPRINTF("uaudio_process_as_ep: "
+    "addr = 0x%x, "
+    "%s/%s, "
+    "maxpktsz = %d, ival = %d\n",
+    addr,
+    uaudio_isoname(UE_GET_ISO_TYPE(attr)),
+    uaudio_usagename(UE_GET_ISO_USAGE(attr)),
+    maxpkt, ival);
+
+ if (UE_GET_XFERTYPE(attr) != UE_ISOCHRONOUS) {
+ printf("%s: skipped non-isoc endpt.\n", DEVNAME(sc));
+ return 1;
+ }
+
+ /*
+ * For each AS interface setting, there's a single data
+ * endpoint and an optional feedback endpoint. The
+ * synchonization type is non-zero and must be set in the data
+ * endpoints.
+ */
+ isotype = UE_GET_ISO_TYPE(attr);
+ if (isotype) {
+ /* this is the data endpoint */
+
+ if (a->data_addr && addr != a->data_addr) {
+ printf("%s: skipped extra data endpt.\n", DEVNAME(sc));
+ return 1;
+ }
+ if (UE_GET_DIR(addr) == UE_DIR_IN) {
+ a->mode = AUMODE_RECORD;
+ a->sync = (isotype == UE_ISO_ADAPT);
+ } else {
+ a->mode = AUMODE_PLAY;
+ a->sync = (isotype == UE_ISO_ASYNC);
+ }
+ a->data_addr = addr;
+ a->fps = sc->ufps / (1 << (ival - 1));
+ a->maxpkt = UE_GET_SIZE(maxpkt);
+ } else {
+ /* this is the sync endpoint */
+
+ if (a->sync_addr && addr != a->sync_addr) {
+ printf("%s: skipped extra sync endpt.\n", DEVNAME(sc));
+ return 1;
+ }
+ a->sync_addr = addr;
+ }
+
+ return 1;
+}
+
+/*
+ * Parse AS general descriptor. Non-PCM interfaces are skipped. UAC
+ * v2.0 report the number of channels. For UAC v1.0 we set the number
+ * of channels to zero, it will be determined later from the format
+ * descriptor.
+ */
+int
+uaudio_process_as_general(struct uaudio_softc *sc,
+ struct uaudio_blob *p, int *rispcm, struct uaudio_alt *a)
+{
+ unsigned int term, fmt, ctl, fmt_type, fmt_map, nch;
+
+ if (!uaudio_getnum(p, 1, &term))
+ return 0;
+ switch (sc->version) {
+ case UAUDIO_V1:
+ if (!uaudio_getnum(p, 1, NULL)) /* bDelay */
+ return 0;
+ if (!uaudio_getnum(p, 1, &fmt))
+ return 0;
+ *rispcm = (fmt == UAUDIO_V1_FMT_PCM);
+ break;
+ case UAUDIO_V2:
+ /* XXX: should we check if alt setting control is valid ? */
+ if (!uaudio_getnum(p, 1, &ctl))
+ return 0;
+ if (!uaudio_getnum(p, 1, &fmt_type))
+ return 0;
+ if (!uaudio_getnum(p, 4, &fmt_map))
+ return 0;
+ if (!uaudio_getnum(p, 1, &nch))
+ return 0;
+ a->nch = nch;
+ *rispcm = (fmt_type == 1) && (fmt_map & UAUDIO_V2_FMT_PCM);
+ }
+ return 1;
+}
+
+/*
+ * Parse AS format descriptor: we support only "Type 1" formats, aka
+ * PCM. Other formats are not really audio, they are data-only
+ * interfaces that we don't wan't to support: ethernet is much better
+ * for raw data transfers.
+ *
+ * XXX: handle ieee 754 32-bit floating point formats.
+ */
+int
+uaudio_process_as_format(struct uaudio_softc *sc,
+ struct uaudio_blob *p, struct uaudio_alt *a)
+{
+ unsigned int type, bps, bits, nch, nrates, rate_min, rate_max, rates;
+ int i, j;
+
+ switch (sc->version) {
+ case UAUDIO_V1:
+ if (!uaudio_getnum(p, 1, &type))
+ return 0;
+ if (type != 1) {
+ DPRINTF("uaudio_process_as_format: class v1: "
+    "skipped unsupported type = %d\n", type);
+ return 1;
+ }
+ if (!uaudio_getnum(p, 1, &nch))
+ return 0;
+ if (!uaudio_getnum(p, 1, &bps))
+ return 0;
+ if (!uaudio_getnum(p, 1, &bits))
+ return 0;
+ if (!uaudio_getnum(p, 1, &nrates))
+ return 0;
+ rates = 0;
+ if (nrates == 0) {
+ if (!uaudio_getnum(p, 3, &rate_min))
+ return 0;
+ if (!uaudio_getnum(p, 3, &rate_max))
+ return 0;
+ for (i = 0; i < UAUDIO_NRATES; i++) {
+ if (uaudio_rates[i] >= rate_min &&
+    uaudio_rates[i] <= rate_max)
+ rates |= 1 << i;
+ }
+ } else {
+ for (j = 0; j < nrates; j++) {
+ if (!uaudio_getnum(p, 3, &rate_min))
+ return 0;
+ for (i = 0; i < UAUDIO_NRATES; i++) {
+ if (uaudio_rates[i] == rate_min)
+ rates |= 1 << i;
+ }
+ }
+ }
+ a->v1_rates = rates;
+ a->nch = nch;
+ break;
+ case UAUDIO_V2:
+ /*
+ * sample rate ranges are obtained with requests to
+ * the clock source, as defined by the clock source
+ * descriptor
+ *
+ * the number of channels is in the GENERAL descriptor
+ */
+ if (!uaudio_getnum(p, 1, &type))
+ return 0;
+ if (type != 1) {
+ DPRINTF("uaudio_process_as_format: class v2: "
+    "skipped unsupported type = %d\n", type);
+ return 1;
+ }
+ if (!uaudio_getnum(p, 1, &bps))
+ return 0;
+ if (!uaudio_getnum(p, 1, &bits))
+ return 0;
+
+ /*
+ * nch is in the v2 general desc, rates come from the
+ * clock source, so we're done.
+ */
+ break;
+ }
+ a->bps = bps;
+ a->bits = bits;
+ return 1;
+}
+
+/*
+ * Parse AS descriptors.
+ *
+ * The audio streaming (AS) interfaces are used to move data between
+ * the host and the device. On the one hand, the device has
+ * analog-to-digital (ADC) and digital-to-analog (DAC) converters
+ * which have their own low-jitter clock source. On other hand, the
+ * USB host runs a bus clock using another clock source. So both
+ * drift. That's why, the device sends feedback to the driver for the
+ * host to adjust continuously its data rate, hence the need for sync
+ * endpoints.
+ */
+int
+uaudio_process_as(struct uaudio_softc *sc,
+    struct uaudio_blob *p, int ifnum, int altnum)
+{
+ struct uaudio_alt *a, *anext, **pa;
+ struct uaudio_blob dp;
+ unsigned char *savep;
+ unsigned int type, subtype;
+ int ispcm = 0;
+
+ a = malloc(sizeof(struct uaudio_alt), M_DEVBUF, M_WAITOK);
+ a->mode = 0;
+ a->nch = 0;
+ a->v1_rates = 0;
+ a->data_addr = 0;
+ a->sync_addr = 0;
+ a->ifnum = ifnum;
+ a->altnum = altnum;
+
+ while (p->rptr != p->wptr) {
+ savep = p->rptr;
+ if (!uaudio_getdesc(p, &dp))
+ goto failed;
+ if (!uaudio_getnum(&dp, 1, &type))
+ goto failed;
+ if (type != UDESC_CS_INTERFACE) {
+ p->rptr = savep;
+ break;
+ }
+ if (!uaudio_getnum(&dp, 1, &subtype))
+ goto failed;
+ switch (subtype) {
+ case UAUDIO_AS_GENERAL:
+ if (!uaudio_process_as_general(sc, &dp, &ispcm, a))
+ goto failed;
+ if (!ispcm) {
+ DPRINTF("uaudio_process_as: non-pcm iface\n");
+ free(a, M_DEVBUF, sizeof(struct uaudio_alt));
+ return 1;
+ }
+ break;
+ case UAUDIO_AS_FORMAT:
+ if (!uaudio_process_as_format(sc, &dp, a))
+ goto failed;
+ break;
+ default:
+ DPRINTF("uaudio_process_as: unknown desc\n");
+ }
+ }
+
+
+ while (p->rptr != p->wptr) {
+ savep = p->rptr;
+ if (!uaudio_getdesc(p, &dp))
+ goto failed;
+ if (!uaudio_getnum(&dp, 1, &type))
+ goto failed;
+ if (type != UDESC_ENDPOINT) {
+ p->rptr = savep;
  break;
  }
- }
-
- /* delete io_terminal */
- for (i = 0; i < 256; i++) {
- if (iot[i].d.desc == NULL)
- continue;
- if (iot[i].inputs != NULL) {
- for (j = 0; j < iot[i].inputs_size; j++)
- free(iot[i].inputs[j], M_TEMP, 0);
- free(iot[i].inputs, M_TEMP, 0);
- }
- free(iot[i].output, M_TEMP, 0);
- iot[i].d.desc = NULL;
- }
- free(iot, M_TEMP, 256 * sizeof(struct io_terminal));
-
- return (USBD_NORMAL_COMPLETION);
-}
 
-int
-uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi)
-{
- struct uaudio_softc *sc = addr;
- struct mixerctl *mc;
- int n, nctls, i;
-
- DPRINTFN(2,("%s: index=%d\n", __func__, mi->index));
- if (usbd_is_dying(sc->sc_udev))
- return (EIO);
-
- n = mi->index;
- nctls = sc->sc_nctls;
-
- switch (n) {
- case UAC_OUTPUT:
- mi->type = AUDIO_MIXER_CLASS;
- mi->mixer_class = UAC_OUTPUT;
- mi->next = mi->prev = AUDIO_MIXER_LAST;
- strlcpy(mi->label.name, AudioCoutputs, sizeof(mi->label.name));
- return (0);
- case UAC_INPUT:
- mi->type = AUDIO_MIXER_CLASS;
- mi->mixer_class = UAC_INPUT;
- mi->next = mi->prev = AUDIO_MIXER_LAST;
- strlcpy(mi->label.name, AudioCinputs, sizeof(mi->label.name));
- return (0);
- case UAC_EQUAL:
- mi->type = AUDIO_MIXER_CLASS;
- mi->mixer_class = UAC_EQUAL;
- mi->next = mi->prev = AUDIO_MIXER_LAST;
- strlcpy(mi->label.name, AudioCequalization,
-    sizeof(mi->label.name));
- return (0);
- case UAC_RECORD:
- mi->type = AUDIO_MIXER_CLASS;
- mi->mixer_class = UAC_RECORD;
- mi->next = mi->prev = AUDIO_MIXER_LAST;
- strlcpy(mi->label.name, AudioCrecord, sizeof(mi->label.name));
- return 0;
- default:
- break;
- }
+ if (!uaudio_process_as_ep(sc, &dp, a))
+ goto failed;
 
- n -= UAC_NCLASSES;
- if (n < 0 || n >= nctls)
- return (ENXIO);
-
- mc = &sc->sc_ctls[n];
- strlcpy(mi->label.name, mc->ctlname, sizeof(mi->label.name));
- 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;
- strlcpy(mi->un.e.member[0].label.name, AudioNoff,
-    sizeof(mi->un.e.member[0].label.name));
- mi->un.e.member[0].ord = 0;
- strlcpy(mi->un.e.member[1].label.name, AudioNon,
-    sizeof(mi->un.e.member[1].label.name));
- mi->un.e.member[1].ord = 1;
- break;
- case MIX_SELECTOR:
- mi->type = AUDIO_MIXER_ENUM;
- mi->un.e.num_mem = mc->maxval - mc->minval + 1;
- for (i = 0; i <= mc->maxval - mc->minval; i++) {
- snprintf(mi->un.e.member[i].label.name,
- sizeof(mi->un.e.member[i].label.name),
- "%d", i + mc->minval);
- mi->un.e.member[i].ord = i + mc->minval;
+ /* skip class-specific descriptors */
+ while (p->rptr != p->wptr) {
+ savep = p->rptr;
+ if (!uaudio_getdesc(p, &dp))
+ goto failed;
+ if (!uaudio_getnum(&dp, 1, &type))
+ goto failed;
+ if (type != UDESC_CS_ENDPOINT) {
+ p->rptr = savep;
+ break;
+ }
  }
- break;
- default:
- mi->type = AUDIO_MIXER_VALUE;
- strlcpy(mi->un.v.units.name, mc->ctlunit,
-    sizeof(mi->un.v.units.name));
- 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(("%s: sc=%p\n", __func__, sc));
- if (usbd_is_dying(sc->sc_udev))
- return (EIO);
+ if (a->mode == 0) {
+ printf("%s: no data endpoints found\n", DEVNAME(sc));
+ free(a, M_DEVBUF, sizeof(struct uaudio_alt));
+ return 1;
+ }
 
- if ((flags & FWRITE) && !(sc->sc_mode & AUMODE_PLAY))
- return (ENXIO);
- if ((flags & FREAD) && !(sc->sc_mode & AUMODE_RECORD))
- return (ENXIO);
+ /*
+ * Check that sync endpoint match data endpoint requirements
+ */
+ if (a->sync) {
+ if (a->sync_addr == 0) {
+ printf("%s: no sync endpoint found\n", DEVNAME(sc));
+ goto failed;
+ }
+ } else {
+ if (a->sync_addr != 0) {
+ printf("%s: sync endpoint not needed\n", DEVNAME(sc));
+ goto failed;
+ }
+ }
 
- return (0);
+ /*
+ * Append to list of alts, but keep the list sorted by number
+ * of channels, bits and rate. From the most capable to the
+ * less capable.
+ */
+ pa = &sc->alts;
+ while (1) {
+ if ((anext = *pa) == NULL)
+ break;
+ if (a->nch > anext->nch)
+ break;
+ else if (a->nch == anext->nch) {
+ if (a->bits > anext->bits)
+ break;
+ else if (sc->version == UAUDIO_V1 &&
+    a->v1_rates > anext->v1_rates)
+ break;
+ }
+ pa = &anext->next;
+ }
+ a->next = *pa;
+ *pa = a;
+ return 1;
+failed:
+ free(a, M_DEVBUF, sizeof(struct uaudio_alt));
+ return 0;
 }
 
 /*
- * Close function is called at splaudio().
+ * Populate the sc->params_list with combinations of play and rec alt
+ * settings that work together in full-duplex.
  */
 void
-uaudio_close(void *addr)
-{
- struct uaudio_softc *sc = addr;
-
- if (sc->sc_playchan.altidx != -1)
- uaudio_chan_close(sc, &sc->sc_playchan);
- if (sc->sc_recchan.altidx != -1)
- uaudio_chan_close(sc, &sc->sc_recchan);
-}
-
-int
-uaudio_halt_out_dma(void *addr)
+uaudio_fixup_params(struct uaudio_softc *sc)
 {
- struct uaudio_softc *sc = addr;
+ struct uaudio_alt *ap, *ar, *a;
+ struct uaudio_params *p, **pp;
+ int rates;
 
- DPRINTF(("%s: enter\n", __func__));
- if (sc->sc_playchan.pipe != NULL) {
- uaudio_chan_close(sc, &sc->sc_playchan);
- sc->sc_playchan.pipe = NULL;
- if (sc->sc_playchan.sync_pipe != NULL)
- sc->sc_playchan.sync_pipe = NULL;
- uaudio_chan_free_buffers(sc, &sc->sc_playchan);
- sc->sc_playchan.intr = NULL;
+ /*
+ * Add full-duplex parameter combinations.
+ */
+ pp = &sc->params_list;
+ for (ap = sc->alts; ap != NULL; ap = ap->next) {
+ if (ap->mode != AUMODE_PLAY)
+ continue;
+ for (ar = sc->alts; ar != NULL; ar = ar->next) {
+ if (ar->mode != AUMODE_RECORD)
+ continue;
+ if (ar->bps != ap->bps || ar->bits != ap->bits)
+ continue;
+ switch (sc->version) {
+ case UAUDIO_V1:
+ rates = ap->v1_rates & ar->v1_rates;
+ if (rates == 0)
+ continue;
+ break;
+ case UAUDIO_V2:
+ /* UAC v2.0 common rates */
+ rates = 0;
+ break;
+ }
+ p = malloc(sizeof(struct uaudio_params),
+    M_DEVBUF, M_WAITOK);
+ p->palt = ap;
+ p->ralt = ar;
+ p->v1_rates = rates;
+ p->next = NULL;
+ *pp = p;
+ pp = &p->next;
+ }
  }
- return (0);
-}
-
-int
-uaudio_halt_in_dma(void *addr)
-{
- struct uaudio_softc *sc = addr;
 
- DPRINTF(("%s: enter\n", __func__));
- if (sc->sc_recchan.pipe != NULL) {
- uaudio_chan_close(sc, &sc->sc_recchan);
- sc->sc_recchan.pipe = NULL;
- if (sc->sc_recchan.sync_pipe != NULL)
- sc->sc_recchan.sync_pipe = NULL;
- uaudio_chan_free_buffers(sc, &sc->sc_recchan);
- sc->sc_recchan.intr = NULL;
+ /*
+ * For unidirectional devices, add play-only and or rec-only
+ * parameters.
+ */
+ if (sc->params_list == NULL) {
+ for (a = sc->alts; a != NULL; a = a->next) {
+ p = malloc(sizeof(struct uaudio_params),
+    M_DEVBUF, M_WAITOK);
+ if (a->mode == AUMODE_PLAY) {
+ p->palt = a;
+ p->ralt = NULL;
+ } else {
+ p->palt = NULL;
+ p->ralt = a;
+ }
+ p->v1_rates = a->v1_rates;
+ p->next = NULL;
+ *pp = p;
+ pp = &p->next;
+ }
  }
- return (0);
 }
 
 /*
- * Make sure the block size is large enough to hold at least 1 transfer.
- * Ideally, the block size should be a multiple of the transfer size.
- * Currently, the transfer size for play and record can differ, and there's
- * no way to round playback and record blocksizes separately.
+ * Parse all descriptors and build configuration of the device.
  */
 int
-uaudio_round_blocksize(void *addr, int blk)
+uaudio_process_conf(struct uaudio_softc *sc, struct uaudio_blob *p)
 {
- struct uaudio_softc *sc = addr;
- int bpf, pbpf, rbpf;
+ struct uaudio_blob dp;
+ unsigned int type, ifnum, altnum, nep, class, subclass;
+ int nac = 0;
 
- DPRINTF(("%s: p.mbpf=%d r.mbpf=%d\n", __func__,
- sc->sc_playchan.max_bytes_per_frame,
- sc->sc_recchan.max_bytes_per_frame));
+ while (p->rptr != p->wptr) {
+ if (!uaudio_getdesc(p, &dp))
+ return 0;
+ if (!uaudio_getnum(&dp, 1, &type))
+ return 0;
+ if (type != UDESC_INTERFACE)
+ continue;
+ if (!uaudio_getnum(&dp, 1, &ifnum))
+ return 0;
+ if (!uaudio_getnum(&dp, 1, &altnum))
+ return 0;
+ if (!uaudio_getnum(&dp, 1, &nep))
+ return 0;
+ if (!uaudio_getnum(&dp, 1, &class))
+ return 0;
+ if (!uaudio_getnum(&dp, 1, &subclass))
+ return 0;
+ if (class != UICLASS_AUDIO) {
+ DPRINTF("uaudio_process_conf: skipped iface\n");
+ continue;
+ }
 
- pbpf = rbpf = 0;
- if (sc->sc_mode & AUMODE_PLAY) {
- pbpf = (sc->sc_playchan.max_bytes_per_frame) *
-    sc->sc_playchan.nframes;
- }
- if (sc->sc_mode & AUMODE_RECORD) {
- rbpf = (sc->sc_recchan.max_bytes_per_frame) *
-    sc->sc_recchan.nframes;
+ switch (subclass) {
+ case UISUBCLASS_AUDIOCONTROL:
+ usbd_claim_iface(sc->udev, ifnum);
+ if (nac == 1) {
+ printf("%s: only one AC iface allowed\n",
+    DEVNAME(sc));
+ return 0;
+ }
+ if (!uaudio_process_ac(sc, p, ifnum))
+ return 0;
+ nac++;
+ break;
+ case UISUBCLASS_AUDIOSTREAM:
+ usbd_claim_iface(sc->udev, ifnum);
+ if (altnum == 0)
+ break; /* 0 is "stop sound", skip it */
+ if (!uaudio_process_as(sc, p, ifnum, altnum))
+ return 0;
+ }
  }
- bpf = max(pbpf, rbpf);
 
- if (blk < bpf)
- blk = bpf;
-
-#ifdef DIAGNOSTIC
- if (blk <= 0) {
- printf("%s: blk=%d\n", __func__, blk);
- blk = 512;
- }
-#endif
+ uaudio_fixup_params(sc);
 
- DPRINTFN(1,("%s: blk=%d\n", __func__, blk));
- return (blk);
+ return 1;
 }
 
+/*
+ * Allocate a isochronous transfer and its bounce-buffers with the
+ * given maximum framesize and maximum frames per transfer.
+ */
 int
-uaudio_get_props(void *addr)
+uaudio_xfer_alloc(struct uaudio_softc *sc, struct uaudio_xfer *xfer,
+ unsigned int framesize, unsigned int count)
 {
- struct uaudio_softc *sc = addr;
- int props = 0;
+ xfer->usb_xfer = usbd_alloc_xfer(sc->udev);
+ if (xfer->usb_xfer == NULL)
+ return ENOMEM;
+
+ xfer->buf = usbd_alloc_buffer(xfer->usb_xfer, framesize * count);
+ if (xfer->buf == NULL)
+ return ENOMEM;
+
+ xfer->sizes = mallocarray(count,
+    sizeof(xfer->sizes[0]), M_DEVBUF, M_WAITOK);
+ if (xfer->sizes == NULL)
+ return ENOMEM;
 
- if (!(sc->sc_quirks & UAUDIO_FLAG_DEPENDENT))
- props |= AUDIO_PROP_INDEPENDENT;
-
- if ((sc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD)) ==
-    (AUMODE_PLAY | AUMODE_RECORD))
- props |= AUDIO_PROP_FULLDUPLEX;
-
- return props;
+ return 0;
 }
 
-int
-uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue,
-   int wIndex, int len)
+/*
+ * Free a isochronous transfer and its bounce-buffers.
+ */
+void
+uaudio_xfer_free(struct uaudio_softc *sc, struct uaudio_xfer *xfer,
+ unsigned int count)
 {
- usb_device_request_t req;
- u_int8_t data[4];
- usbd_status err;
- int val;
-
- if (wValue == -1)
- return (0);
-
- req.bmRequestType = type;
- req.bRequest = which;
- USETW(req.wValue, wValue);
- USETW(req.wIndex, wIndex);
- USETW(req.wLength, len);
- DPRINTFN(2,("%s: type=0x%02x req=0x%02x wValue=0x%04x "
-    "wIndex=0x%04x len=%d\n",
-    __func__, type, which, wValue, wIndex, len));
- err = usbd_do_request(sc->sc_udev, &req, data);
- if (err) {
- DPRINTF(("%s: err=%s\n", __func__, usbd_errstr(err)));
- return (-1);
- }
- switch (len) {
- case 1:
- val = data[0];
- break;
- case 2:
- val = data[0] | (data[1] << 8);
- break;
- default:
- DPRINTF(("%s: bad length=%d\n", __func__, len));
- return (-1);
+ if (xfer->usb_xfer != NULL) {
+ /* frees request buffer as well */
+ usbd_free_xfer(xfer->usb_xfer);
+ xfer->usb_xfer = NULL;
+ }
+ if (xfer->sizes != NULL) {
+ free(xfer->sizes, M_DEVBUF,
+    sizeof(xfer->sizes[0]) * count);
+ xfer->sizes = NULL;
  }
- DPRINTFN(2,("%s: val=%d\n", __func__, val));
- return (val);
 }
 
+/*
+ * Close a stream and free all associated resources
+ */
 void
-uaudio_set(struct uaudio_softc *sc, int which, int type, int wValue,
-   int wIndex, int len, int val)
+uaudio_stream_close(struct uaudio_softc *sc, int dir)
 {
- usb_device_request_t req;
- u_int8_t data[4];
- usbd_status err;
+ struct uaudio_stream *s = &sc->pstream;
+ struct uaudio_alt *a = sc->params->palt;
+ struct usbd_interface *iface;
+ int err, i;
+
+ if (dir == AUMODE_PLAY) {
+ s = &sc->pstream;
+ a = sc->params->palt;
+ } else {
+ s = &sc->rstream;
+ a = sc->params->ralt;
+ }
 
- if (wValue == -1)
- return;
+ err = usbd_device2interface_handle(sc->udev, a->ifnum, &iface);
+ if (err)
+ printf("%s: can't get iface handle\n", DEVNAME(sc));
 
- 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;
+ if (s->data_pipe) {
+ usbd_abort_pipe(s->data_pipe);
+ usbd_close_pipe(s->data_pipe);
+ s->data_pipe = NULL;
  }
- DPRINTFN(2,("%s: type=0x%02x req=0x%02x wValue=0x%04x "
-    "wIndex=0x%04x len=%d, val=%d\n", __func__,
-    type, which, wValue, wIndex, len, val & 0xffff));
- err = usbd_do_request(sc->sc_udev, &req, data);
-#ifdef UAUDIO_DEBUG
- if (err)
- DPRINTF(("%s: err=%d\n", __func__, 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;
+ if (s->sync_pipe) {
+ usbd_abort_pipe(s->sync_pipe);
+ usbd_close_pipe(s->sync_pipe);
+ s->sync_pipe = NULL;
  }
- return (val);
-}
 
-int
-uaudio_unsignext(int type, int val)
-{
- if (!MIX_UNSIGNED(type)) {
- if (MIX_SIZE(type) == 2)
- val = (u_int16_t)val;
- else
- val = (u_int8_t)val;
+ err = usbd_set_interface(iface, 0);
+ if (err)
+ printf("%s: can't reset interface\n", DEVNAME(sc));
+
+ for (i = 0; i < UAUDIO_NXFERS; i++) {
+ uaudio_xfer_free(sc, s->data_xfers + i, s->nframes_max);
+ uaudio_xfer_free(sc, s->sync_xfers + i, 1);
  }
- return (val);
 }
 
+/*
+ * Open a stream with the given buffer settings and set the current
+ * interface alt setting.
+ */
 int
-uaudio_value2bsd(struct mixerctl *mc, int val)
+uaudio_stream_open(struct uaudio_softc *sc, int dir,
+    void *start, void *end, size_t blksz, void (*intr)(void *), void *arg)
 {
- int range;
- DPRINTFN(5, ("%s: type=%03x val=%d min=%d max=%d ",
-     __func__, mc->type, val, mc->minval, mc->maxval));
- if (mc->type == MIX_ON_OFF) {
- val = (val != 0);
- } else if (mc->type == MIX_SELECTOR) {
- if (val < mc->minval || val > mc->maxval)
- val = mc->minval;
+ struct uaudio_stream *s;
+ struct uaudio_alt *a;
+ struct usbd_interface *iface;
+ unsigned char req_buf[4];
+ unsigned int nsamp, spf_max;
+ int err, i;
+
+ if (dir == AUMODE_PLAY) {
+ s = &sc->pstream;
+ a = sc->params->palt;
  } else {
- range = mc->maxval - mc->minval;
- if (range == 0)
- val = 0;
- else
- val = 255 * (uaudio_signext(mc->type, val) -
-    mc->minval) / range;
+ s = &sc->rstream;
+ a = sc->params->ralt;
  }
- DPRINTFN(5, ("val'=%d\n", val));
- return (val);
-}
 
-int
-uaudio_bsd2value(struct mixerctl *mc, int val)
-{
- DPRINTFN(5,("%s: type=%03x val=%d min=%d max=%d ",
-    __func__, mc->type, val, mc->minval, mc->maxval));
- if (mc->type == MIX_ON_OFF) {
- val = (val != 0);
- } else if (mc->type == MIX_SELECTOR) {
- if (val < mc->minval || val > mc->maxval)
- val = mc->minval;
- } else
- val = uaudio_unsignext(mc->type,
-    val * (mc->maxval - mc->minval) / 255 + mc->minval);
- DPRINTFN(5, ("val'=%d\n", val));
- return (val);
-}
+ for (i = 0; i < UAUDIO_NXFERS; i++) {
+ s->data_xfers[i].usb_xfer = NULL;
+ s->data_xfers[i].sizes = NULL;
+ s->sync_xfers[i].usb_xfer = NULL;
+ s->sync_xfers[i].sizes = NULL;
+ }
+ s->data_pipe = NULL;
+ s->sync_pipe = NULL;
 
-int
-uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc,
-       int chan)
-{
- int val;
+ /* block size in samples, i.e. samples per xfer */
+ nsamp = blksz / (a->nch * a->bps);
 
- DPRINTFN(5,("%s: which=%d chan=%d\n", __func__, 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));
-}
+ s->nframes = a->fps * nsamp / sc->rate;
+ if (s->nframes == 0) {
+ printf("%s: zero frames per transfer, blksz = %zu\n",
+    DEVNAME(sc), blksz);
+ return EIO;
+ }
 
-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);
-}
+ /* check that round_blocksize() picked the right blocks size */
+ if (nsamp % s->nframes != 0) {
+ printf("%s: block size (%d samples) not multiple of "
+    "frame per xfer (%d)\n", DEVNAME(sc), nsamp, s->nframes);
+ return EIO;
+ }
 
-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;
+ s->spf = (nsamp / s->nframes) << 16;
+ if (s->spf == 0) {
+ printf("%s: zero samples per frame, blksz = %zu\n",
+    DEVNAME(sc), blksz);
+ return EIO;
+ }
 
- DPRINTFN(2,("%s: index=%d\n", __func__, cp->dev));
+ s->spf_min = s->spf * 7 / 8;
+ s->spf_max = s->spf * 9 / 8;
 
- if (usbd_is_dying(sc->sc_udev))
- return (EIO);
+ spf_max = (a->maxpkt / (a->bps * a->nch)) << 16;
+ if (s->spf_max > spf_max)
+ s->spf_max = spf_max;
 
- n = cp->dev - UAC_NCLASSES;
- if (n < 0 || n >= sc->sc_nctls)
- return (ENXIO);
- mc = &sc->sc_ctls[n];
+ s->nframes_min = (nsamp << 16ULL) / s->spf_max;
+ s->nframes_max = (nsamp << 16ULL) / s->spf_min + 1;
 
- 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 (mc->type == MIX_SELECTOR) {
- 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);
-}
+ s->maxpkt = a->bps * a->nch * ((s->spf_max + 0xffff) >> 16);
 
-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,("%s: index = %d\n", __func__, cp->dev));
- if (usbd_is_dying(sc->sc_udev))
- return (EIO);
-
- n = cp->dev - UAC_NCLASSES;
- 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 (mc->type == MIX_SELECTOR) {
- 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);
-}
+ /*
+ * we want to be sure that the device will cross the block
+ * boundary, so we ask for slightly more frames than a single
+ * block
+ */
+ s->safe_blksz = blksz +
+    (a->fps / USB_FRAMES_PER_SECOND) *
+    (s->spf >> 16) * a->bps * a->nch;
+ s->nframes_max += (a->fps / USB_FRAMES_PER_SECOND);
+
+ DPRINTF("uaudio_stream_open: dir = %s, nsamp = %u, maxpkt = %u\n",
+    dir == AUMODE_PLAY ? "play" : "rec", nsamp, s->maxpkt);
+ DPRINTF("uaudio_stream_open: spf = 0x%x in [0x%x:0x%x]\n",
+    s->spf, s->spf_min, s->spf_max);
+ DPRINTF("uaudio_stream_open: nframes = %u in [%u:%u]\n",
+    s->nframes, s->nframes_min, s->nframes_max);
+ DPRINTF("uaudio_stream_open: blksz = %zd, safe_blksz = %d\n",
+    blksz, s->safe_blksz);
 
-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_recchan;
- usbd_status err;
- int i, s;
+ /*
+ * XXX: there are usb driver bugs & limitations, just fail if
+ * we hit them:
+ *
+ * - there must be at least 3 outstanding transfers, otherwise
+ *   transfers don't complete timely and audio stutters
+ *
+ * - large play xfers don't work (playback stutters in
+ *   full-duplex mode).  It appears that full-duplex with 25ms
+ *   blocks kinda works on uhci and ehci (on root hub only).
+ */
+ if (UAUDIO_NXFERS * blksz > end - start) {
+ printf("%s: number of blocks must be at least %d\n",
+    DEVNAME(sc), UAUDIO_NXFERS);
+ return EIO;
+ }
+ if (s->nframes > 25 * (a->fps / USB_FRAMES_PER_SECOND)) {
+ printf("%s: block size must be at most 25ms\n", DEVNAME(sc));
+ return EIO;
+ }
 
- if (usbd_is_dying(sc->sc_udev))
- return (EIO);
+ /*
+ * XXX: Isochronous transfers don't work with certain USB
+ * host drivers. Until they get fixed, disallow configurations
+ * where audio is well known to be broken.
+ */
+#if NXHCI > 0
+ if (sc->udev->bus->bdev.dv_cfdata->cf_driver == &xhci_cd) {
+ printf("%s: xhci(4) not supported yet, "
+    "try another port\n", DEVNAME(sc));
+ return ENXIO;
+ }
+#endif
+#if NEHCI > 0
+ if (sc->udev->bus->bdev.dv_cfdata->cf_driver == &ehci_cd &&
+    sc->udev->speed == USB_SPEED_FULL &&
+    sc->udev->myhub != sc->udev->bus->root_hub) {
+ printf("%s: usb1.1 device not on ehci(4) root hub, "
+    "try another port.\n", DEVNAME(sc));
+ return ENXIO;
+ }
+#endif
 
- DPRINTFN(3,("%s: sc=%p start=%p end=%p "
-    "blksize=%d\n", __func__, sc, start, end, blksize));
+ for (i = 0; i < UAUDIO_NXFERS; i++) {
+ err = uaudio_xfer_alloc(sc, s->data_xfers + i,
+    s->maxpkt, s->nframes_max);
+ if (err)
+ goto failed;
+ if (a->sync) {
+ err = uaudio_xfer_alloc(sc, s->sync_xfers + i,
+    sc->sync_pktsz, 1);
+ if (err)
+ goto failed;
+ }
+ }
 
- uaudio_chan_set_param(ch, start, end, blksize);
- DPRINTFN(3,("%s: sample_size=%d bytes/frame=%d "
-    "fraction=0.%03d\n",
-    __func__, ch->sample_size, ch->bytes_per_frame,
-    ch->fraction));
+ err = usbd_device2interface_handle(sc->udev, a->ifnum, &iface);
+ if (err) {
+ printf("%s: can't get iface handle\n", DEVNAME(sc));
+ goto failed;
+ }
 
- err = uaudio_chan_alloc_buffers(sc, ch);
- if (err)
- return (EIO);
+ err = usbd_set_interface(iface, a->altnum);
+ if (err) {
+ printf("%s: can't get interface handle\n", DEVNAME(sc));
+ goto failed;
+ }
 
- err = uaudio_chan_open(sc, ch);
+ err = usbd_open_pipe(iface, a->data_addr, 0, &s->data_pipe);
  if (err) {
- uaudio_chan_free_buffers(sc, ch);
- return (EIO);
+ printf("%s: can't open data pipe\n", DEVNAME(sc));
+ goto failed;
  }
 
- ch->intr = intr;
- ch->arg = arg;
+ if (a->sync) {
+ err = usbd_open_pipe(iface, a->sync_addr, 0, &s->sync_pipe);
+ if (err) {
+ printf("%s: can't open sync pipe\n", DEVNAME(sc));
+ goto failed;
+ }
+ }
 
- s = splusb();
- for (i = 0; i < UAUDIO_NCHANBUFS; i++)
- uaudio_chan_rtransfer(ch);
- splx(s);
+ switch (sc->version) {
+ case UAUDIO_V1:
+ req_buf[0] = sc->rate;
+ req_buf[1] = sc->rate >> 8;
+ req_buf[2] = sc->rate >> 16;
+ if (!uaudio_req(sc, UT_WRITE_CLASS_ENDPOINT,
+ UAUDIO_V1_REQ_SET_CUR, UAUDIO_REQSEL_RATE, 0,
+ a->data_addr, 0, req_buf, 3)) {
+ printf("%s: failed to set ep rate\n", DEVNAME(sc));
+ goto failed;
+ }
+ break;
+ case UAUDIO_V2:
+ req_buf[0] = sc->rate;
+ req_buf[1] = sc->rate >> 8;
+ req_buf[2] = sc->rate >> 16;
+ req_buf[3] = sc->rate >> 24;
+ if (!uaudio_req(sc, UT_WRITE_CLASS_INTERFACE,
+ UAUDIO_V2_REQ_CUR, UAUDIO_REQSEL_RATE, 0,
+ a->ifnum, sc->clock->id, req_buf, 4)) {
+ printf("%s: failed to set clock rate\n", DEVNAME(sc));
+ goto failed;
+ }
+ break;
+ }
+
+ s->data_nextxfer = 0;
+ s->sync_nextxfer = 0;
+ s->remain = 0;
+
+ s->intr = intr;
+ s->arg = arg;
+ s->ring_start = start;
+ s->ring_end = end;
+ s->ring_blksz = blksz;
+
+ s->ring_pos = s->ring_start;
+ s->ring_offs = 0;
+
+ /*
+ * For play mode, we don't know if the device is locked, if it
+ * isn't it will play silence, which is OK. For recording, we've
+ * to generate silence for upper layers to stay in sync.
+ */
+ s->locked = (dir == AUMODE_PLAY) ? 1 : 0;
+ return 0;
 
- return (0);
+failed:
+ uaudio_stream_close(sc, dir);
+ return ENOMEM;
 }
 
-int
-uaudio_trigger_output(void *addr, void *start, void *end, int blksize,
-      void (*intr)(void *), void *arg,
-      struct audio_params *param)
+/*
+ * Submit a play data transfer to the USB driver.
+ */
+void
+uaudio_pdata_xfer(struct uaudio_softc *sc)
 {
- struct uaudio_softc *sc = addr;
- struct chan *ch = &sc->sc_playchan;
- usbd_status err;
- int i, s;
+#ifdef UAUDIO_DEBUG
+ struct timeval tv;
+#endif
+ struct uaudio_stream *s = &sc->pstream;
+ struct uaudio_alt *a = sc->params->palt;
+ struct uaudio_xfer *xfer;
+ unsigned int fsize, bpf;
+ int done;
+ size_t count;
+ unsigned char *buf;
+ int err;
+
+ xfer = s->data_xfers + s->data_nextxfer;
+ bpf = a->bps * a->nch;
+ buf = xfer->buf;
+ xfer->nframes = 0;
+ done = s->ring_offs;
 
- if (usbd_is_dying(sc->sc_udev))
- return (EIO);
+ while (1) {
+ /*
+ * if we crossed the next block boundary, we're done
+ */
+ if (done >= s->safe_blksz) {
+ done:
+ xfer->size = done - s->ring_offs;
+ s->ring_offs = done - s->ring_blksz;
+ break;
+ }
 
- DPRINTFN(3,("%s: sc=%p start=%p end=%p "
-    "blksize=%d\n", __func__, sc, start, end, blksize));
+ /*
+ * this can't happen, debug only
+ */
+ if (xfer->nframes == s->nframes_max) {
+ printf("%s: too many frames for xfer: "
+    "done = %u, blksz = %d\n",
+    DEVNAME(sc), done, s->ring_blksz);
+ goto done;
+ }
 
- uaudio_chan_set_param(ch, start, end, blksize);
- DPRINTFN(3,("%s: sample_size=%d bytes/frame=%d "
-    "fraction=0.%03d\n", __func__, ch->sample_size,
-    ch->bytes_per_frame, ch->fraction));
+ /*
+ * calculate frame size and adjust state
+ */
+ s->remain += s->spf;
+ fsize = (s->remain >> 16) * bpf;
+ s->remain &= 0xffff;
+ done += fsize;
+ xfer->sizes[xfer->nframes] = fsize;
+ xfer->nframes++;
 
- err = uaudio_chan_alloc_buffers(sc, ch);
- if (err)
- return (EIO);
+ /*
+ * fill the frame from ring buffer to frame, handling
+ * boundary conditions
+ */
+ while (fsize > 0) {
+ count = s->ring_end - s->ring_pos;
+ if (count > fsize)
+ count = fsize;
+ memcpy(buf, s->ring_pos, count);
+ s->ring_pos += count;
+ if (s->ring_pos == s->ring_end)
+ s->ring_pos = s->ring_start;
+ buf += count;
+ fsize -= count;
+ }
 
- err = uaudio_chan_open(sc, ch);
- if (err) {
- uaudio_chan_free_buffers(sc, ch);
- return (EIO);
  }
 
- ch->intr = intr;
- ch->arg = arg;
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug >= 2) {
+ getmicrotime(&tv);
+ printf("uaudio_pdata_xfer: %llu.%06lu: "
+    "%luB, %u fr, remain = 0x%x, offs = %d\n",
+    tv.tv_sec, tv.tv_usec,
+    buf - xfer->buf, xfer->nframes, s->remain, s->ring_offs);
+ }
+#endif
 
- s = splusb();
- for (i = 0; i < UAUDIO_NCHANBUFS; i++)
- uaudio_chan_ptransfer(ch);
- if (ch->sync_pipe) {
- for (i = 0; i < UAUDIO_NSYNCBUFS; i++)
- uaudio_chan_psync_transfer(ch);
+ /* this can't happen, debug only */
+ if (xfer->nframes == 0) {
+ printf("%s: zero frame play xfer\n", DEVNAME(sc));
+ return;
  }
- splx(s);
 
- return (0);
+ usbd_setup_isoc_xfer(xfer->usb_xfer, s->data_pipe, sc,
+    xfer->sizes, xfer->nframes, USBD_NO_COPY, uaudio_pdata_intr);
+
+ err = usbd_transfer(xfer->usb_xfer);
+ if (err != USBD_IN_PROGRESS)
+ printf("%s: uaudio_pdata_xfer: err = %d\n", DEVNAME(sc), err);
+
+ if (++s->data_nextxfer == UAUDIO_NXFERS)
+ s->data_nextxfer = 0;
+
 }
 
-/* Set up a pipe for a channel. */
-usbd_status
-uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch)
+/*
+ * Callback called by the USB driver upon completion of play data transfer.
+ */
+void
+uaudio_pdata_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status)
 {
- struct as_info *as = &sc->sc_alts[ch->altidx];
- int endpt = as->edesc->bEndpointAddress;
- usbd_status err;
+#ifdef UAUDIO_DEBUG
+ struct timeval tv;
+#endif
+ struct uaudio_softc *sc = arg;
+ struct uaudio_stream *s = &sc->pstream;
+ struct uaudio_xfer *xfer;
 
- DPRINTF(("%s: endpt=0x%02x, speed=%d, alt=%d\n",
- __func__, endpt, ch->sample_rate, as->alt));
+ if (status != 0) {
+ DPRINTF("uaudio_pdata_intr: xfer status = %d\n", status);
+ return;
+ }
 
- /* Set alternate interface corresponding to the mode. */
- err = usbd_set_interface(as->ifaceh, as->alt);
- if (err) {
- DPRINTF(("%s: usbd_set_interface failed\n", __func__));
- return (err);
+ xfer = s->data_xfers + s->data_nextxfer;
+ if (xfer->usb_xfer != usb_xfer) {
+ DPRINTF("uaudio_pdata_intr: wrong xfer\n");
+ return;
  }
 
- /*
- * If just one sampling rate is supported,
- * no need to call uaudio_set_speed().
- * Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request.
- */
- if (as->asf1desc->bSamFreqType != 1) {
- err = uaudio_set_speed(sc, endpt, ch->sample_rate);
- if (err)
- DPRINTF(("%s: set_speed failed err=%s\n",
- __func__, usbd_errstr(err)));
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug >= 2) {
+ getmicrotime(&tv);
+ printf("uaudio_pdata_intr: %llu.%06lu: %uB, status = %d\n",
+    tv.tv_sec, tv.tv_usec, xfer->size, status);
  }
+#endif
+ uaudio_pdata_xfer(sc);
 
- if (sc->sc_quirks & UAUDIO_FLAG_EMU0202)
- uaudio_set_speed_emu0202(ch);
+ mtx_enter(&audio_lock);
+ s->intr(s->arg);
+ mtx_leave(&audio_lock);
+}
 
- ch->pipe = 0;
- ch->sync_pipe = 0;
- DPRINTF(("%s: create pipe to 0x%02x\n", __func__, endpt));
- err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->pipe);
- if (err) {
- printf("%s: error creating pipe: err=%s endpt=0x%02x\n",
-    __func__, usbd_errstr(err), endpt);
- return err;
- }
- if (as->edesc1 != NULL) {
- endpt = as->edesc1->bEndpointAddress;
- DPRINTF(("%s: create sync-pipe to 0x%02x\n", __func__, endpt));
- err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->sync_pipe);
- if (err) {
- printf("%s: error creating sync-pipe: err=%s endpt=0x%02x\n",
-    __func__, usbd_errstr(err), endpt);
- }
+/*
+ * Submit a play sync transfer to the USB driver.
+ */
+void
+uaudio_psync_xfer(struct uaudio_softc *sc)
+{
+#ifdef UAUDIO_DEBUG
+ struct timeval tv;
+#endif
+ struct uaudio_stream *s = &sc->pstream;
+ struct uaudio_xfer *xfer;
+ unsigned int i;
+ int err;
+
+ xfer = s->sync_xfers + s->sync_nextxfer;
+ xfer->nframes = 1;
+
+ for (i = 0; i < xfer->nframes; i++)
+ xfer->sizes[i] = sc->sync_pktsz;
+
+ xfer->size = xfer->nframes * sc->sync_pktsz;
+
+#ifdef UAUDIO_DEBUG
+ memset(xfer->buf, 0xd0, sc->sync_pktsz * xfer->nframes);
+#endif
+
+ usbd_setup_isoc_xfer(xfer->usb_xfer, s->sync_pipe, sc,
+    xfer->sizes, xfer->nframes,
+    USBD_NO_COPY | USBD_SHORT_XFER_OK,
+    uaudio_psync_intr);
+
+ err = usbd_transfer(xfer->usb_xfer);
+ if (err != USBD_IN_PROGRESS)
+ printf("%s: uaudio_psync_xfer: err = %d\n", DEVNAME(sc), err);
+
+ if (++s->sync_nextxfer == UAUDIO_NXFERS)
+ s->sync_nextxfer = 0;
+
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug >= 2) {
+ getmicrotime(&tv);
+ printf("uaudio_psync_xfer: %llu.%06lu: %dB, %d fr\n",
+    tv.tv_sec, tv.tv_usec, sc->sync_pktsz, xfer->nframes);
  }
- return err;
+#endif
 }
 
+/*
+ * Callback called by the USB driver upon completion of play sync transfer.
+ */
 void
-uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch)
+uaudio_psync_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status)
 {
- struct as_info *as = &sc->sc_alts[ch->altidx];
+#ifdef UAUDIO_DEBUG
+ struct timeval tv;
+#endif
+ struct uaudio_softc *sc = arg;
+ struct uaudio_stream *s = &sc->pstream;
+ struct uaudio_xfer *xfer;
+ unsigned char *buf;
+ unsigned int i;
+ int32_t val;
 
- as->sc_busy = 0;
- if (sc->sc_nullalt >= 0) {
- DPRINTF(("%s: set null alt=%d\n",
- __func__, sc->sc_nullalt));
- usbd_set_interface(as->ifaceh, sc->sc_nullalt);
+ if (status != 0) {
+ DPRINTF("uaudio_psync_intr: xfer status = %d\n", status);
+ return;
  }
- if (ch->pipe) {
- usbd_abort_pipe(ch->pipe);
- usbd_close_pipe(ch->pipe);
+
+ xfer = s->sync_xfers + s->sync_nextxfer;
+ if (xfer->usb_xfer != usb_xfer) {
+ DPRINTF("uaudio_psync_intr: wrong xfer\n");
+ return;
  }
- if (ch->sync_pipe) {
- usbd_abort_pipe(ch->sync_pipe);
- usbd_close_pipe(ch->sync_pipe);
+
+ buf = xfer->buf;
+ for (i = 0; i < xfer->nframes; i++) {
+ if (xfer->sizes[i] == sc->sync_pktsz) {
+ val = buf[0] | buf[1] << 8 | buf[2] << 16;
+ if (sc->sync_pktsz == 4)
+ val |= xfer->buf[3] << 24;
+ else
+ val <<= 2;
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug >= 2) {
+ getmicrotime(&tv);
+ printf("uaudio_psync_intr: %llu.%06lu: "
+    "spf: %08x\n",
+    tv.tv_sec, tv.tv_usec, val);
+ }
+#endif
+ if (val > s->spf_max)
+ s->spf = s->spf_max;
+ else if (val < s->spf_min)
+ s->spf = s->spf_min;
+ else
+ s->spf = val;
+ }
+ buf += sc->sync_pktsz;
  }
+
+ uaudio_psync_xfer(sc);
 }
 
-usbd_status
-uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch)
+/*
+ * Submit a rec data transfer to the USB driver.
+ */
+void
+uaudio_rdata_xfer(struct uaudio_softc *sc)
 {
- struct as_info *as = &sc->sc_alts[ch->altidx];
- struct usbd_xfer *xfer;
- void *buf;
- int i, size;
-
- DPRINTF(("%s: max_bytes_per_frame=%d nframes=%d\n", __func__,
-    ch->max_bytes_per_frame, ch->nframes));
+#ifdef UAUDIO_DEBUG
+ struct timeval tv;
+#endif
+ struct uaudio_stream *s = &sc->rstream;
+ struct uaudio_alt *a = sc->params->ralt;
+ struct uaudio_xfer *xfer;
+ unsigned int fsize, bpf;
+ int done;
+ int err;
+
+ xfer = s->data_xfers + s->data_nextxfer;
+ bpf = a->bps * a->nch;
+ xfer->nframes = 0;
+ done = s->ring_offs;
 
- size = ch->max_bytes_per_frame * ch->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;
+ while (1) {
+ /*
+ * if we crossed the next block boundary, we're done
+ */
+ if (done >= s->safe_blksz) {
+ done:
+ xfer->size = done - s->ring_offs;
+ s->ring_offs = done - s->ring_blksz;
+ break;
  }
- ch->chanbufs[i].buffer = buf;
- ch->chanbufs[i].chan = ch;
- }
- if (as->edesc1 != NULL) {
- size = (ch->hi_speed ? 4 : 3) * ch->nsync_frames;
- for (i = 0; i < UAUDIO_NSYNCBUFS; i++) {
- xfer = usbd_alloc_xfer(sc->sc_udev);
- if (xfer == 0)
- goto bad_sync;
- ch->syncbufs[i].xfer = xfer;
- buf = usbd_alloc_buffer(xfer, size);
- if (buf == 0) {
- i++;
- goto bad_sync;
- }
- ch->syncbufs[i].buffer = buf;
- ch->syncbufs[i].chan = ch;
+
+ /*
+ * this can't happen, debug only
+ */
+ if (xfer->nframes == s->nframes_max) {
+ printf("%s: too many frames for rec xfer: "
+    "done = %d, blksz = %d\n",
+    DEVNAME(sc), done, s->ring_blksz);
+ goto done;
  }
- }
 
- return (USBD_NORMAL_COMPLETION);
+ /*
+ * estimate next block using s->spf, but allow
+ * transfers up to maxpkt
+ */
+ s->remain += s->spf;
+ fsize = (s->remain >> 16) * bpf;
+ s->remain &= 0xffff;
+ done += fsize;
+ xfer->sizes[xfer->nframes] = s->maxpkt;
+ xfer->nframes++;
 
-bad:
- while (--i >= 0)
- /* implicit buffer free */
- usbd_free_xfer(ch->chanbufs[i].xfer);
- return (USBD_NOMEM);
+ }
 
-bad_sync:
- while (--i >= 0)
- /* implicit buffer free */
- usbd_free_xfer(ch->syncbufs[i].xfer);
- return (USBD_NOMEM);
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug >= 2) {
+ getmicrotime(&tv);
+ printf("uaudio_rdata_xfer: %llu.%06lu: "
+    "%u fr, %dB (max %d), offs = %d\n",
+    tv.tv_sec, tv.tv_usec,
+    xfer->nframes, xfer->size,
+    s->maxpkt * xfer->nframes, s->ring_offs);
+ }
+#endif
 
-}
+ /* this can't happen, debug only */
+ if (xfer->nframes == 0) {
+ printf("%s: zero frame rec xfer\n", DEVNAME(sc));
+ return;
+ }
 
-void
-uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch)
-{
- struct as_info *as = &sc->sc_alts[ch->altidx];
- int i;
+#ifdef UAUDIO_DEBUG
+ memset(xfer->buf, 0xd0, s->maxpkt * xfer->nframes);
+#endif
+ usbd_setup_isoc_xfer(xfer->usb_xfer, s->data_pipe, sc,
+    xfer->sizes, xfer->nframes, USBD_NO_COPY | USBD_SHORT_XFER_OK,
+    uaudio_rdata_intr);
+
+ err = usbd_transfer(xfer->usb_xfer);
+ if (err != USBD_IN_PROGRESS)
+ printf("%s: uaudio_rdata_xfer: err = %d\n", DEVNAME(sc), err);
 
- for (i = 0; i < UAUDIO_NCHANBUFS; i++)
- usbd_free_xfer(ch->chanbufs[i].xfer);
- if (as->edesc1 != NULL) {
- for (i = 0; i < UAUDIO_NSYNCBUFS; i++)
- usbd_free_xfer(ch->syncbufs[i].xfer);
- }
+ if (++s->data_nextxfer == UAUDIO_NXFERS)
+ s->data_nextxfer = 0;
 }
 
-/* Called at splusb() */
+/*
+ * Callback called by the USB driver upon completion of rec data transfer.
+ */
 void
-uaudio_chan_ptransfer(struct chan *ch)
+uaudio_rdata_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status)
 {
- struct chanbuf *cb;
- u_char *pos;
- int i, n, size, residue, total;
+#ifdef UAUDIO_DEBUG
+ struct timeval tv;
+#endif
+ struct uaudio_softc *sc = arg;
+ struct uaudio_stream *s = &sc->rstream;
+ struct uaudio_alt *a = sc->params->ralt;
+ struct uaudio_xfer *xfer;
+ unsigned char *buf, *framebuf;
+ unsigned int count, fsize, nframes, bpf;
+ unsigned int data_size, null_size;
 
- if (usbd_is_dying(ch->sc->sc_udev))
+ if (status != 0) {
+ DPRINTF("uaudio_rdata_intr: xfer status = %d\n", status);
  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 < ch->nframes; i++) {
- size = ch->bytes_per_frame;
- residue += ch->fraction;
- if (residue >= ch->frac_denom) {
- if ((ch->sc->sc_altflags & UA_NOFRAC) == 0)
- size += ch->sample_size;
- residue -= ch->frac_denom;
- }
- cb->sizes[i] = size;
- total += size;
+ xfer = s->data_xfers + s->data_nextxfer;
+ if (xfer->usb_xfer != usb_xfer) {
+ DPRINTF("uaudio_rdata_intr: wrong xfer\n");
+ return;
  }
- ch->residue = residue;
- cb->size = total;
 
- /*
- * Transfer data from upper layer buffer to channel buffer.  Be sure
- * to let the upper layer know each time a block is moved, so it can
- * add more.
- */
- pos = cb->buffer;
- while (total > 0) {
- n = min(total, ch->end - ch->cur);
- n = min(n, ch->blksize - ch->transferred);
- memcpy(pos, ch->cur, n);
- total -= n;
- pos += n;
- ch->cur += n;
- if (ch->cur >= ch->end)
- ch->cur = ch->start;
+ bpf = a->bps * a->nch;
+ framebuf = xfer->buf;
+ nframes = 0;
+ null_size = 0;
+ data_size = 0;
+ for (nframes = 0; nframes < xfer->nframes; nframes++) {
+ fsize = xfer->sizes[nframes];
+ if (fsize > 0) {
+ data_size += fsize;
+ s->locked = 1;
+ } else if (!s->locked) {
+ /* treat initial null packets as silence */
+ fsize = (s->spf >> 16) * bpf;
+ memset(framebuf, 0, fsize);
+ null_size += fsize;
+ }
 
- ch->transferred += n;
- /* Call back to upper layer */
- if (ch->transferred >= ch->blksize) {
- DPRINTFN(5,("%s: call %p(%p)\n",
-    __func__, ch->intr, ch->arg));
- mtx_enter(&audio_lock);
- ch->intr(ch->arg);
- mtx_leave(&audio_lock);
- ch->transferred -= ch->blksize;
+ /*
+ * fill ring from frame buffer, handling
+ * boundary conditions
+ */
+ buf = framebuf;
+ while (fsize > 0) {
+ count = s->ring_end - s->ring_pos;
+ if (count > fsize)
+ count = fsize;
+ memcpy(s->ring_pos, buf, count);
+ s->ring_pos += count;
+ if (s->ring_pos == s->ring_end)
+ s->ring_pos = s->ring_start;
+ buf += count;
+ fsize -= count;
  }
+
+ framebuf += s->maxpkt;
  }
 
+ s->ring_offs += data_size + null_size - xfer->size;
+
 #ifdef UAUDIO_DEBUG
- if (uaudiodebug > 8) {
- DPRINTF(("%s: buffer=%p, residue=0.%03d\n",
- __func__, cb->buffer, ch->residue));
- for (i = 0; i < ch->nframes; i++) {
- DPRINTF(("   [%d] length %d\n", i, cb->sizes[i]));
- }
+ if (uaudio_debug >= 2) {
+ getmicrotime(&tv);
+ printf("uaudio_rdata_intr: %llu.%06lu: "
+    "%u fr, %uB + %uB = %uB of %uB, offs -> %d\n",
+    tv.tv_sec, tv.tv_usec,
+    nframes, data_size, null_size, data_size + null_size,
+    xfer->size, s->ring_offs);
  }
 #endif
+ uaudio_rdata_xfer(sc);
 
- DPRINTFN(5,("%s: transfer xfer=%p\n", __func__, cb->xfer));
- usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, ch->nframes,
-    USBD_NO_COPY | USBD_SHORT_XFER_OK, uaudio_chan_pintr);
-
- (void)usbd_transfer(cb->xfer);
+ mtx_enter(&audio_lock);
+ s->intr(s->arg);
+ mtx_leave(&audio_lock);
 }
 
+/*
+ * Start simultaneously playback and recording, unless trigger_input()
+ * and trigger_output() were not both called yet.
+ */
 void
-uaudio_chan_pintr(struct usbd_xfer *xfer, void *priv,
-  usbd_status status)
+uaudio_trigger(struct uaudio_softc *sc)
 {
- struct chanbuf *cb = priv;
- struct chan *ch = cb->chan;
- u_int32_t count;
+ int i, s;
 
- /* Return if we are aborting. */
- if (status == USBD_CANCELLED)
+ if (sc->mode != sc->trigger_mode)
  return;
 
- usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
- DPRINTFN(5,("%s: count=%d, transferred=%d\n",
-    __func__, count, ch->transferred));
-#ifdef UAUDIO_DEBUG
- if (count != cb->size) {
- printf("%s: count(%d) != size(%d)\n",
-       __func__, count, cb->size);
+ DPRINTF("uaudio_trigger: starting\n");
+ s = splusb();
+ for (i = 0; i < UAUDIO_NXFERS; i++) {
+ if (sc->mode & AUMODE_PLAY) {
+ if (sc->pstream.sync_pipe)
+ uaudio_psync_xfer(sc);
+ uaudio_pdata_xfer(sc);
+ }
+ if (sc->mode & AUMODE_RECORD)
+ uaudio_rdata_xfer(sc);
  }
-#endif
-
- /* start next transfer */
- uaudio_chan_ptransfer(ch);
+ splx(s);
 }
 
-/* Called at splusb() */
 void
-uaudio_chan_psync_transfer(struct chan *ch)
+uaudio_print(struct uaudio_softc *sc)
 {
- struct syncbuf *sb;
- int i, size, total = 0;
+ struct uaudio_unit *u;
+ struct uaudio_mixent *m;
+ struct uaudio_params *p;
+ int pchan = 0, rchan = 0, async = 0;
+ int nctl = 0;
+
+ for (u = sc->unit_list; u != NULL; u = u->unit_next) {
+ for (m = u->mixent_list; m != NULL; m = m->next)
+ nctl++;
+ }
+
+ for (p = sc->params_list; p != NULL; p = p->next) {
+ if (p->palt && p->palt->nch > pchan)
+ pchan = p->palt->nch;
+ if (p->ralt && p->ralt->nch > rchan)
+ rchan = p->ralt->nch;
+ if (p->palt && p->palt->sync)
+ async = 1;
+ if (p->ralt && p->ralt->sync)
+ async = 1;
+ }
+
+ printf("%s: class v%d, %s, %s, channels: %d play, %d rec, %d ctls\n",
+    DEVNAME(sc),
+    sc->version >> 8,
+    sc->ufps == 1000 ? "full-speed" : "high-speed",
+    async ? "async" : "sync",
+    pchan, rchan, nctl);
+}
 
- if (usbd_is_dying(ch->sc->sc_udev))
- return;
+int
+uaudio_match(struct device *parent, void *match, void *aux)
+{
+ struct usb_attach_arg *arg = aux;
+ struct usb_interface_descriptor *idesc;
 
- /* Pick the next sync buffer. */
- sb = &ch->syncbufs[ch->cursyncbuf];
- if (++ch->cursyncbuf >= UAUDIO_NSYNCBUFS)
- ch->cursyncbuf = 0;
+ if (arg->iface == NULL || arg->device == NULL)
+ return UMATCH_NONE;
 
- size = ch->hi_speed ? 4 : 3;
- for (i = 0; i < ch->nsync_frames; i++) {
- sb->sizes[i] = size;
- sb->offsets[i] = total;
- total += size;
+ idesc = usbd_get_interface_descriptor(arg->iface);
+ if (idesc == NULL) {
+ DPRINTF("uaudio_match: couldn't get idesc\n");
+ return UMATCH_NONE;
  }
- sb->size = total;
 
- DPRINTFN(5,("%s: transfer xfer=%p\n", __func__, sb->xfer));
- usbd_setup_isoc_xfer(sb->xfer, ch->sync_pipe, sb, sb->sizes,
-    ch->nsync_frames, USBD_NO_COPY | USBD_SHORT_XFER_OK,
-    uaudio_chan_psync_intr);
+ if (idesc->bInterfaceClass != UICLASS_AUDIO ||
+    idesc->bInterfaceSubClass != UISUBCLASS_AUDIOSTREAM)
+ return UMATCH_NONE;
 
- (void)usbd_transfer(sb->xfer);
+ return UMATCH_VENDOR_PRODUCT_CONF_IFACE;
 }
 
 void
-uaudio_chan_psync_intr(struct usbd_xfer *xfer, void *priv,
-    usbd_status status)
+uaudio_attach(struct device *parent, struct device *self, void *aux)
 {
- struct syncbuf *sb = priv;
- struct chan *ch = sb->chan;
- u_int32_t count, tmp;
- u_int32_t freq, freq_w, freq_f;
- int i, pos, size;
+ struct uaudio_softc *sc = (struct uaudio_softc *)self;
+ struct usb_attach_arg *arg = aux;
+ struct usb_config_descriptor *cdesc;
+ struct uaudio_blob desc;
+
+ /*
+ * this device has audio AC or AS or MS interface, get the
+ * full config descriptor and attach audio devices
+ */
 
- /* Return if we are aborting. */
- if (status == USBD_CANCELLED)
+ cdesc = usbd_get_config_descriptor(arg->device);
+ if (cdesc == NULL)
  return;
 
- usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
- DPRINTFN(5,("%s: count=%d\n", __func__, count));
+ desc.rptr = (unsigned char *)cdesc;
+ desc.wptr = desc.rptr + UGETW(cdesc->wTotalLength);
 
- size = ch->hi_speed ? 4 : 3;
- for (i = 0; count > 0 && i < ch->nsync_frames; i++) {
- if (sb->sizes[i] != size)
- continue;
- count -= size;
- pos = sb->offsets[i];
- if (ch->hi_speed) {
- /* 16.16 (12.13) -> 16.16 (12.16) */
- freq = sb->buffer[pos+3] << 24 |
-    sb->buffer[pos+2] << 16 |
-    sb->buffer[pos+1] << 8 |
-    sb->buffer[pos];
- } else {
- /* 10.14 (10.10) -> 16.16 (10.16) */
- freq = sb->buffer[pos+2] << 18 |
-    sb->buffer[pos+1] << 10 |
-    sb->buffer[pos] << 2;
- }
- freq_w = (freq >> 16) & (ch->hi_speed ? 0x0fff : 0x03ff);
- freq_f = freq & 0xffff;
- DPRINTFN(5,("%s: freq = %d %d/%d\n", __func__, freq_w, freq_f,
-    ch->frac_denom));
- tmp = freq_w * ch->sample_size;
- if (tmp + (freq_f ? ch->sample_size : 0) >
-    ch->max_bytes_per_frame) {
- DPRINTF(("%s: packet size request too large: %d/%d/%d\n",
-    __func__, tmp, ch->max_bytes_per_frame, ch->maxpktsize));
- } else {
- ch->bytes_per_frame = tmp;
- ch->fraction = freq_f;
- }
+ sc->udev = arg->device;
+ sc->unit_list = NULL;
+ sc->names = NULL;
+ sc->alts = NULL;
+ sc->params_list = NULL;
+ sc->clock = NULL;
+ sc->params = NULL;
+ sc->rate = 0;
+ sc->mode = 0;
+ sc->trigger_mode = 0;
+
+ switch (sc->udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ sc->ufps = 1000;
+ sc->sync_pktsz = 3;
+ break;
+ case USB_SPEED_HIGH:
+ sc->ufps = 8000;
+ sc->sync_pktsz = 4;
+ break;
+ default:
+ /* XXX: what about USB_SPEED_SUPER? */
+ return;
  }
 
- /* start next transfer */
- uaudio_chan_psync_transfer(ch);
+ if (!uaudio_process_conf(sc, &desc))
+ return;
+
+#ifdef UAUDIO_DEBUG
+ if (uaudio_debug)
+ uaudio_conf_print(sc);
+#endif
+ /* print a nice uaudio attach line */
+ uaudio_print(sc);
+
+ audio_attach_mi(&uaudio_hw_if, sc, &sc->dev);
 }
 
-/* Called at splusb() */
-void
-uaudio_chan_rtransfer(struct chan *ch)
+int
+uaudio_detach(struct device *self, int flags)
 {
- struct chanbuf *cb;
- int i, size, total;
+ struct uaudio_softc *sc = (struct uaudio_softc *)self;
+ struct uaudio_unit *unit;
+ struct uaudio_params *params;
+ struct uaudio_alt *alt;
+ struct uaudio_name *name;
+ struct uaudio_mixent *mixent;
+ int rv;
 
- if (usbd_is_dying(ch->sc->sc_udev))
- return;
+ rv = config_detach_children(self, flags);
 
- /* Pick the next channel buffer. */
- cb = &ch->chanbufs[ch->curchanbuf];
- if (++ch->curchanbuf >= UAUDIO_NCHANBUFS)
- ch->curchanbuf = 0;
+ while ((alt = sc->alts) != NULL) {
+ sc->alts = alt->next;
+ free(alt, M_DEVBUF, sizeof(struct uaudio_alt));
+ }
 
- /* Compute the size of each frame in the next transfer. */
- total = 0;
- for (i = 0; i < ch->nframes; i++) {
- size = ch->bytes_per_frame;
- cb->sizes[i] = size;
- cb->offsets[i] = total;
- total += size;
+ while ((params = sc->params_list) != NULL) {
+ sc->params_list = params->next;
+ free(params, M_DEVBUF, sizeof(struct uaudio_params));
  }
- cb->size = total;
 
-#ifdef UAUDIO_DEBUG
- if (uaudiodebug > 8) {
- DPRINTF(("%s: buffer=%p, residue=0.%03d\n",
- __func__, cb->buffer, ch->residue));
- for (i = 0; i < ch->nframes; i++) {
- DPRINTF(("   [%d] length %d\n", i, cb->sizes[i]));
+ while ((unit = sc->unit_list) != NULL) {
+ sc->unit_list = unit->unit_next;
+ while ((mixent = unit->mixent_list) != NULL) {
+ unit->mixent_list = mixent->next;
+ uaudio_ranges_clear(&mixent->ranges);
+ free(mixent, M_DEVBUF, sizeof(struct uaudio_mixent));
  }
+ uaudio_ranges_clear(&unit->rates);
+ free(unit, M_DEVBUF, sizeof(struct uaudio_unit));
  }
-#endif
 
- DPRINTFN(5,("%s: transfer xfer=%p\n", __func__, cb->xfer));
- usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, ch->nframes,
-    USBD_NO_COPY | USBD_SHORT_XFER_OK, uaudio_chan_rintr);
+ while ((name = sc->names)) {
+ sc->names = name->next;
+ free(name, M_DEVBUF, sizeof(struct uaudio_name));
+ }
 
- (void)usbd_transfer(cb->xfer);
+ return rv;
 }
 
-void
-uaudio_chan_rintr(struct usbd_xfer *xfer, void *priv,
-  usbd_status status)
+int
+uaudio_open(void *self, int flags)
 {
- struct chanbuf *cb = priv;
- struct chan *ch = cb->chan;
- u_int16_t pos;
- u_int32_t count;
- int i, n, frsize;
-
- /* Return if we are aborting. */
- if (status == USBD_CANCELLED)
- return;
+ struct uaudio_softc *sc = self;
+ struct uaudio_params *p;
 
- usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
- DPRINTFN(5,("%s: count=%d, transferred=%d\n",
-    __func__, count, ch->transferred));
+ if (usbd_is_dying(sc->udev))
+ return EIO;
 
- /* count < cb->size is normal for asynchronous source */
-#ifdef DIAGNOSTIC
- if (count > cb->size) {
- printf("%s: count(%d) > size(%d)\n",
-       __func__, count, cb->size);
- }
-#endif
+ flags &= (FREAD | FWRITE);
 
- /*
- * Transfer data from channel buffer to upper layer buffer, taking
- * care of wrapping the upper layer buffer.
- */
- for (i = 0; i < ch->nframes; i++) {
- frsize = cb->sizes[i];
- pos = cb->offsets[i];
- while (frsize > 0) {
- n = min(frsize, ch->end - ch->cur);
- n = min(n, ch->blksize - ch->transferred);
- memcpy(ch->cur, cb->buffer + pos, n);
- frsize -= n;
- pos += n;
- ch->cur += n;
- if (ch->cur >= ch->end)
- ch->cur = ch->start;
-
- ch->transferred += n;
- /* Call back to upper layer */
- if (ch->transferred >= ch->blksize) {
- DPRINTFN(5,("%s: call %p(%p)\n",
-    __func__, ch->intr, ch->arg));
- mtx_enter(&audio_lock);
- ch->intr(ch->arg);
- mtx_leave(&audio_lock);
- ch->transferred -= ch->blksize;
- }
- if (count < n)
- printf("%s: count < n\n", __func__);
- else
- count -= n;
+ for (p = sc->params_list; p != NULL; p = p->next) {
+ switch (flags) {
+ case FWRITE:
+ if (!p->palt)
+ break;
+ sc->mode = AUMODE_PLAY;
+ return 0;
+ case FREAD:
+ if (!p->ralt)
+ break;
+ sc->mode = AUMODE_RECORD;
+ return 0;
+ case FREAD | FWRITE:
+ if (!(p->ralt && p->palt))
+ break;
+ sc->mode = AUMODE_RECORD | AUMODE_PLAY;
+ return 0;
  }
  }
- if (count != 0) {
- printf("%s: transfer count - frame total = %d\n",
-    __func__, count);
- }
 
- /* start next transfer */
- uaudio_chan_rtransfer(ch);
+ return ENXIO;
 }
 
 void
-uaudio_chan_init(struct chan *ch, int mode, int altidx,
-    const struct audio_params *param)
+uaudio_close(void *self)
+{
+ struct uaudio_softc *sc = self;
+
+ sc->mode = 0;
+}
+
+int
+uaudio_set_params(void *self, int setmode, int usemode,
+    struct audio_params *ap, struct audio_params *ar)
 {
- struct as_info *ai = &ch->sc->sc_alts[altidx];
- int samples_per_frame, ival, use_maxpkt = 0;
+ struct uaudio_softc *sc = (struct uaudio_softc *)self;
+ struct uaudio_params *p, *best_mode, *best_rate, *best_nch;
+ int rate, rateindex;
 
- if (ai->attributes & UA_SED_MAXPACKETSONLY) {
- DPRINTF(("%s: alt %d needs maxpktsize packets\n",
-    __func__, altidx));
- use_maxpkt = 1;
+#ifdef DIAGNOSTIC
+ if (setmode != usemode || setmode != sc->mode) {
+ printf("%s: bad call to uaudio_set_params()\n", DEVNAME(sc));
+ return EINVAL;
+ }
+ if (sc->mode == 0) {
+ printf("%s: uaudio_set_params(): not open\n", DEVNAME(sc));
+ return EINVAL;
  }
- else if (mode == AUMODE_RECORD) {
- DPRINTF(("%s: using maxpktsize packets for record channel\n",
-    __func__));
- use_maxpkt = 1;
+#endif
+ /*
+ * audio(4) layer requests equal play and record rates
+ */
+ rate = (sc->mode & AUMODE_PLAY) ? ap->sample_rate : ar->sample_rate;
+ rateindex = uaudio_rates_indexof(~0, rate);
+
+ DPRINTF("uaudio_set_params: rate %d -> %d (index %d)\n",
+    rate, uaudio_rates[rateindex], rateindex);
+
+ best_mode = best_rate = best_nch = NULL;
+
+ for (p = sc->params_list; p != NULL; p = p->next) {
+
+ /* test if params match the requested mode */
+ if (sc->mode & AUMODE_PLAY) {
+ if (p->palt == NULL)
+ continue;
+ }
+ if (sc->mode & AUMODE_RECORD) {
+ if (p->ralt == NULL)
+ continue;
+ }
+ if (best_mode == NULL)
+ best_mode = p;
+
+ /* test if params match the requested rate */
+ if ((uaudio_getrates(sc, p) & (1 << rateindex)) == 0)
+ continue;
+ if (best_rate == NULL)
+ best_rate = p;
+
+ /* test if params match the requested channel counts */
+ if (sc->mode & AUMODE_PLAY) {
+ if (p->palt->nch != ap->channels)
+ continue;
+ }
+ if (sc->mode & AUMODE_RECORD) {
+ if (p->ralt->nch != ar->channels)
+ continue;
+ }
+ if (best_nch == NULL)
+ best_nch = p;
+
+ /* test if params match the requested precision */
+ if (sc->mode & AUMODE_PLAY) {
+ if (p->palt->bits != ap->precision)
+ continue;
+ }
+ if (sc->mode & AUMODE_RECORD) {
+ if (p->ralt->bits != ar->precision)
+ continue;
+ }
+
+ /* everything matched, we're done */
+ break;
  }
 
- ch->altidx = altidx;
- ch->maxpktsize = UGETW(ai->edesc->wMaxPacketSize);
- ch->sample_rate = param->sample_rate;
- ch->sample_size = param->channels * param->bps;
- ch->usb_fps = USB_FRAMES_PER_SECOND;
- ch->hi_speed = ch->sc->sc_udev->speed == USB_SPEED_HIGH;
- if (ch->hi_speed) {
- ch->usb_fps *= 8;
- /*
- * Polling interval is considered a frame, as opposed to
- * micro-frame being a frame.
- */
- ival = ch->sc->sc_alts[altidx].edesc->bInterval;
- if (ival > 0 && ival <= 4)
- ch->usb_fps >>= (ival - 1);
- DPRINTF(("%s: detected USB high-speed with ival %d\n",
-    __func__, ival));
+ if (p == NULL) {
+ if (best_nch)
+ p = best_nch;
+ else if (best_rate)
+ p = best_rate;
+ else if (best_mode)
+ p = best_mode;
+ else
+ return ENOTTY;
  }
 
  /*
- * Use UAUDIO_MIN_FRAMES here, so uaudio_round_blocksize() can
- * make sure the blocksize duration will be > 1 USB frame.
- */
- samples_per_frame = ch->sample_rate / ch->usb_fps;
- if (!use_maxpkt) {
- ch->fraction = ch->sample_rate % ch->usb_fps;
- if (samples_per_frame * ch->sample_size > ch->maxpktsize) {
- DPRINTF(("%s: packet size %d too big, max %d\n",
-    __func__, ch->bytes_per_frame, ch->maxpktsize));
- samples_per_frame = ch->maxpktsize / ch->sample_size;
- }
- ch->bytes_per_frame = samples_per_frame * ch->sample_size;
- ch->nframes = UAUDIO_MIN_FRAMES;
- } else {
- ch->fraction = 0;
- ch->bytes_per_frame = ch->maxpktsize;
- ch->nframes = UAUDIO_MIN_FRAMES * samples_per_frame *
-    ch->sample_size / ch->maxpktsize;
- }
- if (ch->nframes > UAUDIO_MAX_FRAMES)
- ch->nframes = UAUDIO_MAX_FRAMES;
- else if (ch->nframes < 1)
- ch->nframes = 1;
-
- ch->max_bytes_per_frame = ch->bytes_per_frame;
- if (!use_maxpkt)
- ch->max_bytes_per_frame += ch->sample_size;
- if (ch->max_bytes_per_frame > ch->maxpktsize)
- ch->max_bytes_per_frame = ch->maxpktsize;
-
- ch->residue = 0;
- ch->frac_denom = ch->usb_fps;
- if (ai->edesc1 != NULL) {
- /*
- * The lower 16-bits of the sync request represent
- * fractional samples.  Scale up the fraction here once
- * so all fractions are using the same denominator.
- */
- ch->frac_denom = 1 << 16;
- ch->fraction = (ch->fraction * ch->frac_denom) / ch->usb_fps;
+ * Recalculate rate index, because the choosen parameters
+ * may not support the requested one
+ */
+ rateindex = uaudio_rates_indexof(uaudio_getrates(sc, p), rate);
 
- /*
- * Have to set nsync_frames somewhere.  We can request
- * a lot of sync data; the device will reply when it's
- * ready, with empty frames meaning to keep using the
- * current rate.
- */
- ch->nsync_frames = UAUDIO_MAX_FRAMES;
+ sc->params = p;
+ sc->rate = uaudio_rates[rateindex];
+ sc->nsamp_per_ufr = sc->rate / USB_FRAMES_PER_SECOND;
+
+ DPRINTF("uaudio_set_params: rate = %u, spf = %u\n",
+    sc->rate, sc->nsamp_per_ufr);
+
+ if (sc->mode & AUMODE_PLAY) {
+ ap->sample_rate = sc->rate;
+ ap->precision = p->palt->bits;
+ ap->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ ap->bps = p->palt->bps;
+ ap->msb = 1;
+ ap->channels = p->palt->nch;
+ }
+ if (sc->mode & AUMODE_RECORD) {
+ ar->sample_rate = sc->rate;
+ ar->precision = p->ralt->bits;
+ ar->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ ar->bps = p->ralt->bps;
+ ar->msb = 1;
+ ar->channels = p->ralt->nch;
  }
- DPRINTF(("%s: residual sample fraction: %d/%d\n", __func__,
-    ch->fraction, ch->frac_denom));
+
+ return 0;
 }
 
-void
-uaudio_chan_set_param(struct chan *ch, u_char *start, u_char *end, int blksize)
+int
+uaudio_round_blocksize(void *self, int blksz)
 {
- ch->start = start;
- ch->end = end;
- ch->cur = start;
- ch->transferred = 0;
- ch->curchanbuf = 0;
- ch->blksize = blksize;
+ struct uaudio_softc *sc = self;
+ struct uaudio_alt *a;
+ unsigned int bpu, rbpf, pbpf;
+ unsigned int blksz_max;
 
  /*
- * Recompute nframes based on blksize, but make sure nframes
- * is not longer in time duration than blksize.
+ * We don't know if we're called for the play or record
+ * direction. But as the number of play and record channels
+ * may differ, we have to calculate the smallest possible
+ * block size multiplier suitable for both playback and
+ * recording. This the LCM of the play and record number of
+ * channels multiplied by what would be the mono block size.
  */
- ch->nframes = ch->blksize * ch->usb_fps /
-    (ch->bytes_per_frame * ch->usb_fps +
-    ch->sample_size * ch->fraction);
- if (ch->nframes > UAUDIO_MAX_FRAMES)
- ch->nframes = UAUDIO_MAX_FRAMES;
- else if (ch->nframes < 1)
- ch->nframes = 1;
 
- ch->reqms = ch->bytes_per_frame / ch->sample_size *
-    ch->nframes * 1000 / ch->sample_rate;
+ if (sc->mode & AUMODE_PLAY) {
+ a = sc->params->palt;
+ pbpf = a->bps * a->nch;
+ } else
+ pbpf = 1;
+
+ if (sc->mode & AUMODE_RECORD) {
+ a = sc->params->ralt;
+ rbpf = a->bps * a->nch;
+ } else
+ rbpf = 1;
+
+ bpu = sc->nsamp_per_ufr * rbpf * pbpf / uaudio_gcd(pbpf, rbpf);
+
+ /*
+ * XXX: usb support is broken for large play xfers (playback
+ * stutters in full-duplex mode).  It appears that full-duplex
+ * with 25ms blocks kinda works on uhci and ehci (on root hub
+ * only). So, just limit the block size until usb gets fixed.
+ */
+ blksz_max = (pbpf > rbpf ? pbpf : rbpf) * sc->rate * 25 / 1000;
+ if (blksz > blksz_max)
+ blksz = blksz_max;
 
- DPRINTF(("%s: alt=%d blk=%d maxpkt=%u bpf=%u rate=%u nframes=%u reqms=%u\n",
-    __func__, ch->altidx, ch->blksize, ch->maxpktsize,
-    ch->bytes_per_frame, ch->sample_rate, ch->nframes, ch->reqms));
+ blksz -= blksz % bpu;
+ if (blksz == 0)
+ blksz = bpu;
+ return blksz;
 }
 
 int
-uaudio_match_alt_rate(void *addr, int alt, int rate)
+uaudio_trigger_output(void *self, void *start, void *end, int blksz,
+    void (*intr)(void *), void *arg, struct audio_params *param)
 {
- struct uaudio_softc *sc = addr;
- const struct usb_audio_streaming_type1_descriptor *a1d;
- int i, j, r;
+ struct uaudio_softc *sc = self;
+ int err;
 
- a1d = sc->sc_alts[alt].asf1desc;
- if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
- if ((UA_SAMP_LO(a1d) <= rate) &&
-    (UA_SAMP_HI(a1d) >= rate)) {
- return rate;
- } else {
- if (UA_SAMP_LO(a1d) > rate)
- return UA_SAMP_LO(a1d);
- else
- return UA_SAMP_HI(a1d);
- }
- } else {
- for (i = 0; i < 100; i++) {
- for (j = 0; j < a1d->bSamFreqType; j++) {
- r = UA_GETSAMP(a1d, j);
- if ((r - (500 * i) <= rate) &&
-    (r + (500 * i) >= rate))
- return r;
- }
- }
- /* assumes rates are listed in order from lowest to highest */
- if (rate < UA_GETSAMP(a1d, 0))
- j = 0;
- else
- j = a1d->bSamFreqType - 1;
- return UA_GETSAMP(a1d, j);
- }
- DPRINTF(("%s: could not match rate\n", __func__));
- return rate;
+ err = uaudio_stream_open(sc,
+    AUMODE_PLAY, start, end, blksz, intr, arg);
+ if (err)
+ return err;
+
+ sc->trigger_mode |= AUMODE_PLAY;
+ uaudio_trigger(sc);
+ return 0;
 }
 
 int
-uaudio_match_alt(void *addr, struct audio_params *p, int mode)
+uaudio_trigger_input(void *self, void *start, void *end, int blksz,
+    void (*intr)(void *), void *arg, struct audio_params *param)
 {
- struct uaudio_softc *sc = addr;
- const struct usb_audio_streaming_type1_descriptor *a1d;
- int i, j, dir, rate;
- int alts_eh, alts_ch, ualt;
-
- DPRINTF(("%s: mode=%s rate=%ld ch=%d pre=%d bps=%d enc=%d\n",
-    __func__, mode == AUMODE_RECORD ? "rec" : "play", p->sample_rate,
-    p->channels, p->precision, p->bps, p->encoding));
+ struct uaudio_softc *sc = self;
+ int err;
 
- alts_eh = 0;
- for (i = 0; i < sc->sc_nalts; i++) {
- dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
- if ((mode == AUMODE_RECORD && dir != UE_DIR_IN) ||
-    (mode == AUMODE_PLAY && dir == UE_DIR_IN))
- continue;
- DPRINTFN(6,("%s: matched %s alt %d for direction\n", __func__,
-    mode == AUMODE_RECORD ? "rec" : "play", i));
- if (sc->sc_alts[i].encoding != p->encoding)
- continue;
- a1d = sc->sc_alts[i].asf1desc;
- if (a1d->bBitResolution != p->precision)
- continue;
- alts_eh |= 1 << i;
- DPRINTFN(6,("%s: matched %s alt %d for enc/pre\n", __func__,
-    mode == AUMODE_RECORD ? "rec" : "play", i));
- }
- if (alts_eh == 0) {
- DPRINTF(("%s: could not match dir/enc/prec\n", __func__));
- return -1;
- }
+ err = uaudio_stream_open(sc,
+    AUMODE_RECORD, start, end, blksz, intr, arg);
+ if (err)
+ return err;
 
- alts_ch = 0;
- for (i = 0; i < 3; i++) {
- for (j = 0; j < sc->sc_nalts; j++) {
- if (!(alts_eh & (1 << j)))
- continue;
- a1d = sc->sc_alts[j].asf1desc;
- if (a1d->bNrChannels == p->channels) {
- alts_ch |= 1 << j;
- DPRINTFN(6,("%s: matched alt %d for channels\n",
-    __func__, j));
- }
- }
- if (alts_ch)
- break;
- if (p->channels == 2)
- p->channels = 1;
- else
- p->channels = 2;
- }
- if (!alts_ch) {
- /* just use the first alt that matched the encoding */
- for (i = 0; i < sc->sc_nalts; i++)
- if (alts_eh & (1 << i))
- break;
- alts_ch = 1 << i;
- a1d = sc->sc_alts[i].asf1desc;
- p->channels = a1d->bNrChannels;
- }
+ sc->trigger_mode |= AUMODE_RECORD;
+ uaudio_trigger(sc);
+ return 0;
+}
 
- ualt = -1;
- for (i = 0; i < sc->sc_nalts; i++) {
- if (alts_ch & (1 << i)) {
- rate = uaudio_match_alt_rate(sc, i, p->sample_rate);
- if (rate - 50 <= p->sample_rate &&
-    rate + 50 >= p->sample_rate) {
- DPRINTFN(6,("%s: alt %d matched rate %ld with %d\n",
-    __func__, i, p->sample_rate, rate));
- p->sample_rate = rate;
- break;
- }
- }
- }
- if (i < sc->sc_nalts) {
- ualt = i;
- } else {
- for (i = 0; i < sc->sc_nalts; i++) {
- if (alts_ch & (1 << i)) {
- ualt = i;
- p->sample_rate = uaudio_match_alt_rate(sc,
-    i, p->sample_rate);
- break;
- }
- }
- }
+int
+uaudio_halt_output(void *self)
+{
+ struct uaudio_softc *sc = (struct uaudio_softc *)self;
 
- return ualt;
+ uaudio_stream_close(sc, AUMODE_PLAY);
+ sc->trigger_mode &= ~AUMODE_PLAY;
+ return 0;
 }
 
 int
-uaudio_set_params(void *addr, int setmode, int usemode,
-    struct audio_params *play, struct audio_params *rec)
+uaudio_halt_input(void *self)
 {
- struct uaudio_softc *sc = addr;
- int flags = sc->sc_altflags;
- int i;
- int paltidx = -1, raltidx = -1;
- struct audio_params *p;
- int mode;
-
- if (usbd_is_dying(sc->sc_udev))
- return (EIO);
-
- if (((usemode & AUMODE_PLAY) && sc->sc_playchan.pipe != NULL) ||
-    ((usemode & AUMODE_RECORD) && sc->sc_recchan.pipe != NULL))
- return (EBUSY);
-
- if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1)
- sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 0;
- if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1)
- sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 0;
-
- /* Some uaudio devices are unidirectional.  Don't try to find a
-   matching mode for the unsupported direction. */
- setmode &= sc->sc_mode;
-
- for (mode = AUMODE_RECORD; mode != -1;
-     mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
- if ((setmode & mode) == 0)
- continue;
+ struct uaudio_softc *sc = (struct uaudio_softc *)self;
 
- p = (mode == AUMODE_PLAY) ? play : rec;
+ uaudio_stream_close(sc, AUMODE_RECORD);
+ sc->trigger_mode &= ~AUMODE_RECORD;
+ return 0;
+}
 
- switch (p->precision) {
- case 24:
- if (!(flags & HAS_24)) {
- if (flags & HAS_16)
- p->precision = 16;
- else
- p->precision = 8;
- }
- break;
- case 16:
- if (!(flags & HAS_16)) {
- if (flags & HAS_24)
- p->precision = 24;
- else
- p->precision = 8;
- }
- break;
- case 8:
- if (!(flags & HAS_8) && !(flags & HAS_8U)) {
- if (flags & HAS_16)
- p->precision = 16;
- else
- p->precision = 24;
- }
- break;
- }
+int
+uaudio_get_props(void *self)
+{
+ return AUDIO_PROP_FULLDUPLEX;
+}
 
- i = uaudio_match_alt(sc, p, mode);
- if (i < 0) {
- DPRINTF(("%s: uaudio_match_alt failed for %s\n",
-    __func__, mode == AUMODE_RECORD ? "rec" : "play"));
- continue;
+int
+uaudio_get_port(void *arg, struct mixer_ctrl *ctl)
+{
+ struct uaudio_softc *sc = arg;
+ struct uaudio_unit *u;
+ struct uaudio_mixent *m;
+ unsigned char req_buf[4];
+ struct uaudio_blob p;
+ int i, nch, val;
+
+ if (!uaudio_mixer_byindex(sc, ctl->dev, &u, &m))
+ return ENOENT;
+
+ switch (m->type) {
+ case UAUDIO_MIX_SW:
+ p.rptr = p.wptr = req_buf;
+ if (!uaudio_req(sc,
+ UT_READ_CLASS_INTERFACE,
+ UAUDIO_V1_REQ_GET_CUR,
+ m->req_sel,
+ m->chan < 0 ? 0 : m->chan,
+ sc->ctl_ifnum,
+ u->id,
+ req_buf,
+ 1))
+ return EIO;
+ p.wptr++;
+ if (!uaudio_getnum(&p, 1, &val))
+ return EIO;
+ ctl->un.ord = !!val;
+ break;
+ case UAUDIO_MIX_NUM:
+ nch = uaudio_mixer_nchan(m, NULL);
+ ctl->un.value.num_channels = nch;
+ for (i = 0; i < nch; i++) {
+ p.rptr = p.wptr = req_buf;
+ if (!uaudio_req(sc,
+ UT_READ_CLASS_INTERFACE,
+ UAUDIO_V1_REQ_GET_CUR,
+ m->req_sel,
+ m->chan < 0 ? 0 : i + 1,
+ sc->ctl_ifnum,
+ u->id,
+ req_buf,
+ 2))
+ return EIO;
+ p.wptr += 2;
+ if (!uaudio_getnum(&p, 2, &val))
+ return EIO;
+ ctl->un.value.level[i] =
+    uaudio_ranges_decode(&m->ranges,
+ uaudio_sign_expand(val, 2));
+ m = m->next;
  }
-
- p->bps = sc->sc_alts[i].asf1desc->bSubFrameSize;
- p->msb = 1;
-
- if (mode == AUMODE_PLAY)
- paltidx = i;
- else
- raltidx = i;
+ break;
+ case UAUDIO_MIX_ENUM:
+ /* XXX: not used yet */
+ break;
  }
+ return 0;
+}
 
- if (setmode & AUMODE_PLAY) {
- if (paltidx == -1) {
- DPRINTF(("%s: did not find alt for playback\n",
-    __func__));
- return (EINVAL);
- }
- /* XXX abort transfer if currently happening? */
- uaudio_chan_init(&sc->sc_playchan, AUMODE_PLAY, paltidx, play);
- }
- if (setmode & AUMODE_RECORD) {
- if (raltidx == -1) {
- DPRINTF(("%s: did not find alt for recording\n",
-    __func__));
- return (EINVAL);
+int
+uaudio_set_port(void *arg, struct mixer_ctrl *ctl)
+{
+ struct uaudio_softc *sc = arg;
+ struct uaudio_unit *u;
+ struct uaudio_mixent *m;
+ unsigned char req_buf[4];
+ unsigned int val;
+ int i, nch;
+
+ if (!uaudio_mixer_byindex(sc, ctl->dev, &u, &m))
+ return ENOENT;
+
+ switch (m->type) {
+ case UAUDIO_MIX_SW:
+ if (ctl->un.ord < 0 || ctl->un.ord > 1)
+ return EINVAL;
+ req_buf[0] = ctl->un.ord;
+ if (!uaudio_req(sc,
+ UT_WRITE_CLASS_INTERFACE,
+ UAUDIO_V1_REQ_SET_CUR,
+ m->req_sel,
+ m->chan < 0 ? 0 : m->chan,
+ sc->ctl_ifnum,
+ u->id,
+ req_buf,
+ 1))
+ return EIO;
+ break;
+ case UAUDIO_MIX_NUM:
+ nch = uaudio_mixer_nchan(m, NULL);
+ ctl->un.value.num_channels = nch;
+ for (i = 0; i < nch; i++) {
+ val = uaudio_ranges_encode(&m->ranges,
+    ctl->un.value.level[i]);
+ DPRINTF("uaudio_set_port: ch %d, ctl %d, num val %d\n",
+    i, ctl->un.value.level[i], val);
+ req_buf[0] = val;
+ req_buf[1] = val >> 8;
+ if (!uaudio_req(sc,
+ UT_WRITE_CLASS_INTERFACE,
+ UAUDIO_V1_REQ_SET_CUR,
+ m->req_sel,
+ m->chan < 0 ? 0 : i + 1,
+ sc->ctl_ifnum,
+ u->id,
+ req_buf,
+ 2))
+ return EIO;
+ m = m->next;
  }
- /* XXX abort transfer if currently happening? */
- uaudio_chan_init(&sc->sc_recchan, AUMODE_RECORD, raltidx, rec);
+ break;
+ case UAUDIO_MIX_ENUM:
+ /* XXX: not used yet */
+ break;
  }
-
- if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1)
- sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 1;
- if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1)
- sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 1;
-
- DPRINTF(("%s: use altidx=p%d/r%d, altno=p%d/r%d\n", __func__,
- sc->sc_playchan.altidx, sc->sc_recchan.altidx,
- (sc->sc_playchan.altidx >= 0)
-   ?sc->sc_alts[sc->sc_playchan.altidx].idesc->bAlternateSetting
-   : -1,
- (sc->sc_recchan.altidx >= 0)
-   ? sc->sc_alts[sc->sc_recchan.altidx].idesc->bAlternateSetting
-   : -1));
-
- return (0);
+ return 0;
 }
 
-usbd_status
-uaudio_set_speed(struct uaudio_softc *sc, int endpt, u_int speed)
+int
+uaudio_query_devinfo(void *arg, struct mixer_devinfo *devinfo)
 {
- usb_device_request_t req;
- u_int8_t data[3];
+ struct uaudio_softc *sc = arg;
+ struct uaudio_unit *u;
+ struct uaudio_mixent *m;
 
- DPRINTFN(5,("%s: endpt=%d speed=%u\n", __func__, 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));
-}
+ devinfo->next = -1;
+ devinfo->prev = -1;
+ switch (devinfo->index) {
+ case UAUDIO_CLASS_REC:
+ strlcpy(devinfo->label.name, AudioCrecord, MAX_AUDIO_DEV_LEN);
+ devinfo->type = AUDIO_MIXER_CLASS;
+ devinfo->mixer_class = -1;
+ return 0;
+ case UAUDIO_CLASS_IN:
+ strlcpy(devinfo->label.name, AudioCinputs, MAX_AUDIO_DEV_LEN);
+ devinfo->type = AUDIO_MIXER_CLASS;
+ devinfo->mixer_class = -1;
+ return 0;
+ case UAUDIO_CLASS_OUT:
+ strlcpy(devinfo->label.name, AudioCoutputs, MAX_AUDIO_DEV_LEN);
+ devinfo->type = AUDIO_MIXER_CLASS;
+ devinfo->mixer_class = -1;
+ return 0;
+ }
 
-void
-uaudio_set_speed_emu0202(struct chan *ch)
-{
- usb_device_request_t req;
- int rates[6] = { 44100, 48000, 88200, 96000, 176400, 192000 };
- int i;
- u_int8_t data[1];
+ /*
+ * find the unit & mixent structure for the given index
+ */
+ if (!uaudio_mixer_byindex(sc, devinfo->index, &u, &m))
+ return ENOENT;
 
- for (i = 0; i < 6; i++)
- if (rates[i] >= ch->sample_rate)
- break;
- if (i >= 6) {
- DPRINTF(("%s: unhandled rate %d\n", __func__, ch->sample_rate));
- i = 0;
+ if (strcmp(m->fname, "level") == 0) {
+ /*
+ * mixer(4) interface doesn't give a names to level
+ * controls
+ */
+ strlcpy(devinfo->label.name, u->name, MAX_AUDIO_DEV_LEN);
+ } else {
+ snprintf(devinfo->label.name,
+    MAX_AUDIO_DEV_LEN, "%s_%s", u->name, m->fname);
  }
 
- req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
- req.bRequest = SET_CUR;
- USETW2(req.wValue, 0x03, 0);
- USETW2(req.wIndex, 12, ch->sc->sc_ac_iface);
- USETW(req.wLength, 1);
- data[0] = i;
-
- usbd_do_request(ch->sc->sc_udev, &req, data);
+ devinfo->mixer_class = u->mixer_class;
+ switch (m->type) {
+ case UAUDIO_MIX_SW:
+ devinfo->type = AUDIO_MIXER_ENUM;
+ devinfo->un.e.num_mem = 2;
+ devinfo->un.e.member[0].ord = 0;
+ strlcpy(devinfo->un.e.member[0].label.name, "off",
+    MAX_AUDIO_DEV_LEN);
+ devinfo->un.e.member[1].ord = 1;
+ strlcpy(devinfo->un.e.member[1].label.name, "on",
+    MAX_AUDIO_DEV_LEN);
+ break;
+ case UAUDIO_MIX_NUM:
+ devinfo->type = AUDIO_MIXER_VALUE;
+ devinfo->un.v.num_channels = uaudio_mixer_nchan(m, NULL);
+ devinfo->un.v.delta = 1;
+ break;
+ case UAUDIO_MIX_ENUM:
+ /* XXX: not used yet */
+ devinfo->type = AUDIO_MIXER_ENUM;
+ devinfo->un.e.num_mem = 0;
+ break;
+ }
+ return 0;
 }
Index: uaudioreg.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/uaudioreg.h,v
retrieving revision 1.15
diff -u -p -u -p -r1.15 uaudioreg.h
--- uaudioreg.h 15 Apr 2013 09:23:02 -0000 1.15
+++ uaudioreg.h 31 Dec 2018 15:17:23 -0000
@@ -1,386 +0,0 @@
-/* $OpenBSD: uaudioreg.h,v 1.15 2013/04/15 09:23:02 mglocker Exp $ */
-/* $NetBSD: uaudioreg.h,v 1.11 2002/10/23 02:32:37 christos Exp $ */
-
-/*
- * Copyright (c) 1999 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Lennart Augustsson ([hidden email]) 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.
- *
- * 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_CONFIG 0x22
-#define UDESC_CS_STRING 0x23
-#define UDESC_CS_INTERFACE 0x24
-#define UDESC_CS_ENDPOINT 0x25
-
-#define UDESCSUB_AC_HEADER 1
-#define UDESCSUB_AC_INPUT 2
-#define UDESCSUB_AC_OUTPUT 3
-#define UDESCSUB_AC_MIXER 4
-#define UDESCSUB_AC_SELECTOR 5
-#define UDESCSUB_AC_FEATURE 6
-#define UDESCSUB_AC_PROCESSING 7
-#define UDESCSUB_AC_EXTENSION 8
-
-/* The first fields are identical to usb_endpoint_descriptor_t */
-struct usb_endpoint_descriptor_audio {
- 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;
-} __packed;
-#define USB_ENDPOINT_DESCRIPTOR_AUDIO_SIZE 9
-
-struct usb_audio_control_descriptor {
- uByte bLength;
- uByte bDescriptorType;
- uByte bDescriptorSubtype;
- uWord bcdADC;
- uWord wTotalLength;
- uByte bInCollection;
- uByte baInterfaceNr[1];
-} __packed;
-
-struct usb_audio_streaming_interface_descriptor {
- uByte bLength;
- uByte bDescriptorType;
- uByte bDescriptorSubtype;
- uByte bTerminalLink;
- uByte bDelay;
- uWord wFormatTag;
-} __packed;
-
-struct usb_audio_streaming_endpoint_descriptor {
- uByte bLength;
- uByte bDescriptorType;
- uByte bDescriptorSubtype;
- uByte bmAttributes;
-#define UA_SED_FREQ_CONTROL 0x01
-#define UA_SED_PITCH_CONTROL 0x02
-#define UA_SED_MAXPACKETSONLY 0x80
- uByte bLockDelayUnits;
- uWord wLockDelay;
-} __packed;
-
-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)
-} __packed;
-
-struct usb_audio_cluster {
- uByte bNrChannels;
- uWord wChannelConfig;
- uByte iChannelNames;
-} __packed;
-
-/* Shared by all units and terminals */
-struct usb_audio_unit {
- uByte bLength;
- uByte bDescriptorType;
- uByte bDescriptorSubtype;
- uByte bUnitId;
-};
-
-/* 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;
-} __packed;
-
-/* UDESCSUB_AC_OUTPUT */
-struct usb_audio_output_terminal {
- uByte bLength;
- uByte bDescriptorType;
- uByte bDescriptorSubtype;
- uByte bTerminalId;
- uWord wTerminalType;
- uByte bAssocTerminal;
- uByte bSourceId;
- uByte iTerminal;
-} __packed;
-
-/* 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 */
-} __packed;
-struct usb_audio_mixer_unit_1 {
- uByte bNrChannels;
- uWord wChannelConfig;
- uByte iChannelNames;
- uByte bmControls[255]; /* [bNrChannels] */
- /*uByte iMixer;*/
-} __packed;
-
-/* UDESCSUB_AC_SELECTOR */
-struct usb_audio_selector_unit {
- uByte bLength;
- uByte bDescriptorType;
- uByte bDescriptorSubtype;
- uByte bUnitId;
- uByte bNrInPins;
- uByte baSourceId[255]; /* [bNrInPins] */
- /* uByte iSelector; */
-} __packed;
-
-/* 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; */
-} __packed;
-
-/* 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 */
-} __packed;
-struct usb_audio_processing_unit_1{
- uByte bNrChannels;
- uWord wChannelConfig;
- uByte iChannelNames;
- uByte bControlSize;
- uByte bmControls[255]; /* [bControlSize] */
-#define UA_PROC_ENABLE_MASK 1
-} __packed;
-
-struct usb_audio_processing_unit_updown {
- uByte iProcessing;
- uByte bNrModes;
- uWord waModes[255]; /* [bNrModes] */
-} __packed;
-
-/* 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 */
-} __packed;
-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;*/
-} __packed;
-
-/* 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 UA_FMT_MPEG 0x1001
-#define UA_FMT_AC3 0x1002
-
-#define SAMPLING_FREQ_CONTROL 0x01
-#define PITCH_CONTROL 0x02
-
-#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

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Christian Weisgerber
On 2018-12-31, Alexandre Ratchov <[hidden email]> wrote:

> Our current USB sub-system has limitations that currently allow only
> the following combinations:
>  - USB v2 devices on ehci(4) only
>  - USB v1 devices on uhci(4), ohci(4) or ehci(4) root hub only
>
> If you have an audio device that is class compliant (aka vendor claims
> it's "driverless" on MacOS) *and* one of the above host/hub/device
> combinations then I'd be very interested in test reports.

Just to clarify: The new driver supports ONLY these combinations.

The majority of people who have xhci don't need to try:
uaudio0: xhci(4) not supported yet, try another port

--
Christian "naddy" Weisgerber                          [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Mike Monsivais
In reply to this post by Alexandre Ratchov-2
Thanks Alexandre! This is right off the top of my wish list.

I tested with the following hardware:

Sony UDA-1 (UACv2)
Schiit Fulla (UACv1)

Initial tests have audio playing great through both.

> Then do your regular audio work, let me know how it works and
> send me output of:
>
> dmesg
> mixerctl -v -f /dev/mixer1

With the Fulla I get the following output from mixerctl:

$ mixerctl -v -f /dev/mixer1
outputs.play_mute=off  [ off on ]
record.enable=sysctl  [ off on sysctl ]

But, with the UDA-1 I get an error. Though audio still plays great:

$ mixerctl -v -f /dev/mixer1
mixerctl: AUDIO_MIXER_READ: Input/output error

dmesg follows

--
Mike M


OpenBSD 6.4-current (GENERIC.MP) #0: Tue Jan  1 19:08:27 MST 2019
    [hidden email]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 17118175232 (16325MB)
avail mem = 16590073856 (15821MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.7 @ 0xbff3c020 (11 entries)
bios0: vendor coreboot version "CBET4000 20181210" date 11/22/2018
bios0: LENOVO 3368CTO
acpi0 at bios0: rev 2
acpi0: sleep states S0 S3 S4 S5
acpi0: tables DSDT FACP SSDT MCFG TCPA APIC HPET
acpi0: wakeup devices HDEF(S4) EHC1(S4) EHC2(S4) XHC_(S4) SLPB(S3) LID_(S3)
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpimcfg0 at acpi0
acpimcfg0: addr 0xf0000000, bus 0-63
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM) i3-3227U CPU @ 1.90GHz, 1895.98 MHz, 06-3a-09
cpu0: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,POPCNT,DEADLINE,XSAVE,AVX,F16C,NXE,RDTSCP,LONG,LAHF,PERF,ITSC,FSGSBASE,SMEP,ERMS,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu0: 256KB 64b/line 8-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 10 var ranges, 88 fixed ranges
cpu0: apic clock running at 99MHz
cpu0: mwait min=64, max=64, C-substates=0.2.1.1.2, IBE
cpu1 at mainbus0: apid 1 (application processor)
cpu1: Intel(R) Core(TM) i3-3227U CPU @ 1.90GHz, 1895.70 MHz, 06-3a-09
cpu1: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,POPCNT,DEADLINE,XSAVE,AVX,F16C,NXE,RDTSCP,LONG,LAHF,PERF,ITSC,FSGSBASE,SMEP,ERMS,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu1: 256KB 64b/line 8-way L2 cache
cpu1: smt 1, core 0, package 0
cpu2 at mainbus0: apid 2 (application processor)
cpu2: Intel(R) Core(TM) i3-3227U CPU @ 1.90GHz, 1895.70 MHz, 06-3a-09
cpu2: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,POPCNT,DEADLINE,XSAVE,AVX,F16C,NXE,RDTSCP,LONG,LAHF,PERF,ITSC,FSGSBASE,SMEP,ERMS,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu2: 256KB 64b/line 8-way L2 cache
cpu2: smt 0, core 1, package 0
cpu3 at mainbus0: apid 3 (application processor)
cpu3: Intel(R) Core(TM) i3-3227U CPU @ 1.90GHz, 1895.70 MHz, 06-3a-09
cpu3: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,POPCNT,DEADLINE,XSAVE,AVX,F16C,NXE,RDTSCP,LONG,LAHF,PERF,ITSC,FSGSBASE,SMEP,ERMS,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu3: 256KB 64b/line 8-way L2 cache
cpu3: smt 1, core 1, package 0
ioapic0 at mainbus0: apid 2 pa 0xfec00000, version 20, 24 pins
acpihpet0 at acpi0: 14318179 Hz
acpiprt0 at acpi0: bus 1 (RP01)
acpiprt1 at acpi0: bus 2 (RP02)
acpiprt2 at acpi0: bus 3 (RP03)
acpiprt3 at acpi0: bus 4 (RP04)
acpiprt4 at acpi0: bus -1 (RP05)
acpiprt5 at acpi0: bus -1 (RP06)
acpiprt6 at acpi0: bus -1 (RP07)
acpiprt7 at acpi0: bus -1 (RP08)
acpiprt8 at acpi0: bus 0 (PCI0)
acpiec0 at acpi0
acpicpu0 at acpi0: C3(200@90 mwait.1@0x30), C2(500@63 mwait.1@0x10), C1(1000@1 mwait.1), PSS
acpicpu1 at acpi0: C3(200@90 mwait.1@0x30), C2(500@63 mwait.1@0x10), C1(1000@1 mwait.1), PSS
acpicpu2 at acpi0: C3(200@90 mwait.1@0x30), C2(500@63 mwait.1@0x10), C1(1000@1 mwait.1), PSS
acpicpu3 at acpi0: C3(200@90 mwait.1@0x30), C2(500@63 mwait.1@0x10), C1(1000@1 mwait.1), PSS
acpitz0 at acpi0: critical temperature is 100 degC
acpipwrres0 at acpi0: FPWR, resource for FAN_
acpitz1 at acpi0: critical temperature is 99 degC
acpipci0 at acpi0 PCI0: 0x00000000 0x00000011 0x00000001
acpiac0 at acpi0: AC unit offline
acpibat0 at acpi0: BAT0 model "45N1176" serial  2589 type LION oem "SANYO"
acpibat1 at acpi0: BAT1 not present
acpibtn0 at acpi0: SLPB
acpibtn1 at acpi0: LID_
acpithinkpad0 at acpi0
acpicmos0 at acpi0
tpm0 at acpi0: TPM_ addr 0xfed40000/0x5000: device 0x0000104a rev 0x4e
"PNP0C0B" at acpi0 not configured
"BOOT0000" at acpi0 not configured
acpivideo0 at acpi0: GFX0
acpivout0 at acpivideo0: LCD0
cpu0: Enhanced SpeedStep 1895 MHz: speeds: 1901, 1900, 1600, 1400, 1200, 1000, 800 MHz
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel Core 3G Host" rev 0x09
inteldrm0 at pci0 dev 2 function 0 "Intel HD Graphics 4000" rev 0x09
drm0 at inteldrm0
inteldrm0: msi
inteldrm0: 1366x768, 32bpp
wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation)
wsdisplay0: screen 1-5 added (std, vt100 emulation)
xhci0 at pci0 dev 20 function 0 "Intel 7 Series xHCI" rev 0x04: msi, xHCI 1.0
usb0 at xhci0: USB revision 3.0
uhub0 at usb0 configuration 1 interface 0 "Intel xHCI root hub" rev 3.00/1.00 addr 1
ehci0 at pci0 dev 26 function 0 "Intel 7 Series USB" rev 0x04: apic 2 int 21
usb1 at ehci0: USB revision 2.0
uhub1 at usb1 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
azalia0 at pci0 dev 27 function 0 "Intel 7 Series HD Audio" rev 0x04: msi
azalia0: codecs: Realtek ALC269, Intel/0x2806, using Realtek ALC269
audio0 at azalia0
ppb0 at pci0 dev 28 function 0 "Intel 7 Series PCIE" rev 0xc4
pci1 at ppb0 bus 1
ppb1 at pci0 dev 28 function 1 "Intel 7 Series PCIE" rev 0xc4
pci2 at ppb1 bus 2
iwm0 at pci2 dev 0 function 0 "Intel Dual Band Wireless AC 7260" rev 0x73, msi
ppb2 at pci0 dev 28 function 2 "Intel 7 Series PCIE" rev 0xc4
pci3 at ppb2 bus 3
rtsx0 at pci3 dev 0 function 0 "Realtek RTS5209 Card Reader" rev 0x01: msi
sdmmc0 at rtsx0: 4-bit, dma
ppb3 at pci0 dev 28 function 3 "Intel 7 Series PCIE" rev 0xc4
pci4 at ppb3 bus 4
re0 at pci4 dev 0 function 0 "Realtek 8168" rev 0x07: RTL8168E/8111E-VL (0x2c80), msi, address c4:54:44:30:6a:a1
rgephy0 at re0 phy 7: RTL8169S/8110S/8211 PHY, rev. 5
ehci1 at pci0 dev 29 function 0 "Intel 7 Series USB" rev 0x04: apic 2 int 19
usb2 at ehci1: USB revision 2.0
uhub2 at usb2 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
pcib0 at pci0 dev 31 function 0 "Intel HM77 LPC" rev 0x04
ahci0 at pci0 dev 31 function 2 "Intel 7 Series AHCI" rev 0x04: msi, AHCI 1.3
ahci0: port 1: 6.0Gb/s
scsibus1 at ahci0: 32 targets
sd0 at scsibus1 targ 1 lun 0: <ATA, Crucial_CT120M50, MU03> SCSI3 0/direct fixed naa.500a07510966c3e4
sd0: 114473MB, 512 bytes/sector, 234441648 sectors, thin
ichiic0 at pci0 dev 31 function 3 "Intel 7 Series SMBus" rev 0x04: apic 2 int 23
iic0 at ichiic0
iic0: addr 0x24 03=04 09=58 0a=29 0b=05 0c=04 0d=02 0e=01 0f=19 words 00=00ff 01=00ff 02=00ff 03=04ff 04=00ff 05=00ff 06=00ff 07=00ff
spdmem0 at iic0 addr 0x50: 8GB DDR3 SDRAM PC3-12800 SO-DIMM
spdmem1 at iic0 addr 0x52: 8GB DDR3 SDRAM PC3-12800 SO-DIMM
isa0 at pcib0
isadma0 at isa0
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
wsmouse1 at pms0 mux 0
pms0: Synaptics clickpad, firmware 8.0, 0x1e2b1 0x940300
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
vmm0 at mainbus0: VMX/EPT
efifb at mainbus0 not configured
error: [drm:pid0:cpt_set_fifo_underrun_reporting] *ERROR* uncleared pch fifo underrun on pch transcoder A
error: [drm:pid0:intel_pch_fifo_underrun_irq_handler] *ERROR* PCH transcoder A FIFO underrun
uvideo0 at uhub0 port 4 configuration 1 interface 0 "Vimicro corp. Integrated Camera" rev 2.00/0.10 addr 2
video0 at uvideo0
uhub3 at uhub1 port 1 configuration 1 interface 0 "Intel Rate Matching Hub" rev 2.00/0.00 addr 2
uhub4 at uhub2 port 1 configuration 1 interface 0 "Intel Rate Matching Hub" rev 2.00/0.00 addr 2
ugen0 at uhub4 port 5 "Intel product 0x07dc" rev 2.00/0.01 addr 3
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
sd1 at scsibus3 targ 1 lun 0: <OPENBSD, SR CRYPTO, 006> SCSI2 0/direct fixed
sd1: 114470MB, 512 bytes/sector, 234435953 sectors
root on sd1a (e83024ac8c722441.a) swap on sd1b dump on sd1b
iwm0: hw rev 0x140, fw ver 16.242414.0, address XX:XX:XX:XX:XX:XX
uaudio0 at uhub3 port 2 configuration 1 interface 1 "Sony Sony USB DAC Amplifier" rev 2.00/1.08 addr 3
uaudio0: class v2, high-speed, async, channels: 2 play, 0 rec, 1 ctls
audio1 at uaudio0
uhidev0 at uhub3 port 2 configuration 1 interface 2 "Sony Sony USB DAC Amplifier" rev 2.00/1.08 addr 3
uhidev0: iclass 3/0
uhid0 at uhidev0: input=16, output=16, feature=0
audio1 detached
uaudio0 detached
uhid0 detached
uhidev0 detached
uaudio0 at uhub3 port 2 configuration 1 interface 1 "Schiit Audio I'm Fulla Schiit" rev 2.00/1.03 addr 3
uaudio0: class v1, high-speed, async, channels: 2 play, 0 rec, 1 ctls
audio1 at uaudio0
uhidev0 at uhub3 port 2 configuration 1 interface 2 "Schiit Audio I'm Fulla Schiit" rev 2.00/1.03 addr 3
uhidev0: iclass 3/0
uhid0 at uhidev0: input=16, output=16, feature=0

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Alexandre Ratchov-2
In reply to this post by Christian Weisgerber
On Tue, Jan 01, 2019 at 11:02:39PM -0000, Christian Weisgerber wrote:

> On 2018-12-31, Alexandre Ratchov <[hidden email]> wrote:
>
> > Our current USB sub-system has limitations that currently allow only
> > the following combinations:
> >  - USB v2 devices on ehci(4) only
> >  - USB v1 devices on uhci(4), ohci(4) or ehci(4) root hub only
> >
> > If you have an audio device that is class compliant (aka vendor claims
> > it's "driverless" on MacOS) *and* one of the above host/hub/device
> > combinations then I'd be very interested in test reports.
>
> Just to clarify: The new driver supports ONLY these combinations.
>

well, the old one supports only these as well, except that it prints
no error message.

> The majority of people who have xhci don't need to try:
> uaudio0: xhci(4) not supported yet, try another port

Thanks for insisting on this.

Until the xhci driver is finished, those who absolutly want to
test/use the new uaudio driver could try to disable xhci on the boot
prompt. On most machines, ehci will attach instead of xhci. This would
allow using usb 2.0 devices out of the box. To test usb 1.1 devices,
you also need to have a usb connectors on the ehci root hub, certain
machines have them, others don't. On those which have them, there's a
compagnion uhci device, example:

usb0 at ehci0: USB revision 2.0
usb1 at uhci0: USB revision 1.0
usb2 at uhci1: USB revision 1.0
usb3 at uhci2: USB revision 1.0
usb4 at uhci3: USB revision 1.0
uhub0 at usb1 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
uhub1 at usb2 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
uhub2 at usb3 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
uhub3 at usb4 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
uhub4 at usb0 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
uaudio0 at uhub1 port 1 configuration 1 interface 1 "ABC C-Media USB Headphone Set" rev 1.10/1.00 addr 2
uaudio0: class v1, full-speed, sync, channels: 2 play, 1 rec, 8 ctls

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Max Fillinger
In reply to this post by Alexandre Ratchov-2
I tested the diff with a FiiO E10K Olympus 2.

Previously, it only produced stuttering audio, which I assumed was
because it's a UAC v1.0 device and I connected it on ehci.  Now, it
can't be opened at all.

The first thing I noticed was that the kernel now finds two uaudio
devices instead of one (the full dmesg is at the end of my mail):

> uaudio0 at uhub3 port 2 configuration 1 interface 2 "FiiO DigiHug USB Audio" rev 1.10/0.01 addr 3
> uaudio0: no sync endpoint found
> uaudio1 at uhub3 port 2 configuration 1 interface 3 "FiiO DigiHug USB Audio" rev 1.10/0.01 addr 3
> uaudio1: no sync endpoint found

Before applying the diff, I saw the follwing in the dmesg:

> uaudio0 at uhub3 port 2 configuration 1 interface 1 "FiiO DigiHug USB Audio" rev 1.10/0.01 addr 3
> uaudio0: ignored setting with type 8193 format
> uaudio0: audio rev 1.00, 2 mixer controls

I've set the sndiod flags as follows: -f rsnd/1 -f rsnd/2 -f rsnd/0

rsnd/0 is the built-in speakers/headphone jack of my laptop, rsnd/1
and rsnd/2 should be the USB audio devices.

$ aucat -i test.wav -f snd/0
snd/0: couldn't open audio device

$ aucat -i test.wav -f snd/1
snd/1: couldn't open audio device

$ aucat -i test.wav -f snd/2
... plays audio on built-in laptop speakers

$ mixerctl -v -f /dev/mixer1
mixerctl: /dev/mixer1: Device not configured

Let me know if I can do anything to help debug this! (Or if I need to
buy a better USB audio device ;) )


$ dmesg
OpenBSD 6.4-current (GENERIC.MP) #0: Tue Jan  1 13:21:00 CET 2019
    [hidden email]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 4062691328 (3874MB)
avail mem = 3930357760 (3748MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.6 @ 0xe0010 (78 entries)
bios0: vendor LENOVO version "6IET79WW (1.39 )" date 07/15/2011
bios0: LENOVO 252225G
acpi0 at bios0: rev 2
acpi0: sleep states S0 S3 S4 S5
acpi0: tables DSDT FACP SSDT ECDT APIC MCFG HPET ASF! SLIC BOOT SSDT TCPA SSDT SSDT SSDT
acpi0: wakeup devices LID_(S3) SLPB(S3) IGBE(S4) EXP1(S4) EXP2(S4) EXP3(S4) EXP4(S4) EXP5(S4) EHC1(S3) EHC2(S3) HDEF(S4)
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpiec0 at acpi0
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM) i5 CPU M 540 @ 2.53GHz, 2793.47 MHz, 06-25-02
cpu0: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,SSE4.2,POPCNT,AES,NXE,RDTSCP,LONG,LAHF,PERF,ITSC,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,MELTDOWN
cpu0: 256KB 64b/line 8-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 8 var ranges, 88 fixed ranges
cpu0: apic clock running at 132MHz
cpu0: mwait min=64, max=64, C-substates=0.2.1.1, IBE
cpu1 at mainbus0: apid 1 (application processor)
cpu1: Intel(R) Core(TM) i5 CPU M 540 @ 2.53GHz, 2793.00 MHz, 06-25-02
cpu1: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,SSE4.2,POPCNT,AES,NXE,RDTSCP,LONG,LAHF,PERF,ITSC,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,MELTDOWN
cpu1: 256KB 64b/line 8-way L2 cache
cpu1: smt 1, core 0, package 0
cpu2 at mainbus0: apid 4 (application processor)
cpu2: Intel(R) Core(TM) i5 CPU M 540 @ 2.53GHz, 2793.00 MHz, 06-25-02
cpu2: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,SSE4.2,POPCNT,AES,NXE,RDTSCP,LONG,LAHF,PERF,ITSC,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,MELTDOWN
cpu2: 256KB 64b/line 8-way L2 cache
cpu2: smt 0, core 2, package 0
cpu3 at mainbus0: apid 5 (application processor)
cpu3: Intel(R) Core(TM) i5 CPU M 540 @ 2.53GHz, 2793.00 MHz, 06-25-02
cpu3: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,SSE4.2,POPCNT,AES,NXE,RDTSCP,LONG,LAHF,PERF,ITSC,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,MELTDOWN
cpu3: 256KB 64b/line 8-way L2 cache
cpu3: smt 1, core 2, package 0
ioapic0 at mainbus0: apid 1 pa 0xfec00000, version 20, 24 pins, remapped
acpimcfg0 at acpi0
acpimcfg0: addr 0xe0000000, bus 0-255
acpihpet0 at acpi0: 14318179 Hz
acpiprt0 at acpi0: bus 0 (PCI0)
acpiprt1 at acpi0: bus -1 (PEG_)
acpiprt2 at acpi0: bus 2 (EXP1)
acpiprt3 at acpi0: bus 3 (EXP2)
acpiprt4 at acpi0: bus -1 (EXP3)
acpiprt5 at acpi0: bus 5 (EXP4)
acpiprt6 at acpi0: bus 13 (EXP5)
acpicpu0 at acpi0: C3(350@245 mwait.3@0x20), C2(500@205 mwait.3@0x10), C1(1000@3 mwait.1), PSS
acpicpu1 at acpi0: C3(350@245 mwait.3@0x20), C2(500@205 mwait.3@0x10), C1(1000@3 mwait.1), PSS
acpicpu2 at acpi0: C3(350@245 mwait.3@0x20), C2(500@205 mwait.3@0x10), C1(1000@3 mwait.1), PSS
acpicpu3 at acpi0: C3(350@245 mwait.3@0x20), C2(500@205 mwait.3@0x10), C1(1000@3 mwait.1), PSS
acpipwrres0 at acpi0: PUBS, resource for EHC1, EHC2
acpitz0 at acpi0: critical temperature is 100 degC
acpibtn0 at acpi0: LID_
acpibtn1 at acpi0: SLPB
acpipci0 at acpi0 UNCR: _OSC failed
acpipci1 at acpi0 PCI0: 0x00000000 0x00000011 0x00000001
acpicmos0 at acpi0
tpm0 at acpi0: TPM_ addr 0xfed40000/0x5000: device 0x0000104a rev 0x4e
acpibat0 at acpi0: BAT0 model "45N1173" serial 30766 type LION oem "SANYO"
acpiac0 at acpi0: AC unit online
acpithinkpad0 at acpi0
"*pnp0c14" at acpi0 not configured
"PNP0C14" at acpi0 not configured
acpivideo0 at acpi0: VID_
acpivout0 at acpivideo0: LCD0
acpivideo1 at acpi0: VID_
cpu0: Enhanced SpeedStep 2793 MHz: speeds: 2534, 2533, 2399, 2266, 2133, 1999, 1866, 1733, 1599, 1466, 1333, 1199 MHz
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel Core Host" rev 0x02
inteldrm0 at pci0 dev 2 function 0 "Intel HD Graphics" rev 0x02
drm0 at inteldrm0
intagp0 at inteldrm0
agp0 at intagp0: aperture at 0xd0000000, size 0x10000000
inteldrm0: msi
inteldrm0: 1440x900, 32bpp
wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation)
wsdisplay0: screen 1-5 added (std, vt100 emulation)
"Intel 3400 MEI" rev 0x06 at pci0 dev 22 function 0 not configured
em0 at pci0 dev 25 function 0 "Intel 82577LM" rev 0x06: msi, address 5c:ff:35:0f:57:21
ehci0 at pci0 dev 26 function 0 "Intel 3400 USB" rev 0x06: apic 1 int 23
usb0 at ehci0: USB revision 2.0
uhub0 at usb0 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
azalia0 at pci0 dev 27 function 0 "Intel 3400 HD Audio" rev 0x06: msi
azalia0: codecs: Conexant/0x5069, Intel/0x2804, using Conexant/0x5069
audio0 at azalia0
ppb0 at pci0 dev 28 function 0 "Intel 3400 PCIE" rev 0x06: msi
pci1 at ppb0 bus 2
ppb1 at pci0 dev 28 function 1 "Intel 3400 PCIE" rev 0x06: msi
pci2 at ppb1 bus 3
iwn0 at pci2 dev 0 function 0 "Intel Centrino Advanced-N 6200" rev 0x35: msi, MIMO 2T2R, MoW, address 00:27:10:18:d1:10
ppb2 at pci0 dev 28 function 3 "Intel 3400 PCIE" rev 0x06: msi
pci3 at ppb2 bus 5
ppb3 at pci0 dev 28 function 4 "Intel 3400 PCIE" rev 0x06: msi
pci4 at ppb3 bus 13
sdhc0 at pci4 dev 0 function 0 "Ricoh 5U822 SD/MMC" rev 0x01: apic 1 int 16
sdhc0: SDHC 1.0, 50 MHz base clock
sdmmc0 at sdhc0: 4-bit, sd high-speed, mmc high-speed
"Ricoh 5U230 Memory Stick" rev 0x01 at pci4 dev 0 function 1 not configured
"Ricoh 5U832 Firewire" rev 0x01 at pci4 dev 0 function 3 not configured
ehci1 at pci0 dev 29 function 0 "Intel 3400 USB" rev 0x06: apic 1 int 19
usb1 at ehci1: USB revision 2.0
uhub1 at usb1 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
ppb4 at pci0 dev 30 function 0 "Intel 82801BAM Hub-to-PCI" rev 0xa6
pci5 at ppb4 bus 14
pcib0 at pci0 dev 31 function 0 "Intel QM57 LPC" rev 0x06
ahci0 at pci0 dev 31 function 2 "Intel 3400 AHCI" rev 0x06: msi, AHCI 1.3
ahci0: port 0: 3.0Gb/s
ahci0: port 1: 1.5Gb/s
scsibus1 at ahci0: 32 targets
sd0 at scsibus1 targ 0 lun 0: <ATA, Samsung SSD 840, EXT0> SCSI3 0/direct fixed naa.50025388a02cba98
sd0: 476940MB, 512 bytes/sector, 976773168 sectors, thin
cd0 at scsibus1 targ 1 lun 0: <HL-DT-ST, DVDRAM GU10N, MX05> ATAPI 5/cdrom removable
ichiic0 at pci0 dev 31 function 3 "Intel 3400 SMBus" rev 0x06: apic 1 int 23
iic0 at ichiic0
spdmem0 at iic0 addr 0x50: 2GB DDR3 SDRAM PC3-8500 SO-DIMM
spdmem1 at iic0 addr 0x51: 2GB DDR3 SDRAM PC3-8500 SO-DIMM
itherm0 at pci0 dev 31 function 6 "Intel 3400 Thermal" rev 0x06
isa0 at pcib0
isadma0 at isa0
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
aps0 at isa0 port 0x1600/31
pci6 at mainbus0 bus 255
pchb1 at pci6 dev 0 function 0 "Intel QuickPath" rev 0x02
pchb2 at pci6 dev 0 function 1 "Intel QuickPath" rev 0x02
pchb3 at pci6 dev 2 function 0 "Intel QPI Link" rev 0x02
pchb4 at pci6 dev 2 function 1 "Intel QPI Physical" rev 0x02
pchb5 at pci6 dev 2 function 2 "Intel Reserved" rev 0x02
pchb6 at pci6 dev 2 function 3 "Intel Reserved" rev 0x02
sdmmc0: can't enable card
uhub2 at uhub0 port 1 configuration 1 interface 0 "Intel Rate Matching Hub" rev 2.00/0.00 addr 2
uhidev0 at uhub2 port 2 configuration 1 interface 0 "Lenovo Lenovo Laser Wireless Mouse" rev 2.00/1.70 addr 3
uhidev0: iclass 3/1, 1 report id
ums0 at uhidev0 reportid 1: 5 buttons, Z and W dir
wsmouse1 at ums0 mux 0
ugen0 at uhub2 port 3 "UPEK Biometric Coprocessor" rev 1.01/0.02 addr 4
uhub3 at uhub1 port 1 configuration 1 interface 0 "Intel Rate Matching Hub" rev 2.00/0.00 addr 2
uhidev1 at uhub3 port 2 configuration 1 interface 0 "FiiO DigiHug USB Audio" rev 1.10/0.01 addr 3
uhidev1: iclass 3/0
uhid0 at uhidev1: input=18, output=27, feature=0
uaudio0 at uhub3 port 2 configuration 1 interface 2 "FiiO DigiHug USB Audio" rev 1.10/0.01 addr 3
uaudio0: no sync endpoint found
uaudio1 at uhub3 port 2 configuration 1 interface 3 "FiiO DigiHug USB Audio" rev 1.10/0.01 addr 3
uaudio1: no sync endpoint found
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
sd1 at scsibus3 targ 1 lun 0: <OPENBSD, SR CRYPTO, 006> SCSI2 0/direct fixed
sd1: 354308MB, 512 bytes/sector, 725623392 sectors
root on sd1a (8a039255777d8a50.a) swap on sd1b dump on sd1b

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Ville Valkonen
In reply to this post by Alexandre Ratchov-2
On Wed, 2 Jan 2019 at 08:56, Alexandre Ratchov <[hidden email]> wrote:

> On Tue, Jan 01, 2019 at 11:02:39PM -0000, Christian Weisgerber wrote:
> > On 2018-12-31, Alexandre Ratchov <[hidden email]> wrote:
> >
> > > Our current USB sub-system has limitations that currently allow only
> > > the following combinations:
> > >  - USB v2 devices on ehci(4) only
> > >  - USB v1 devices on uhci(4), ohci(4) or ehci(4) root hub only
> > >
> > > If you have an audio device that is class compliant (aka vendor claims
> > > it's "driverless" on MacOS) *and* one of the above host/hub/device
> > > combinations then I'd be very interested in test reports.
> >
> > Just to clarify: The new driver supports ONLY these combinations.
> >
>
> well, the old one supports only these as well, except that it prints
> no error message.
>
> > The majority of people who have xhci don't need to try:
> > uaudio0: xhci(4) not supported yet, try another port
>
> Thanks for insisting on this.
>
> Until the xhci driver is finished, those who absolutly want to
> test/use the new uaudio driver could try to disable xhci on the boot
> prompt. On most machines, ehci will attach instead of xhci. This would
> allow using usb 2.0 devices out of the box. To test usb 1.1 devices,
> you also need to have a usb connectors on the ehci root hub, certain
> machines have them, others don't. On those which have them, there's a
> compagnion uhci device, example:
>
> usb0 at ehci0: USB revision 2.0
> usb1 at uhci0: USB revision 1.0
> usb2 at uhci1: USB revision 1.0
> usb3 at uhci2: USB revision 1.0
> usb4 at uhci3: USB revision 1.0
> uhub0 at usb1 configuration 1 interface 0 "Intel UHCI root hub" rev
> 1.00/1.00 addr 1
> uhub1 at usb2 configuration 1 interface 0 "Intel UHCI root hub" rev
> 1.00/1.00 addr 1
> uhub2 at usb3 configuration 1 interface 0 "Intel UHCI root hub" rev
> 1.00/1.00 addr 1
> uhub3 at usb4 configuration 1 interface 0 "Intel UHCI root hub" rev
> 1.00/1.00 addr 1
> uhub4 at usb0 configuration 1 interface 0 "Intel EHCI root hub" rev
> 2.00/1.00 addr 1
> uaudio0 at uhub1 port 1 configuration 1 interface 1 "ABC C-Media USB
> Headphone Set" rev 1.10/1.00 addr 2
> uaudio0: class v1, full-speed, sync, channels: 2 play, 1 rec, 8 ctls
>

 Hello Alexandre,

and thank you so much for your hard work on this and audio related
contributions.

I applied the patch, disabled USB3 from BIOS and booted to the newly
compiled
kernel.

When using mpv to play an mp3 file I hear nothing. Tailing dmesg (see below)
informs I'm hitting an error.

Please let me know if more information is needed.

Thanks.

$ mixerctl -v -f /dev/mixer1
record.enable=sysctl  [ off on sysctl ]

$ grep ^sndiod /etc/rc.conf.local
sndiod_flags=-f rsnd/1

OpenBSD 6.4-current (GENERIC.MP) #0: Tue Jan  1 22:36:48 EET 2019
    [hidden email]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 8278011904 (7894MB)
avail mem = 8017928192 (7646MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.7 @ 0xccbfd000 (64 entries)
bios0: vendor LENOVO version "N10ET49W (1.28 )" date 12/18/2017
bios0: LENOVO 20CLS06D00
acpi0 at bios0: rev 2
acpi0: sleep states S0 S3 S4 S5
acpi0: tables DSDT FACP ASF! HPET ECDT APIC MCFG SSDT SSDT SSDT SSDT SSDT
SSDT SSDT SSDT SSDT PCCT SSDT UEFI POAT BATB FPDT UEFI DMAR
acpi0: wakeup devices LID_(S4) SLPB(S3) IGBE(S4) EXP2(S4) XHCI(S3) EHC1(S3)
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpihpet0 at acpi0: 14318179 Hz
acpiec0 at acpi0
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz, 798.27 MHz, 06-3d-04
cpu0:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,RDSEED,ADX,SMAP,PT,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu0: 256KB 64b/line 8-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 10 var ranges, 88 fixed ranges
cpu0: apic clock running at 99MHz
cpu0: mwait min=64, max=64, C-substates=0.2.1.2.4.1.1.1, IBE
cpu1 at mainbus0: apid 1 (application processor)
cpu1: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz, 798.15 MHz, 06-3d-04
cpu1:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,RDSEED,ADX,SMAP,PT,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu1: 256KB 64b/line 8-way L2 cache
cpu1: smt 1, core 0, package 0
cpu2 at mainbus0: apid 2 (application processor)
cpu2: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz, 798.16 MHz, 06-3d-04
cpu2:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,RDSEED,ADX,SMAP,PT,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu2: 256KB 64b/line 8-way L2 cache
cpu2: smt 0, core 1, package 0
cpu3 at mainbus0: apid 3 (application processor)
cpu3: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz, 798.15 MHz, 06-3d-04
cpu3:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,PCLMUL,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,SDBG,FMA3,CX16,xTPR,PDCM,PCID,SSE4.1,SSE4.2,x2APIC,MOVBE,POPCNT,DEADLINE,AES,XSAVE,AVX,F16C,RDRAND,NXE,PAGE1GB,RDTSCP,LONG,LAHF,ABM,3DNOWP,PERF,ITSC,FSGSBASE,BMI1,HLE,AVX2,SMEP,BMI2,ERMS,INVPCID,RTM,RDSEED,ADX,SMAP,PT,IBRS,IBPB,STIBP,L1DF,SSBD,SENSOR,ARAT,XSAVEOPT,MELTDOWN
cpu3: 256KB 64b/line 8-way L2 cache
cpu3: smt 1, core 1, package 0
ioapic0 at mainbus0: apid 2 pa 0xfec00000, version 20, 40 pins
acpimcfg0 at acpi0
acpimcfg0: addr 0xf8000000, bus 0-63
acpiprt0 at acpi0: bus 0 (PCI0)
acpiprt1 at acpi0: bus -1 (PEG_)
acpiprt2 at acpi0: bus 2 (EXP1)
acpiprt3 at acpi0: bus 3 (EXP2)
acpiprt4 at acpi0: bus -1 (EXP3)
acpicpu0 at acpi0: C3(200@233 mwait.1@0x40), C2(200@148 mwait.1@0x33),
C1(1000@1 mwait.1), PSS
acpicpu1 at acpi0: C3(200@233 mwait.1@0x40), C2(200@148 mwait.1@0x33),
C1(1000@1 mwait.1), PSS
acpicpu2 at acpi0: C3(200@233 mwait.1@0x40), C2(200@148 mwait.1@0x33),
C1(1000@1 mwait.1), PSS
acpicpu3 at acpi0: C3(200@233 mwait.1@0x40), C2(200@148 mwait.1@0x33),
C1(1000@1 mwait.1), PSS
acpipwrres0 at acpi0: PUBS, resource for XHCI, EHC1
acpipwrres1 at acpi0: NVP3, resource for PEG_
acpipwrres2 at acpi0: NVP2, resource for PEG_
acpitz0 at acpi0: critical temperature is 128 degC
acpibtn0 at acpi0: LID_
acpibtn1 at acpi0: SLPB
acpipci0 at acpi0 PCI0: 0x00000000 0x00000011 0x00000001
acpicmos0 at acpi0
acpibat0 at acpi0: BAT0 model "45N1773" serial   109 type LION oem "SANYO"
acpibat1 at acpi0: BAT1 model "45N1775" serial  3585 type LION oem "SANYO"
acpiac0 at acpi0: AC unit offline
acpithinkpad0 at acpi0
"PNP0C14" at acpi0 not configured
"PNP0C14" at acpi0 not configured
"PNP0C14" at acpi0 not configured
"INT340F" at acpi0 not configured
acpivideo0 at acpi0: VID_
acpivout at acpivideo0 not configured
acpivideo1 at acpi0: VID_
cpu0: Enhanced SpeedStep 798 MHz: speeds: 2601, 2600, 2500, 2300, 2100,
2000, 1800, 1700, 1500, 1400, 1200, 1100, 900, 800, 600, 500 MHz
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel Core 5G Host" rev 0x09
inteldrm0 at pci0 dev 2 function 0 "Intel HD Graphics 5500" rev 0x09
drm0 at inteldrm0
inteldrm0: msi
inteldrm0: 1920x1080, 32bpp
wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation)
wsdisplay0: screen 1-5 added (std, vt100 emulation)
azalia0 at pci0 dev 3 function 0 "Intel Core 5G HD Audio" rev 0x09: msi
"Intel 9 Series MEI" rev 0x03 at pci0 dev 22 function 0 not configured
em0 at pci0 dev 25 function 0 "Intel I218-LM" rev 0x03: msi, address
68:f7:28:61:6f:a6
azalia1 at pci0 dev 27 function 0 "Intel 9 Series HD Audio" rev 0x03: msi
azalia1: codecs: Realtek ALC292
audio0 at azalia1
ppb0 at pci0 dev 28 function 0 "Intel 9 Series PCIE" rev 0xe3: msi
pci1 at ppb0 bus 2
rtsx0 at pci1 dev 0 function 0 "Realtek RTS5227 Card Reader" rev 0x01: msi
sdmmc0 at rtsx0: 4-bit, dma
ppb1 at pci0 dev 28 function 1 "Intel 9 Series PCIE" rev 0xe3: msi
pci2 at ppb1 bus 3
iwm0 at pci2 dev 0 function 0 "Intel Dual Band Wireless AC 7265" rev 0x59,
msi
ehci0 at pci0 dev 29 function 0 "Intel 9 Series USB" rev 0x03: apic 2 int 23
usb0 at ehci0: USB revision 2.0
uhub0 at usb0 configuration 1 interface 0 "Intel EHCI root hub" rev
2.00/1.00 addr 1
pcib0 at pci0 dev 31 function 0 "Intel 9 Series LPC" rev 0x03
ahci0 at pci0 dev 31 function 2 "Intel 9 Series AHCI" rev 0x03: msi, AHCI
1.3
ahci0: port 0: 6.0Gb/s
scsibus1 at ahci0: 32 targets
sd0 at scsibus1 targ 0 lun 0: <ATA, INTEL SSDSC2BF36, LTVi> SCSI3 0/direct
fixed naa.55cd2e404bde3c69
sd0: 343399MB, 512 bytes/sector, 703282608 sectors, thin
ichiic0 at pci0 dev 31 function 3 "Intel 9 Series SMBus" rev 0x03: apic 2
int 18
iic0 at ichiic0
pchtemp0 at pci0 dev 31 function 6 "Intel 9 Series Thermal" rev 0x03
isa0 at pcib0
isadma0 at isa0
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbdprobe: reset response 0xfe
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
wsmouse1 at pms0 mux 0
pms0: Synaptics clickpad, firmware 8.1, 0x1e2b1 0x943300
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
vmm0 at mainbus0: VMX/EPT
Unclaimed register detected before reading register 0x23a0
uhub1 at uhub0 port 1 configuration 1 interface 0 "Intel Rate Matching Hub"
rev 2.00/0.03 addr 2
ugen0 at uhub1 port 7 "Intel Bluetooth" rev 2.01/0.01 addr 3
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
root on sd0a (ce822de83a7969c0.a) swap on sd0b dump on sd0b
iwm0: hw rev 0x210, fw ver 16.242414.0, address 60:57:18:6a:df:8d
uaudio0 at uhub1 port 2 configuration 1 interface 1 "Cambridge Audio
Cambridge Audio USB Audio 2.0" rev 2.00/3.26 addr 4
uaudio0: class v2, high-speed, async, channels: 2 play, 0 rec, 0 ctls
audio1 at uaudio0
ugen1 at uhub1 port 2 configuration 1 "Cambridge Audio Cambridge Audio USB
Audio 2.0" rev 2.00/3.26 addr 4
uaudio0: request failed: TIMEOUT
uaudio0: failed to set clock rate
audio1: failed to start playback
uaudio0: request failed: TIMEOUT
uaudio0: failed to set clock rate
audio1: failed to start playback

--
Regards,
Ville Valkonen
Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Martijn Rijkeboer
In reply to this post by Alexandre Ratchov-2
On 31-12-18 16:58, Alexandre Ratchov wrote:

> Hi,
>
> Here's a new driver for both USB audio class (UAC) v1.0 and v2.0
> devices, it would replace the current one. It focuses on reliability
> and proper synchronization, including in low-latency configurations.
>
> Our current USB sub-system has limitations that currently allow only
> the following combinations:
>   - USB v2 devices on ehci(4) only
>   - USB v1 devices on uhci(4), ohci(4) or ehci(4) root hub only
>
> If you have an audio device that is class compliant (aka vendor claims
> it's "driverless" on MacOS) *and* one of the above host/hub/device
> combinations then I'd be very interested in test reports. Especially
> I'd like to know about possible regressions.
>
> To test, apply this diff, rebuild the kernel and set:
>
> sndiod_flags=-f rsnd/1
>
> in /etc/rc.conf.local (assuming your uaudio device shows as uaudio1 in
> dmesg). Then do your regular audio work, let me know how it works and
> send me output of:
>
> dmesg
> mixerctl -v -f /dev/mixer1
>
> If something is broken, please check if this is a regression.

Hi Alexandre,

Sorry for being late to the party. With this patch audio playback with
my Schiit Bifrost DAC works, however I get an error with the output of
mixerctl. This DAC didn't work before. Please see dmesg and mixerctl
output below.

Kind regards,


Martijn Rijkeboer


dmesg
=====
OpenBSD 6.4-current (GENERIC.MP) #0: Sat Jan  5 17:39:01 CET 2019
     [hidden email]:/sys/arch/amd64/compile/GENERIC.MP
real mem = 1987788800 (1895MB)
avail mem = 1918291968 (1829MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.5 @ 0xe0010 (44 entries)
bios0: vendor LENOVO version "6JET65WW (1.23 )" date 10/19/2009
bios0: LENOVO 28477LG
acpi0 at bios0: rev 4
acpi0: sleep states S0 S3 S4 S5
acpi0: tables DSDT FACP HPET MCFG APIC BOOT SLIC SSDT SSDT SSDT
acpi0: wakeup devices P0P2(S4) P0P1(S4) USB0(S3) USB1(S3) USB2(S3)
USBR(S3) EHC1(S3) USB3(S3) USB4(S3) USB5(S3) EHC2(S3) HDEF(S4) PXSX(S4)
RP01(S4) PXSX(S4) RP02(S4) [...]
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpihpet0 at acpi0: 14318179 Hz
acpimcfg0 at acpi0
acpimcfg0: addr 0xe0000000, bus 0-255
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM)2 Duo CPU T6570 @ 2.10GHz, 2095.05 MHz, 06-17-0a
cpu0:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,XSAVE,NXE,LONG,LAHF,PERF,SENSOR,MELTDOWN
cpu0: 2MB 64b/line 8-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 7 var ranges, 88 fixed ranges
cpu0: apic clock running at 241MHz
cpu0: mwait min=64, max=64, C-substates=0.2.2.2.2.1.3, IBE
cpu1 at mainbus0: apid 1 (application processor)
cpu1: Intel(R) Core(TM)2 Duo CPU T6570 @ 2.10GHz, 2534.66 MHz, 06-17-0a
cpu1:
FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,DTES64,MWAIT,DS-CPL,VMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,SSE4.1,XSAVE,NXE,LONG,LAHF,PERF,SENSOR,MELTDOWN
cpu1: 2MB 64b/line 8-way L2 cache
cpu1: smt 0, core 1, package 0
ioapic0 at mainbus0: apid 2 pa 0xfec00000, version 20, 24 pins
acpiprt0 at acpi0: bus 0 (PCI0)
acpiprt1 at acpi0: bus -1 (P0P2)
acpiprt2 at acpi0: bus 9 (P0P1)
acpiprt3 at acpi0: bus 2 (RP01)
acpiprt4 at acpi0: bus 3 (RP02)
acpiprt5 at acpi0: bus 4 (RP03)
acpiprt6 at acpi0: bus 5 (RP04)
acpiprt7 at acpi0: bus 6 (RP05)
acpiprt8 at acpi0: bus 8 (RP06)
acpiec0 at acpi0
acpicpu0 at acpi0: !C2(100@57 mwait.3@0x30), C1(1000@1 mwait.1), PSS
acpicpu1 at acpi0: !C2(100@57 mwait.3@0x30), C1(1000@1 mwait.1), PSS
acpitz0 at acpi0: critical temperature is 105 degC
acpipci0 at acpi0 PCI0: _OSC failed
acpicmos0 at acpi0
acpithinkpad0 at acpi0
acpiac0 at acpi0: AC unit online
acpibat0 at acpi0: BAT1 model "42T4763" serial  5901 type LION oem "SANYO"
acpibtn0 at acpi0: LID_
acpibtn1 at acpi0: PWRB
acpibtn2 at acpi0: SLPB
"PNP0C14" at acpi0 not configured
acpivideo0 at acpi0: VGA_
acpivideo1 at acpi0: GFX0
acpivout0 at acpivideo1: DD03
cpu0: Enhanced SpeedStep 2095 MHz: speeds: 2101, 2100, 1600, 1200 MHz
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel GM45 Host" rev 0x07
inteldrm0 at pci0 dev 2 function 0 "Intel GM45 Video" rev 0x07
drm0 at inteldrm0
intagp0 at inteldrm0
agp0 at intagp0: aperture at 0xd0000000, size 0x10000000
inteldrm0: msi
inteldrm0: 1366x768, 32bpp
wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation)
wsdisplay0: screen 1-5 added (std, vt100 emulation)
"Intel GM45 Video" rev 0x07 at pci0 dev 2 function 1 not configured
uhci0 at pci0 dev 26 function 0 "Intel 82801I USB" rev 0x03: apic 2 int 16
uhci1 at pci0 dev 26 function 1 "Intel 82801I USB" rev 0x03: apic 2 int 21
uhci2 at pci0 dev 26 function 2 "Intel 82801I USB" rev 0x03: apic 2 int 19
ehci0 at pci0 dev 26 function 7 "Intel 82801I USB" rev 0x03: apic 2 int 19
usb0 at ehci0: USB revision 2.0
uhub0 at usb0 configuration 1 interface 0 "Intel EHCI root hub" rev
2.00/1.00 addr 1
azalia0 at pci0 dev 27 function 0 "Intel 82801I HD Audio" rev 0x03: msi
azalia0: codecs: Realtek ALC269, Intel/0x2802, using Realtek ALC269
audio0 at azalia0
ppb0 at pci0 dev 28 function 0 "Intel 82801I PCIE" rev 0x03: msi
pci1 at ppb0 bus 2
"JMicron SD/MMC" rev 0x00 at pci1 dev 0 function 0 not configured
sdhc0 at pci1 dev 0 function 2 "JMicron SD Host Controller" rev 0x00:
apic 2 int 16
sdhc0: SDHC 2.0, 50 MHz base clock
sdmmc0 at sdhc0: 4-bit
"JMicron Memory Stick" rev 0x00 at pci1 dev 0 function 3 not configured
"JMicron xD" rev 0x00 at pci1 dev 0 function 4 not configured
ppb1 at pci0 dev 28 function 1 "Intel 82801I PCIE" rev 0x03: msi
pci2 at ppb1 bus 3
ppb2 at pci0 dev 28 function 2 "Intel 82801I PCIE" rev 0x03: msi
pci3 at ppb2 bus 4
ppb3 at pci0 dev 28 function 3 "Intel 82801I PCIE" rev 0x03: msi
pci4 at ppb3 bus 5
iwn0 at pci4 dev 0 function 0 "Intel WiFi Link 1000" rev 0x00: msi, MIMO
1T2R, BGS, address 00:1e:64:6b:b7:80
ppb4 at pci0 dev 28 function 4 "Intel 82801I PCIE" rev 0x03: msi
pci5 at ppb4 bus 6
ppb5 at pci0 dev 28 function 5 "Intel 82801I PCIE" rev 0x03: msi
pci6 at ppb5 bus 8
re0 at pci6 dev 0 function 0 "Realtek 8168" rev 0x03: RTL8168D/8111D
(0x2800), msi, address 00:26:9e:b2:04:e3
rgephy0 at re0 phy 7: RTL8169S/8110S/8211 PHY, rev. 2
uhci3 at pci0 dev 29 function 0 "Intel 82801I USB" rev 0x03: apic 2 int 23
uhci4 at pci0 dev 29 function 1 "Intel 82801I USB" rev 0x03: apic 2 int 19
uhci5 at pci0 dev 29 function 2 "Intel 82801I USB" rev 0x03: apic 2 int 18
ehci1 at pci0 dev 29 function 7 "Intel 82801I USB" rev 0x03: apic 2 int 23
usb1 at ehci1: USB revision 2.0
uhub1 at usb1 configuration 1 interface 0 "Intel EHCI root hub" rev
2.00/1.00 addr 1
ppb6 at pci0 dev 30 function 0 "Intel 82801BAM Hub-to-PCI" rev 0x93
pci7 at ppb6 bus 9
pcib0 at pci0 dev 31 function 0 "Intel 82801IBM LPC" rev 0x03
ahci0 at pci0 dev 31 function 2 "Intel 82801I AHCI" rev 0x03: msi, AHCI 1.2
ahci0: port 0: 3.0Gb/s
ahci0: port 1: 1.5Gb/s
scsibus1 at ahci0: 32 targets
sd0 at scsibus1 targ 0 lun 0: <ATA, HITACHI HTS54502, PB2Z> SCSI3
0/direct fixed naa.5000cca5e7c97b08
sd0: 238475MB, 512 bytes/sector, 488397168 sectors
cd0 at scsibus1 targ 1 lun 0: <HL-DT-ST, DVDRAM GSA-T50N, RS06> ATAPI
5/cdrom removable
ichiic0 at pci0 dev 31 function 3 "Intel 82801I SMBus" rev 0x03: apic 2
int 19
iic0 at ichiic0
spdmem0 at iic0 addr 0x50: 2GB DDR3 SDRAM PC3-8500 SO-DIMM
usb2 at uhci0: USB revision 1.0
uhub2 at usb2 configuration 1 interface 0 "Intel UHCI root hub" rev
1.00/1.00 addr 1
usb3 at uhci1: USB revision 1.0
uhub3 at usb3 configuration 1 interface 0 "Intel UHCI root hub" rev
1.00/1.00 addr 1
usb4 at uhci2: USB revision 1.0
uhub4 at usb4 configuration 1 interface 0 "Intel UHCI root hub" rev
1.00/1.00 addr 1
usb5 at uhci3: USB revision 1.0
uhub5 at usb5 configuration 1 interface 0 "Intel UHCI root hub" rev
1.00/1.00 addr 1
usb6 at uhci4: USB revision 1.0
uhub6 at usb6 configuration 1 interface 0 "Intel UHCI root hub" rev
1.00/1.00 addr 1
usb7 at uhci5: USB revision 1.0
uhub7 at usb7 configuration 1 interface 0 "Intel UHCI root hub" rev
1.00/1.00 addr 1
isa0 at pcib0
isadma0 at isa0
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
wsmouse1 at pms0 mux 0
pms0: Synaptics touchpad, firmware 7.2, 0x1c0b1 0xb40000
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
uaudio0 at uhub0 port 1 configuration 1 interface 1 "Schiit Schiit USB
Audio Device" rev 2.00/1.02 addr 2
uaudio0: class v2, high-speed, async, channels: 2 play, 0 rec, 1 ctls
audio1 at uaudio0
uhidev0 at uhub0 port 1 configuration 1 interface 2 "Schiit Schiit USB
Audio Device" rev 2.00/1.02 addr 2
uhidev0: iclass 3/0
uhid0 at uhidev0: input=16, output=16, feature=0
uvideo0 at uhub1 port 6 configuration 1 interface 0 "Chicony Electronics
Co., Ltd. Integrated Camera" rev 2.00/82.54 addr 2
video0 at uvideo0
ugen0 at uhub7 port 1 "Broadcom Corp Broadcom Bluetooth Device" rev
2.00/3.60 addr 2
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
root on sd0a (1e7ab7334da7c164.a) swap on sd0b dump on sd0b
uaudio0: request failed: STALLED
uaudio0: request failed: STALLED


mixerctl -v -f /dev/mixer1
==========================
mixerctl: AUDIO_MIXER_READ: Input/output error

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Martin Pieuchot
In reply to this post by Alexandre Ratchov-2
On 31/12/18(Mon) 16:58, Alexandre Ratchov wrote:
> Hi,
>
> Here's a new driver for both USB audio class (UAC) v1.0 and v2.0
> devices, it would replace the current one. It focuses on reliability
> and proper synchronization, including in low-latency configurations.

Some comments about the code.  I believe this should go in now so we
can tackle the remaining issue in tree, so ok mpi@

- Please put the UE_* macro in dev/usb/usb.h (not sys/usb.h) ;o)

- You could get rid of UAUDIO_NRATES and use nitems() instead

- What does UAUDIO_USE_FRAC mean?

- Some values read from descriptors are passed to malloc(9) w/o being
  checked.  Take the example of `count' in uaudio_req_ranges().  What's
  the maximum size for `count' and `req_size'?
  What's the maximum value for `nframes_max'?

- You don't need to call usbd_abort_pipe(9) before calling
  usbd_close_pipe(9), it does it for you.

- What about using %s __func__ in DPRINTF() instead of function names
  to help search through your code? ;o)

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Alexandre Ratchov-2
On Wed, Jan 23, 2019 at 01:23:05PM -0200, Martin Pieuchot wrote:
> On 31/12/18(Mon) 16:58, Alexandre Ratchov wrote:
> > Hi,
> >
> > Here's a new driver for both USB audio class (UAC) v1.0 and v2.0
> > devices, it would replace the current one. It focuses on reliability
> > and proper synchronization, including in low-latency configurations.
>
> Some comments about the code.  I believe this should go in now so we
> can tackle the remaining issue in tree, so ok mpi@

I got a lot of very helpful test reports. I've almost finished fixing
the problems people reported, so I'll repost a new diff soon and
commit that one instead if you're still OK.

>
> - Please put the UE_* macro in dev/usb/usb.h (not sys/usb.h) ;o)
>

;-)

> - You could get rid of UAUDIO_NRATES and use nitems() instead
>
> - What does UAUDIO_USE_FRAC mean?
>

If the sample rate is not multiple of the USB frame rate, the number
of samples per frame can't be constant: For instance, if frame rate is
1000 frames-per-second and sample rate is 44100 samples-per-second,
the number of samples per frame would be 44.1. Devices handle this
with a variable number for frames: in this example, most of the frames
carry 44 samples, but every 10 samples we have one 45-sample frame, to
reach the 44.1 samples per frame average. In this case the driver has
to manipulate "fractional" frames.

The diff I posted doesn't handle such rates. It's a pure arithmetic
problem, I didn't want to distract people with it.

> - Some values read from descriptors are passed to malloc(9) w/o being
>   checked.  Take the example of `count' in uaudio_req_ranges().  What's
>   the maximum size for `count' and `req_size'?
>   What's the maximum value for `nframes_max'?
>

- count is 16-bit, so worst case req_size is roughly 65535 multiplied
  by the word size (at most 4-byte) plus two bytes of header. That's
  less than 256kB. Most devices have a signle range, but who knows...

  I've added a comment to say so.

- I my current version of the diff nframes_max limited to 240 on usb2
  and 30 on usb1, which is 30ms of audio. This is because ehci and
  uhci don't seem to handle large isoc transfers. As 30ms enough for
  audio, I didn't try to understand the reason of the limitation yet.

> - You don't need to call usbd_abort_pipe(9) before calling
>   usbd_close_pipe(9), it does it for you.
>
> - What about using %s __func__ in DPRINTF() instead of function names
>   to help search through your code? ;o)

thans, fixed these as well.

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Sascha Paunovic
In reply to this post by Alexandre Ratchov-2
Hi Alexandre,

sorry if I'm a bit late to the party, but here's my report.

Seems to be working well for me! :)

dmesg
=====
OpenBSD 6.4-current (GENERIC.MP) #624: Sun Jan 20 14:45:45 MST 2019
    [hidden email]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 4132433920 (3940MB)
avail mem = 3997593600 (3812MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.5 @ 0xf0450 (80 entries)
bios0: vendor Dell Inc. version "A22" date 06/11/2012
bios0: Dell Inc. OptiPlex 755
acpi0 at bios0: rev 2
acpi0: TCPA checksum error
acpi0: sleep states S0 S1 S3 S4 S5
acpi0: tables DSDT FACP SSDT APIC BOOT ASF! MCFG HPET TCPA ____ SLIC SSDT SSDT SSDT
acpi0: wakeup devices VBTN(S4) PCI0(S5) PCI4(S5) PCI2(S5) PCI3(S5) PCI1(S5) PCI5(S5) PCI6(S5) USB0(S3) USB1(S3) USB2(S3) USB3(S3) USB4(S3) USB5(S3)
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz, 2327.80 MHz, 06-0f-0b
cpu0: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,NXE,LONG,LAHF,PERF,SENSOR,MELTDOWN
cpu0: 4MB 64b/line 16-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 7 var ranges, 88 fixed ranges
cpu0: apic clock running at 332MHz
cpu0: mwait min=64, max=64, C-substates=0.2.2, IBE
cpu1 at mainbus0: apid 1 (application processor)
cpu1: Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz, 2327.49 MHz, 06-0f-0b
cpu1: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,NXE,LONG,LAHF,PERF,SENSOR,MELTDOWN
cpu1: 4MB 64b/line 16-way L2 cache
cpu1: smt 0, core 1, package 0
ioapic0 at mainbus0: apid 8 pa 0xfec00000, version 20, 24 pins, remapped
acpimcfg0 at acpi0
acpimcfg0: addr 0xe0000000, bus 0-255
acpimcfg0: addr 0xddffffeedeef0000, bus 255-239
acpihpet0 at acpi0: 14318179 Hz
acpiprt0 at acpi0: bus 3 (PCI4)
acpiprt1 at acpi0: bus 2 (PCI2)
acpiprt2 at acpi0: bus -1 (PCI3)
acpiprt3 at acpi0: bus 1 (PCI1)
acpiprt4 at acpi0: bus -1 (PCI5)
acpiprt5 at acpi0: bus -1 (PCI6)
acpiprt6 at acpi0: bus 0 (PCI0)
acpicpu0 at acpi0: C1(@1 halt!), PSS
acpicpu1 at acpi0: C1(@1 halt!), PSS
acpibtn0 at acpi0: VBTN
acpipci0 at acpi0 PCI0: _OSC failed
acpicmos0 at acpi0
cpu0: Enhanced SpeedStep 2327 MHz: speeds: 2333, 2000 MHz
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel 82Q35 Host" rev 0x02
ppb0 at pci0 dev 1 function 0 "Intel 82Q35 PCIE" rev 0x02: msi
pci1 at ppb0 bus 1
inteldrm0 at pci0 dev 2 function 0 "Intel 82Q35 Video" rev 0x02
drm0 at inteldrm0
intagp0 at inteldrm0
agp0 at intagp0: aperture at 0xd0000000, size 0x10000000
inteldrm0: msi
inteldrm0: 1280x1024, 32bpp
wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation)
wsdisplay0: screen 1-5 added (std, vt100 emulation)
"Intel 82Q35 Video" rev 0x02 at pci0 dev 2 function 1 not configured
"Intel 82Q35 HECI" rev 0x02 at pci0 dev 3 function 0 not configured
pciide0 at pci0 dev 3 function 2 "Intel 82Q35 PT IDER" rev 0x02: DMA (unsupported), channel 0 wired to native-PCI, channel 1 wired to native-PCI
pciide0: using apic 8 int 18 for native-PCI interrupt
pciide0: channel 0 ignored (not responding; disabled or no drives?)
pciide0: channel 1 ignored (not responding; disabled or no drives?)
puc0 at pci0 dev 3 function 3 "Intel 82Q35 KT" rev 0x02: ports: 16 com
com4 at puc0 port 0 apic 8 int 17: ns16550a, 16 byte fifo
com4: probed fifo depth: 15 bytes
em0 at pci0 dev 25 function 0 "Intel ICH9 IGP AMT" rev 0x02: msi, address 00:1e:4f:c1:9b:ea
uhci0 at pci0 dev 26 function 0 "Intel 82801I USB" rev 0x02: apic 8 int 16
uhci1 at pci0 dev 26 function 1 "Intel 82801I USB" rev 0x02: apic 8 int 17
ehci0 at pci0 dev 26 function 7 "Intel 82801I USB" rev 0x02: apic 8 int 22
usb0 at ehci0: USB revision 2.0
uhub0 at usb0 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
azalia0 at pci0 dev 27 function 0 "Intel 82801I HD Audio" rev 0x02: msi
azalia0: codecs: Analog Devices AD1984
audio0 at azalia0
ppb1 at pci0 dev 28 function 0 "Intel 82801I PCIE" rev 0x02: msi
pci2 at ppb1 bus 2
athn0 at pci2 dev 0 function 0 "Atheros AR9287" rev 0x01: apic 8 int 16
athn0: AR9287 rev 2 (2T2R), ROM rev 4, address e8:de:27:39:dd:fc
uhci2 at pci0 dev 29 function 0 "Intel 82801I USB" rev 0x02: apic 8 int 23
uhci3 at pci0 dev 29 function 1 "Intel 82801I USB" rev 0x02: apic 8 int 17
uhci4 at pci0 dev 29 function 2 "Intel 82801I USB" rev 0x02: apic 8 int 18
ehci1 at pci0 dev 29 function 7 "Intel 82801I USB" rev 0x02: apic 8 int 23
usb1 at ehci1: USB revision 2.0
uhub1 at usb1 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
ppb2 at pci0 dev 30 function 0 "Intel 82801BA Hub-to-PCI" rev 0x92
pci3 at ppb2 bus 3
pcib0 at pci0 dev 31 function 0 "Intel 82801IO LPC" rev 0x02
ahci0 at pci0 dev 31 function 2 "Intel 82801I AHCI" rev 0x02: msi, AHCI 1.2
ahci0: port 0: 3.0Gb/s
ahci0: port 1: 1.5Gb/s
ahci0: PHY offline on port 2
ahci0: PHY offline on port 3
ahci0: PHY offline on port 5
scsibus1 at ahci0: 32 targets
sd0 at scsibus1 targ 0 lun 0: <ATA, Samsung SSD 860, RVT0> SCSI3 0/direct fixed naa.5002538e4033b898
sd0: 238475MB, 512 bytes/sector, 488397168 sectors, thin
cd0 at scsibus1 targ 1 lun 0: <TSSTcorp, DVD-ROM TS-H353B, D200> ATAPI 5/cdrom removable
ichiic0 at pci0 dev 31 function 3 "Intel 82801I SMBus" rev 0x02: apic 8 int 18
iic0 at ichiic0
spdmem0 at iic0 addr 0x50: 1GB DDR2 SDRAM non-parity PC2-6400CL6
spdmem1 at iic0 addr 0x51: 1GB DDR2 SDRAM non-parity PC2-5300CL5
spdmem2 at iic0 addr 0x52: 1GB DDR2 SDRAM non-parity PC2-6400CL6
spdmem3 at iic0 addr 0x53: 1GB DDR2 SDRAM non-parity PC2-5300CL5
usb2 at uhci0: USB revision 1.0
uhub2 at usb2 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
usb3 at uhci1: USB revision 1.0
uhub3 at usb3 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
usb4 at uhci2: USB revision 1.0
uhub4 at usb4 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
usb5 at uhci3: USB revision 1.0
uhub5 at usb5 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
usb6 at uhci4: USB revision 1.0
uhub6 at usb6 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
isa0 at pcib0
isadma0 at isa0
fdc0 at isa0 port 0x3f0/6 irq 6 drq 2
com0 at isa0 port 0x3f8/8 irq 4: ns16550a, 16 byte fifo
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
lpt0 at isa0 port 0x378/4 irq 7
uhub7 at uhub1 port 3 configuration 1 interface 0 "Standard Microsystems product 0x2514" rev 2.00/0.00 addr 2
fd0 at fdc0 drive 0: 1.44MB 80 cyl, 2 head, 18 sec
uhidev0 at uhub7 port 1 configuration 1 interface 0 "PixArt Dell MS116 USB Optical Mouse" rev 2.00/1.00 addr 3
uhidev0: iclass 3/1
ums0 at uhidev0: 3 buttons, Z dir
wsmouse0 at ums0 mux 0
uhub8 at uhub7 port 2 configuration 1 interface 0 "Dell USB 2.0 Hub [MTT]" rev 2.00/32.98 addr 4
uhidev1 at uhub8 port 4 configuration 1 interface 0 "Dell Dell Wired Multimedia Keyboard" rev 1.10/0.06 addr 5
uhidev1: iclass 3/1
ukbd0 at uhidev1: 8 variable keys, 6 key codes
wskbd1 at ukbd0 mux 1
wskbd1: connecting to wsdisplay0
uhidev2 at uhub8 port 4 configuration 1 interface 1 "Dell Dell Wired Multimedia Keyboard" rev 1.10/0.06 addr 5
uhidev2: iclass 3/1, 5 report ids
ums1 at uhidev2 reportid 1: 3 buttons, Z dir
wsmouse1 at ums1 mux 0
uhid0 at uhidev2 reportid 2: input=1, output=0, feature=0
uhid1 at uhidev2 reportid 3: input=3, output=0, feature=0
uhid2 at uhidev2 reportid 5: input=0, output=0, feature=5
uaudio0 at uhub3 port 2 configuration 1 interface 0 "Sennheiser Sennheiser USB Headset" rev 2.00/1.17 addr 2
uaudio0: audio rev 1.00, 7 mixer controls
audio1 at uaudio0
uhidev3 at uhub3 port 2 configuration 1 interface 3 "Sennheiser Sennheiser USB Headset" rev 2.00/1.17 addr 2
uhidev3: iclass 3/0, 9 report ids
uhid3 at uhidev3 reportid 1: input=1, output=0, feature=0
uhid4 at uhidev3 reportid 2: input=2, output=0, feature=0
uhid5 at uhidev3 reportid 4: input=0, output=36, feature=0
uhid6 at uhidev3 reportid 5: input=32, output=0, feature=0
uhid7 at uhidev3 reportid 6: input=0, output=36, feature=0
uhid8 at uhidev3 reportid 7: input=32, output=0, feature=0
uhid9 at uhidev3 reportid 8: input=1, output=0, feature=0
uhid10 at uhidev3 reportid 9: input=0, output=1, feature=0
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
root on sd0a (534136b06a6621e2.a) swap on sd0b dump on sd0b
syncing disks... done
rebooting...
OpenBSD 6.4-current (GENERIC.MP) #1: Fri Jan 25 10:21:52 CET 2019
    [hidden email]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 4132433920 (3940MB)
avail mem = 3997589504 (3812MB)
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.5 @ 0xf0450 (80 entries)
bios0: vendor Dell Inc. version "A22" date 06/11/2012
bios0: Dell Inc. OptiPlex 755
acpi0 at bios0: rev 2
acpi0: TCPA checksum error
acpi0: sleep states S0 S1 S3 S4 S5
acpi0: tables DSDT FACP SSDT APIC BOOT ASF! MCFG HPET TCPA ____ SLIC SSDT SSDT SSDT
acpi0: wakeup devices VBTN(S4) PCI0(S5) PCI4(S5) PCI2(S5) PCI3(S5) PCI1(S5) PCI5(S5) PCI6(S5) USB0(S3) USB1(S3) USB2(S3) USB3(S3) USB4(S3) USB5(S3)
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz, 2327.79 MHz, 06-0f-0b
cpu0: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,NXE,LONG,LAHF,PERF,SENSOR,MELTDOWN
cpu0: 4MB 64b/line 16-way L2 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 7 var ranges, 88 fixed ranges
cpu0: apic clock running at 332MHz
cpu0: mwait min=64, max=64, C-substates=0.2.2, IBE
cpu1 at mainbus0: apid 1 (application processor)
cpu1: Intel(R) Core(TM)2 Duo CPU E6550 @ 2.33GHz, 2327.49 MHz, 06-0f-0b
cpu1: FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,DS,ACPI,MMX,FXSR,SSE,SSE2,SS,HTT,TM,PBE,SSE3,DTES64,MWAIT,DS-CPL,VMX,SMX,EST,TM2,SSSE3,CX16,xTPR,PDCM,NXE,LONG,LAHF,PERF,SENSOR,MELTDOWN
cpu1: 4MB 64b/line 16-way L2 cache
cpu1: smt 0, core 1, package 0
ioapic0 at mainbus0: apid 8 pa 0xfec00000, version 20, 24 pins, remapped
acpimcfg0 at acpi0
acpimcfg0: addr 0xe0000000, bus 0-255
acpimcfg0: addr 0xddffffeedeef0000, bus 255-239
acpihpet0 at acpi0: 14318179 Hz
acpiprt0 at acpi0: bus 3 (PCI4)
acpiprt1 at acpi0: bus 2 (PCI2)
acpiprt2 at acpi0: bus -1 (PCI3)
acpiprt3 at acpi0: bus 1 (PCI1)
acpiprt4 at acpi0: bus -1 (PCI5)
acpiprt5 at acpi0: bus -1 (PCI6)
acpiprt6 at acpi0: bus 0 (PCI0)
acpicpu0 at acpi0: C1(@1 halt!), PSS
acpicpu1 at acpi0: C1(@1 halt!), PSS
acpibtn0 at acpi0: VBTN
acpipci0 at acpi0 PCI0: _OSC failed
acpicmos0 at acpi0
cpu0: Enhanced SpeedStep 2327 MHz: speeds: 2333, 2000 MHz
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel 82Q35 Host" rev 0x02
ppb0 at pci0 dev 1 function 0 "Intel 82Q35 PCIE" rev 0x02: msi
pci1 at ppb0 bus 1
inteldrm0 at pci0 dev 2 function 0 "Intel 82Q35 Video" rev 0x02
drm0 at inteldrm0
intagp0 at inteldrm0
agp0 at intagp0: aperture at 0xd0000000, size 0x10000000
inteldrm0: msi
inteldrm0: 1280x1024, 32bpp
wsdisplay0 at inteldrm0 mux 1: console (std, vt100 emulation)
wsdisplay0: screen 1-5 added (std, vt100 emulation)
"Intel 82Q35 Video" rev 0x02 at pci0 dev 2 function 1 not configured
"Intel 82Q35 HECI" rev 0x02 at pci0 dev 3 function 0 not configured
pciide0 at pci0 dev 3 function 2 "Intel 82Q35 PT IDER" rev 0x02: DMA (unsupported), channel 0 wired to native-PCI, channel 1 wired to native-PCI
pciide0: using apic 8 int 18 for native-PCI interrupt
pciide0: channel 0 ignored (not responding; disabled or no drives?)
pciide0: channel 1 ignored (not responding; disabled or no drives?)
puc0 at pci0 dev 3 function 3 "Intel 82Q35 KT" rev 0x02: ports: 16 com
com4 at puc0 port 0 apic 8 int 17: ns16550a, 16 byte fifo
com4: probed fifo depth: 0 bytes
em0 at pci0 dev 25 function 0 "Intel ICH9 IGP AMT" rev 0x02: msi, address 00:1e:4f:c1:9b:ea
uhci0 at pci0 dev 26 function 0 "Intel 82801I USB" rev 0x02: apic 8 int 16
uhci1 at pci0 dev 26 function 1 "Intel 82801I USB" rev 0x02: apic 8 int 17
ehci0 at pci0 dev 26 function 7 "Intel 82801I USB" rev 0x02: apic 8 int 22
usb0 at ehci0: USB revision 2.0
uhub0 at usb0 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
azalia0 at pci0 dev 27 function 0 "Intel 82801I HD Audio" rev 0x02: msi
azalia0: codecs: Analog Devices AD1984
audio0 at azalia0
ppb1 at pci0 dev 28 function 0 "Intel 82801I PCIE" rev 0x02: msi
pci2 at ppb1 bus 2
athn0 at pci2 dev 0 function 0 "Atheros AR9287" rev 0x01: apic 8 int 16
athn0: AR9287 rev 2 (2T2R), ROM rev 4, address e8:de:27:39:dd:fc
uhci2 at pci0 dev 29 function 0 "Intel 82801I USB" rev 0x02: apic 8 int 23
uhci3 at pci0 dev 29 function 1 "Intel 82801I USB" rev 0x02: apic 8 int 17
uhci4 at pci0 dev 29 function 2 "Intel 82801I USB" rev 0x02: apic 8 int 18
ehci1 at pci0 dev 29 function 7 "Intel 82801I USB" rev 0x02: apic 8 int 23
usb1 at ehci1: USB revision 2.0
uhub1 at usb1 configuration 1 interface 0 "Intel EHCI root hub" rev 2.00/1.00 addr 1
ppb2 at pci0 dev 30 function 0 "Intel 82801BA Hub-to-PCI" rev 0x92
pci3 at ppb2 bus 3
pcib0 at pci0 dev 31 function 0 "Intel 82801IO LPC" rev 0x02
ahci0 at pci0 dev 31 function 2 "Intel 82801I AHCI" rev 0x02: msi, AHCI 1.2
ahci0: port 0: 3.0Gb/s
ahci0: port 1: 1.5Gb/s
ahci0: PHY offline on port 2
ahci0: PHY offline on port 3
ahci0: PHY offline on port 5
scsibus1 at ahci0: 32 targets
sd0 at scsibus1 targ 0 lun 0: <ATA, Samsung SSD 860, RVT0> SCSI3 0/direct fixed naa.5002538e4033b898
sd0: 238475MB, 512 bytes/sector, 488397168 sectors, thin
cd0 at scsibus1 targ 1 lun 0: <TSSTcorp, DVD-ROM TS-H353B, D200> ATAPI 5/cdrom removable
ichiic0 at pci0 dev 31 function 3 "Intel 82801I SMBus" rev 0x02: apic 8 int 18
iic0 at ichiic0
spdmem0 at iic0 addr 0x50: 1GB DDR2 SDRAM non-parity PC2-6400CL6
spdmem1 at iic0 addr 0x51: 1GB DDR2 SDRAM non-parity PC2-5300CL5
spdmem2 at iic0 addr 0x52: 1GB DDR2 SDRAM non-parity PC2-6400CL6
spdmem3 at iic0 addr 0x53: 1GB DDR2 SDRAM non-parity PC2-5300CL5
usb2 at uhci0: USB revision 1.0
uhub2 at usb2 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
usb3 at uhci1: USB revision 1.0
uhub3 at usb3 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
usb4 at uhci2: USB revision 1.0
uhub4 at usb4 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
usb5 at uhci3: USB revision 1.0
uhub5 at usb5 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
usb6 at uhci4: USB revision 1.0
uhub6 at usb6 configuration 1 interface 0 "Intel UHCI root hub" rev 1.00/1.00 addr 1
isa0 at pcib0
isadma0 at isa0
fdc0 at isa0 port 0x3f0/6 irq 6 drq 2
com0 at isa0 port 0x3f8/8 irq 4: ns16550a, 16 byte fifo
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
lpt0 at isa0 port 0x378/4 irq 7
uhub7 at uhub1 port 3 configuration 1 interface 0 "Standard Microsystems product 0x2514" rev 2.00/0.00 addr 2
fd0 at fdc0 drive 0: 1.44MB 80 cyl, 2 head, 18 sec
uhidev0 at uhub7 port 1 configuration 1 interface 0 "PixArt Dell MS116 USB Optical Mouse" rev 2.00/1.00 addr 3
uhidev0: iclass 3/1
ums0 at uhidev0: 3 buttons, Z dir
wsmouse0 at ums0 mux 0
uhub8 at uhub7 port 2 configuration 1 interface 0 "Dell USB 2.0 Hub [MTT]" rev 2.00/32.98 addr 4
uhidev1 at uhub8 port 4 configuration 1 interface 0 "Dell Dell Wired Multimedia Keyboard" rev 1.10/0.06 addr 5
uhidev1: iclass 3/1
ukbd0 at uhidev1: 8 variable keys, 6 key codes
wskbd1 at ukbd0 mux 1
wskbd1: connecting to wsdisplay0
uhidev2 at uhub8 port 4 configuration 1 interface 1 "Dell Dell Wired Multimedia Keyboard" rev 1.10/0.06 addr 5
uhidev2: iclass 3/1, 5 report ids
ums1 at uhidev2 reportid 1: 3 buttons, Z dir
wsmouse1 at ums1 mux 0
uhid0 at uhidev2 reportid 2: input=1, output=0, feature=0
uhid1 at uhidev2 reportid 3: input=3, output=0, feature=0
uhid2 at uhidev2 reportid 5: input=0, output=0, feature=5
uaudio0 at uhub3 port 2 configuration 1 interface 1 "Sennheiser Sennheiser USB Headset" rev 2.00/1.17 addr 2
uaudio0: class v1, full-speed, sync, channels: 2 play, 2 rec, 7 ctls
audio1 at uaudio0
uhidev3 at uhub3 port 2 configuration 1 interface 3 "Sennheiser Sennheiser USB Headset" rev 2.00/1.17 addr 2
uhidev3: iclass 3/0, 9 report ids
uhid3 at uhidev3 reportid 1: input=1, output=0, feature=0
uhid4 at uhidev3 reportid 2: input=2, output=0, feature=0
uhid5 at uhidev3 reportid 4: input=0, output=36, feature=0
uhid6 at uhidev3 reportid 5: input=32, output=0, feature=0
uhid7 at uhidev3 reportid 6: input=0, output=36, feature=0
uhid8 at uhidev3 reportid 7: input=32, output=0, feature=0
uhid9 at uhidev3 reportid 8: input=1, output=0, feature=0
uhid10 at uhidev3 reportid 9: input=0, output=1, feature=0
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
root on sd0a (534136b06a6621e2.a) swap on sd0b dump on sd0b
uaudio0: uaudio_pdata_xfer: err = 0
uaudio0: uaudio_rdata_xfer: err = 0
uaudio0: too many frames for rec xfer: done = 3648, blksz = 3840

mixerctl -v -f /dev/mixer1
==========================
inputs.mic1=0
inputs.mic1_mute=off  [ off on ]
outputs.spkr=186,186
outputs.spkr_mute=off  [ off on ]
record.record=251
record.record_mute=off  [ off on ]
record.enable=sysctl  [ off on sysctl ]


On Mon, 31 Dec 2018 16:58:52 +0100
Alexandre Ratchov <[hidden email]> wrote:

> Hi,
>
> Here's a new driver for both USB audio class (UAC) v1.0 and v2.0
> devices, it would replace the current one. It focuses on reliability
> and proper synchronization, including in low-latency configurations.
>
> Our current USB sub-system has limitations that currently allow only
> the following combinations:
>  - USB v2 devices on ehci(4) only
>  - USB v1 devices on uhci(4), ohci(4) or ehci(4) root hub only
>
> If you have an audio device that is class compliant (aka vendor claims
> it's "driverless" on MacOS) *and* one of the above host/hub/device
> combinations then I'd be very interested in test reports. Especially
> I'd like to know about possible regressions.
>
> To test, apply this diff, rebuild the kernel and set:
>
> sndiod_flags=-f rsnd/1
>
> in /etc/rc.conf.local (assuming your uaudio device shows as uaudio1 in
> dmesg). Then do your regular audio work, let me know how it works and
> send me output of:
>
> dmesg
> mixerctl -v -f /dev/mixer1
>
> If something is broken, please check if this is a regression.
>
> Known bugs and limitations:
>
>  - Our USB stack requires at least 3 outstanding isochronous transfers
>    to work. So the audio ring buffer size must be at least 3
>    blocks. If you're using sndiod's -bz options, ensuire the -b
>    argument is at least 3 times the -z argument.
>
>  - Fractional frames are not used on purpose (except on devices using
>    a feedback pipe for synchronization). This means that only sample
>    rates multiple of 8kHz are reachable. Most azalia(4)'s do so as
>    well.
>
>  - UAC v2.0 devices with multiple clock sources, clock multipliers and
>    rate converters would use the vendor defaults (by lack of hardware
>    to test).
>
>  - Certain mixerctl(1) control names would be too long and are
>    truncated. This is hard to fix without changing the mixer(4)
>    interface.
>
>  - Controls of selectors or mixers are not exposed in the mixer(4)
>    interface, so the device will use vendor defaults there. Such
>    controls are very rare, it seems that MacOS & Windows don't expose
>    them either.
>
> Thanks in advance.
>
> -- Alexandre
>
> Index: uaudio.c
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/uaudio.c,v
> retrieving revision 1.133
> diff -u -p -u -p -r1.133 uaudio.c
> --- uaudio.c 31 Aug 2018 07:18:18 -0000 1.133
> +++ uaudio.c 31 Dec 2018 15:17:23 -0000
> @@ -1,3394 +1,4037 @@
> -/* $OpenBSD: uaudio.c,v 1.133 2018/08/31 07:18:18 miko Exp $ */
> -/* $NetBSD: uaudio.c,v 1.90 2004/10/29 17:12:53 kent Exp $ */
> -
> +/* $OpenBSD$ */
>  /*
> - * Copyright (c) 1999 The NetBSD Foundation, Inc.
> - * All rights reserved.
> + * Copyright (c) 2018 Alexandre Ratchov <[hidden email]>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
>   *
> - * This code is derived from software contributed to The NetBSD Foundation
> - * by Lennart Augustsson ([hidden email]) 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.
> - *
> - * 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.
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>   */
> -
>  /*
> - * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
> - *                  http://www.usb.org/developers/devclass_docs/frmts10.pdf
> - *                  http://www.usb.org/developers/devclass_docs/termt10.pdf
> + * The USB Audio Class (UAC) defines what is an audio device and how
> + * to use it. There are two versions of the UAC: v1.0 and v2.0. They
> + * are not compatible with each other but they are close enough to
> + * attempt to have the same driver for both.
> + *
>   */
> -
>  #include <sys/param.h>
> -#include <sys/systm.h>
> -#include <sys/kernel.h>
> -#include <sys/malloc.h>
> +#include <sys/types.h>
>  #include <sys/device.h>
> -#include <sys/ioctl.h>
> -#include <sys/tty.h>
> +#include <sys/errno.h>
>  #include <sys/fcntl.h>
> -#include <sys/selinfo.h>
> -#include <sys/poll.h>
> -
> -#include <machine/bus.h>
> -
> +#include <sys/malloc.h>
> +#include <sys/systm.h>
> +#include <sys/time.h>
>  #include <sys/audioio.h>
> +#include <machine/bus.h>
>  #include <dev/audio_if.h>
> -
>  #include <dev/usb/usb.h>
> -#include <dev/usb/usbdevs.h>
>  #include <dev/usb/usbdi.h>
> -#include <dev/usb/usbdi_util.h>
>  #include <dev/usb/usbdivar.h>
> +#include "ehci.h"
> +#include "xhci.h"
>  
> -#include <dev/usb/uaudioreg.h>
> -
> -/* #define UAUDIO_DEBUG */
>  #ifdef UAUDIO_DEBUG
> -#define DPRINTF(x) do { if (uaudiodebug) printf x; } while (0)
> -#define DPRINTFN(n,x) do { if (uaudiodebug>(n)) printf x; } while (0)
> -int uaudiodebug = 0;
> +#define DPRINTF(...) \
> + do { \
> + if (uaudio_debug) \
> + printf(__VA_ARGS__); \
> + } while (0)
>  #else
> -#define DPRINTF(x)
> -#define DPRINTFN(n,x)
> +#define DPRINTF(...) do {} while(0)
>  #endif
>  
> -#define UAUDIO_NCHANBUFS 3 /* number of outstanding request */
> -#define UAUDIO_MIN_FRAMES 2 /* ms of sound in each request */
> -#define UAUDIO_MAX_FRAMES 16
> -#define UAUDIO_NSYNCBUFS 3 /* number of outstanding sync requests */
> -
> -#define UAUDIO_MAX_ALTS  32 /* max alt settings allowed by driver */
> -
> -#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_SELECTOR 5
> -#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_int8_t class;
> - char ctlname[MAX_AUDIO_DEV_LEN];
> - char *ctlunit;
> -};
> -#define MAKE(h,l) (((h) << 8) | (l))
> +#define DEVNAME(sc) ((sc)->dev.dv_xname)
>  
> -struct as_info {
> - u_int8_t alt;
> - u_int8_t encoding;
> - u_int8_t attributes; /* Copy of bmAttributes of
> -     * usb_audio_streaming_endpoint_descriptor
> -     */
> - struct usbd_interface *ifaceh;
> - const usb_interface_descriptor_t *idesc;
> - const struct usb_endpoint_descriptor_audio *edesc;
> - const struct usb_endpoint_descriptor_audio *edesc1;
> - const struct usb_audio_streaming_type1_descriptor *asf1desc;
> - int sc_busy; /* currently used */
> -};
> +/*
> + * Isochronous endpoint usage (XXX: these belong to sys/usb.h).
> + */
> +#define UE_ISO_USAGE 0x30
> +#define  UE_ISO_USAGE_DATA 0x00
> +#define  UE_ISO_USAGE_FEEDBACK 0x10
> +#define  UE_ISO_USAGE_IMPL 0x20
> +#define UE_GET_ISO_USAGE(a) ((a) & UE_ISO_USAGE)
> +
> +/*
> + * Max length of unit names
> + */
> +#define UAUDIO_NAMEMAX MAX_AUDIO_DEV_LEN
> +
> +/*
> + * USB audio class versions
> + */
> +#define UAUDIO_V1 0x100
> +#define UAUDIO_V2 0x200
> +
> +/*
> + * AC class-specific descriptor interface sub-type
> + */
> +#define UAUDIO_AC_HEADER 0x1
> +#define UAUDIO_AC_INPUT 0x2
> +#define UAUDIO_AC_OUTPUT 0x3
> +#define UAUDIO_AC_MIXER 0x4
> +#define UAUDIO_AC_SELECTOR 0x5
> +#define UAUDIO_AC_FEATURE 0x6
> +#define UAUDIO_AC_EFFECT 0x7
> +#define UAUDIO_AC_PROCESSING 0x8
> +#define UAUDIO_AC_EXTENSION 0x9
> +#define UAUDIO_AC_CLKSRC 0xa
> +#define UAUDIO_AC_CLKSEL 0xb
> +#define UAUDIO_AC_CLKMULT 0xc
> +#define UAUDIO_AC_RATECONV 0xd
> +
> +/*
> + * AS class-specific interface sub-types
> + */
> +#define UAUDIO_AS_GENERAL 0x1
> +#define UAUDIO_AS_FORMAT 0x2
> +
> +/*
> + * AS class-specific endpoint sub-type
> + */
> +#define UAUDIO_EP_GENERAL 0x1
> +
> +/*
> + * UAC v1 formats, wFormatTag is an enum
> + */
> +#define UAUDIO_V1_FMT_PCM 0x1
> +#define UAUDIO_V1_FMT_PCM8 0x2
> +#define UAUDIO_V1_FMT_FLOAT 0x3
> +#define UAUDIO_V1_FMT_ALAW 0x4
> +#define UAUDIO_V1_FMT_MULAW 0x5
> +
> +/*
> + * UAC v2 formats, bmFormats is a bitmap
> + */
> +#define UAUDIO_V2_FMT_PCM 0x01
> +#define UAUDIO_V2_FMT_PCM8 0x02
> +#define UAUDIO_V2_FMT_FLOAT 0x04
> +#define UAUDIO_V2_FMT_ALAW 0x08
> +#define UAUDIO_V2_FMT_MULAW 0x10
> +
> +/*
> + * AC requests
> + */
> +#define UAUDIO_V1_REQ_SET_CUR 0x01
> +#define UAUDIO_V1_REQ_SET_MIN 0x02
> +#define UAUDIO_V1_REQ_SET_MAX 0x03
> +#define UAUDIO_V1_REQ_SET_RES 0x04
> +#define UAUDIO_V1_REQ_GET_CUR 0x81
> +#define UAUDIO_V1_REQ_GET_MIN 0x82
> +#define UAUDIO_V1_REQ_GET_MAX 0x83
> +#define UAUDIO_V1_REQ_GET_RES 0x84
> +#define UAUDIO_V2_REQ_CUR 1
> +#define UAUDIO_V2_REQ_RANGES 2
> +
> +/*
> + * AC request "selector control"
> + */
> +#define UAUDIO_V2_REQSEL_CLKFREQ 1
> +#define UAUDIO_V2_REQSEL_CLKSEL 1
> +
> +/*
> + * AS class-specific endpoint attributes
> + */
> +#define UAUDIO_EP_FREQCTL 0x01
> +
> +/*
> + * AC feature control selectors (aka wValue in the request)
> + */
> +#define UAUDIO_REQSEL_MUTE 0x01
> +#define UAUDIO_REQSEL_VOLUME 0x02
> +#define UAUDIO_REQSEL_BASS 0x03
> +#define UAUDIO_REQSEL_MID 0x04
> +#define UAUDIO_REQSEL_TREBLE 0x05
> +#define UAUDIO_REQSEL_EQ 0x06
> +#define UAUDIO_REQSEL_AGC 0x07
> +#define UAUDIO_REQSEL_DELAY 0x08
> +#define UAUDIO_REQSEL_BASSBOOST 0x09
> +#define UAUDIO_REQSEL_LOUDNESS 0x0a
> +#define UAUDIO_REQSEL_GAIN 0x0b
> +#define UAUDIO_REQSEL_GAINPAD 0x0c
> +#define UAUDIO_REQSEL_PHASEINV 0x0d
>  
> -struct chan {
> - void (*intr)(void *); /* DMA completion intr handler */
> - void *arg; /* arg for intr() */
> - struct usbd_pipe *pipe;
> - struct usbd_pipe *sync_pipe;
> -
> - u_int sample_size;
> - u_int sample_rate;
> - u_int bytes_per_frame;
> - u_int max_bytes_per_frame;
> - u_int fraction; /* fraction/frac_denom is the extra samples/frame */
> - u_int frac_denom; /* denominator for fractional samples */
> - u_int residue; /* accumulates the fractional samples */
> - u_int nframes; /* # of frames per transfer */
> - u_int nsync_frames; /* # of frames per sync transfer */
> - u_int usb_fps;
> - u_int maxpktsize;
> - u_int reqms; /* usb request data duration, in ms */
> - u_int hi_speed;
> -
> - u_char *start; /* upper layer buffer start */
> - u_char *end; /* upper layer buffer end */
> - u_char *cur; /* current position in upper layer buffer */
> - int blksize; /* chunk size to report up */
> - int transferred; /* transferred bytes not reported up */
> -
> - int altidx; /* currently used altidx */
> -
> - int curchanbuf;
> - int cursyncbuf;
> -
> - struct chanbuf {
> - struct chan *chan;
> - struct usbd_xfer *xfer;
> - u_char *buffer;
> - u_int16_t sizes[UAUDIO_MAX_FRAMES];
> - u_int16_t offsets[UAUDIO_MAX_FRAMES];
> - u_int16_t size;
> - } chanbufs[UAUDIO_NCHANBUFS];
> -
> - struct syncbuf {
> - struct chan *chan;
> - struct usbd_xfer *xfer;
> - u_char *buffer;
> - u_int16_t sizes[UAUDIO_MAX_FRAMES];
> - u_int16_t offsets[UAUDIO_MAX_FRAMES];
> - u_int16_t size;
> - } syncbufs[UAUDIO_NSYNCBUFS];
> +/*
> + * Endpoint (UAC v1) or clock-source unit (UAC v2) sample rate control
> + */
> +#define UAUDIO_REQSEL_RATE 0x01
> +
> +/*
> + * Number of fixed sample rates we support
> + */
> +#define UAUDIO_NRATES (sizeof(uaudio_rates) / sizeof(uaudio_rates[0]))
>  
> - struct uaudio_softc *sc; /* our softc */
> +/*
> + * read/write pointers for secure sequencial access of binary data,
> + * ex. usb descriptors, tables and alike. Bytes are read using the
> + * read pointer up to the write pointer.
> + */
> +struct uaudio_blob {
> + unsigned char *rptr, *wptr;
>  };
>  
> -#define UAUDIO_FLAG_BAD_AUDIO 0x0001 /* claims audio class, but isn't */
> -#define UAUDIO_FLAG_NO_FRAC 0x0002 /* don't use fractional samples */
> -#define UAUDIO_FLAG_NO_XU 0x0004 /* has broken extension unit */
> -#define UAUDIO_FLAG_BAD_ADC 0x0008 /* bad audio spec version number */
> -#define UAUDIO_FLAG_VENDOR_CLASS 0x0010 /* claims vendor class but works */
> -#define UAUDIO_FLAG_DEPENDENT 0x0020 /* play and record params must equal */
> -#define UAUDIO_FLAG_EMU0202 0x0040
> -#define UAUDIO_FLAG_BAD_ADC_LEN 0x0080 /* bad audio control descriptor size */
> -
> -struct uaudio_devs {
> - struct usb_devno uv_dev;
> - int flags;
> -} uaudio_devs[] = {
> - { { USB_VENDOR_YAMAHA, USB_PRODUCT_YAMAHA_UR22 },
> - UAUDIO_FLAG_VENDOR_CLASS },
> - { { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70 },
> - UAUDIO_FLAG_BAD_ADC } ,
> - { { USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495 },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4_GSM },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4_CDMA },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_6 },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH_2G },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH_3G },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPOD_TOUCH_4G },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPAD },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPAD2 },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_CREATIVE, USB_PRODUCT_CREATIVE_EMU0202 },
> - UAUDIO_FLAG_VENDOR_CLASS | UAUDIO_FLAG_EMU0202 |
> - UAUDIO_FLAG_DEPENDENT },
> - { { USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502 },
> - UAUDIO_FLAG_NO_XU | UAUDIO_FLAG_BAD_ADC },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMNBDLX },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRONB },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMPRO4K },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_QUICKCAMZOOM },
> - UAUDIO_FLAG_BAD_AUDIO },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC200 },
> - UAUDIO_FLAG_BAD_ADC_LEN },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC210 },
> - UAUDIO_FLAG_BAD_ADC_LEN },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC250 },
> - UAUDIO_FLAG_BAD_ADC_LEN },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC270 },
> - UAUDIO_FLAG_BAD_ADC_LEN },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC310 },
> - UAUDIO_FLAG_BAD_ADC_LEN },
> - { { USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_WEBCAMC500 },
> - UAUDIO_FLAG_BAD_ADC_LEN },
> - { { USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1 },
> - UAUDIO_FLAG_NO_FRAC }
> +/*
> + * Ranges of integer values used to represent controls values and
> + * sample frequencies.
> + */
> +struct uaudio_ranges {
> + unsigned int nval;
> + struct uaudio_ranges_el {
> + struct uaudio_ranges_el *next;
> + int min, max, res;
> + } *el;
>  };
> -#define uaudio_lookup(v, p) \
> - ((struct uaudio_devs *)usb_lookup(uaudio_devs, v, p))
>  
>  struct uaudio_softc {
> - struct device sc_dev; /* base device */
> - struct usbd_device *sc_udev; /* USB device */
> - int sc_ac_iface; /* Audio Control interface */
> - struct chan sc_playchan; /* play channel */
> - struct chan sc_recchan; /* record channel */
> - int sc_nullalt;
> - int sc_audio_rev;
> - struct as_info *sc_alts; /* alternate settings */
> - int sc_nalts; /* # of alternate settings */
> - int sc_altflags;
> -#define HAS_8 0x01
> -#define HAS_16 0x02
> -#define HAS_8U 0x04
> -#define HAS_ALAW 0x08
> -#define HAS_MULAW 0x10
> -#define UA_NOFRAC 0x20 /* don't do sample rate adjustment */
> -#define HAS_24 0x40
> - int sc_mode; /* play/record capability */
> - struct mixerctl *sc_ctls; /* mixer controls */
> - int sc_nctls; /* # of mixer controls */
> - int sc_quirks;
> -};
> + struct device dev;
> + struct usbd_device *udev;
> + int version;
>  
> -struct terminal_list {
> - int size;
> - uint16_t terminals[1];
> -};
> -#define TERMINAL_LIST_SIZE(N) (offsetof(struct terminal_list, terminals) \
> - + sizeof(uint16_t) * (N))
> + /*
> + * UAC exposes the device as a circuit of units. Input and
> + * output jacks are known as terminal units, others are
> + * processing units. The purpose of this driver is to give
> + * them reasonable names and expose them as mixer(1)
> + * controls. Control names are derived from the type of the
> + * unit and its role in the circuit.
> + *
> + * UAC v2.0 exposes also the clock circuitry using units, so
> + * selecting the sample rate also involves units usage.
> + */
> + struct uaudio_unit {
> + struct uaudio_unit *unit_next, *src_next, *dst_next;
> + struct uaudio_unit *src_list, *dst_list;
> + char name[UAUDIO_NAMEMAX];
> + unsigned int nch;
> + int type, id;
> +
> + /* clock source, if a terminal or selector */
> + struct uaudio_unit *clock;
> +
> + /* sample rates, if this is a clock source */
> + struct uaudio_ranges rates;
> +
> + /* mixer(4) bits */
> +#define UAUDIO_CLASS_REC 0
> +#define UAUDIO_CLASS_OUT 1
> +#define UAUDIO_CLASS_IN 2
> +#define UAUDIO_CLASS_COUNT 3
> + int mixer_class;
> + struct uaudio_mixent {
> + struct uaudio_mixent *next;
> + char *fname;
> +#define UAUDIO_MIX_SW 0
> +#define UAUDIO_MIX_NUM 1
> +#define UAUDIO_MIX_ENUM 2
> + int type;
> + int chan;
> + int req_sel;
> + struct uaudio_ranges ranges;
> + } *mixent_list;
> + } *unit_list;
> +
> + /*
> + * Current clock, UAC v2.0 only
> + */
> + struct uaudio_unit *clock;
> +
> + /*
> + * When unique names are needed, they are generated using a
> + * base string suffixed with a number. Ex. "spkr5". The
> + * following structure is used to keep track of strings we
> + * allocated.
> + */
> + struct uaudio_name {
> + struct uaudio_name *next;
> + char *templ;
> + unsigned int unit;
> + } *names;
> +
> + /*
> + * Audio streaming (AS) alternate settings, i.e. stream format
> + * and USB-related parameters to use it.
> + */
> + struct uaudio_alt {
> + struct uaudio_alt *next;
> + int ifnum, altnum;
> + int mode; /* one of AUMODE_{RECORD,PLAY} */
> + int sync; /* is sync endpoint used */
> + int data_addr; /* data endpoint address */
> + int sync_addr; /* feedback endpoint address */
> + int maxpkt; /* max supported bytes per frame */
> + int fps; /* USB (micro-)frames per second */
> + int bps, bits, nch; /* audio encoding */
> + int v1_rates; /* if UAC 1.0, bitmap of rates */
> + } *alts;
> +
> + /*
> + * Audio parameters: play and record stream formats usable
> + * together.
> + */
> + struct uaudio_params {
> + struct uaudio_params *next;
> + struct uaudio_alt *palt, *ralt;
> + int v1_rates;
> + } *params_list, *params;
> +
> + /*
> + * One direction audio stream, aka "DMA" in progress
> + */
> + struct uaudio_stream {
> +#define UAUDIO_NXFERS 3
> + struct uaudio_xfer {
> + struct usbd_xfer *usb_xfer;
> + unsigned char *buf;
> + uint16_t *sizes;
> + unsigned int size; /* bytes requested */
> + unsigned int nframes; /* frames requested */
> + } data_xfers[UAUDIO_NXFERS], sync_xfers[UAUDIO_NXFERS];
> +
> + unsigned int remain; /* samples 16-bit fixed-point */
> + unsigned int spf; /* avg samples per frame */
> + unsigned int spf_min, spf_max; /* allowed boundaries */
> +
> + unsigned int maxpkt;
> +
> + unsigned int nframes;
> + unsigned int nframes_min, nframes_max;
> +
> + unsigned int data_nextxfer, sync_nextxfer;
> + struct usbd_pipe *data_pipe;
> + struct usbd_pipe *sync_pipe;
> + void (*intr)(void *);
> + void *arg;
> + unsigned char *ring_start, *ring_end, *ring_pos;
> + int ring_offs, ring_blksz, safe_blksz;
>  
> -struct io_terminal {
> - union {
> - const usb_descriptor_t *desc;
> - const struct usb_audio_input_terminal *it;
> - const struct usb_audio_output_terminal *ot;
> - const struct usb_audio_mixer_unit *mu;
> - const struct usb_audio_selector_unit *su;
> - const struct usb_audio_feature_unit *fu;
> - const struct usb_audio_processing_unit *pu;
> - const struct usb_audio_extension_unit *eu;
> - } d;
> - int inputs_size;
> - struct terminal_list **inputs; /* list of source input terminals */
> - struct terminal_list *output; /* list of destination output terminals */
> - int direct; /* directly connected to an output terminal */
> + /*
> + * Device clock may take some time to lock during which
> + * we'd receive null packets for which we need to
> + * generate silence. We consider that the device clock
> + * is locked as soon as we receive the first non-null
> + * packet.
> + *
> + * Certain devices (UAC v2 async recording) seem to
> + * generate rare null microframes, but this seems to be
> + * caused bogus^sophisticated fractional sample
> + * calculations, so we shouldn't insert silence in this
> + * case.
> + */
> + int locked;
> + } pstream, rstream;
> +
> + int ctl_ifnum; /* aka AC interface */
> +
> + int rate; /* current sample rate */
> + int mode; /* open() mode */
> + int trigger_mode; /* trigger() mode */
> +
> + unsigned int ufps; /* USB frames per second */
> + unsigned int sync_pktsz; /* size of sync packet */
> + unsigned int nsamp_per_ufr; /* default samples per USB frame */
>  };
>  
> -#define UAC_OUTPUT 0
> -#define UAC_INPUT 1
> -#define UAC_EQUAL 2
> -#define UAC_RECORD 3
> -#define UAC_NCLASSES 4
> +int uaudio_match(struct device *, void *, void *);
> +void uaudio_attach(struct device *, struct device *, void *);
> +int uaudio_detach(struct device *, int);
> +
> +int uaudio_open(void *, int);
> +void uaudio_close(void *);
> +int uaudio_set_params(void *, int, int, struct audio_params *,
> +    struct audio_params *);
> +int uaudio_round_blocksize(void *, int);
> +int uaudio_trigger_output(void *, void *, void *, int,
> +    void (*)(void *), void *, struct audio_params *);
> +int uaudio_trigger_input(void *, void *, void *, int,
> +    void (*)(void *), void *, struct audio_params *);
> +int uaudio_halt_output(void *);
> +int uaudio_halt_input(void *);
> +int uaudio_query_devinfo(void *, struct mixer_devinfo *);
> +int uaudio_get_port(void *, struct mixer_ctrl *);
> +int uaudio_set_port(void *, struct mixer_ctrl *);
> +int uaudio_get_props(void *);
> +
> +int uaudio_process_unit(struct uaudio_softc *,
> +    struct uaudio_unit *, int,
> +    struct uaudio_blob,
> +    struct uaudio_unit **);
> +
> +void uaudio_pdata_intr(struct usbd_xfer *, void *, usbd_status);
> +void uaudio_rdata_intr(struct usbd_xfer *, void *, usbd_status);
> +void uaudio_psync_intr(struct usbd_xfer *, void *, usbd_status);
> +
>  #ifdef UAUDIO_DEBUG
> -const char *uac_names[] = {
> - AudioCoutputs, AudioCinputs, AudioCequalization, AudioCrecord,
> -};
> +char *uaudio_isoname(int isotype);
> +char *uaudio_modename(int mode);
> +char *uaudio_usagename(int usage);
> +void uaudio_rates_print(int rates);
> +void uaudio_ranges_print(struct uaudio_ranges *r);
> +void uaudio_print_unit(struct uaudio_softc *sc, struct uaudio_unit *u);
> +void uaudio_mixer_print(struct uaudio_softc *sc);
> +void uaudio_conf_print(struct uaudio_softc *sc);
> +
> +/*
> + * 0 - nothing, as if UAUDIO_DEBUG isn't defined
> + * 1 - initialisations & setup
> + * 2 - transfers
> + */
> +int uaudio_debug = 1;
>  #endif
>  
> -usbd_status uaudio_identify_ac
> - (struct uaudio_softc *, const usb_config_descriptor_t *);
> -usbd_status uaudio_identify_as
> - (struct uaudio_softc *, const usb_config_descriptor_t *);
> -usbd_status uaudio_process_as
> - (struct uaudio_softc *, const char *, int *, int,
> - const usb_interface_descriptor_t *);
> -
> -void uaudio_add_alt(struct uaudio_softc *, const struct as_info *);
> -
> -const usb_interface_descriptor_t *uaudio_find_iface
> - (const char *, int, int *, int, int);
> -
> -void uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *);
> -uByte uaudio_get_cluster_nchan
> - (int, const struct io_terminal *);
> -void uaudio_add_input
> - (struct uaudio_softc *, const struct io_terminal *, int);
> -void uaudio_add_output
> - (struct uaudio_softc *, const struct io_terminal *, int);
> -void uaudio_add_mixer
> - (struct uaudio_softc *, const struct io_terminal *, int);
> -void uaudio_add_selector
> - (struct uaudio_softc *, const struct io_terminal *, int);
> -#ifdef UAUDIO_DEBUG
> -const char *uaudio_get_terminal_name(int);
> -#endif
> -int uaudio_determine_class
> - (const struct io_terminal *, struct mixerctl *);
> -const char *uaudio_feature_name
> - (const struct io_terminal *, struct mixerctl *);
> -void uaudio_add_feature
> - (struct uaudio_softc *, const struct io_terminal *, int);
> -void uaudio_add_processing_updown
> - (struct uaudio_softc *, const struct io_terminal *, int);
> -void uaudio_add_processing
> - (struct uaudio_softc *, const struct io_terminal *, int);
> -void uaudio_add_extension
> - (struct uaudio_softc *, const struct io_terminal *, int);
> -struct terminal_list *uaudio_merge_terminal_list
> - (const struct io_terminal *);
> -struct terminal_list *uaudio_io_terminaltype
> - (int, struct io_terminal *, int);
> -usbd_status uaudio_identify
> - (struct uaudio_softc *, const usb_config_descriptor_t *);
> -
> -int uaudio_signext(int, int);
> -int uaudio_unsignext(int, int);
> -int uaudio_value2bsd(struct mixerctl *, int);
> -int uaudio_bsd2value(struct mixerctl *, int);
> -int uaudio_get(struct uaudio_softc *, int, int, int, int, int);
> -int uaudio_ctl_get
> - (struct uaudio_softc *, int, struct mixerctl *, int);
> -void uaudio_set
> - (struct uaudio_softc *, int, int, int, int, int, int);
> -void uaudio_ctl_set
> - (struct uaudio_softc *, int, struct mixerctl *, int, int);
> -
> -usbd_status uaudio_set_speed(struct uaudio_softc *, int, u_int);
> -void uaudio_set_speed_emu0202(struct chan *ch);
> -
> -usbd_status uaudio_chan_open(struct uaudio_softc *, struct chan *);
> -void uaudio_chan_close(struct uaudio_softc *, struct chan *);
> -usbd_status uaudio_chan_alloc_buffers
> - (struct uaudio_softc *, struct chan *);
> -void uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *);
> -void uaudio_chan_init
> - (struct chan *, int, int, const struct audio_params *);
> -void uaudio_chan_set_param(struct chan *, u_char *, u_char *, int);
> -void uaudio_chan_ptransfer(struct chan *);
> -void uaudio_chan_pintr
> - (struct usbd_xfer *, void *, usbd_status);
> -void uaudio_chan_psync_transfer(struct chan *);
> -void uaudio_chan_psync_intr
> - (struct usbd_xfer *, void *, usbd_status);
> -
> -void uaudio_chan_rtransfer(struct chan *);
> -void uaudio_chan_rintr
> - (struct usbd_xfer *, void *, usbd_status);
> -
> -int uaudio_open(void *, int);
> -void uaudio_close(void *);
> -void uaudio_get_minmax_rates
> - (int, const struct as_info *, const struct audio_params *,
> - int, int, int, u_long *, u_long *);
> -int uaudio_match_alt_rate(void *, int, int);
> -int uaudio_match_alt(void *, struct audio_params *, int);
> -int uaudio_set_params
> - (void *, int, int, struct audio_params *, struct audio_params *);
> -int uaudio_round_blocksize(void *, int);
> -int uaudio_trigger_output
> - (void *, void *, void *, int, void (*)(void *), void *,
> - struct audio_params *);
> -int uaudio_trigger_input
> - (void *, void *, void *, int, void (*)(void *), void *,
> - struct audio_params *);
> -int uaudio_halt_in_dma(void *);
> -int uaudio_halt_out_dma(void *);
> -int uaudio_mixer_set_port(void *, mixer_ctrl_t *);
> -int uaudio_mixer_get_port(void *, mixer_ctrl_t *);
> -int uaudio_query_devinfo(void *, mixer_devinfo_t *);
> -int uaudio_get_props(void *);
> +extern struct cfdriver ehci_cd;
> +extern struct cfdriver xhci_cd;
>  
> -struct audio_hw_if uaudio_hw_if = {
> - uaudio_open,
> - uaudio_close,
> - uaudio_set_params,
> - uaudio_round_blocksize,
> - NULL,
> - NULL,
> - NULL,
> - NULL,
> - NULL,
> - uaudio_halt_out_dma,
> - uaudio_halt_in_dma,
> - NULL,
> - NULL,
> - uaudio_mixer_set_port,
> - uaudio_mixer_get_port,
> - uaudio_query_devinfo,
> - NULL,
> - NULL,
> - NULL,
> - uaudio_get_props,
> - uaudio_trigger_output,
> - uaudio_trigger_input
> +struct cfdriver uaudio_cd = {
> + NULL, "uaudio", DV_DULL
>  };
>  
> -int uaudio_match(struct device *, void *, void *);
> -void uaudio_attach(struct device *, struct device *, void *);
> -int uaudio_detach(struct device *, int);
> -
> -struct cfdriver uaudio_cd = {
> - NULL, "uaudio", DV_DULL
> -};
> -
>  const struct cfattach uaudio_ca = {
>   sizeof(struct uaudio_softc), uaudio_match, uaudio_attach, uaudio_detach
>  };
>  
> -int
> -uaudio_match(struct device *parent, void *match, void *aux)
> -{
> - struct usb_attach_arg *uaa = aux;
> - usb_interface_descriptor_t *id;
> - const usb_interface_descriptor_t *cd_id;
> - usb_config_descriptor_t *cdesc;
> - struct uaudio_devs *quirk;
> - const char *buf;
> - int flags = 0, size, offs;
> -
> - if (uaa->iface == NULL || uaa->device == NULL)
> - return (UMATCH_NONE);
> -
> - quirk = uaudio_lookup(uaa->vendor, uaa->product);
> - if (quirk)
> - flags = quirk->flags;
> -
> - if (flags & UAUDIO_FLAG_BAD_AUDIO)
> - return (UMATCH_NONE);
> -
> - id = usbd_get_interface_descriptor(uaa->iface);
> - if (id == NULL)
> - return (UMATCH_NONE);
> -
> - if (!(id->bInterfaceClass == UICLASS_AUDIO ||
> -    ((flags & UAUDIO_FLAG_VENDOR_CLASS) &&
> -    id->bInterfaceClass == UICLASS_VENDOR)))
> - return (UMATCH_NONE);
> +struct audio_hw_if uaudio_hw_if = {
> + uaudio_open, /* open */
> + uaudio_close, /* close */
> + uaudio_set_params, /* set_params */
> + uaudio_round_blocksize, /* round_blocksize */
> + NULL, /* commit_settings */
> + NULL, /* init_output */
> + NULL, /* init_input */
> + NULL, /* start_output */
> + NULL, /* start_input */
> + uaudio_halt_output, /* halt_output */
> + uaudio_halt_input, /* halt_input */
> + NULL, /* speaker_ctl */
> + NULL, /* setfd */
> + uaudio_set_port, /* set_port */
> + uaudio_get_port, /* get_port */
> + uaudio_query_devinfo, /* query_devinfo */
> + NULL, /* malloc, we use bounce buffers :'( */
> + NULL, /* free */
> + NULL, /* round_buffersize */
> + uaudio_get_props, /* get_props */
> + uaudio_trigger_output, /* trigger_output */
> + uaudio_trigger_input /* trigger_input */
> +};
>  
> - if (id->bInterfaceSubClass != UISUBCLASS_AUDIOCONTROL)
> - return (UMATCH_NONE);
> +/*
> + * To keep things simple, we support only the following rates, we
> + * don't care about continuous sample rates or other "advanced"
> + * features which complicate implementation.
> + *
> + * Given that USB2.0 frame rate is 8000 fps, if we require a fixed
> + * number of samples per frame, only sample rates multiple of 8000Hz
> + * can be used. To support other rates, we've to use variable number
> + * of samples per frame such that it achives the desired sample rate
> + * over one transfer.
> + */
> +int uaudio_rates[] = {
> + 8000,
> +#ifdef UAUDIO_USE_FRAC
> + 11025,
> +#endif
> + 12000,
> + 16000,
> +#ifdef UAUDIO_USE_FRAC
> + 22050,
> +#endif
> + 24000,
> + 32000,
> +#ifdef UAUDIO_USE_FRAC
> + 44100,
> +#endif
> + 48000,
> + 64000,
> +#ifdef UAUDIO_USE_FRAC
> + 88200,
> +#endif
> + 96000,
> + 128000,
> +#ifdef UAUDIO_USE_FRAC
> + 176400,
> +#endif
> + 192000
> +};
>  
> - cdesc = usbd_get_config_descriptor(uaa->device);
> - if (cdesc == NULL)
> - return (UMATCH_NONE);
> +/*
> + * Convert 8, 16, or 24-bit signed value to an int by expanding the
> + * sign bit.
> + */
> +int
> +uaudio_sign_expand(unsigned int val, int opsize)
> +{
> + unsigned int s;
>  
> - size = UGETW(cdesc->wTotalLength);
> - buf = (const char *)cdesc;
> + s = 1 << (8 * opsize - 1);
> + return (val ^ s) - s;
> +}
>  
> - offs = 0;
> - cd_id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM,
> -    flags);
> - if (cd_id == NULL)
> - return (UMATCH_NONE);
> -
> - offs = 0;
> - cd_id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOCONTROL,
> -    flags);
> - if (cd_id == NULL)
> - return (UMATCH_NONE);
> +/*
> + * XXX: there are at least 3 such routines in the kernel, but we're
> + * not supposed to need this. Fix this by using a less stupid audio(9)
> + * round_blocksize() semantics.
> + */
> +unsigned int
> +uaudio_gcd(unsigned int a, unsigned int b)
> +{
> + unsigned int r;
>  
> - return (UMATCH_VENDOR_PRODUCT_CONF_IFACE);
> + while (b > 0) {
> + r = a % b;
> + a = b;
> + b = r;
> + }
> + return a;
>  }
>  
> -void
> -uaudio_attach(struct device *parent, struct device *self, void *aux)
> +int
> +uaudio_req(struct uaudio_softc *sc,
> +    unsigned int type,
> +    unsigned int req,
> +    unsigned int sel,
> +    unsigned int chan,
> +    unsigned int ifnum,
> +    unsigned int id,
> +    unsigned char *buf,
> +    size_t size)
>  {
> - struct uaudio_softc *sc = (struct uaudio_softc *)self;
> - struct usb_attach_arg *uaa = aux;
> - struct uaudio_devs *quirk;
> - usb_interface_descriptor_t *id;
> - usb_config_descriptor_t *cdesc;
> - usbd_status err;
> - int i, j, found;
> -
> - sc->sc_udev = uaa->device;
> -
> - quirk = uaudio_lookup(uaa->vendor, uaa->product);
> - if (quirk)
> - sc->sc_quirks = quirk->flags;
> -
> - cdesc = usbd_get_config_descriptor(sc->sc_udev);
> - if (cdesc == NULL) {
> - printf("%s: failed to get configuration descriptor\n",
> -       sc->sc_dev.dv_xname);
> - return;
> - }
> + struct usb_device_request r;
> + int err;
>  
> - err = uaudio_identify(sc, cdesc);
> - if (err) {
> - printf("%s: audio descriptors make no sense, error=%d\n",
> -       sc->sc_dev.dv_xname, err);
> - return;
> - }
> + r.bmRequestType = type;
> + r.bRequest = req;
> + USETW(r.wValue, sel << 8 | chan);
> + USETW(r.wIndex, id << 8 | ifnum);
> + USETW(r.wLength, size);
>  
> - /* Pick up the AS interface. */
> - for (i = 0; i < uaa->nifaces; i++) {
> - if (usbd_iface_claimed(sc->sc_udev, i))
> - 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)
> - usbd_claim_iface(sc->sc_udev, i);
> - }
> + DPRINTF("req: type = 0x%x, req = 0x%x, val = 0x%x, "
> +    "index = 0x%x, size = %d\n",
> +    type, req, UGETW(r.wValue), UGETW(r.wIndex), UGETW(r.wLength));
>  
> - for (j = 0; j < sc->sc_nalts; j++) {
> - if (sc->sc_alts[j].ifaceh == NULL) {
> - printf("%s: alt %d missing AS interface(s)\n",
> -    sc->sc_dev.dv_xname, j);
> - return;
> - }
> + err = usbd_do_request(sc->udev, &r, buf);
> + if (err) {
> + printf("%s: request failed: %s\n",
> +    DEVNAME(sc), usbd_errstr(err));
> + return 0;
>   }
> + return 1;
> +}
>  
> - printf("%s: audio rev %d.%02x", sc->sc_dev.dv_xname,
> -       sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff);
> -
> - sc->sc_playchan.sc = sc->sc_recchan.sc = sc;
> - sc->sc_playchan.altidx = -1;
> - sc->sc_recchan.altidx = -1;
> +/*
> + * Read a number of the given size (in bytes) from the given
> + * blob. Return 0 on error.
> + */
> +int
> +uaudio_getnum(struct uaudio_blob *p, unsigned int size, unsigned int *ret)
> +{
> + unsigned int i, num = 0;
>  
> - if (sc->sc_quirks & UAUDIO_FLAG_NO_FRAC)
> - sc->sc_altflags |= UA_NOFRAC;
> + if (p->wptr - p->rptr < size) {
> + DPRINTF("uaudio_getnum: %d: too small\n", size);
> + return 0;
> + }
>  
> - printf(", %d mixer controls\n", sc->sc_nctls);
> + for (i = 0; i < size; i++)
> + num |= *p->rptr++ << (8 * i);
>  
> - DPRINTF(("%s: doing audio_attach_mi\n", __func__));
> - audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev);
> + if (ret)
> + *ret = num;
> + return 1;
>  }
>  
> +/*
> + * Read a USB descriptor from the given blob. Return 0 on error.
> + */
>  int
> -uaudio_detach(struct device *self, int flags)
> +uaudio_getdesc(struct uaudio_blob *p, struct uaudio_blob *ret)
>  {
> - struct uaudio_softc *sc = (struct uaudio_softc *)self;
> + unsigned int size;
>  
> - /*
> - * sc_alts may be NULL if uaudio_identify_as() failed, in
> - * which case uaudio_attach() didn't finish and there's
> - * nothing to detach.
> - */
> - if (sc->sc_alts == NULL)
> - return (0);
> - return (config_detach_children(self, flags));
> + if (!uaudio_getnum(p, 1, &size))
> + return 0;
> + if (size-- == 0) {
> + DPRINTF("uaudio_getdesc: zero sized desc\n");
> + return 0;
> + }
> + if (p->wptr - p->rptr < size) {
> + DPRINTF("uaudio_getdesc: too small\n");
> + return 0;
> + }
> + ret->rptr = p->rptr;
> + ret->wptr = p->rptr + size;
> + p->rptr += size;
> + return 1;
>  }
>  
> -const usb_interface_descriptor_t *
> -uaudio_find_iface(const char *buf, int size, int *offsp, int subtype, int flags)
> +/*
> + * Find the unit with the given id, return NULL if not found.
> + */
> +struct uaudio_unit *
> +uaudio_unit_byid(struct uaudio_softc *sc, unsigned int id)
>  {
> - const usb_interface_descriptor_t *d;
> + struct uaudio_unit *u;
>  
> - while (*offsp < size) {
> - d = (const void *)(buf + *offsp);
> - *offsp += d->bLength;
> - if (d->bDescriptorType == UDESC_INTERFACE &&
> -    d->bInterfaceSubClass == subtype &&
> -    (d->bInterfaceClass == UICLASS_AUDIO ||
> -    (d->bInterfaceClass == UICLASS_VENDOR &&
> -    (flags & UAUDIO_FLAG_VENDOR_CLASS))))
> - return (d);
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + if (u->id == id)
> + break;
>   }
> - return (NULL);
> + return u;
>  }
>  
> -void
> -uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc)
> +/*
> + * Return a terminal name for the given terminal type.
> + */
> +char *
> +uaudio_tname(unsigned int type, int isout)
>  {
> - int res, range;
> - size_t len;
> - struct mixerctl *nmc;
> -
> - if (mc->class < UAC_NCLASSES) {
> - DPRINTF(("%s: adding %s.%s\n",
> - __func__, uac_names[mc->class], mc->ctlname));
> - } else {
> - DPRINTF(("%s: adding %s\n", __func__, mc->ctlname));
> - }
> + unsigned int hi, lo;
> + char *name;
>  
> - nmc = mallocarray(sc->sc_nctls + 1, sizeof(*mc), M_USBDEV, M_NOWAIT);
> - if (nmc == NULL) {
> - printf("%s: no memory\n", __func__);
> - return;
> - }
> - len = sizeof(*mc) * (sc->sc_nctls + 1);
> + hi = type >> 8;
> + lo = type & 0xff;
>  
> - /* Copy old data, if there was any */
> - if (sc->sc_nctls != 0) {
> - memcpy(nmc, sc->sc_ctls, sizeof(*mc) * (sc->sc_nctls));
> - free(sc->sc_ctls, M_USBDEV, sc->sc_nctls * sizeof(*mc));
> - }
> - sc->sc_ctls = nmc;
> -
> - mc->delta = 0;
> - if (mc->type == MIX_ON_OFF) {
> - mc->minval = 0;
> - mc->maxval = 1;
> - } else if (mc->type == MIX_SELECTOR) {
> - ;
> - } else {
> - /* 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 = uaudio_signext(mc->type,
> - uaudio_get(sc, GET_MAX, UT_READ_CLASS_INTERFACE,
> -   mc->wValue[0], mc->wIndex,
> -   MIX_SIZE(mc->type)));
> - range = mc->maxval - mc->minval;
> - res = uaudio_get(sc, GET_RES, UT_READ_CLASS_INTERFACE,
> - mc->wValue[0], mc->wIndex,
> - MIX_SIZE(mc->type));
> - if (res > 0 && range > 0)
> - mc->delta = (res * 255 + res - 1) / range;
> - }
> -
> - sc->sc_ctls[sc->sc_nctls++] = *mc;
> -
> -#ifdef UAUDIO_DEBUG
> - if (uaudiodebug > 2) {
> - int i;
> - DPRINTF(("%s: wValue=%04x", __func__, 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));
> + switch (hi) {
> + case 1:
> + /* usb data stream */
> + name = isout ? "record" : "play";
> + break;
> + case 2:
> + /* embedded inputs */
> + name = isout ? "mic-out" : "mic";
> + break;
> + case 3:
> + /* embedded outputs, mostly speakers, except 0x302 */
> + switch (lo) {
> + case 0x02:
> + name = isout ? "hp" : "hp-in";
> + break;
> + default:
> + name = isout ? "spkr" : "spkr-in";
> + break;
> + }
> + break;
> + case 4:
> + /* handsets and headset */
> + name = isout ? "spkr" : "mic";
> + break;
> + case 5:
> + /* phone line */
> + name = isout ? "phone-in" : "phone-out";
> + break;
> + case 6:
> + /* external sources/sinks */
> + switch (lo) {
> + case 0x02:
> + case 0x05:
> + case 0x06:
> + case 0x07:
> + case 0x09:
> + case 0x0a:
> + name = isout ? "dig-out" : "dig-in";
> + break;
> + default:
> + name = isout ? "line-out" : "line-in";
> + break;
> + }
> + break;
> + case 7:
> + /* internal devices */
> + name = isout ? "int-out" : "int-in";
> + break;
> + default:
> + name = isout ? "unk-out" : "unk-in";
>   }
> -#endif
> + return name;
>  }
>  
> -uByte
> -uaudio_get_cluster_nchan(int id, const struct io_terminal *iot)
> +/*
> + * Return a clock name for the given clock type.
> + */
> +char *
> +uaudio_clkname(unsigned int attr)
>  {
> - struct usb_audio_cluster r;
> - const usb_descriptor_t *dp;
> - int i;
> + static char *names[] = {"ext", "fixed", "var", "prog"};
>  
> - for (i = 0; i < 25; i++) { /* avoid infinite loops */
> - dp = iot[id].d.desc;
> - if (dp == 0)
> - goto bad;
> - switch (dp->bDescriptorSubtype) {
> - case UDESCSUB_AC_INPUT:
> - return (iot[id].d.it->bNrChannels);
> - case UDESCSUB_AC_OUTPUT:
> - id = iot[id].d.ot->bSourceId;
> - break;
> - case UDESCSUB_AC_MIXER:
> - r = *(struct usb_audio_cluster *)
> - &iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins];
> - return (r.bNrChannels);
> - case UDESCSUB_AC_SELECTOR:
> - /* XXX This is not really right */
> - id = iot[id].d.su->baSourceId[0];
> - break;
> - case UDESCSUB_AC_FEATURE:
> - id = iot[id].d.fu->bSourceId;
> - break;
> - case UDESCSUB_AC_PROCESSING:
> - r = *(struct usb_audio_cluster *)
> - &iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins];
> - return (r.bNrChannels);
> - case UDESCSUB_AC_EXTENSION:
> - r = *(struct usb_audio_cluster *)
> - &iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins];
> - return (r.bNrChannels);
> - default:
> - goto bad;
> - }
> - }
> -bad:
> - printf("%s: bad data\n", __func__);
> - return (0);
> + return names[attr & 3];
>  }
>  
> +/*
> + * Return an unique name for the given template.
> + */
>  void
> -uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
> +uaudio_mkname(struct uaudio_softc *sc, char *templ, char *res)
>  {
> -#ifdef UAUDIO_DEBUG
> - const struct usb_audio_input_terminal *d = iot[id].d.it;
> + struct uaudio_name *n;
> + char *sep;
>  
> - DPRINTFN(2,("%s: bTerminalId=%d wTerminalType=0x%04x "
> -    "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d "
> -    "iChannelNames=%d iTerminal=%d\n",
> -    __func__,
> -    d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
> -    d->bNrChannels, UGETW(d->wChannelConfig),
> -    d->iChannelNames, d->iTerminal));
> -#endif
> + /*
> + * if this is not a terminal name (i.e. there's a underscore
> + * in the name, like in "spkr2_mic3"), then use underscore as
> + * separator to avoid concatenating two numbers
> + */
> + sep = strchr(templ, '_') != NULL ? "_" : "";
> +
> + n = sc->names;
> + while (1) {
> + if (n == NULL) {
> + n = malloc(sizeof(struct uaudio_name),
> +    M_DEVBUF, M_WAITOK);
> + n->templ = templ;
> + n->unit = 0;
> + n->next = sc->names;
> + sc->names = n;
> + }
> + if (strcmp(n->templ, templ) == 0)
> + break;
> + n = n->next;
> + }
> + if (n->unit == 0)
> + snprintf(res, UAUDIO_NAMEMAX, "%s", templ);
> + else
> + snprintf(res, UAUDIO_NAMEMAX, "%s%s%u", templ, sep, n->unit);
> + n->unit++;
>  }
>  
> -void
> -uaudio_add_output(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
> +/*
> + * Convert UAC v1.0 feature bitmap to UAC v2.0 feature bitmap.
> + */
> +unsigned int
> +uaudio_feature_fixup(struct uaudio_softc *sc, unsigned int ctl)
>  {
> -#ifdef UAUDIO_DEBUG
> - const struct usb_audio_output_terminal *d = iot[id].d.ot;
> + int i;
> + unsigned int bits, n;
>  
> - DPRINTFN(2,("%s: bTerminalId=%d wTerminalType=0x%04x "
> -    "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n",
> -    __func__,
> -    d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
> -    d->bSourceId, d->iTerminal));
> -#endif
> + switch (sc->version) {
> + case UAUDIO_V1:
> + n = 0;
> + for (i = 0; i < 16; i++) {
> + bits = (ctl >> i) & 1;
> + if (bits)
> + bits |= 2;
> + n |= bits << (2 * i);
> + }
> + return n;
> + case UAUDIO_V2:
> + break;
> + }
> + return ctl;
>  }
>  
> +/*
> + * Initialize a uaudio_ranges to the empty set
> + */
>  void
> -uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
> +uaudio_ranges_init(struct uaudio_ranges *r)
>  {
> - const struct usb_audio_mixer_unit *d = iot[id].d.mu;
> - struct usb_audio_mixer_unit_1 *d1;
> - int c, chs, ochs, i, o, bno, p, mo, mc, k;
> -#ifdef UAUDIO_DEBUG
> - int ichs = 0;
> -#endif
> - uByte *bm;
> - struct mixerctl mix;
> -
> - DPRINTFN(2,("%s: bUnitId=%d bNrInPins=%d\n", __func__,
> -    d->bUnitId, d->bNrInPins));
> + r->el = NULL;
> + r->nval = 0;
> +}
>  
> -#ifdef UAUDIO_DEBUG
> - /* Compute the number of input channels */
> - for (i = 0; i < d->bNrInPins; i++)
> - ichs += uaudio_get_cluster_nchan(d->baSourceId[i], iot);
> -#endif
> +/*
> + * Add the given range to the the uaudio_ranges structures. Ranges are
> + * not supposed to overlap (required by USB spec). If they do we just
> + * return.
> + */
> +void
> +uaudio_ranges_add(struct uaudio_ranges *r, int min, int max, int res)
> +{
> + struct uaudio_ranges_el *e, **pe;
>  
> - /* and the number of output channels */
> - d1 = (struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins];
> - ochs = d1->bNrChannels;
> - DPRINTFN(2,("%s: ichs=%d ochs=%d\n", __func__, ichs, ochs));
> + if (min > max) {
> + DPRINTF("uaudio_ranges_add: [%d:%d]/%d: bad range\n",
> +    min, max, res);
> + return;
> + }
>  
> - bm = d1->bmControls;
> - mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
> - uaudio_determine_class(&iot[id], &mix);
> - mix.type = MIX_SIGNED_16;
> - mix.ctlunit = AudioNvolume;
> -#define BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1)
> - for (p = i = 0; i < d->bNrInPins; i++) {
> - chs = uaudio_get_cluster_nchan(d->baSourceId[i], iot);
> - 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);
> - }
> - snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-i%d",
> -    d->bUnitId, d->baSourceId[i]);
> - mix.nchan = chs;
> - uaudio_mixer_add_ctl(sc, &mix);
> - } else {
> - /* XXX */
> + for (pe = &r->el; (e = *pe) != NULL; pe = &e->next) {
> + if (min <= e->max && max >= e->min) {
> + DPRINTF("uaudio_ranges_add: overlaping ranges\n");
> + return;
>   }
> -#undef BIT
> - p += chs;
> + if (min < e->max)
> + break;
>   }
>  
> + /* XXX: use 'res' here */
> + r->nval += max - min + 1;
> +
> + e = malloc(sizeof(struct uaudio_ranges_el), M_DEVBUF, M_WAITOK);
> + e->min = min;
> + e->max = max;
> + e->res = res;
> + e->next = *pe;
> + *pe = e;
>  }
>  
> +/*
> + * Free all ranges making the uaudio_ranges the empty set
> + */
>  void
> -uaudio_add_selector(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
> -{
> - const struct usb_audio_selector_unit *d = iot[id].d.su;
> - struct mixerctl mix;
> - int i, wp;
> -
> - DPRINTFN(2,("%s: bUnitId=%d bNrInPins=%d\n", __func__,
> -    d->bUnitId, d->bNrInPins));
> - mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
> - mix.wValue[0] = MAKE(0, 0);
> - uaudio_determine_class(&iot[id], &mix);
> - mix.nchan = 1;
> - mix.type = MIX_SELECTOR;
> - mix.ctlunit = "";
> - mix.minval = 1;
> - mix.maxval = d->bNrInPins;
> - wp = snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId);
> - for (i = 1; i <= d->bNrInPins; i++) {
> - wp += snprintf(mix.ctlname + wp, MAX_AUDIO_DEV_LEN - wp,
> -       "i%d", d->baSourceId[i - 1]);
> - if (wp > MAX_AUDIO_DEV_LEN - 1)
> - break;
> - }
> - uaudio_mixer_add_ctl(sc, &mix);
> -}
> -
> -#ifdef UAUDIO_DEBUG
> -const char *
> -uaudio_get_terminal_name(int terminal_type)
> -{
> - static char buf[100];
> -
> - switch (terminal_type) {
> - /* USB terminal types */
> - case UAT_UNDEFINED: return "UAT_UNDEFINED";
> - case UAT_STREAM: return "UAT_STREAM";
> - case UAT_VENDOR: return "UAT_VENDOR";
> - /* input terminal types */
> - case UATI_UNDEFINED: return "UATI_UNDEFINED";
> - case UATI_MICROPHONE: return "UATI_MICROPHONE";
> - case UATI_DESKMICROPHONE: return "UATI_DESKMICROPHONE";
> - case UATI_PERSONALMICROPHONE: return "UATI_PERSONALMICROPHONE";
> - case UATI_OMNIMICROPHONE: return "UATI_OMNIMICROPHONE";
> - case UATI_MICROPHONEARRAY: return "UATI_MICROPHONEARRAY";
> - case UATI_PROCMICROPHONEARR: return "UATI_PROCMICROPHONEARR";
> - /* output terminal types */
> - case UATO_UNDEFINED: return "UATO_UNDEFINED";
> - case UATO_SPEAKER: return "UATO_SPEAKER";
> - case UATO_HEADPHONES: return "UATO_HEADPHONES";
> - case UATO_DISPLAYAUDIO: return "UATO_DISPLAYAUDIO";
> - case UATO_DESKTOPSPEAKER: return "UATO_DESKTOPSPEAKER";
> - case UATO_ROOMSPEAKER: return "UATO_ROOMSPEAKER";
> - case UATO_COMMSPEAKER: return "UATO_COMMSPEAKER";
> - case UATO_SUBWOOFER: return "UATO_SUBWOOFER";
> - /* bidir terminal types */
> - case UATB_UNDEFINED: return "UATB_UNDEFINED";
> - case UATB_HANDSET: return "UATB_HANDSET";
> - case UATB_HEADSET: return "UATB_HEADSET";
> - case UATB_SPEAKERPHONE: return "UATB_SPEAKERPHONE";
> - case UATB_SPEAKERPHONEESUP: return "UATB_SPEAKERPHONEESUP";
> - case UATB_SPEAKERPHONEECANC: return "UATB_SPEAKERPHONEECANC";
> - /* telephony terminal types */
> - case UATT_UNDEFINED: return "UATT_UNDEFINED";
> - case UATT_PHONELINE: return "UATT_PHONELINE";
> - case UATT_TELEPHONE: return "UATT_TELEPHONE";
> - case UATT_DOWNLINEPHONE: return "UATT_DOWNLINEPHONE";
> - /* external terminal types */
> - case UATE_UNDEFINED: return "UATE_UNDEFINED";
> - case UATE_ANALOGCONN: return "UATE_ANALOGCONN";
> - case UATE_LINECONN: return "UATE_LINECONN";
> - case UATE_LEGACYCONN: return "UATE_LEGACYCONN";
> - case UATE_DIGITALAUIFC: return "UATE_DIGITALAUIFC";
> - case UATE_SPDIF: return "UATE_SPDIF";
> - case UATE_1394DA: return "UATE_1394DA";
> - case UATE_1394DV: return "UATE_1394DV";
> - /* embedded function terminal types */
> - case UATF_UNDEFINED: return "UATF_UNDEFINED";
> - case UATF_CALIBNOISE: return "UATF_CALIBNOISE";
> - case UATF_EQUNOISE: return "UATF_EQUNOISE";
> - case UATF_CDPLAYER: return "UATF_CDPLAYER";
> - case UATF_DAT: return "UATF_DAT";
> - case UATF_DCC: return "UATF_DCC";
> - case UATF_MINIDISK: return "UATF_MINIDISK";
> - case UATF_ANALOGTAPE: return "UATF_ANALOGTAPE";
> - case UATF_PHONOGRAPH: return "UATF_PHONOGRAPH";
> - case UATF_VCRAUDIO: return "UATF_VCRAUDIO";
> - case UATF_VIDEODISCAUDIO: return "UATF_VIDEODISCAUDIO";
> - case UATF_DVDAUDIO: return "UATF_DVDAUDIO";
> - case UATF_TVTUNERAUDIO: return "UATF_TVTUNERAUDIO";
> - case UATF_SATELLITE: return "UATF_SATELLITE";
> - case UATF_CABLETUNER: return "UATF_CABLETUNER";
> - case UATF_DSS: return "UATF_DSS";
> - case UATF_RADIORECV: return "UATF_RADIORECV";
> - case UATF_RADIOXMIT: return "UATF_RADIOXMIT";
> - case UATF_MULTITRACK: return "UATF_MULTITRACK";
> - case UATF_SYNTHESIZER: return "UATF_SYNTHESIZER";
> - default:
> - snprintf(buf, sizeof(buf), "unknown type (0x%.4x)", terminal_type);
> - return buf;
> +uaudio_ranges_clear(struct uaudio_ranges *r)
> +{
> + struct uaudio_ranges_el *e;
> +
> + while ((e = r->el) != NULL) {
> + r->el = e->next;
> + free(e, M_DEVBUF, sizeof(struct uaudio_ranges_el));
>   }
> + r->nval = 0;
>  }
> -#endif
>  
> +/*
> + * Convert a value in the given uaudio_ranges, into a 0..255 integer
> + * suitable for mixer usage
> + */
>  int
> -uaudio_determine_class(const struct io_terminal *iot, struct mixerctl *mix)
> +uaudio_ranges_decode(struct uaudio_ranges *r, int val)
>  {
> - int terminal_type;
> + struct uaudio_ranges_el *e;
> + int diff, pos;
>  
> - if (iot == NULL || iot->output == NULL) {
> - mix->class = UAC_OUTPUT;
> - return 0;
> - }
> - terminal_type = 0;
> - if (iot->output->size == 1)
> - terminal_type = iot->output->terminals[0];
> - /*
> - * If the only output terminal is USB,
> - * the class is UAC_RECORD.
> - */
> - if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
> - mix->class = UAC_RECORD;
> - if (iot->inputs_size == 1
> -    && iot->inputs[0] != NULL
> -    && iot->inputs[0]->size == 1)
> - return iot->inputs[0]->terminals[0];
> - else
> - return 0;
> - }
> - /*
> - * If the ultimate destination of the unit is just one output
> - * terminal and the unit is connected to the output terminal
> - * directly, the class is UAC_OUTPUT.
> - */
> - if (terminal_type != 0 && iot->direct) {
> - mix->class = UAC_OUTPUT;
> - return terminal_type;
> - }
> - /*
> - * If the unit is connected to just one input terminal,
> - * the class is UAC_INPUT.
> - */
> - if (iot->inputs_size == 1 && iot->inputs[0] != NULL
> -    && iot->inputs[0]->size == 1) {
> - mix->class = UAC_INPUT;
> - return iot->inputs[0]->terminals[0];
> - }
> - /*
> - * Otherwise, the class is UAC_OUTPUT.
> - */
> - mix->class = UAC_OUTPUT;
> - return terminal_type;
> -}
> -
> -const char *
> -uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix)
> -{
> - int terminal_type;
> -
> - terminal_type = uaudio_determine_class(iot, mix);
> - if (mix->class == UAC_RECORD && terminal_type == 0)
> - return AudioNmixerout;
> - DPRINTF(("%s: terminal_type=%s\n", __func__,
> - uaudio_get_terminal_name(terminal_type)));
> - switch (terminal_type) {
> - case UAT_STREAM:
> - return AudioNdac;
> -
> - case UATI_MICROPHONE:
> - case UATI_DESKMICROPHONE:
> - case UATI_PERSONALMICROPHONE:
> - case UATI_OMNIMICROPHONE:
> - case UATI_MICROPHONEARRAY:
> - case UATI_PROCMICROPHONEARR:
> - return AudioNmicrophone;
> -
> - case UATO_SPEAKER:
> - case UATO_DESKTOPSPEAKER:
> - case UATO_ROOMSPEAKER:
> - case UATO_COMMSPEAKER:
> - return AudioNspeaker;
> -
> - case UATO_HEADPHONES:
> - return AudioNheadphone;
> -
> - case UATO_SUBWOOFER:
> - return AudioNlfe;
> -
> - /* telephony terminal types */
> - case UATT_UNDEFINED:
> - case UATT_PHONELINE:
> - case UATT_TELEPHONE:
> - case UATT_DOWNLINEPHONE:
> - return "phone";
> -
> - case UATE_ANALOGCONN:
> - case UATE_LINECONN:
> - case UATE_LEGACYCONN:
> - return AudioNline;
> -
> - case UATE_DIGITALAUIFC:
> - case UATE_SPDIF:
> - case UATE_1394DA:
> - case UATE_1394DV:
> - return AudioNaux;
> -
> - case UATF_CDPLAYER:
> - return AudioNcd;
> -
> - case UATF_SYNTHESIZER:
> - return AudioNfmsynth;
> -
> - case UATF_VIDEODISCAUDIO:
> - case UATF_DVDAUDIO:
> - case UATF_TVTUNERAUDIO:
> - return AudioNvideo;
> -
> - case UAT_UNDEFINED:
> - case UAT_VENDOR:
> - case UATI_UNDEFINED:
> -/* output terminal types */
> - case UATO_UNDEFINED:
> - case UATO_DISPLAYAUDIO:
> -/* bidir terminal types */
> - case UATB_UNDEFINED:
> - case UATB_HANDSET:
> - case UATB_HEADSET:
> - case UATB_SPEAKERPHONE:
> - case UATB_SPEAKERPHONEESUP:
> - case UATB_SPEAKERPHONEECANC:
> -/* external terminal types */
> - case UATE_UNDEFINED:
> -/* embedded function terminal types */
> - case UATF_UNDEFINED:
> - case UATF_CALIBNOISE:
> - case UATF_EQUNOISE:
> - case UATF_DAT:
> - case UATF_DCC:
> - case UATF_MINIDISK:
> - case UATF_ANALOGTAPE:
> - case UATF_PHONOGRAPH:
> - case UATF_VCRAUDIO:
> - case UATF_SATELLITE:
> - case UATF_CABLETUNER:
> - case UATF_DSS:
> - case UATF_RADIORECV:
> - case UATF_RADIOXMIT:
> - case UATF_MULTITRACK:
> - case 0xffff:
> - default:
> - DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type));
> - return AudioNmaster;
> + pos = 0;
> +
> + for (e = r->el; e != NULL; e = e->next) {
> + if (val >= e->min && val <= e->max) {
> + pos += val - e->min;
> + return (r->nval == 1) ? 0 :
> +    (pos * 255 + (r->nval - 1) / 2) / (r->nval - 1);
> + }
> + diff = e->max - e->min + 1;
> + pos += diff;
>   }
> + return 0;
>  }
>  
> -void
> -uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
> +/*
> + * Convert a 0..255 to a value in the uaudio_ranges suitable for a USB
> + * request.
> + */
> +unsigned int
> +uaudio_ranges_encode(struct uaudio_ranges *r, int val)
>  {
> - const struct usb_audio_feature_unit *d = iot[id].d.fu;
> - uByte *ctls = (uByte *)d->bmaControls;
> - int ctlsize = d->bControlSize;
> - u_int fumask, mmask, cmask;
> - struct mixerctl mix;
> - int chan, ctl, i, nchan, unit;
> - const char *mixername;
> + struct uaudio_ranges_el *e;
> + int diff, pos;
>  
> -#define GET(i) (ctls[(i)*ctlsize] | \
> - (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0))
> + pos = (val * (r->nval - 1) + 127) / 255;
>  
> - if (ctlsize == 0) {
> - DPRINTF(("ignoring feature %d: bControlSize == 0\n", id));
> - return;
> + for (e = r->el; e != NULL; e = e->next) {
> + diff = e->max - e->min + 1;
> + if (pos < diff)
> + return e->min + pos;
> + pos -= diff;
>   }
> - nchan = (d->bLength - 7) / ctlsize;
> - mmask = GET(0);
> - /* Figure out what we can control */
> - for (cmask = 0, chan = 1; chan < nchan; chan++) {
> - DPRINTFN(9,("%s: chan=%d mask=%x\n",
> -    __func__, chan, GET(chan)));
> - cmask |= GET(chan);
> - }
> -
> - DPRINTFN(1,("%s: bUnitId=%d, "
> -    "%d channels, mmask=0x%04x, cmask=0x%04x\n",
> -    __func__, d->bUnitId, nchan, mmask, cmask));
> -
> - if (nchan > MIX_MAX_CHAN)
> - nchan = MIX_MAX_CHAN;
> - unit = d->bUnitId;
> - mix.wIndex = MAKE(unit, sc->sc_ac_iface);
> - for (ctl = MUTE_CONTROL; ctl < LOUDNESS_CONTROL; ctl++) {
> - fumask = FU_MASK(ctl);
> - DPRINTFN(4,("%s: ctl=%d fumask=0x%04x\n",
> -    __func__, 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
> - mixername = uaudio_feature_name(&iot[id], &mix);
> - switch (ctl) {
> - case MUTE_CONTROL:
> - mix.type = MIX_ON_OFF;
> - mix.ctlunit = "";
> - snprintf(mix.ctlname, sizeof(mix.ctlname),
> - "%s.%s", mixername, AudioNmute);
> - break;
> - case VOLUME_CONTROL:
> - mix.type = MIX_SIGNED_16;
> - mix.ctlunit = AudioNvolume;
> - strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname));
> - break;
> - case BASS_CONTROL:
> - mix.type = MIX_SIGNED_8;
> - mix.ctlunit = AudioNbass;
> - snprintf(mix.ctlname, sizeof(mix.ctlname),
> - "%s.%s", mixername, AudioNbass);
> - break;
> - case MID_CONTROL:
> - mix.type = MIX_SIGNED_8;
> - mix.ctlunit = AudioNmid;
> - snprintf(mix.ctlname, sizeof(mix.ctlname),
> - "%s.%s", mixername, AudioNmid);
> - break;
> - case TREBLE_CONTROL:
> - mix.type = MIX_SIGNED_8;
> - mix.ctlunit = AudioNtreble;
> - snprintf(mix.ctlname, sizeof(mix.ctlname),
> - "%s.%s", mixername, AudioNtreble);
> - break;
> - case GRAPHIC_EQUALIZER_CONTROL:
> - continue; /* XXX don't add anything */
> - break;
> - case AGC_CONTROL:
> - mix.type = MIX_ON_OFF;
> - mix.ctlunit = "";
> - snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s",
> - mixername, AudioNagc);
> - break;
> - case DELAY_CONTROL:
> - mix.type = MIX_UNSIGNED_16;
> - mix.ctlunit = "4 ms";
> - snprintf(mix.ctlname, sizeof(mix.ctlname),
> - "%s.%s", mixername, AudioNdelay);
> - break;
> - case BASS_BOOST_CONTROL:
> - mix.type = MIX_ON_OFF;
> - mix.ctlunit = "";
> - snprintf(mix.ctlname, sizeof(mix.ctlname),
> - "%s.%s", mixername, AudioNbassboost);
> - break;
> - case LOUDNESS_CONTROL:
> - mix.type = MIX_ON_OFF;
> - mix.ctlunit = "";
> - snprintf(mix.ctlname, sizeof(mix.ctlname),
> - "%s.%s", mixername, AudioNloudness);
> - break;
> - }
> - uaudio_mixer_add_ctl(sc, &mix);
> - }
> -}
> -
> -void
> -uaudio_add_processing_updown(struct uaudio_softc *sc,
> -     const struct io_terminal *iot, int id)
> -{
> - const struct usb_audio_processing_unit *d = iot[id].d.pu;
> - const struct usb_audio_processing_unit_1 *d1 =
> -    (const struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins];
> - const struct usb_audio_processing_unit_updown *ud =
> -    (const struct usb_audio_processing_unit_updown *)
> - &d1->bmControls[d1->bControlSize];
> - struct mixerctl mix;
> - int i;
> -
> - DPRINTFN(2,("%s: bUnitId=%d bNrModes=%d\n",
> -    __func__, d->bUnitId, ud->bNrModes));
> + return 0;
> +}
>  
> - if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) {
> - DPRINTF(("%s: no mode select\n", __func__));
> - return;
> +/*
> + * Return the bitmap of supported rates included in the given ranges.
> + * This is not a mixer thing, UAC v2.0 uses ranges to report sample
> + * rates.
> + */
> +int
> +uaudio_ranges_getrates(struct uaudio_ranges *r,
> +    unsigned int mult, unsigned int div)
> +{
> + struct uaudio_ranges_el *e;
> + int rates, i, v;
> +
> + rates = 0;
> +
> + for (e = r->el; e != NULL; e = e->next) {
> + for (i = 0; i < UAUDIO_NRATES; i++) {
> + v = (unsigned long long)uaudio_rates[i] * mult / div;
> + if (v < e->min || v > e->max)
> + continue;
> + if (e->res == 0 || v - e->min % e->res == 0)
> + rates |= 1 << i;
> + }
>   }
>  
> - mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
> - mix.nchan = 1;
> - mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0);
> - uaudio_determine_class(&iot[id], &mix);
> - mix.type = MIX_ON_OFF; /* XXX */
> - mix.ctlunit = "";
> - snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d-mode", d->bUnitId);
> -
> - for (i = 0; i < ud->bNrModes; i++) {
> - DPRINTFN(2,("%s: i=%d bm=0x%x\n",
> -    __func__, i, UGETW(ud->waModes[i])));
> - /* XXX */
> - }
> - uaudio_mixer_add_ctl(sc, &mix);
> + return rates;
>  }
>  
> -void
> -uaudio_add_processing(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
> -{
> - const struct usb_audio_processing_unit *d = iot[id].d.pu;
> - const struct usb_audio_processing_unit_1 *d1 =
> -    (const struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins];
> - int ptype = UGETW(d->wProcessType);
> - struct mixerctl mix;
> -
> - DPRINTFN(2,("%s: wProcessType=%d bUnitId=%d "
> -    "bNrInPins=%d\n", __func__, 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);
> - uaudio_determine_class(&iot[id], &mix);
> - mix.type = MIX_ON_OFF;
> - mix.ctlunit = "";
> - snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d.%d-enable",
> -    d->bUnitId, ptype);
> - uaudio_mixer_add_ctl(sc, &mix);
> - }
> -
> - switch(ptype) {
> - case UPDOWNMIX_PROCESS:
> - uaudio_add_processing_updown(sc, iot, id);
> - break;
> - case DOLBY_PROLOGIC_PROCESS:
> - case P3D_STEREO_EXTENDER_PROCESS:
> - case REVERBATION_PROCESS:
> - case CHORUS_PROCESS:
> - case DYN_RANGE_COMP_PROCESS:
> - default:
> - DPRINTF(("%s: unit %d, type=%d not impl.\n",
> -       __func__, d->bUnitId, ptype));
> - break;
> +/*
> + * Return the index in the uaudio_rates[] array of rate closest to the
> + * given rate in Hz.
> + */
> +int
> +uaudio_rates_indexof(int mask, int rate)
> +{
> + int i, diff, best_index, best_diff;
> +
> + best_index = -1;
> + best_diff = INT_MAX;
> + for (i = 0; i < UAUDIO_NRATES; i++) {
> + if ((mask & (1 << i)) == 0)
> + continue;
> + diff = uaudio_rates[i] - rate;
> + if (diff < 0)
> + diff = -diff;
> + if (diff < best_diff) {
> + best_index = i;
> + best_diff = diff;
> + }
>   }
> + return best_index;
>  }
>  
> -void
> -uaudio_add_extension(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
> -{
> - const struct usb_audio_extension_unit *d = iot[id].d.eu;
> - const struct usb_audio_extension_unit_1 *d1 =
> -    (const struct usb_audio_extension_unit_1 *)&d->baSourceId[d->bNrInPins];
> - struct mixerctl mix;
> +/*
> + * Do a request that results in a uaudio_ranges. On UAC v1.0, this is
> + * simply a min/max/res triplet. On UAC v2.0, this is an array of
> + * min/max/res triplets.
> + */
> +int
> +uaudio_req_ranges(struct uaudio_softc *sc,
> +    unsigned int opsize,
> +    unsigned int sel,
> +    unsigned int chan,
> +    unsigned int ifnum,
> +    unsigned int id,
> +    struct uaudio_ranges *r)
> +{
> + unsigned char req_buf[16], *req = NULL;
> + size_t req_size;
> + struct uaudio_blob p;
> + unsigned int count, min, max, res;
> + int i;
>  
> - DPRINTFN(2,("%s: bUnitId=%d bNrInPins=%d\n",
> -    __func__, d->bUnitId, d->bNrInPins));
> + switch (sc->version) {
> + case UAUDIO_V1:
> + count = 1;
> + req = req_buf;
> + p.rptr = p.wptr = req;
> + if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
> + UAUDIO_V1_REQ_GET_MIN, sel, chan,
> + ifnum, id, p.wptr, opsize))
> + return 0;
> + p.wptr += opsize;
> + if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
> + UAUDIO_V1_REQ_GET_MAX, sel, chan,
> + ifnum, id, p.wptr, opsize))
> + return 0;
> + p.wptr += opsize;
> + if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
> + UAUDIO_V1_REQ_GET_RES, sel, chan,
> + ifnum, id, p.wptr, opsize))
> + return 0;
> + p.wptr += opsize;
> + break;
> + case UAUDIO_V2:
> + /* fetch the ranges count only (first 2 bytes) */
> + if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
> + UAUDIO_V2_REQ_RANGES, sel, chan,
> + ifnum, id, req_buf, 2))
> + return 0;
> + count = req_buf[0] | req_buf[1] << 8;
>  
> - if (sc->sc_quirks & UAUDIO_FLAG_NO_XU)
> - return;
> + /* restart the request on a large enough buffer */
> + req_size = 2 + 3 * opsize * count;
> + if (sizeof(req_buf) >= req_size)
> + req = req_buf;
> + else
> + req = malloc(req_size, M_DEVBUF, M_WAITOK);
>  
> - 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);
> - uaudio_determine_class(&iot[id], &mix);
> - mix.type = MIX_ON_OFF;
> - mix.ctlunit = "";
> - snprintf(mix.ctlname, sizeof(mix.ctlname), "ext%d-enable",
> -    d->bUnitId);
> - uaudio_mixer_add_ctl(sc, &mix);
> + p.rptr = p.wptr = req;
> + if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
> + UAUDIO_V2_REQ_RANGES, sel, chan,
> + ifnum, id, p.wptr, req_size))
> + return 0;
> + p.wptr += req_size;
> +
> + /* skip initial 2 bytes of count */
> + p.rptr += 2;
> + break;
> + }
> +
> + for (i = 0; i < count; i++) {
> + if (!uaudio_getnum(&p, opsize, &min))
> + return 0;
> + if (!uaudio_getnum(&p, opsize, &max))
> + return 0;
> + if (!uaudio_getnum(&p, opsize, &res))
> + return 0;
> + uaudio_ranges_add(r,
> +    uaudio_sign_expand(min, opsize),
> +    uaudio_sign_expand(max, opsize),
> +    uaudio_sign_expand(res, opsize));
>   }
> +
> + if (req != req_buf)
> + free(req, M_DEVBUF, req_size);
> +
> + return 1;
>  }
>  
> -struct terminal_list*
> -uaudio_merge_terminal_list(const struct io_terminal *iot)
> -{
> - struct terminal_list *tml;
> - uint16_t *ptm;
> - int i, len;
> -
> - len = 0;
> - if (iot->inputs == NULL)
> - return NULL;
> - for (i = 0; i < iot->inputs_size; i++) {
> - if (iot->inputs[i] != NULL)
> - len += iot->inputs[i]->size;
> - }
> - tml = malloc(TERMINAL_LIST_SIZE(len), M_TEMP, M_NOWAIT);
> - if (tml == NULL) {
> - printf("%s: no memory\n", __func__);
> - return NULL;
> - }
> - tml->size = 0;
> - ptm = tml->terminals;
> - for (i = 0; i < iot->inputs_size; i++) {
> - if (iot->inputs[i] == NULL)
> - continue;
> - if (iot->inputs[i]->size > len)
> - break;
> - memcpy(ptm, iot->inputs[i]->terminals,
> -       iot->inputs[i]->size * sizeof(uint16_t));
> - tml->size += iot->inputs[i]->size;
> - ptm += iot->inputs[i]->size;
> - len -= iot->inputs[i]->size;
> - }
> - return tml;
> -}
> -
> -struct terminal_list *
> -uaudio_io_terminaltype(int outtype, struct io_terminal *iot, int id)
> -{
> - struct terminal_list *tml;
> - struct io_terminal *it;
> - int src_id, i;
> -
> - it = &iot[id];
> - if (it->output != NULL) {
> - /* already has outtype? */
> - for (i = 0; i < it->output->size; i++)
> - if (it->output->terminals[i] == outtype)
> - return uaudio_merge_terminal_list(it);
> - tml = malloc(TERMINAL_LIST_SIZE(it->output->size + 1),
> -    M_TEMP, M_NOWAIT);
> - if (tml == NULL) {
> - printf("%s: no memory\n", __func__);
> - return uaudio_merge_terminal_list(it);
> - }
> - memcpy(tml, it->output, TERMINAL_LIST_SIZE(it->output->size));
> - tml->terminals[it->output->size] = outtype;
> - tml->size++;
> - free(it->output, M_TEMP, 0);
> - it->output = tml;
> - if (it->inputs != NULL) {
> - for (i = 0; i < it->inputs_size; i++)
> - free(it->inputs[i], M_TEMP, 0);
> - free(it->inputs, M_TEMP, 0);
> - }
> - it->inputs_size = 0;
> - it->inputs = NULL;
> - } else { /* end `iot[id] != NULL' */
> - it->inputs_size = 0;
> - it->inputs = NULL;
> - it->output = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
> - if (it->output == NULL) {
> - printf("%s: no memory\n", __func__);
> - return NULL;
> - }
> - it->output->terminals[0] = outtype;
> - it->output->size = 1;
> - it->direct = 0;
> - }
> -
> - switch (it->d.desc->bDescriptorSubtype) {
> - case UDESCSUB_AC_INPUT:
> - it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
> - if (it->inputs == NULL) {
> - printf("%s: no memory\n", __func__);
> - return NULL;
> - }
> - tml = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
> - if (tml == NULL) {
> - printf("%s: no memory\n", __func__);
> - free(it->inputs, M_TEMP, 0);
> - it->inputs = NULL;
> - return NULL;
> - }
> - it->inputs[0] = tml;
> - tml->terminals[0] = UGETW(it->d.it->wTerminalType);
> - tml->size = 1;
> - it->inputs_size = 1;
> - return uaudio_merge_terminal_list(it);
> - case UDESCSUB_AC_FEATURE:
> - src_id = it->d.fu->bSourceId;
> - it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
> - if (it->inputs == NULL) {
> - printf("%s: no memory\n", __func__);
> - return uaudio_io_terminaltype(outtype, iot, src_id);
> - }
> - it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id);
> - it->inputs_size = 1;
> - return uaudio_merge_terminal_list(it);
> - case UDESCSUB_AC_OUTPUT:
> - it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
> - if (it->inputs == NULL) {
> - printf("%s: no memory\n", __func__);
> - return NULL;
> - }
> - src_id = it->d.ot->bSourceId;
> - it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id);
> - it->inputs_size = 1;
> - iot[src_id].direct = 1;
> - return NULL;
> - case UDESCSUB_AC_MIXER:
> - it->inputs_size = 0;
> - it->inputs = mallocarray(it->d.mu->bNrInPins,
> -    sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
> - if (it->inputs == NULL) {
> - printf("%s: no memory\n", __func__);
> - return NULL;
> - }
> - for (i = 0; i < it->d.mu->bNrInPins; i++) {
> - src_id = it->d.mu->baSourceId[i];
> - it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
> -       src_id);
> - it->inputs_size++;
> - }
> - return uaudio_merge_terminal_list(it);
> - case UDESCSUB_AC_SELECTOR:
> - it->inputs_size = 0;
> - it->inputs = mallocarray(it->d.su->bNrInPins,
> -    sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
> - if (it->inputs == NULL) {
> - printf("%s: no memory\n", __func__);
> - return NULL;
> - }
> - for (i = 0; i < it->d.su->bNrInPins; i++) {
> - src_id = it->d.su->baSourceId[i];
> - it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
> -       src_id);
> - it->inputs_size++;
> - }
> - return uaudio_merge_terminal_list(it);
> - case UDESCSUB_AC_PROCESSING:
> - it->inputs_size = 0;
> - it->inputs = mallocarray(it->d.pu->bNrInPins,
> -    sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
> - if (it->inputs == NULL) {
> - printf("%s: no memory\n", __func__);
> - return NULL;
> - }
> - for (i = 0; i < it->d.pu->bNrInPins; i++) {
> - src_id = it->d.pu->baSourceId[i];
> - it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
> -       src_id);
> - it->inputs_size++;
> - }
> - return uaudio_merge_terminal_list(it);
> - case UDESCSUB_AC_EXTENSION:
> - it->inputs_size = 0;
> - it->inputs = mallocarray(it->d.eu->bNrInPins,
> -    sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
> - if (it->inputs == NULL) {
> - printf("%s: no memory\n", __func__);
> - return NULL;
> - }
> - for (i = 0; i < it->d.eu->bNrInPins; i++) {
> - src_id = it->d.eu->baSourceId[i];
> - it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
> -       src_id);
> - it->inputs_size++;
> +/*
> + * Return the rates bitmap of the given interface alt setting
> + */
> +int
> +uaudio_alt_getrates(struct uaudio_softc *sc, struct uaudio_alt *p)
> +{
> + struct uaudio_unit *u;
> + unsigned int mult = 1, div = 1;
> +
> + switch (sc->version) {
> + case UAUDIO_V1:
> + return p->v1_rates;
> + case UAUDIO_V2:
> + u = sc->clock;
> + while (1) {
> + switch (u->type) {
> + case UAUDIO_AC_CLKSRC:
> + return uaudio_ranges_getrates(&u->rates,
> +    mult, div);
> + case UAUDIO_AC_CLKSEL:
> + u = u->clock;
> + break;
> + case UAUDIO_AC_CLKMULT:
> + case UAUDIO_AC_RATECONV:
> + /* XXX: adjust rate with multiplier */
> + u = u->src_list;
> + break;
> + default:
> + DPRINTF("uaudio_alt_getrates: no clock\n");
> + return 0;
> + }
>   }
> - return uaudio_merge_terminal_list(it);
> - case UDESCSUB_AC_HEADER:
> - default:
> - return NULL;
>   }
> + return 0;
>  }
>  
> -usbd_status
> -uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
> +/*
> + * Return the rates bitmap of the given parameters setting
> + */
> +int
> +uaudio_getrates(struct uaudio_softc *sc, struct uaudio_params *p)
>  {
> - usbd_status err;
> -
> - err = uaudio_identify_ac(sc, cdesc);
> - if (err)
> - return (err);
> - return (uaudio_identify_as(sc, cdesc));
> + return uaudio_alt_getrates(sc, p->palt ? p->palt : p->ralt);
>  }
>  
> +/*
> + * Add the given feature (aka mixer control) to the given unit.
> + */
>  void
> -uaudio_add_alt(struct uaudio_softc *sc, const struct as_info *ai)
> +uaudio_feature_addent(struct uaudio_softc *sc,
> +    struct uaudio_unit *u, int uac_type, int chan)
>  {
> - struct as_info *nai;
> + static struct {
> + char *name;
> + int mix_type;
> + int req_sel;
> + } features[] = {
> + {"mute", UAUDIO_MIX_SW, UAUDIO_REQSEL_MUTE},
> + {"level", UAUDIO_MIX_NUM, UAUDIO_REQSEL_VOLUME},
> + {"bass", UAUDIO_MIX_NUM, UAUDIO_REQSEL_BASS},
> + {"mid", UAUDIO_MIX_NUM, UAUDIO_REQSEL_MID},
> + {"treble", UAUDIO_MIX_NUM, UAUDIO_REQSEL_TREBLE},
> + {"eq", UAUDIO_MIX_NUM, UAUDIO_REQSEL_EQ},
> + {"agc", UAUDIO_MIX_SW, UAUDIO_REQSEL_AGC},
> + {NULL, -1, -1}, /* delay */
> + {"bassboost", UAUDIO_MIX_SW, UAUDIO_REQSEL_BASSBOOST},
> + {"loud", UAUDIO_MIX_SW, UAUDIO_REQSEL_LOUDNESS},
> + {"gain", UAUDIO_MIX_NUM, UAUDIO_REQSEL_GAIN},
> + {"gainpad", UAUDIO_MIX_SW, UAUDIO_REQSEL_GAINPAD},
> + {"phase", UAUDIO_MIX_SW, UAUDIO_REQSEL_PHASEINV},
> + {NULL, -1, -1}, /* undeflow */
> + {NULL, -1, -1} /* overflow */
> + };
> + struct uaudio_mixent *m, *i, **pi;
> + int cmp;
>  
> - nai = mallocarray(sc->sc_nalts + 1, sizeof(*ai), M_USBDEV, M_NOWAIT);
> - if (nai == NULL) {
> - printf("%s: no memory\n", __func__);
> + if (uac_type >= sizeof(features) / sizeof(features[0])) {
> + printf("%s: skipped unknown feature\n", DEVNAME(sc));
>   return;
>   }
>  
> - /* Copy old data, if there was any */
> - if (sc->sc_nalts != 0) {
> - memcpy(nai, sc->sc_alts, sizeof(*ai) * (sc->sc_nalts));
> - free(sc->sc_alts, M_USBDEV, sc->sc_nalts * sizeof(*ai));
> - }
> - sc->sc_alts = nai;
> - DPRINTFN(2,("%s: adding alt=%d, enc=%d\n",
> -    __func__, ai->alt, ai->encoding));
> - sc->sc_alts[sc->sc_nalts++] = *ai;
> -}
> -
> -usbd_status
> -uaudio_process_as(struct uaudio_softc *sc, const char *buf, int *offsp,
> -  int size, const usb_interface_descriptor_t *id)
> -#define offs (*offsp)
> -{
> - const struct usb_audio_streaming_interface_descriptor *asid;
> - const struct usb_audio_streaming_type1_descriptor *asf1d;
> - const struct usb_endpoint_descriptor_audio *ed;
> - const struct usb_endpoint_descriptor_audio *sync_ed;
> - const struct usb_audio_streaming_endpoint_descriptor *sed;
> - int format, chan, prec, enc, bps;
> - int dir, type, sync, sync_addr;
> - struct as_info ai;
> - const char *format_str;
> -
> - asid = (const void *)(buf + offs);
> - if (asid->bDescriptorType != UDESC_CS_INTERFACE ||
> -    asid->bDescriptorSubtype != AS_GENERAL)
> - return (USBD_INVAL);
> - DPRINTF(("%s: asid: bTerminalLink=%d wFormatTag=%d\n", __func__,
> - asid->bTerminalLink, UGETW(asid->wFormatTag)));
> - offs += asid->bLength;
> - if (offs > size)
> - return (USBD_INVAL);
> -
> - asf1d = (const 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",
> -       sc->sc_dev.dv_xname, UGETW(asid->wFormatTag));
> - return (USBD_NORMAL_COMPLETION);
> - }
> -
> - ed = (const void *)(buf + offs);
> - if (ed->bDescriptorType != UDESC_ENDPOINT)
> - return (USBD_INVAL);
> - DPRINTF(("%s: endpoint[0] bLength=%d bDescriptorType=%d "
> - "bEndpointAddress=%d bmAttributes=0x%x wMaxPacketSize=%d "
> - "bInterval=%d bRefresh=%d bSynchAddress=%d\n",
> - __func__,
> - 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);
> -
> - /* Check for sync endpoint. */
> - sync = 0;
> - sync_addr = 0;
> - if (id->bNumEndpoints > 1 &&
> -    ((dir == UE_DIR_IN && type == UE_ISO_ADAPT) ||
> -    (dir != UE_DIR_IN && type == UE_ISO_ASYNC)))
> - sync = 1;
> -
> - /* Check whether sync endpoint address is given. */
> - if (ed->bLength >= USB_ENDPOINT_DESCRIPTOR_AUDIO_SIZE) {
> - /* bSynchAdress set to 0 indicates sync is not used. */
> - if (ed->bSynchAddress == 0)
> - sync = 0;
> - else
> - sync_addr = ed->bSynchAddress;
> + m = malloc(sizeof(struct uaudio_mixent), M_DEVBUF, M_WAITOK);
> + m->chan = chan;
> + m->fname = features[uac_type].name;
> + m->type = features[uac_type].mix_type;
> + m->req_sel = features[uac_type].req_sel;
> + uaudio_ranges_init(&m->ranges);
> +
> + if (m->type == UAUDIO_MIX_NUM) {
> + if (!uaudio_req_ranges(sc, 2,
> + m->req_sel, chan < 0 ? 0 : chan + 1,
> + sc->ctl_ifnum, u->id,
> + &m->ranges)) {
> + DPRINTF("%s.%s[%d]: failed\n", u->name, m->fname, chan);
> + free(m, M_DEVBUF, sizeof(struct uaudio_mixent));
> + return;
> + }
> + if (m->ranges.el == NULL) {
> + printf("%s: skipped %s control with empty range\n",
> +    DEVNAME(sc), m->fname);
> + free(m, M_DEVBUF, sizeof(struct uaudio_mixent));
> + return;
> + }
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug)
> + uaudio_ranges_print(&m->ranges);
> +#endif
> + }
> +
> + /*
> + * Add to unit's mixer controls list, sorting entries by name
> + * and increasing channel number.
> + */
> + for (pi = &u->mixent_list; (i = *pi) != NULL; pi = &i->next) {
> + cmp = strcmp(i->fname, m->fname);
> + if (cmp == 0)
> + cmp = i->chan - m->chan;
> + if (cmp == 0) {
> + DPRINTF("%02u: %s.%s: duplicate feature for chan %d\n",
> +    u->id, u->name, m->fname, m->chan);
> + free(m, M_DEVBUF, sizeof(struct uaudio_mixent));
> + return;
> + }
> + if (cmp > 0)
> + break;
> + }
> + m->next = *pi;
> + *pi = m;
> +
> + DPRINTF("\t%s[%d]\n", m->fname, m->chan);
> +}
> +
> +/*
> + * For the given unit, parse the list of its sources and recursively
> + * call uaudio_process_unit() for each.
> + */
> +int
> +uaudio_process_srcs(struct uaudio_softc *sc,
> + struct uaudio_unit *u, struct uaudio_blob units,
> + struct uaudio_blob *p)
> +{
> + struct uaudio_unit *s, **ps;
> + unsigned int i, npin, sid;
> +
> + if (!uaudio_getnum(p, 1, &npin))
> + return 0;
> + ps = &u->src_list;
> + for (i = 0; i < npin; i++) {
> + if (!uaudio_getnum(p, 1, &sid))
> + return 0;
> + if (!uaudio_process_unit(sc, u, sid, units, &s))
> + return 0;
> + s->src_next = NULL;
> + *ps = s;
> + ps = &s->src_next;
>   }
> + return 1;
> +}
>  
> - sed = (const void *)(buf + offs);
> - if (sed->bDescriptorType != UDESC_CS_ENDPOINT ||
> -    sed->bDescriptorSubtype != AS_GENERAL)
> - return (USBD_INVAL);
> - DPRINTF((" streaming_endpoint: offset=%d bLength=%d\n", offs, sed->bLength));
> - offs += sed->bLength;
> - if (offs > size)
> - return (USBD_INVAL);
> -
> - sync_ed = NULL;
> - if (sync == 1) {
> - sync_ed = (const void*)(buf + offs);
> - if (sync_ed->bDescriptorType != UDESC_ENDPOINT) {
> - printf("%s: sync ep descriptor wrong type\n",
> -    sc->sc_dev.dv_xname);
> - return (USBD_NORMAL_COMPLETION);
> - }
> - DPRINTF(("%s: endpoint[1] bLength=%d "
> - "bDescriptorType=%d bEndpointAddress=%d "
> - "bmAttributes=0x%x wMaxPacketSize=%d bInterval=%d "
> - "bRefresh=%d bSynchAddress=%d\n",
> - __func__,
> - sync_ed->bLength, sync_ed->bDescriptorType,
> - sync_ed->bEndpointAddress, sync_ed->bmAttributes,
> - UGETW(sync_ed->wMaxPacketSize), sync_ed->bInterval,
> - sync_ed->bRefresh, sync_ed->bSynchAddress));
> - offs += sync_ed->bLength;
> - if (offs > size) {
> - printf("%s: sync ep descriptor too large\n",
> -    sc->sc_dev.dv_xname);
> - return (USBD_NORMAL_COMPLETION);
> - }
> - if (dir == UE_GET_DIR(sync_ed->bEndpointAddress)) {
> - printf("%s: sync ep wrong direction\n",
> -       sc->sc_dev.dv_xname);
> - return (USBD_NORMAL_COMPLETION);
> - }
> - if (UE_GET_XFERTYPE(sync_ed->bmAttributes) != UE_ISOCHRONOUS) {
> - printf("%s: sync ep wrong xfer type\n",
> -       sc->sc_dev.dv_xname);
> - return (USBD_NORMAL_COMPLETION);
> - }
> - if (sync_ed->bLength >=
> -    USB_ENDPOINT_DESCRIPTOR_AUDIO_SIZE &&
> -    sync_ed->bSynchAddress != 0) {
> - printf("%s: sync ep bSynchAddress != 0\n",
> -       sc->sc_dev.dv_xname);
> - return (USBD_NORMAL_COMPLETION);
> - }
> - if (sync_addr &&
> -    UE_GET_ADDR(sync_ed->bEndpointAddress) !=
> -    UE_GET_ADDR(sync_addr)) {
> - printf("%s: sync ep address mismatch\n",
> -       sc->sc_dev.dv_xname);
> - return (USBD_NORMAL_COMPLETION);
> - }
> - }
> - if (sync_ed != NULL && dir == UE_DIR_IN) {
> - printf("%s: sync pipe for recording not yet implemented\n",
> -    sc->sc_dev.dv_xname);
> - return (USBD_NORMAL_COMPLETION);
> - }
> -
> - format = UGETW(asid->wFormatTag);
> - chan = asf1d->bNrChannels;
> - prec = asf1d->bBitResolution;
> - bps = asf1d->bSubFrameSize;
> - if ((prec != 8 && prec != 16 && prec != 24) || (bps < 1 || bps > 4)) {
> - printf("%s: ignored setting with precision %d bps %d\n",
> -       sc->sc_dev.dv_xname, prec, bps);
> - return (USBD_NORMAL_COMPLETION);
> - }
> - switch (format) {
> - case UA_FMT_PCM:
> - if (prec == 8) {
> - sc->sc_altflags |= HAS_8;
> - } else if (prec == 16) {
> - sc->sc_altflags |= HAS_16;
> - } else if (prec == 24) {
> - sc->sc_altflags |= HAS_24;
> - }
> - enc = AUDIO_ENCODING_SLINEAR_LE;
> - format_str = "pcm";
> - break;
> - case UA_FMT_PCM8:
> - enc = AUDIO_ENCODING_ULINEAR_LE;
> - sc->sc_altflags |= HAS_8U;
> - format_str = "pcm8";
> - break;
> - case UA_FMT_ALAW:
> - enc = AUDIO_ENCODING_ALAW;
> - sc->sc_altflags |= HAS_ALAW;
> - format_str = "alaw";
> - break;
> - case UA_FMT_MULAW:
> - enc = AUDIO_ENCODING_ULAW;
> - sc->sc_altflags |= HAS_MULAW;
> - format_str = "mulaw";
> +/*
> + * Parse the number of channels.
> + */
> +int
> +uaudio_process_nch(struct uaudio_softc *sc,
> + struct uaudio_unit *u, struct uaudio_blob *p)
> +{
> + if (!uaudio_getnum(p, 1, &u->nch))
> + return 0;
> + /* skip junk */
> + switch (sc->version) {
> + case UAUDIO_V1:
> + if (!uaudio_getnum(p, 2, NULL)) /* bmChannelConfig */
> + return 0;
> + break;
> + case UAUDIO_V2:
> + if (!uaudio_getnum(p, 4, NULL)) /* wChannelConfig */
> + return 0;
>   break;
> - case UA_FMT_IEEE_FLOAT:
> - default:
> - printf("%s: ignored setting with format %d\n",
> -       sc->sc_dev.dv_xname, format);
> - return (USBD_NORMAL_COMPLETION);
>   }
> + if (!uaudio_getnum(p, 1, NULL)) /* iChannelNames */
> + return 0;
> + return 1;
> +}
> +
> +/*
> + * Find the AC class-specific descriptor for this unit id.
> + */
> +int
> +uaudio_unit_getdesc(struct uaudio_softc *sc, int id,
> + struct uaudio_blob units,
> + struct uaudio_blob *p,
> + unsigned int *rtype)
> +{
> + unsigned int i, type, subtype;
> +
> + /*
> + * Find the usb descriptor for this id.
> + */
> + while (1) {
> + if (units.rptr == units.wptr) {
> + DPRINTF("uaudio_unit_getdesc: %02u: not found\n", id);
> + return 0;
> + }
> + if (!uaudio_getdesc(&units, p))
> + return 0;
> + if (!uaudio_getnum(p, 1, &type))
> + return 0;
> + if (!uaudio_getnum(p, 1, &subtype))
> + return 0;
> + if (!uaudio_getnum(p, 1, &i))
> + return 0;
> + if (i == id)
> + break;
> + }
> + *rtype = subtype;
> + return 1;
> +}
> +
> +/*
> + * Parse a unit, possibly calling uaudio_process_unit() for each of
> + * its sources.
> + */
> +int
> +uaudio_process_unit(struct uaudio_softc *sc,
> + struct uaudio_unit *dest, int id,
> + struct uaudio_blob units,
> + struct uaudio_unit **rchild)
> +{
> + struct uaudio_blob p;
> + struct uaudio_unit *u, *s;
> + unsigned int i, j, term, size, attr, ctl, type, subtype, assoc, clk;
>  #ifdef UAUDIO_DEBUG
> - printf("%s: %s: %d-ch %d-bit %d-byte %s,", sc->sc_dev.dv_xname,
> -       dir == UE_DIR_IN ? "recording" : "playback",
> -       chan, prec, bps, format_str);
> - if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
> - printf(" %d-%dHz\n", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
> + unsigned int bit;
> +#endif
> +
> + if (!uaudio_unit_getdesc(sc, id, units, &p, &subtype))
> + return 0;
> +
> + /*
> + * find this unit on the list as it may be already processed as
> + * the source of another destination
> + */
> + u = uaudio_unit_byid(sc, id);
> + if (u == NULL) {
> + u = malloc(sizeof(struct uaudio_unit), M_DEVBUF, M_WAITOK);
> + u->id = id;
> + u->type = subtype;
> + u->src_list = NULL;
> + u->dst_list = NULL;
> + u->clock = NULL;
> + u->mixent_list = NULL;
> + u->nch = 0;
> + u->name[0] = 0;
> + uaudio_ranges_init(&u->rates);
> + u->unit_next = sc->unit_list;
> + sc->unit_list = u;
>   } else {
> - int r;
> - printf(" %d", UA_GETSAMP(asf1d, 0));
> - for (r = 1; r < asf1d->bSamFreqType; r++)
> - printf(",%d", UA_GETSAMP(asf1d, r));
> - printf("Hz\n");
> - }
> -#endif
> - ai.alt = id->bAlternateSetting;
> - ai.encoding = enc;
> - ai.attributes = sed->bmAttributes;
> - ai.idesc = id;
> - ai.edesc = ed;
> - ai.edesc1 = sync_ed;
> - ai.asf1desc = asf1d;
> - ai.sc_busy = 0;
> - if (sc->sc_nalts < UAUDIO_MAX_ALTS)
> - uaudio_add_alt(sc, &ai);
> -#ifdef UAUDIO_DEBUG
> - if (ai.attributes & UA_SED_FREQ_CONTROL)
> - DPRINTFN(1, ("%s:  FREQ_CONTROL\n", __func__));
> - if (ai.attributes & UA_SED_PITCH_CONTROL)
> - DPRINTFN(1, ("%s:  PITCH_CONTROL\n", __func__));
> -#endif
> - sc->sc_mode |= (dir == UE_DIR_OUT) ? AUMODE_PLAY : AUMODE_RECORD;
> -
> - return (USBD_NORMAL_COMPLETION);
> -}
> -#undef offs
> -
> -usbd_status
> -uaudio_identify_as(struct uaudio_softc *sc,
> -   const usb_config_descriptor_t *cdesc)
> -{
> - const usb_interface_descriptor_t *id;
> - const char *buf;
> - int size, offs;
> -
> - size = UGETW(cdesc->wTotalLength);
> - buf = (const char *)cdesc;
> -
> - /* Locate the AudioStreaming interface descriptor. */
> - offs = 0;
> - id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM,
> -    sc->sc_quirks);
> - if (id == NULL)
> - return (USBD_INVAL);
> -
> - /* Loop through all the alternate settings. */
> - while (offs <= size) {
> - DPRINTFN(2, ("%s: interface=%d offset=%d\n",
> -    __func__, id->bInterfaceNumber, offs));
> - switch (id->bNumEndpoints) {
> - case 0:
> - DPRINTFN(2, ("%s: AS null alt=%d\n",
> -     __func__, id->bAlternateSetting));
> - sc->sc_nullalt = id->bAlternateSetting;
> - break;
> - case 1:
> - case 2:
> - uaudio_process_as(sc, buf, &offs, size, id);
> - break;
> - default:
> - printf("%s: ignored audio interface with %d "
> -       "endpoints\n",
> -       sc->sc_dev.dv_xname, id->bNumEndpoints);
> - break;
> - }
> - id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM,
> -    sc->sc_quirks);
> - if (id == NULL)
> - break;
> - }
> - if (offs > size)
> - return (USBD_INVAL);
> - DPRINTF(("%s: %d alts available\n", __func__, sc->sc_nalts));
> -
> - if (sc->sc_mode == 0) {
> - printf("%s: no usable endpoint found\n",
> -       sc->sc_dev.dv_xname);
> - return (USBD_INVAL);
> - }
> -
> - return (USBD_NORMAL_COMPLETION);
> -}
> -
> -usbd_status
> -uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
> -{
> - struct io_terminal* iot;
> - const usb_interface_descriptor_t *id;
> - const struct usb_audio_control_descriptor *acdp;
> - const usb_descriptor_t *dp;
> - const struct usb_audio_output_terminal *pot;
> - struct terminal_list *tml;
> - const char *buf, *ibuf, *ibufend;
> - int size, offs, aclen, ndps, i, j;
> -
> - size = UGETW(cdesc->wTotalLength);
> - buf = (char *)cdesc;
> -
> - /* Locate the AudioControl interface descriptor. */
> - offs = 0;
> - id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOCONTROL,
> -    sc->sc_quirks);
> - if (id == NULL)
> - return (USBD_INVAL);
> - if (offs + sizeof *acdp > size)
> - return (USBD_INVAL);
> - sc->sc_ac_iface = id->bInterfaceNumber;
> - DPRINTFN(2,("%s: AC interface is %d\n",
> -    __func__, sc->sc_ac_iface));
> -
> - /* A class-specific AC interface header should follow. */
> - ibuf = buf + offs;
> - acdp = (const 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 (!(sc->sc_quirks & UAUDIO_FLAG_BAD_ADC) &&
> -     UGETW(acdp->bcdADC) != UAUDIO_VERSION)
> - return (USBD_INVAL);
> -
> - sc->sc_audio_rev = UGETW(acdp->bcdADC);
> - DPRINTFN(2,("%s: found AC header, vers=%03x, len=%d\n",
> - __func__, sc->sc_audio_rev, aclen));
> -
> - /* Some webcams descriptors advertise an off-by-one wTotalLength */
> - if (sc->sc_quirks & UAUDIO_FLAG_BAD_ADC_LEN)
> - aclen++;
> -
> - sc->sc_nullalt = -1;
> -
> - /* Scan through all the AC specific descriptors */
> - ibufend = ibuf + aclen;
> - dp = (const usb_descriptor_t *)ibuf;
> - ndps = 0;
> - iot = mallocarray(256, sizeof(struct io_terminal),
> -    M_TEMP, M_NOWAIT | M_ZERO);
> - if (iot == NULL) {
> - printf("%s: no memory\n", __func__);
> - return USBD_NOMEM;
> - }
> - for (;;) {
> - ibuf += dp->bLength;
> - if (ibuf >= ibufend)
> - break;
> - dp = (const usb_descriptor_t *)ibuf;
> - if (ibuf + dp->bLength > ibufend) {
> - free(iot, M_TEMP, 0);
> - return (USBD_INVAL);
> - }
> - if (dp->bDescriptorType != UDESC_CS_INTERFACE) {
> - printf("%s: skip desc type=0x%02x\n",
> -       __func__, dp->bDescriptorType);
> - continue;
> + switch (u->type) {
> + case UAUDIO_AC_CLKSRC:
> + case UAUDIO_AC_CLKSEL:
> + case UAUDIO_AC_CLKMULT:
> + case UAUDIO_AC_RATECONV:
> + /* not using 'dest' list */
> + *rchild = u;
> + return 1;
>   }
> - i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId;
> - iot[i].d.desc = dp;
> - if (i > ndps)
> - ndps = i;
> - }
> - ndps++;
> -
> - /* construct io_terminal */
> - for (i = 0; i < ndps; i++) {
> - dp = iot[i].d.desc;
> - if (dp == NULL)
> - continue;
> - if (dp->bDescriptorSubtype != UDESCSUB_AC_OUTPUT)
> - continue;
> - pot = iot[i].d.ot;
> - tml = uaudio_io_terminaltype(UGETW(pot->wTerminalType), iot, i);
> - free(tml, M_TEMP, 0);
>   }
>  
> -#ifdef UAUDIO_DEBUG
> - for (i = 0; i < 256; i++) {
> - if (iot[i].d.desc == NULL)
> - continue;
> - printf("id %d:\t", i);
> - switch (iot[i].d.desc->bDescriptorSubtype) {
> - case UDESCSUB_AC_INPUT:
> - printf("AC_INPUT type=%s\n", uaudio_get_terminal_name
> -       (UGETW(iot[i].d.it->wTerminalType)));
> - break;
> - case UDESCSUB_AC_OUTPUT:
> - printf("AC_OUTPUT type=%s ", uaudio_get_terminal_name
> -       (UGETW(iot[i].d.ot->wTerminalType)));
> - printf("src=%d\n", iot[i].d.ot->bSourceId);
> - break;
> - case UDESCSUB_AC_MIXER:
> - printf("AC_MIXER src=");
> - for (j = 0; j < iot[i].d.mu->bNrInPins; j++)
> - printf("%d ", iot[i].d.mu->baSourceId[j]);
> - printf("\n");
> - break;
> - case UDESCSUB_AC_SELECTOR:
> - printf("AC_SELECTOR src=");
> - for (j = 0; j < iot[i].d.su->bNrInPins; j++)
> - printf("%d ", iot[i].d.su->baSourceId[j]);
> - printf("\n");
> + if (dest) {
> + dest->dst_next = u->dst_list;
> + u->dst_list = dest;
> + if (dest->dst_next != NULL) {
> + /* already seen */
> + *rchild = u;
> + return 1;
> + }
> + }
> +
> + switch (u->type) {
> + case UAUDIO_AC_INPUT:
> + if (!uaudio_getnum(&p, 2, &term))
> + return 0;
> + if (!uaudio_getnum(&p, 1, &assoc))
> + return 0;
> + switch (sc->version) {
> + case UAUDIO_V1:
>   break;
> - case UDESCSUB_AC_FEATURE:
> - printf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId);
> + case UAUDIO_V2:
> + if (!uaudio_getnum(&p, 1, &clk))
> + return 0;
> + if (!uaudio_process_unit(sc, NULL,
> + clk, units, &u->clock))
> + return 0;
>   break;
> - case UDESCSUB_AC_PROCESSING:
> - printf("AC_PROCESSING src=");
> - for (j = 0; j < iot[i].d.pu->bNrInPins; j++)
> - printf("%d ", iot[i].d.pu->baSourceId[j]);
> - printf("\n");
> + }
> + if (!uaudio_getnum(&p, 1, &u->nch))
> + return 0;
> + uaudio_mkname(sc, uaudio_tname(term, 0), u->name);
> + DPRINTF("%02u: "
> +    "in, nch = %d, term = 0x%x, assoc = %d\n",
> +    u->id, u->nch, term, assoc);
> + break;
> + case UAUDIO_AC_OUTPUT:
> + if (!uaudio_getnum(&p, 2, &term))
> + return 0;
> + if (!uaudio_getnum(&p, 1, &assoc))
> + return 0;
> + if (!uaudio_getnum(&p, 1, &id))
> + return 0;
> + if (!uaudio_process_unit(sc, u, id, units, &s))
> + return 0;
> + switch (sc->version) {
> + case UAUDIO_V1:
>   break;
> - case UDESCSUB_AC_EXTENSION:
> - printf("AC_EXTENSION src=");
> - for (j = 0; j < iot[i].d.eu->bNrInPins; j++)
> - printf("%d ", iot[i].d.eu->baSourceId[j]);
> - printf("\n");
> + case UAUDIO_V2:
> + if (!uaudio_getnum(&p, 1, &clk))
> + return 0;
> + if (!uaudio_process_unit(sc, NULL,
> + clk, units, &u->clock))
> + return 0;
> + break;
> + }
> + u->src_list = s;
> + s->src_next = NULL;
> + u->nch = s->nch;
> + uaudio_mkname(sc, uaudio_tname(term, 1), u->name);
> + DPRINTF("%02u: "
> +    "out, id = %d, nch = %d, term = 0x%x, assoc = %d\n",
> +    u->id, id, u->nch, term, assoc);
> + break;
> + case UAUDIO_AC_MIXER:
> + if (!uaudio_process_srcs(sc, u, units, &p))
> + return 0;
> + if (!uaudio_process_nch(sc, u, &p))
> + return 0;
> + DPRINTF("%02u: mixer, nch = %u:\n", u->id, u->nch);
> +
> +#ifdef UAUDIO_DEBUG
> + /*
> + * Print the list of available mixer's unit knobs (a bit
> + * matrix). Matrix mixers are rare because levels are
> + * already controlled by feature units, making the mixer
> + * knobs redundant with the feature's knobs. So, for
> + * now, we don't add clutter to the mixer(4) interface
> + * and ignore all knobs. Other popular OSes doesn't
> + * seem to expose them either.
> + */
> + bit = 0;
> + for (s = u->src_list; s != NULL; s = s->src_next) {
> + for (i = 0; i < s->nch; i++) {
> + for (j = 0; j < u->nch; j++) {
> + if ((bit++ & 7) == 0) {
> + if (!uaudio_getnum(&p, 1, &ctl))
> + return 0;
> + }
> + if (ctl & 0x80)
> + DPRINTF("\t%02u[%d] -> [%d]\n",
> +    s->id, i, j);
> + ctl <<= 1;
> + }
> + }
> + }
> +#endif
> + break;
> + case UAUDIO_AC_SELECTOR:
> + /*
> + * Selectors are extreamly rare, so not supported yet.
> + */
> + if (!uaudio_process_srcs(sc, u, units, &p))
> + return 0;
> + if (u->src_list == NULL) {
> + printf("%s: selector %02u has no sources\n",
> +    DEVNAME(sc), u->id);
> + return 0;
> + }
> + u->nch = u->src_list->nch;
> + DPRINTF("%02u: selector, nch = %u\n", u->id, u->nch);
> + break;
> + case UAUDIO_AC_FEATURE:
> + if (!uaudio_getnum(&p, 1, &id))
> + return 0;
> + if (!uaudio_process_unit(sc, u, id, units, &s))
> + return 0;
> + s->src_next = u->src_list;
> + u->src_list = s;
> + u->nch = s->nch;
> + switch (sc->version) {
> + case UAUDIO_V1:
> + if (!uaudio_getnum(&p, 1, &size))
> + return 0;
> + break;
> + case UAUDIO_V2:
> + size = 4;
>   break;
> - default:
> - printf("unknown audio control (subtype=%d)\n",
> -       iot[i].d.desc->bDescriptorSubtype);
>   }
> - for (j = 0; j < iot[i].inputs_size; j++) {
> - int k;
> - printf("\tinput%d: ", j);
> - tml = iot[i].inputs[j];
> - if (tml == NULL) {
> - printf("NULL\n");
> - continue;
> + DPRINTF("%02d: feature id = %d, nch = %d, size = %d\n",
> +    u->id, id, u->nch, size);
> + if (!uaudio_getnum(&p, size, &ctl))
> + return 0;
> + ctl = uaudio_feature_fixup(sc, ctl);
> + for (i = 0; i < 16; i++) {
> + if ((ctl & 3) == 3)
> + uaudio_feature_addent(sc, u, i, -1);
> + ctl >>= 2;
> + }
> + for (j = 0; j < u->nch; j++) {
> + if (!uaudio_getnum(&p, size, &ctl))
> + return 0;
> + ctl = uaudio_feature_fixup(sc, ctl);
> + for (i = 0; i < 16; i++) {
> + if ((ctl & 3) == 3)
> + uaudio_feature_addent(sc, u, i, j);
> + ctl >>= 2;
>   }
> - for (k = 0; k < tml->size; k++)
> - printf("%s ", uaudio_get_terminal_name
> -       (tml->terminals[k]));
> - printf("\n");
>   }
> - printf("\toutput: ");
> - tml = iot[i].output;
> - for (j = 0; j < tml->size; j++)
> - printf("%s ", uaudio_get_terminal_name(tml->terminals[j]));
> - printf("\n");
> + break;
> + case UAUDIO_AC_EFFECT:
> + if (!uaudio_getnum(&p, 2, &type))
> + return 0;
> + if (!uaudio_getnum(&p, 1, &id))
> + return 0;
> + if (!uaudio_process_unit(sc, u, id, units, &s))
> + return 0;
> + s->src_next = u->src_list;
> + u->src_list = s;
> + u->nch = s->nch;
> + DPRINTF("%02d: effect, type = %u, id = %d, nch = %d\n",
> +    u->id, type, id, u->nch);
> + break;
> + case UAUDIO_AC_PROCESSING:
> + case UAUDIO_AC_EXTENSION:
> + if (!uaudio_getnum(&p, 2, &type))
> + return 0;
> + if (!uaudio_process_srcs(sc, u, units, &p))
> + return 0;
> + if (!uaudio_process_nch(sc, u, &p))
> + return 0;
> + DPRINTF("%02u: proc/ext, type = 0x%x, nch = %u\n",
> +    u->id, type, u->nch);
> + for (s = u->src_list; s != NULL; s = s->src_next) {
> + DPRINTF("%u:\tpin %u:\n", u->id, s->id);
> + }
> + break;
> + case UAUDIO_AC_CLKSRC:
> + if (!uaudio_getnum(&p, 1, &attr))
> + return 0;
> + if (!uaudio_getnum(&p, 1, &ctl))
> + return 0;
> + DPRINTF("%02u: clock source, attr = 0x%x, ctl = 0x%x\n",
> +    u->id, attr, ctl);
> + uaudio_mkname(sc, uaudio_clkname(attr), u->name);
> + break;
> + case UAUDIO_AC_CLKSEL:
> + DPRINTF("%02u: clock sel\n", u->id);
> + if (!uaudio_process_srcs(sc, u, units, &p))
> + return 0;
> + if (u->src_list == NULL) {
> + printf("%s: clock selector %02u with no srcs\n",
> +    DEVNAME(sc), u->id);
> + return 0;
> + }
> + uaudio_mkname(sc, "clksel", u->name);
> + break;
> + case UAUDIO_AC_CLKMULT:
> + DPRINTF("%02u: clock mult\n", u->id);
> +
> + /* XXX: fetch multiplier */
> + printf("%s: clock multiplier not supported\n", DEVNAME(sc));
> + break;
> + case UAUDIO_AC_RATECONV:
> + DPRINTF("%02u: rate conv\n", u->id);
> +
> + /* XXX: fetch multiplier */
> + printf("%s: rate converter not supported\n", DEVNAME(sc));
> + break;
>   }
> -#endif
> + if (rchild)
> + *rchild = u;
> + return 1;
> +}
>  
> - for (i = 0; i < ndps; i++) {
> - dp = iot[i].d.desc;
> - if (dp == NULL)
> - continue;
> - DPRINTF(("%s: id=%d subtype=%d\n",
> - __func__, i, dp->bDescriptorSubtype));
> - switch (dp->bDescriptorSubtype) {
> - case UDESCSUB_AC_HEADER:
> - printf("%s: unexpected AC header\n", __func__);
> - break;
> - case UDESCSUB_AC_INPUT:
> - uaudio_add_input(sc, iot, i);
> - break;
> - case UDESCSUB_AC_OUTPUT:
> - uaudio_add_output(sc, iot, i);
> +/*
> + * Try to set the unit name to the name of its destination terminal. If
> + * the name is ambigus (already given to another source unit or having
> + * multiple destinations) then return 0.
> + */
> +int
> +uaudio_setname_dsts(struct uaudio_softc *sc, struct uaudio_unit *u, char *name)
> +{
> + struct uaudio_unit *d = u;
> +
> + while (d != NULL) {
> + if (d->dst_list == NULL || d->dst_list->dst_next != NULL)
>   break;
> - case UDESCSUB_AC_MIXER:
> - uaudio_add_mixer(sc, iot, i);
> + d = d->dst_list;
> + if (d->src_list == NULL || d->src_list->src_next != NULL)
>   break;
> - case UDESCSUB_AC_SELECTOR:
> - uaudio_add_selector(sc, iot, i);
> + if (d->name[0] != '\0') {
> + if (name != NULL && strcmp(name, d->name) != 0)
> + break;
> + strlcpy(u->name, d->name, UAUDIO_NAMEMAX);
> + return 1;
> + }
> + }
> + return 0;
> +}
> +
> +/*
> + * Try to set the unit name to the name of its source terminal. If the
> + * name is ambigus (already given to another destination unit or
> + * having multiple sources) then return 0.
> + */
> +int
> +uaudio_setname_srcs(struct uaudio_softc *sc, struct uaudio_unit *u, char *name)
> +{
> + struct uaudio_unit *s = u;
> +
> + while (s != NULL) {
> + if (s->src_list == NULL || s->src_list->src_next != NULL)
>   break;
> - case UDESCSUB_AC_FEATURE:
> - uaudio_add_feature(sc, iot, i);
> + s = s->src_list;
> + if (s->dst_list == NULL || s->dst_list->dst_next != NULL)
>   break;
> - case UDESCSUB_AC_PROCESSING:
> - uaudio_add_processing(sc, iot, i);
> + if (s->name[0] != '\0') {
> + if (name != NULL && strcmp(name, s->name) != 0)
> + break;
> + strlcpy(u->name, s->name, UAUDIO_NAMEMAX);
> + return 1;
> + }
> + }
> + return 0;
> +}
> +
> +/*
> + * Set the name of the given unit by using both its source and
> + * destination units. This is naming scheme is only useful to units
> + * that would have ambigous names if only sources or only destination
> + * were used.
> + */
> +void
> +uaudio_setname_middle(struct uaudio_softc *sc, struct uaudio_unit *u)
> +{
> + struct uaudio_unit *s, *d;
> + char name[UAUDIO_NAMEMAX];
> +
> + s = u->src_list;
> + while (1) {
> + if (s == NULL) {
> + DPRINTF("uaudio_setname_middle: %02u: has no srcs\n",
> +  u->id);
> + return;
> + }
> + if (s->name[0] != '\0')
>   break;
> - case UDESCSUB_AC_EXTENSION:
> - uaudio_add_extension(sc, iot, i);
> + s = s->src_list;
> + }
> +
> + d = u->dst_list;
> + while (1) {
> + if (d == NULL) {
> + DPRINTF("uaudio_setname_middle: %02u: has no dests\n",
> +  u->id);
> + return;
> + }
> + if (d->name[0] != '\0')
>   break;
> - default:
> - printf("%s: bad AC desc subtype=0x%02x\n",
> -       __func__, dp->bDescriptorSubtype);
> + d = d->dst_list;
> + }
> +
> + snprintf(name, UAUDIO_NAMEMAX, "%s_%s", d->name, s->name);
> + uaudio_mkname(sc, name, u->name);
> +}
> +
> +#ifdef UAUDIO_DEBUG
> +/*
> + * Return the synchronization type name, for debug purposes only.
> + */
> +char *
> +uaudio_isoname(int isotype)
> +{
> + switch (isotype) {
> + case UE_ISO_ASYNC:
> + return "async";
> + case UE_ISO_ADAPT:
> + return "adapt";
> + case UE_ISO_SYNC:
> + return "sync";
> + default:
> + return "unk";
> + }
> +}
> +
> +/*
> + * Return the name of the given mode, debug only
> + */
> +char *
> +uaudio_modename(int mode)
> +{
> + switch (mode) {
> + case 0:
> + return "none";
> + case AUMODE_PLAY:
> + return "play";
> + case AUMODE_RECORD:
> + return "rec";
> + case AUMODE_PLAY | AUMODE_RECORD:
> + return "duplex";
> + default:
> + return "unk";
> + }
> +}
> +
> +/*
> + * Return UAC v2.0 endpoint usage, debug only
> + */
> +char *
> +uaudio_usagename(int usage)
> +{
> + switch (usage) {
> + case UE_ISO_USAGE_DATA:
> + return "data";
> + case UE_ISO_USAGE_FEEDBACK:
> + return "feed";
> + case UE_ISO_USAGE_IMPL:
> + return "impl";
> + default:
> + return "unk";
> + }
> +}
> +
> +/*
> + * Print a bitmap of rates on the console.
> + */
> +void
> +uaudio_rates_print(int rates)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < UAUDIO_NRATES; i++) {
> + if (rates & (1 << i))
> + printf(" %d", uaudio_rates[i]);
> + }
> + printf("\n");
> +}
> +
> +
> +/*
> + * Print uaudio_ranges to console.
> + */
> +void
> +uaudio_ranges_print(struct uaudio_ranges *r)
> +{
> + struct uaudio_ranges_el *e;
> + int more = 0;
> +
> + for (e = r->el; e != NULL; e = e->next) {
> + if (more)
> + printf(", ");
> + if (e->min == e->max)
> + printf("%d", e->min);
> + else
> + printf("[%d:%d]/%d", e->min, e->max, e->res);
> + more = 1;
> + }
> + printf(" (%d vals)\n", r->nval);
> +}
> +
> +/*
> + * Print unit to the console.
> + */
> +void
> +uaudio_print_unit(struct uaudio_softc *sc, struct uaudio_unit *u)
> +{
> + struct uaudio_unit *s;
> +
> + switch (u->type) {
> + case UAUDIO_AC_INPUT:
> + printf("%02u: input <%s>, dest = %02u <%s>\n",
> +    u->id, u->name, u->dst_list->id, u->dst_list->name);
> + break;
> + case UAUDIO_AC_OUTPUT:
> + printf("%02u: output <%s>, source = %02u <%s>\n",
> +    u->id, u->name, u->src_list->id, u->src_list->name);
> + break;
> + case UAUDIO_AC_MIXER:
> + printf("%02u: mixer <%s>:\n", u->id, u->name);
> + for (s = u->src_list; s != NULL; s = s->src_next)
> + printf("%02u:\tsource %u <%s>:\n",
> +    u->id, s->id, s->name);
> + break;
> + case UAUDIO_AC_SELECTOR:
> + printf("%02u: selector <%s>:\n", u->id, u->name);
> + for (s = u->src_list; s != NULL; s = s->src_next)
> + printf("%02u:\tsource %u <%s>:\n",
> +    u->id, s->id, s->name);
> + break;
> + case UAUDIO_AC_FEATURE:
> + printf("%02u: feature <%s>, "
> +    "src = %02u <%s>, dst = %02u <%s>, cls = %d\n",
> +    u->id, u->name,
> +    u->src_list->id, u->src_list->name,
> +    u->dst_list->id, u->dst_list->name, u->mixer_class);
> + break;
> + case UAUDIO_AC_EFFECT:
> + printf("%02u: effect <%s>, "
> +    "src = %02u <%s>, dst = %02u <%s>\n",
> +    u->id, u->name,
> +    u->src_list->id, u->src_list->name,
> +    u->dst_list->id, u->dst_list->name);
> + break;
> + case UAUDIO_AC_PROCESSING:
> + case UAUDIO_AC_EXTENSION:
> + printf("%02u: proc/ext <%s>:\n", u->id, u->name);
> + for (s = u->src_list; s != NULL; s = s->src_next)
> + printf("%02u:\tsource %u <%s>:\n",
> +    u->id, s->id, s->name);
> + break;
> + case UAUDIO_AC_CLKSRC:
> + printf("%02u: clock source <%s>\n", u->id, u->name);
> + break;
> + case UAUDIO_AC_CLKSEL:
> + printf("%02u: clock sel <%s>\n", u->id, u->name);
> + break;
> + case UAUDIO_AC_CLKMULT:
> + printf("%02u: clock mult\n", u->id);
> + break;
> + case UAUDIO_AC_RATECONV:
> + printf("%02u: rate conv\n", u->id);
> + break;
> + }
> +}
> +
> +/*
> + * Print the full mixer on the console.
> + */
> +void
> +uaudio_mixer_print(struct uaudio_softc *sc)
> +{
> + struct uaudio_mixent *m;
> + struct uaudio_unit *u;
> +
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + for (m = u->mixent_list; m != NULL; m = m->next) {
> + printf("%02u:\t%s.%s",
> +    u->id, u->name, m->fname);
> + if (m->chan >= 0)
> + printf("[%u]", m->chan);
> + printf("\n");
> + }
> + }
> +}
> +
> +/*
> + * Print the full device configuration on the console.
> + */
> +void
> +uaudio_conf_print(struct uaudio_softc *sc)
> +{
> + struct uaudio_alt *a;
> + struct uaudio_params *p;
> + struct mixer_devinfo mi;
> + struct mixer_ctrl ctl;
> + int i, rates;
> +
> + mi.index = 0;
> + while (1) {
> + if (uaudio_query_devinfo(sc, &mi) != 0)
> + break;
> +
> + if (mi.type != AUDIO_MIXER_CLASS) {
> + ctl.dev = mi.index;
> + if (uaudio_get_port(sc, &ctl) != 0) {
> + printf("%02u: failed to get port\n", mi.index);
> + memset(&ctl.un, 0, sizeof(ctl.un));
> + }
> + }
> +
> + printf("%02u: <%s>, next = %d, prev = %d, class = %d",
> +    mi.index, mi.label.name, mi.next, mi.prev, mi.mixer_class);
> +
> + switch (mi.type) {
> + case AUDIO_MIXER_CLASS:
> + break;
> + case AUDIO_MIXER_VALUE:
> + printf(", nch = %d, delta = %d",
> +    mi.un.v.num_channels, mi.un.v.delta);
> + printf(", val =");
> + for (i = 0; i < mi.un.v.num_channels; i++)
> + printf(" %d", ctl.un.value.level[i]);
> + break;
> + case AUDIO_MIXER_ENUM:
> + printf(", members:");
> + for (i = 0; i != mi.un.e.num_mem; i++) {
> + printf(" %s(=%d)",
> +    mi.un.e.member[i].label.name,
> +    mi.un.e.member[i].ord);
> + }
> + printf(", val = %d", ctl.un.ord);
> + break;
> + }
> +
> + printf("\n");
> + mi.index++;
> + }
> +
> + printf("%d controls\n", mi.index);
> +
> + printf("alts:\n");
> + for (a = sc->alts; a != NULL; a = a->next) {
> + rates = uaudio_alt_getrates(sc, a);
> + printf("mode = %s, ifnum = %d, altnum = %d, "
> +    "addr = 0x%x, maxpkt = %d, sync = 0x%x, "
> +    "nch = %d, fmt = s%dle%d, rates:",
> +    uaudio_modename(a->mode),
> +    a->ifnum, a->altnum,
> +    a->data_addr, a->maxpkt,
> +    a->sync_addr,
> +    a->nch, a->bits, a->bps);
> + uaudio_rates_print(rates);
> + }
> +
> + printf("parameters:\n");
> + for (p = sc->params_list; p != NULL; p = p->next) {
> + switch (sc->version) {
> + case UAUDIO_V1:
> + rates = p->v1_rates;
> + break;
> + case UAUDIO_V2:
> + rates = uaudio_getrates(sc, p);
> + break;
> + }
> + printf("pchan = %d, s%dle%d, rchan = %d, s%dle%d, rates:",
> +    p->palt ? p->palt->nch : 0,
> +    p->palt ? p->palt->bits : 0,
> +    p->palt ? p->palt->bps : 0,
> +    p->ralt ? p->ralt->nch : 0,
> +    p->ralt ? p->ralt->bits : 0,
> +    p->ralt ? p->ralt->bps : 0);
> + uaudio_rates_print(rates);
> + }
> +}
> +#endif
> +
> +/*
> + * Return the number of mixer controls that have the same name but
> + * control different channels of the same stream.
> + */
> +int
> +uaudio_mixer_nchan(struct uaudio_mixent *m, struct uaudio_mixent **rnext)
> +{
> + char *name;
> + int i;
> +
> + i = 0;
> + name = m->fname;
> + while (m != NULL && strcmp(name, m->fname) == 0) {
> + m = m->next;
> + i++;
> + }
> + if (rnext)
> + *rnext = m;
> + return i;
> +}
> +
> +/*
> + * Return pointer to the unit and mixer entry which have the given
> + * index exposed by the mixer(4) API.
> + */
> +int
> +uaudio_mixer_byindex(struct uaudio_softc *sc, int index,
> +    struct uaudio_unit **ru, struct uaudio_mixent **rm)
> +{
> + struct uaudio_unit *u;
> + struct uaudio_mixent *m;
> + char *name;
> + int i;
> +
> + i = UAUDIO_CLASS_COUNT;
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + m = u->mixent_list;
> + while (1) {
> + if (m == NULL)
> + break;
> + if (index == i) {
> + *ru = u;
> + *rm = m;
> + return 1;
> + }
> + if (m->type == UAUDIO_MIX_NUM) {
> + name = m->fname;
> + while (m != NULL &&
> +    strcmp(name, m->fname) == 0)
> + m = m->next;
> + } else
> + m = m->next;
> + i++;
> + }
> + }
> + return 0;
> +}
> +
> +/*
> + * Parse AC header descriptor, we use it only to determine UAC
> + * version. Other properties (like wTotalLength) can be determined
> + * using other descriptors, so we try to no rely on them to avoid
> + * inconsistencies and the need for certain quirks.
> + */
> +int
> +uaudio_process_header(struct uaudio_softc *sc, struct uaudio_blob *p)
> +{
> + struct uaudio_blob ph;
> + unsigned int type, subtype;
> +
> + if (!uaudio_getdesc(p, &ph))
> + return 0;
> + if (!uaudio_getnum(&ph, 1, &type))
> + return 0;
> + if (type != UDESC_CS_INTERFACE) {
> + DPRINTF("uaudio_process_header: expected cs iface desc\n");
> + return 0;
> + }
> + if (!uaudio_getnum(&ph, 1, &subtype))
> + return 0;
> + if (subtype != UAUDIO_AC_HEADER) {
> + DPRINTF("uaudio_process_header: expected header desc\n");
> + return 0;
> + }
> + if (!uaudio_getnum(&ph, 2, &sc->version))
> + return 0;
> +
> + DPRINTF("uaudio_process_header: version 0x%x\n", sc->version);
> + return 1;
> +}
> +
> +/*
> + * Process AC interrupt endpoint descriptor, this is mainly to skip
> + * the descriptor as we use neither of it's properties. Our mixer
> + * interface doesn't support unsolicitated state changes, so we've no
> + * use of it yet.
> + */
> +int
> +uaudio_process_ac_ep(struct uaudio_softc *sc, struct uaudio_blob *p)
> +{
> +#ifdef UAUDIO_DEBUG
> + static const char *xfer[] = {
> + "ctl", "iso", "bulk", "intr"
> + };
> +#endif
> + struct uaudio_blob dp;
> + unsigned int type, addr, attr, maxpkt, ival;
> + unsigned char *savepos;
> +
> + /*
> + * parse optional interrupt endpoint descriptor
> + */
> + if (p->rptr == p->wptr)
> + return 1;
> + savepos = p->rptr;
> + if (!uaudio_getdesc(p, &dp))
> + return 0;
> + if (!uaudio_getnum(&dp, 1, &type))
> + return 0;
> + if (type != UDESC_ENDPOINT) {
> + p->rptr = savepos;
> + return 1;
> + }
> +
> + if (!uaudio_getnum(&dp, 1, &addr))
> + return 0;
> + if (!uaudio_getnum(&dp, 1, &attr))
> + return 0;
> + if (!uaudio_getnum(&dp, 2, &maxpkt))
> + return 0;
> + if (!uaudio_getnum(&dp, 1, &ival))
> + return 0;
> +
> + DPRINTF("uaudio_process_ac_ep: "
> +    "addr = 0x%x, type = %s, maxpkt = %d, ival = %d\n",
> +    addr, xfer[UE_GET_XFERTYPE(attr)], UE_GET_SIZE(maxpkt), ival);
> +
> + return 1;
> +}
> +
> +/*
> + * Process the AC interface descriptors: mainly build the mixer and,
> + * for UAC v2.0, find the clock source.
> + *
> + * The audio device exposes an audio control (AC) interface with a big
> + * set of USB descriptors which expose the complete circuit the
> + * device. The circuit describes how the signal flows between the USB
> + * streaming interfaces to the terminal connectors (jacks, speakers,
> + * mics, ...). The circuit is build of mixers, source selectors, gain
> + * controls, mutters, processors, and alike; each comes with its own
> + * set of controls. Most of the boring driver work is to parse the
> + * circuit and build a human-usable set of controls that could be
> + * exposed through the mixer(4) interface.
> + */
> +int
> +uaudio_process_ac(struct uaudio_softc *sc, struct uaudio_blob *p, int ifnum)
> +{
> + struct uaudio_blob units, pu;
> + struct uaudio_unit *u, *v;
> + unsigned char *savepos;
> + unsigned int type, subtype, id;
> + char *name, val;
> +
> + DPRINTF("uaudio_process_ac: ifnum = %d, %zd bytes to processs\n",
> +    ifnum, p->wptr - p->rptr);
> +
> + sc->ctl_ifnum = ifnum;
> +
> + /* The first AC class-specific descriptor is the AC header */
> + if (!uaudio_process_header(sc, p))
> + return 0;
> +
> + /*
> + * Determine the size of the AC descriptors array: scan
> + * descriptors until we get the first non-class-specific
> + * descriptor. This avoids relying on the wTotalLength field.
> + */
> + savepos = p->rptr;
> + units.rptr = p->rptr;
> + while (p->rptr != p->wptr) {
> + if (!uaudio_getdesc(p, &pu))
> + return 0;
> + if (!uaudio_getnum(&pu, 1, &type))
> + return 0;
> + if (type != UDESC_CS_INTERFACE)
> + break;
> + units.wptr = p->rptr;
> + }
> + p->rptr = savepos;
> +
> + /*
> + * Load units, walking from outputs to inputs, as
> + * the usb audio class spec requires.
> + */
> + while (p->rptr != units.wptr) {
> + if (!uaudio_getdesc(p, &pu))
> + return 0;
> + if (!uaudio_getnum(&pu, 1, &type))
> + return 0;
> + if (!uaudio_getnum(&pu, 1, &subtype))
> + return 0;
> + if (subtype == UAUDIO_AC_OUTPUT) {
> + if (!uaudio_getnum(&pu, 1, &id))
> + return 0;
> + if (!uaudio_process_unit(sc, NULL, id, units, NULL))
> + return 0;
> + }
> + }
> +
> + /*
> + * set effect and processor unit names
> + */
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + switch (u->type) {
> + case UAUDIO_AC_EFFECT:
> + uaudio_mkname(sc, "fx", u->name);
> + break;
> + case UAUDIO_AC_PROCESSING:
> + uaudio_mkname(sc, "proc", u->name);
> + break;
> + case UAUDIO_AC_EXTENSION:
> + uaudio_mkname(sc, "ext", u->name);
> + break;
> + }
> + }
> +
> + /*
> + * set mixer/selector unit names
> + */
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + if (u->type != UAUDIO_AC_MIXER &&
> +    u->type != UAUDIO_AC_SELECTOR)
> + continue;
> + if (!uaudio_setname_dsts(sc, u, NULL)) {
> + switch (u->type) {
> + case UAUDIO_AC_MIXER:
> + name = "mix";
> + break;
> + case UAUDIO_AC_SELECTOR:
> + name = "sel";
> + break;
> + }
> + uaudio_mkname(sc, name, u->name);
> + }
> + }
> +
> + /*
> + * set feature unit names and classes
> + */
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + if (u->type != UAUDIO_AC_FEATURE)
> + continue;
> + if (uaudio_setname_dsts(sc, u, "record")) {
> + u->mixer_class = UAUDIO_CLASS_REC;
> + continue;
> + }
> + if (uaudio_setname_srcs(sc, u, "play")) {
> + u->mixer_class = UAUDIO_CLASS_OUT;
> + continue;
> + }
> + if (uaudio_setname_dsts(sc, u, NULL)) {
> + u->mixer_class = UAUDIO_CLASS_OUT;
> + continue;
> + }
> + if (uaudio_setname_srcs(sc, u, NULL)) {
> + u->mixer_class = UAUDIO_CLASS_IN;
> + continue;
> + }
> + uaudio_setname_middle(sc, u);
> + u->mixer_class = UAUDIO_CLASS_IN;
> + }
> +
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug) {
> + printf("%s: units list:\n", DEVNAME(sc));
> + for (u = sc->unit_list; u != NULL; u = u->unit_next)
> + uaudio_print_unit(sc, u);
> +
> + printf("%s: mixer controls:\n", DEVNAME(sc));
> + uaudio_mixer_print(sc);
> + }
> +#endif
> +
> + /* follows optional interrupt endpoint descriptor */
> + if (!uaudio_process_ac_ep(sc, p))
> + return 0;
> +
> + /* fetch clock source rates */
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + switch (u->type) {
> + case UAUDIO_AC_CLKSRC:
> + if (!uaudio_req_ranges(sc, 4,
> + UAUDIO_V2_REQSEL_CLKFREQ,
> + 0, /* channel (not used) */
> + sc->ctl_ifnum,
> + u->id,
> + &u->rates))
> + return 1;
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug) {
> + printf("%02u: clock rates: ", u->id);
> + uaudio_ranges_print(&u->rates);
> + }
> +#endif
> + break;
> + case UAUDIO_AC_CLKSEL:
> + if (!uaudio_req(sc, UT_READ_CLASS_INTERFACE,
> + UAUDIO_V2_REQ_CUR,
> + UAUDIO_V2_REQSEL_CLKSEL, 0,
> + sc->ctl_ifnum, u->id,
> + &val, 1))
> + return 0;
> + for (v = u->src_list; v != NULL; v = v->src_next) {
> + if (--val == 0)
> + break;
> + }
> + u->clock = v;
> + break;
> + }
> + }
> +
> + if (sc->version == UAUDIO_V2) {
> + /*
> + * Find common clock unit. We assume all terminals
> + * belong to the same clock domain (ie are connected
> + * to the same source)
> + */
> + sc->clock = NULL;
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + if (u->type != UAUDIO_AC_INPUT &&
> +    u->type != UAUDIO_AC_OUTPUT)
> + continue;
> + if (sc->clock == NULL) {
> + if (u->clock == NULL) {
> + printf("%s: terminal with no clock\n",
> +    DEVNAME(sc));
> + return 0;
> + }
> + sc->clock = u->clock;
> + } else if (u->clock != sc->clock) {
> + printf("%s: only one clock domain supported\n",
> +    DEVNAME(sc));
> + return 0;
> + }
> + }
> +
> + if (sc->clock == NULL) {
> + printf("%s: no clock found\n", DEVNAME(sc));
> + return 0;
> + }
> + }
> + return 1;
> +}
> +
> +/*
> + * Parse endpoint descriptor with the following fromat:
> + *
> + * For playback there's a output data endpoint, of the
> + * following types:
> + *
> + *  type sync descr
> + *  -------------------------------------------------------
> + *  async: Yes the device uses it's own clock but
> + * sends feedback on a (input) sync endpoint
> + * for the host to adjust next packet size
> + *
> + *  sync: - data rate is constant, and device
> + * is clocked to the usb bus.
> + *
> + *  adapt: - the device adapts to data rate of the
> + * host. If fixed packet size is used,
> + * data rate is equivalent to the usb clock
> + * so this mode is the same as the
> + * sync mode.
> + *
> + * For recording there's and input data endpoint, of
> + * the following types:
> + *
> + *  type sync descr
> + *  -------------------------------------------------------
> + *  async: - the device uses its own clock and
> + * adjusts packet sizes.
> + *
> + *  sync: - the device uses usb clock rate
> + *
> + *  adapt: Yes the device uses host's feedback (on
> + * on a dedicated (output) sync endpoint
> + * to adapt to software's desired rate
> + *
> + *
> + * For usb1.1 ival is cardcoded to 1 for isochronous
> + * transfers, which means one transfer every ms. I.e one
> + * transfer every frame period.
> + *
> + * For usb2, ival the poll interval is:
> + *
> + * frame_period * 2^(ival - 1)
> + *
> + * so, if we use this formula, we get something working in all
> + * cases.
> + *
> + * The MaxPacketsOnly attribute is used only by "Type II" encodings,
> + * so we don't care about it.
> + */
> +int
> +uaudio_process_as_ep(struct uaudio_softc *sc,
> + struct uaudio_blob *p, struct uaudio_alt *a)
> +{
> + unsigned int addr, attr, maxpkt, isotype, ival;
> +
> + if (!uaudio_getnum(p, 1, &addr))
> + return 0;
> + if (!uaudio_getnum(p, 1, &attr))
> + return 0;
> + if (!uaudio_getnum(p, 2, &maxpkt))
> + return 0;
> + if (!uaudio_getnum(p, 1, &ival)) /* bInterval */
> + return 0;
> +
> + DPRINTF("uaudio_process_as_ep: "
> +    "addr = 0x%x, "
> +    "%s/%s, "
> +    "maxpktsz = %d, ival = %d\n",
> +    addr,
> +    uaudio_isoname(UE_GET_ISO_TYPE(attr)),
> +    uaudio_usagename(UE_GET_ISO_USAGE(attr)),
> +    maxpkt, ival);
> +
> + if (UE_GET_XFERTYPE(attr) != UE_ISOCHRONOUS) {
> + printf("%s: skipped non-isoc endpt.\n", DEVNAME(sc));
> + return 1;
> + }
> +
> + /*
> + * For each AS interface setting, there's a single data
> + * endpoint and an optional feedback endpoint. The
> + * synchonization type is non-zero and must be set in the data
> + * endpoints.
> + */
> + isotype = UE_GET_ISO_TYPE(attr);
> + if (isotype) {
> + /* this is the data endpoint */
> +
> + if (a->data_addr && addr != a->data_addr) {
> + printf("%s: skipped extra data endpt.\n", DEVNAME(sc));
> + return 1;
> + }
> + if (UE_GET_DIR(addr) == UE_DIR_IN) {
> + a->mode = AUMODE_RECORD;
> + a->sync = (isotype == UE_ISO_ADAPT);
> + } else {
> + a->mode = AUMODE_PLAY;
> + a->sync = (isotype == UE_ISO_ASYNC);
> + }
> + a->data_addr = addr;
> + a->fps = sc->ufps / (1 << (ival - 1));
> + a->maxpkt = UE_GET_SIZE(maxpkt);
> + } else {
> + /* this is the sync endpoint */
> +
> + if (a->sync_addr && addr != a->sync_addr) {
> + printf("%s: skipped extra sync endpt.\n", DEVNAME(sc));
> + return 1;
> + }
> + a->sync_addr = addr;
> + }
> +
> + return 1;
> +}
> +
> +/*
> + * Parse AS general descriptor. Non-PCM interfaces are skipped. UAC
> + * v2.0 report the number of channels. For UAC v1.0 we set the number
> + * of channels to zero, it will be determined later from the format
> + * descriptor.
> + */
> +int
> +uaudio_process_as_general(struct uaudio_softc *sc,
> + struct uaudio_blob *p, int *rispcm, struct uaudio_alt *a)
> +{
> + unsigned int term, fmt, ctl, fmt_type, fmt_map, nch;
> +
> + if (!uaudio_getnum(p, 1, &term))
> + return 0;
> + switch (sc->version) {
> + case UAUDIO_V1:
> + if (!uaudio_getnum(p, 1, NULL)) /* bDelay */
> + return 0;
> + if (!uaudio_getnum(p, 1, &fmt))
> + return 0;
> + *rispcm = (fmt == UAUDIO_V1_FMT_PCM);
> + break;
> + case UAUDIO_V2:
> + /* XXX: should we check if alt setting control is valid ? */
> + if (!uaudio_getnum(p, 1, &ctl))
> + return 0;
> + if (!uaudio_getnum(p, 1, &fmt_type))
> + return 0;
> + if (!uaudio_getnum(p, 4, &fmt_map))
> + return 0;
> + if (!uaudio_getnum(p, 1, &nch))
> + return 0;
> + a->nch = nch;
> + *rispcm = (fmt_type == 1) && (fmt_map & UAUDIO_V2_FMT_PCM);
> + }
> + return 1;
> +}
> +
> +/*
> + * Parse AS format descriptor: we support only "Type 1" formats, aka
> + * PCM. Other formats are not really audio, they are data-only
> + * interfaces that we don't wan't to support: ethernet is much better
> + * for raw data transfers.
> + *
> + * XXX: handle ieee 754 32-bit floating point formats.
> + */
> +int
> +uaudio_process_as_format(struct uaudio_softc *sc,
> + struct uaudio_blob *p, struct uaudio_alt *a)
> +{
> + unsigned int type, bps, bits, nch, nrates, rate_min, rate_max, rates;
> + int i, j;
> +
> + switch (sc->version) {
> + case UAUDIO_V1:
> + if (!uaudio_getnum(p, 1, &type))
> + return 0;
> + if (type != 1) {
> + DPRINTF("uaudio_process_as_format: class v1: "
> +    "skipped unsupported type = %d\n", type);
> + return 1;
> + }
> + if (!uaudio_getnum(p, 1, &nch))
> + return 0;
> + if (!uaudio_getnum(p, 1, &bps))
> + return 0;
> + if (!uaudio_getnum(p, 1, &bits))
> + return 0;
> + if (!uaudio_getnum(p, 1, &nrates))
> + return 0;
> + rates = 0;
> + if (nrates == 0) {
> + if (!uaudio_getnum(p, 3, &rate_min))
> + return 0;
> + if (!uaudio_getnum(p, 3, &rate_max))
> + return 0;
> + for (i = 0; i < UAUDIO_NRATES; i++) {
> + if (uaudio_rates[i] >= rate_min &&
> +    uaudio_rates[i] <= rate_max)
> + rates |= 1 << i;
> + }
> + } else {
> + for (j = 0; j < nrates; j++) {
> + if (!uaudio_getnum(p, 3, &rate_min))
> + return 0;
> + for (i = 0; i < UAUDIO_NRATES; i++) {
> + if (uaudio_rates[i] == rate_min)
> + rates |= 1 << i;
> + }
> + }
> + }
> + a->v1_rates = rates;
> + a->nch = nch;
> + break;
> + case UAUDIO_V2:
> + /*
> + * sample rate ranges are obtained with requests to
> + * the clock source, as defined by the clock source
> + * descriptor
> + *
> + * the number of channels is in the GENERAL descriptor
> + */
> + if (!uaudio_getnum(p, 1, &type))
> + return 0;
> + if (type != 1) {
> + DPRINTF("uaudio_process_as_format: class v2: "
> +    "skipped unsupported type = %d\n", type);
> + return 1;
> + }
> + if (!uaudio_getnum(p, 1, &bps))
> + return 0;
> + if (!uaudio_getnum(p, 1, &bits))
> + return 0;
> +
> + /*
> + * nch is in the v2 general desc, rates come from the
> + * clock source, so we're done.
> + */
> + break;
> + }
> + a->bps = bps;
> + a->bits = bits;
> + return 1;
> +}
> +
> +/*
> + * Parse AS descriptors.
> + *
> + * The audio streaming (AS) interfaces are used to move data between
> + * the host and the device. On the one hand, the device has
> + * analog-to-digital (ADC) and digital-to-analog (DAC) converters
> + * which have their own low-jitter clock source. On other hand, the
> + * USB host runs a bus clock using another clock source. So both
> + * drift. That's why, the device sends feedback to the driver for the
> + * host to adjust continuously its data rate, hence the need for sync
> + * endpoints.
> + */
> +int
> +uaudio_process_as(struct uaudio_softc *sc,
> +    struct uaudio_blob *p, int ifnum, int altnum)
> +{
> + struct uaudio_alt *a, *anext, **pa;
> + struct uaudio_blob dp;
> + unsigned char *savep;
> + unsigned int type, subtype;
> + int ispcm = 0;
> +
> + a = malloc(sizeof(struct uaudio_alt), M_DEVBUF, M_WAITOK);
> + a->mode = 0;
> + a->nch = 0;
> + a->v1_rates = 0;
> + a->data_addr = 0;
> + a->sync_addr = 0;
> + a->ifnum = ifnum;
> + a->altnum = altnum;
> +
> + while (p->rptr != p->wptr) {
> + savep = p->rptr;
> + if (!uaudio_getdesc(p, &dp))
> + goto failed;
> + if (!uaudio_getnum(&dp, 1, &type))
> + goto failed;
> + if (type != UDESC_CS_INTERFACE) {
> + p->rptr = savep;
> + break;
> + }
> + if (!uaudio_getnum(&dp, 1, &subtype))
> + goto failed;
> + switch (subtype) {
> + case UAUDIO_AS_GENERAL:
> + if (!uaudio_process_as_general(sc, &dp, &ispcm, a))
> + goto failed;
> + if (!ispcm) {
> + DPRINTF("uaudio_process_as: non-pcm iface\n");
> + free(a, M_DEVBUF, sizeof(struct uaudio_alt));
> + return 1;
> + }
> + break;
> + case UAUDIO_AS_FORMAT:
> + if (!uaudio_process_as_format(sc, &dp, a))
> + goto failed;
> + break;
> + default:
> + DPRINTF("uaudio_process_as: unknown desc\n");
> + }
> + }
> +
> +
> + while (p->rptr != p->wptr) {
> + savep = p->rptr;
> + if (!uaudio_getdesc(p, &dp))
> + goto failed;
> + if (!uaudio_getnum(&dp, 1, &type))
> + goto failed;
> + if (type != UDESC_ENDPOINT) {
> + p->rptr = savep;
>   break;
>   }
> - }
> -
> - /* delete io_terminal */
> - for (i = 0; i < 256; i++) {
> - if (iot[i].d.desc == NULL)
> - continue;
> - if (iot[i].inputs != NULL) {
> - for (j = 0; j < iot[i].inputs_size; j++)
> - free(iot[i].inputs[j], M_TEMP, 0);
> - free(iot[i].inputs, M_TEMP, 0);
> - }
> - free(iot[i].output, M_TEMP, 0);
> - iot[i].d.desc = NULL;
> - }
> - free(iot, M_TEMP, 256 * sizeof(struct io_terminal));
> -
> - return (USBD_NORMAL_COMPLETION);
> -}
>  
> -int
> -uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi)
> -{
> - struct uaudio_softc *sc = addr;
> - struct mixerctl *mc;
> - int n, nctls, i;
> -
> - DPRINTFN(2,("%s: index=%d\n", __func__, mi->index));
> - if (usbd_is_dying(sc->sc_udev))
> - return (EIO);
> -
> - n = mi->index;
> - nctls = sc->sc_nctls;
> -
> - switch (n) {
> - case UAC_OUTPUT:
> - mi->type = AUDIO_MIXER_CLASS;
> - mi->mixer_class = UAC_OUTPUT;
> - mi->next = mi->prev = AUDIO_MIXER_LAST;
> - strlcpy(mi->label.name, AudioCoutputs, sizeof(mi->label.name));
> - return (0);
> - case UAC_INPUT:
> - mi->type = AUDIO_MIXER_CLASS;
> - mi->mixer_class = UAC_INPUT;
> - mi->next = mi->prev = AUDIO_MIXER_LAST;
> - strlcpy(mi->label.name, AudioCinputs, sizeof(mi->label.name));
> - return (0);
> - case UAC_EQUAL:
> - mi->type = AUDIO_MIXER_CLASS;
> - mi->mixer_class = UAC_EQUAL;
> - mi->next = mi->prev = AUDIO_MIXER_LAST;
> - strlcpy(mi->label.name, AudioCequalization,
> -    sizeof(mi->label.name));
> - return (0);
> - case UAC_RECORD:
> - mi->type = AUDIO_MIXER_CLASS;
> - mi->mixer_class = UAC_RECORD;
> - mi->next = mi->prev = AUDIO_MIXER_LAST;
> - strlcpy(mi->label.name, AudioCrecord, sizeof(mi->label.name));
> - return 0;
> - default:
> - break;
> - }
> + if (!uaudio_process_as_ep(sc, &dp, a))
> + goto failed;
>  
> - n -= UAC_NCLASSES;
> - if (n < 0 || n >= nctls)
> - return (ENXIO);
> -
> - mc = &sc->sc_ctls[n];
> - strlcpy(mi->label.name, mc->ctlname, sizeof(mi->label.name));
> - 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;
> - strlcpy(mi->un.e.member[0].label.name, AudioNoff,
> -    sizeof(mi->un.e.member[0].label.name));
> - mi->un.e.member[0].ord = 0;
> - strlcpy(mi->un.e.member[1].label.name, AudioNon,
> -    sizeof(mi->un.e.member[1].label.name));
> - mi->un.e.member[1].ord = 1;
> - break;
> - case MIX_SELECTOR:
> - mi->type = AUDIO_MIXER_ENUM;
> - mi->un.e.num_mem = mc->maxval - mc->minval + 1;
> - for (i = 0; i <= mc->maxval - mc->minval; i++) {
> - snprintf(mi->un.e.member[i].label.name,
> - sizeof(mi->un.e.member[i].label.name),
> - "%d", i + mc->minval);
> - mi->un.e.member[i].ord = i + mc->minval;
> + /* skip class-specific descriptors */
> + while (p->rptr != p->wptr) {
> + savep = p->rptr;
> + if (!uaudio_getdesc(p, &dp))
> + goto failed;
> + if (!uaudio_getnum(&dp, 1, &type))
> + goto failed;
> + if (type != UDESC_CS_ENDPOINT) {
> + p->rptr = savep;
> + break;
> + }
>   }
> - break;
> - default:
> - mi->type = AUDIO_MIXER_VALUE;
> - strlcpy(mi->un.v.units.name, mc->ctlunit,
> -    sizeof(mi->un.v.units.name));
> - 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(("%s: sc=%p\n", __func__, sc));
> - if (usbd_is_dying(sc->sc_udev))
> - return (EIO);
> + if (a->mode == 0) {
> + printf("%s: no data endpoints found\n", DEVNAME(sc));
> + free(a, M_DEVBUF, sizeof(struct uaudio_alt));
> + return 1;
> + }
>  
> - if ((flags & FWRITE) && !(sc->sc_mode & AUMODE_PLAY))
> - return (ENXIO);
> - if ((flags & FREAD) && !(sc->sc_mode & AUMODE_RECORD))
> - return (ENXIO);
> + /*
> + * Check that sync endpoint match data endpoint requirements
> + */
> + if (a->sync) {
> + if (a->sync_addr == 0) {
> + printf("%s: no sync endpoint found\n", DEVNAME(sc));
> + goto failed;
> + }
> + } else {
> + if (a->sync_addr != 0) {
> + printf("%s: sync endpoint not needed\n", DEVNAME(sc));
> + goto failed;
> + }
> + }
>  
> - return (0);
> + /*
> + * Append to list of alts, but keep the list sorted by number
> + * of channels, bits and rate. From the most capable to the
> + * less capable.
> + */
> + pa = &sc->alts;
> + while (1) {
> + if ((anext = *pa) == NULL)
> + break;
> + if (a->nch > anext->nch)
> + break;
> + else if (a->nch == anext->nch) {
> + if (a->bits > anext->bits)
> + break;
> + else if (sc->version == UAUDIO_V1 &&
> +    a->v1_rates > anext->v1_rates)
> + break;
> + }
> + pa = &anext->next;
> + }
> + a->next = *pa;
> + *pa = a;
> + return 1;
> +failed:
> + free(a, M_DEVBUF, sizeof(struct uaudio_alt));
> + return 0;
>  }
>  
>  /*
> - * Close function is called at splaudio().
> + * Populate the sc->params_list with combinations of play and rec alt
> + * settings that work together in full-duplex.
>   */
>  void
> -uaudio_close(void *addr)
> -{
> - struct uaudio_softc *sc = addr;
> -
> - if (sc->sc_playchan.altidx != -1)
> - uaudio_chan_close(sc, &sc->sc_playchan);
> - if (sc->sc_recchan.altidx != -1)
> - uaudio_chan_close(sc, &sc->sc_recchan);
> -}
> -
> -int
> -uaudio_halt_out_dma(void *addr)
> +uaudio_fixup_params(struct uaudio_softc *sc)
>  {
> - struct uaudio_softc *sc = addr;
> + struct uaudio_alt *ap, *ar, *a;
> + struct uaudio_params *p, **pp;
> + int rates;
>  
> - DPRINTF(("%s: enter\n", __func__));
> - if (sc->sc_playchan.pipe != NULL) {
> - uaudio_chan_close(sc, &sc->sc_playchan);
> - sc->sc_playchan.pipe = NULL;
> - if (sc->sc_playchan.sync_pipe != NULL)
> - sc->sc_playchan.sync_pipe = NULL;
> - uaudio_chan_free_buffers(sc, &sc->sc_playchan);
> - sc->sc_playchan.intr = NULL;
> + /*
> + * Add full-duplex parameter combinations.
> + */
> + pp = &sc->params_list;
> + for (ap = sc->alts; ap != NULL; ap = ap->next) {
> + if (ap->mode != AUMODE_PLAY)
> + continue;
> + for (ar = sc->alts; ar != NULL; ar = ar->next) {
> + if (ar->mode != AUMODE_RECORD)
> + continue;
> + if (ar->bps != ap->bps || ar->bits != ap->bits)
> + continue;
> + switch (sc->version) {
> + case UAUDIO_V1:
> + rates = ap->v1_rates & ar->v1_rates;
> + if (rates == 0)
> + continue;
> + break;
> + case UAUDIO_V2:
> + /* UAC v2.0 common rates */
> + rates = 0;
> + break;
> + }
> + p = malloc(sizeof(struct uaudio_params),
> +    M_DEVBUF, M_WAITOK);
> + p->palt = ap;
> + p->ralt = ar;
> + p->v1_rates = rates;
> + p->next = NULL;
> + *pp = p;
> + pp = &p->next;
> + }
>   }
> - return (0);
> -}
> -
> -int
> -uaudio_halt_in_dma(void *addr)
> -{
> - struct uaudio_softc *sc = addr;
>  
> - DPRINTF(("%s: enter\n", __func__));
> - if (sc->sc_recchan.pipe != NULL) {
> - uaudio_chan_close(sc, &sc->sc_recchan);
> - sc->sc_recchan.pipe = NULL;
> - if (sc->sc_recchan.sync_pipe != NULL)
> - sc->sc_recchan.sync_pipe = NULL;
> - uaudio_chan_free_buffers(sc, &sc->sc_recchan);
> - sc->sc_recchan.intr = NULL;
> + /*
> + * For unidirectional devices, add play-only and or rec-only
> + * parameters.
> + */
> + if (sc->params_list == NULL) {
> + for (a = sc->alts; a != NULL; a = a->next) {
> + p = malloc(sizeof(struct uaudio_params),
> +    M_DEVBUF, M_WAITOK);
> + if (a->mode == AUMODE_PLAY) {
> + p->palt = a;
> + p->ralt = NULL;
> + } else {
> + p->palt = NULL;
> + p->ralt = a;
> + }
> + p->v1_rates = a->v1_rates;
> + p->next = NULL;
> + *pp = p;
> + pp = &p->next;
> + }
>   }
> - return (0);
>  }
>  
>  /*
> - * Make sure the block size is large enough to hold at least 1 transfer.
> - * Ideally, the block size should be a multiple of the transfer size.
> - * Currently, the transfer size for play and record can differ, and there's
> - * no way to round playback and record blocksizes separately.
> + * Parse all descriptors and build configuration of the device.
>   */
>  int
> -uaudio_round_blocksize(void *addr, int blk)
> +uaudio_process_conf(struct uaudio_softc *sc, struct uaudio_blob *p)
>  {
> - struct uaudio_softc *sc = addr;
> - int bpf, pbpf, rbpf;
> + struct uaudio_blob dp;
> + unsigned int type, ifnum, altnum, nep, class, subclass;
> + int nac = 0;
>  
> - DPRINTF(("%s: p.mbpf=%d r.mbpf=%d\n", __func__,
> - sc->sc_playchan.max_bytes_per_frame,
> - sc->sc_recchan.max_bytes_per_frame));
> + while (p->rptr != p->wptr) {
> + if (!uaudio_getdesc(p, &dp))
> + return 0;
> + if (!uaudio_getnum(&dp, 1, &type))
> + return 0;
> + if (type != UDESC_INTERFACE)
> + continue;
> + if (!uaudio_getnum(&dp, 1, &ifnum))
> + return 0;
> + if (!uaudio_getnum(&dp, 1, &altnum))
> + return 0;
> + if (!uaudio_getnum(&dp, 1, &nep))
> + return 0;
> + if (!uaudio_getnum(&dp, 1, &class))
> + return 0;
> + if (!uaudio_getnum(&dp, 1, &subclass))
> + return 0;
> + if (class != UICLASS_AUDIO) {
> + DPRINTF("uaudio_process_conf: skipped iface\n");
> + continue;
> + }
>  
> - pbpf = rbpf = 0;
> - if (sc->sc_mode & AUMODE_PLAY) {
> - pbpf = (sc->sc_playchan.max_bytes_per_frame) *
> -    sc->sc_playchan.nframes;
> - }
> - if (sc->sc_mode & AUMODE_RECORD) {
> - rbpf = (sc->sc_recchan.max_bytes_per_frame) *
> -    sc->sc_recchan.nframes;
> + switch (subclass) {
> + case UISUBCLASS_AUDIOCONTROL:
> + usbd_claim_iface(sc->udev, ifnum);
> + if (nac == 1) {
> + printf("%s: only one AC iface allowed\n",
> +    DEVNAME(sc));
> + return 0;
> + }
> + if (!uaudio_process_ac(sc, p, ifnum))
> + return 0;
> + nac++;
> + break;
> + case UISUBCLASS_AUDIOSTREAM:
> + usbd_claim_iface(sc->udev, ifnum);
> + if (altnum == 0)
> + break; /* 0 is "stop sound", skip it */
> + if (!uaudio_process_as(sc, p, ifnum, altnum))
> + return 0;
> + }
>   }
> - bpf = max(pbpf, rbpf);
>  
> - if (blk < bpf)
> - blk = bpf;
> -
> -#ifdef DIAGNOSTIC
> - if (blk <= 0) {
> - printf("%s: blk=%d\n", __func__, blk);
> - blk = 512;
> - }
> -#endif
> + uaudio_fixup_params(sc);
>  
> - DPRINTFN(1,("%s: blk=%d\n", __func__, blk));
> - return (blk);
> + return 1;
>  }
>  
> +/*
> + * Allocate a isochronous transfer and its bounce-buffers with the
> + * given maximum framesize and maximum frames per transfer.
> + */
>  int
> -uaudio_get_props(void *addr)
> +uaudio_xfer_alloc(struct uaudio_softc *sc, struct uaudio_xfer *xfer,
> + unsigned int framesize, unsigned int count)
>  {
> - struct uaudio_softc *sc = addr;
> - int props = 0;
> + xfer->usb_xfer = usbd_alloc_xfer(sc->udev);
> + if (xfer->usb_xfer == NULL)
> + return ENOMEM;
> +
> + xfer->buf = usbd_alloc_buffer(xfer->usb_xfer, framesize * count);
> + if (xfer->buf == NULL)
> + return ENOMEM;
> +
> + xfer->sizes = mallocarray(count,
> +    sizeof(xfer->sizes[0]), M_DEVBUF, M_WAITOK);
> + if (xfer->sizes == NULL)
> + return ENOMEM;
>  
> - if (!(sc->sc_quirks & UAUDIO_FLAG_DEPENDENT))
> - props |= AUDIO_PROP_INDEPENDENT;
> -
> - if ((sc->sc_mode & (AUMODE_PLAY | AUMODE_RECORD)) ==
> -    (AUMODE_PLAY | AUMODE_RECORD))
> - props |= AUDIO_PROP_FULLDUPLEX;
> -
> - return props;
> + return 0;
>  }
>  
> -int
> -uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue,
> -   int wIndex, int len)
> +/*
> + * Free a isochronous transfer and its bounce-buffers.
> + */
> +void
> +uaudio_xfer_free(struct uaudio_softc *sc, struct uaudio_xfer *xfer,
> + unsigned int count)
>  {
> - usb_device_request_t req;
> - u_int8_t data[4];
> - usbd_status err;
> - int val;
> -
> - if (wValue == -1)
> - return (0);
> -
> - req.bmRequestType = type;
> - req.bRequest = which;
> - USETW(req.wValue, wValue);
> - USETW(req.wIndex, wIndex);
> - USETW(req.wLength, len);
> - DPRINTFN(2,("%s: type=0x%02x req=0x%02x wValue=0x%04x "
> -    "wIndex=0x%04x len=%d\n",
> -    __func__, type, which, wValue, wIndex, len));
> - err = usbd_do_request(sc->sc_udev, &req, data);
> - if (err) {
> - DPRINTF(("%s: err=%s\n", __func__, usbd_errstr(err)));
> - return (-1);
> - }
> - switch (len) {
> - case 1:
> - val = data[0];
> - break;
> - case 2:
> - val = data[0] | (data[1] << 8);
> - break;
> - default:
> - DPRINTF(("%s: bad length=%d\n", __func__, len));
> - return (-1);
> + if (xfer->usb_xfer != NULL) {
> + /* frees request buffer as well */
> + usbd_free_xfer(xfer->usb_xfer);
> + xfer->usb_xfer = NULL;
> + }
> + if (xfer->sizes != NULL) {
> + free(xfer->sizes, M_DEVBUF,
> +    sizeof(xfer->sizes[0]) * count);
> + xfer->sizes = NULL;
>   }
> - DPRINTFN(2,("%s: val=%d\n", __func__, val));
> - return (val);
>  }
>  
> +/*
> + * Close a stream and free all associated resources
> + */
>  void
> -uaudio_set(struct uaudio_softc *sc, int which, int type, int wValue,
> -   int wIndex, int len, int val)
> +uaudio_stream_close(struct uaudio_softc *sc, int dir)
>  {
> - usb_device_request_t req;
> - u_int8_t data[4];
> - usbd_status err;
> + struct uaudio_stream *s = &sc->pstream;
> + struct uaudio_alt *a = sc->params->palt;
> + struct usbd_interface *iface;
> + int err, i;
> +
> + if (dir == AUMODE_PLAY) {
> + s = &sc->pstream;
> + a = sc->params->palt;
> + } else {
> + s = &sc->rstream;
> + a = sc->params->ralt;
> + }
>  
> - if (wValue == -1)
> - return;
> + err = usbd_device2interface_handle(sc->udev, a->ifnum, &iface);
> + if (err)
> + printf("%s: can't get iface handle\n", DEVNAME(sc));
>  
> - 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;
> + if (s->data_pipe) {
> + usbd_abort_pipe(s->data_pipe);
> + usbd_close_pipe(s->data_pipe);
> + s->data_pipe = NULL;
>   }
> - DPRINTFN(2,("%s: type=0x%02x req=0x%02x wValue=0x%04x "
> -    "wIndex=0x%04x len=%d, val=%d\n", __func__,
> -    type, which, wValue, wIndex, len, val & 0xffff));
> - err = usbd_do_request(sc->sc_udev, &req, data);
> -#ifdef UAUDIO_DEBUG
> - if (err)
> - DPRINTF(("%s: err=%d\n", __func__, 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;
> + if (s->sync_pipe) {
> + usbd_abort_pipe(s->sync_pipe);
> + usbd_close_pipe(s->sync_pipe);
> + s->sync_pipe = NULL;
>   }
> - return (val);
> -}
>  
> -int
> -uaudio_unsignext(int type, int val)
> -{
> - if (!MIX_UNSIGNED(type)) {
> - if (MIX_SIZE(type) == 2)
> - val = (u_int16_t)val;
> - else
> - val = (u_int8_t)val;
> + err = usbd_set_interface(iface, 0);
> + if (err)
> + printf("%s: can't reset interface\n", DEVNAME(sc));
> +
> + for (i = 0; i < UAUDIO_NXFERS; i++) {
> + uaudio_xfer_free(sc, s->data_xfers + i, s->nframes_max);
> + uaudio_xfer_free(sc, s->sync_xfers + i, 1);
>   }
> - return (val);
>  }
>  
> +/*
> + * Open a stream with the given buffer settings and set the current
> + * interface alt setting.
> + */
>  int
> -uaudio_value2bsd(struct mixerctl *mc, int val)
> +uaudio_stream_open(struct uaudio_softc *sc, int dir,
> +    void *start, void *end, size_t blksz, void (*intr)(void *), void *arg)
>  {
> - int range;
> - DPRINTFN(5, ("%s: type=%03x val=%d min=%d max=%d ",
> -     __func__, mc->type, val, mc->minval, mc->maxval));
> - if (mc->type == MIX_ON_OFF) {
> - val = (val != 0);
> - } else if (mc->type == MIX_SELECTOR) {
> - if (val < mc->minval || val > mc->maxval)
> - val = mc->minval;
> + struct uaudio_stream *s;
> + struct uaudio_alt *a;
> + struct usbd_interface *iface;
> + unsigned char req_buf[4];
> + unsigned int nsamp, spf_max;
> + int err, i;
> +
> + if (dir == AUMODE_PLAY) {
> + s = &sc->pstream;
> + a = sc->params->palt;
>   } else {
> - range = mc->maxval - mc->minval;
> - if (range == 0)
> - val = 0;
> - else
> - val = 255 * (uaudio_signext(mc->type, val) -
> -    mc->minval) / range;
> + s = &sc->rstream;
> + a = sc->params->ralt;
>   }
> - DPRINTFN(5, ("val'=%d\n", val));
> - return (val);
> -}
>  
> -int
> -uaudio_bsd2value(struct mixerctl *mc, int val)
> -{
> - DPRINTFN(5,("%s: type=%03x val=%d min=%d max=%d ",
> -    __func__, mc->type, val, mc->minval, mc->maxval));
> - if (mc->type == MIX_ON_OFF) {
> - val = (val != 0);
> - } else if (mc->type == MIX_SELECTOR) {
> - if (val < mc->minval || val > mc->maxval)
> - val = mc->minval;
> - } else
> - val = uaudio_unsignext(mc->type,
> -    val * (mc->maxval - mc->minval) / 255 + mc->minval);
> - DPRINTFN(5, ("val'=%d\n", val));
> - return (val);
> -}
> + for (i = 0; i < UAUDIO_NXFERS; i++) {
> + s->data_xfers[i].usb_xfer = NULL;
> + s->data_xfers[i].sizes = NULL;
> + s->sync_xfers[i].usb_xfer = NULL;
> + s->sync_xfers[i].sizes = NULL;
> + }
> + s->data_pipe = NULL;
> + s->sync_pipe = NULL;
>  
> -int
> -uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc,
> -       int chan)
> -{
> - int val;
> + /* block size in samples, i.e. samples per xfer */
> + nsamp = blksz / (a->nch * a->bps);
>  
> - DPRINTFN(5,("%s: which=%d chan=%d\n", __func__, 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));
> -}
> + s->nframes = a->fps * nsamp / sc->rate;
> + if (s->nframes == 0) {
> + printf("%s: zero frames per transfer, blksz = %zu\n",
> +    DEVNAME(sc), blksz);
> + return EIO;
> + }
>  
> -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);
> -}
> + /* check that round_blocksize() picked the right blocks size */
> + if (nsamp % s->nframes != 0) {
> + printf("%s: block size (%d samples) not multiple of "
> +    "frame per xfer (%d)\n", DEVNAME(sc), nsamp, s->nframes);
> + return EIO;
> + }
>  
> -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;
> + s->spf = (nsamp / s->nframes) << 16;
> + if (s->spf == 0) {
> + printf("%s: zero samples per frame, blksz = %zu\n",
> +    DEVNAME(sc), blksz);
> + return EIO;
> + }
>  
> - DPRINTFN(2,("%s: index=%d\n", __func__, cp->dev));
> + s->spf_min = s->spf * 7 / 8;
> + s->spf_max = s->spf * 9 / 8;
>  
> - if (usbd_is_dying(sc->sc_udev))
> - return (EIO);
> + spf_max = (a->maxpkt / (a->bps * a->nch)) << 16;
> + if (s->spf_max > spf_max)
> + s->spf_max = spf_max;
>  
> - n = cp->dev - UAC_NCLASSES;
> - if (n < 0 || n >= sc->sc_nctls)
> - return (ENXIO);
> - mc = &sc->sc_ctls[n];
> + s->nframes_min = (nsamp << 16ULL) / s->spf_max;
> + s->nframes_max = (nsamp << 16ULL) / s->spf_min + 1;
>  
> - 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 (mc->type == MIX_SELECTOR) {
> - 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);
> -}
> + s->maxpkt = a->bps * a->nch * ((s->spf_max + 0xffff) >> 16);
>  
> -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,("%s: index = %d\n", __func__, cp->dev));
> - if (usbd_is_dying(sc->sc_udev))
> - return (EIO);
> -
> - n = cp->dev - UAC_NCLASSES;
> - 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 (mc->type == MIX_SELECTOR) {
> - 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);
> -}
> + /*
> + * we want to be sure that the device will cross the block
> + * boundary, so we ask for slightly more frames than a single
> + * block
> + */
> + s->safe_blksz = blksz +
> +    (a->fps / USB_FRAMES_PER_SECOND) *
> +    (s->spf >> 16) * a->bps * a->nch;
> + s->nframes_max += (a->fps / USB_FRAMES_PER_SECOND);
> +
> + DPRINTF("uaudio_stream_open: dir = %s, nsamp = %u, maxpkt = %u\n",
> +    dir == AUMODE_PLAY ? "play" : "rec", nsamp, s->maxpkt);
> + DPRINTF("uaudio_stream_open: spf = 0x%x in [0x%x:0x%x]\n",
> +    s->spf, s->spf_min, s->spf_max);
> + DPRINTF("uaudio_stream_open: nframes = %u in [%u:%u]\n",
> +    s->nframes, s->nframes_min, s->nframes_max);
> + DPRINTF("uaudio_stream_open: blksz = %zd, safe_blksz = %d\n",
> +    blksz, s->safe_blksz);
>  
> -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_recchan;
> - usbd_status err;
> - int i, s;
> + /*
> + * XXX: there are usb driver bugs & limitations, just fail if
> + * we hit them:
> + *
> + * - there must be at least 3 outstanding transfers, otherwise
> + *   transfers don't complete timely and audio stutters
> + *
> + * - large play xfers don't work (playback stutters in
> + *   full-duplex mode).  It appears that full-duplex with 25ms
> + *   blocks kinda works on uhci and ehci (on root hub only).
> + */
> + if (UAUDIO_NXFERS * blksz > end - start) {
> + printf("%s: number of blocks must be at least %d\n",
> +    DEVNAME(sc), UAUDIO_NXFERS);
> + return EIO;
> + }
> + if (s->nframes > 25 * (a->fps / USB_FRAMES_PER_SECOND)) {
> + printf("%s: block size must be at most 25ms\n", DEVNAME(sc));
> + return EIO;
> + }
>  
> - if (usbd_is_dying(sc->sc_udev))
> - return (EIO);
> + /*
> + * XXX: Isochronous transfers don't work with certain USB
> + * host drivers. Until they get fixed, disallow configurations
> + * where audio is well known to be broken.
> + */
> +#if NXHCI > 0
> + if (sc->udev->bus->bdev.dv_cfdata->cf_driver == &xhci_cd) {
> + printf("%s: xhci(4) not supported yet, "
> +    "try another port\n", DEVNAME(sc));
> + return ENXIO;
> + }
> +#endif
> +#if NEHCI > 0
> + if (sc->udev->bus->bdev.dv_cfdata->cf_driver == &ehci_cd &&
> +    sc->udev->speed == USB_SPEED_FULL &&
> +    sc->udev->myhub != sc->udev->bus->root_hub) {
> + printf("%s: usb1.1 device not on ehci(4) root hub, "
> +    "try another port.\n", DEVNAME(sc));
> + return ENXIO;
> + }
> +#endif
>  
> - DPRINTFN(3,("%s: sc=%p start=%p end=%p "
> -    "blksize=%d\n", __func__, sc, start, end, blksize));
> + for (i = 0; i < UAUDIO_NXFERS; i++) {
> + err = uaudio_xfer_alloc(sc, s->data_xfers + i,
> +    s->maxpkt, s->nframes_max);
> + if (err)
> + goto failed;
> + if (a->sync) {
> + err = uaudio_xfer_alloc(sc, s->sync_xfers + i,
> +    sc->sync_pktsz, 1);
> + if (err)
> + goto failed;
> + }
> + }
>  
> - uaudio_chan_set_param(ch, start, end, blksize);
> - DPRINTFN(3,("%s: sample_size=%d bytes/frame=%d "
> -    "fraction=0.%03d\n",
> -    __func__, ch->sample_size, ch->bytes_per_frame,
> -    ch->fraction));
> + err = usbd_device2interface_handle(sc->udev, a->ifnum, &iface);
> + if (err) {
> + printf("%s: can't get iface handle\n", DEVNAME(sc));
> + goto failed;
> + }
>  
> - err = uaudio_chan_alloc_buffers(sc, ch);
> - if (err)
> - return (EIO);
> + err = usbd_set_interface(iface, a->altnum);
> + if (err) {
> + printf("%s: can't get interface handle\n", DEVNAME(sc));
> + goto failed;
> + }
>  
> - err = uaudio_chan_open(sc, ch);
> + err = usbd_open_pipe(iface, a->data_addr, 0, &s->data_pipe);
>   if (err) {
> - uaudio_chan_free_buffers(sc, ch);
> - return (EIO);
> + printf("%s: can't open data pipe\n", DEVNAME(sc));
> + goto failed;
>   }
>  
> - ch->intr = intr;
> - ch->arg = arg;
> + if (a->sync) {
> + err = usbd_open_pipe(iface, a->sync_addr, 0, &s->sync_pipe);
> + if (err) {
> + printf("%s: can't open sync pipe\n", DEVNAME(sc));
> + goto failed;
> + }
> + }
>  
> - s = splusb();
> - for (i = 0; i < UAUDIO_NCHANBUFS; i++)
> - uaudio_chan_rtransfer(ch);
> - splx(s);
> + switch (sc->version) {
> + case UAUDIO_V1:
> + req_buf[0] = sc->rate;
> + req_buf[1] = sc->rate >> 8;
> + req_buf[2] = sc->rate >> 16;
> + if (!uaudio_req(sc, UT_WRITE_CLASS_ENDPOINT,
> + UAUDIO_V1_REQ_SET_CUR, UAUDIO_REQSEL_RATE, 0,
> + a->data_addr, 0, req_buf, 3)) {
> + printf("%s: failed to set ep rate\n", DEVNAME(sc));
> + goto failed;
> + }
> + break;
> + case UAUDIO_V2:
> + req_buf[0] = sc->rate;
> + req_buf[1] = sc->rate >> 8;
> + req_buf[2] = sc->rate >> 16;
> + req_buf[3] = sc->rate >> 24;
> + if (!uaudio_req(sc, UT_WRITE_CLASS_INTERFACE,
> + UAUDIO_V2_REQ_CUR, UAUDIO_REQSEL_RATE, 0,
> + a->ifnum, sc->clock->id, req_buf, 4)) {
> + printf("%s: failed to set clock rate\n", DEVNAME(sc));
> + goto failed;
> + }
> + break;
> + }
> +
> + s->data_nextxfer = 0;
> + s->sync_nextxfer = 0;
> + s->remain = 0;
> +
> + s->intr = intr;
> + s->arg = arg;
> + s->ring_start = start;
> + s->ring_end = end;
> + s->ring_blksz = blksz;
> +
> + s->ring_pos = s->ring_start;
> + s->ring_offs = 0;
> +
> + /*
> + * For play mode, we don't know if the device is locked, if it
> + * isn't it will play silence, which is OK. For recording, we've
> + * to generate silence for upper layers to stay in sync.
> + */
> + s->locked = (dir == AUMODE_PLAY) ? 1 : 0;
> + return 0;
>  
> - return (0);
> +failed:
> + uaudio_stream_close(sc, dir);
> + return ENOMEM;
>  }
>  
> -int
> -uaudio_trigger_output(void *addr, void *start, void *end, int blksize,
> -      void (*intr)(void *), void *arg,
> -      struct audio_params *param)
> +/*
> + * Submit a play data transfer to the USB driver.
> + */
> +void
> +uaudio_pdata_xfer(struct uaudio_softc *sc)
>  {
> - struct uaudio_softc *sc = addr;
> - struct chan *ch = &sc->sc_playchan;
> - usbd_status err;
> - int i, s;
> +#ifdef UAUDIO_DEBUG
> + struct timeval tv;
> +#endif
> + struct uaudio_stream *s = &sc->pstream;
> + struct uaudio_alt *a = sc->params->palt;
> + struct uaudio_xfer *xfer;
> + unsigned int fsize, bpf;
> + int done;
> + size_t count;
> + unsigned char *buf;
> + int err;
> +
> + xfer = s->data_xfers + s->data_nextxfer;
> + bpf = a->bps * a->nch;
> + buf = xfer->buf;
> + xfer->nframes = 0;
> + done = s->ring_offs;
>  
> - if (usbd_is_dying(sc->sc_udev))
> - return (EIO);
> + while (1) {
> + /*
> + * if we crossed the next block boundary, we're done
> + */
> + if (done >= s->safe_blksz) {
> + done:
> + xfer->size = done - s->ring_offs;
> + s->ring_offs = done - s->ring_blksz;
> + break;
> + }
>  
> - DPRINTFN(3,("%s: sc=%p start=%p end=%p "
> -    "blksize=%d\n", __func__, sc, start, end, blksize));
> + /*
> + * this can't happen, debug only
> + */
> + if (xfer->nframes == s->nframes_max) {
> + printf("%s: too many frames for xfer: "
> +    "done = %u, blksz = %d\n",
> +    DEVNAME(sc), done, s->ring_blksz);
> + goto done;
> + }
>  
> - uaudio_chan_set_param(ch, start, end, blksize);
> - DPRINTFN(3,("%s: sample_size=%d bytes/frame=%d "
> -    "fraction=0.%03d\n", __func__, ch->sample_size,
> -    ch->bytes_per_frame, ch->fraction));
> + /*
> + * calculate frame size and adjust state
> + */
> + s->remain += s->spf;
> + fsize = (s->remain >> 16) * bpf;
> + s->remain &= 0xffff;
> + done += fsize;
> + xfer->sizes[xfer->nframes] = fsize;
> + xfer->nframes++;
>  
> - err = uaudio_chan_alloc_buffers(sc, ch);
> - if (err)
> - return (EIO);
> + /*
> + * fill the frame from ring buffer to frame, handling
> + * boundary conditions
> + */
> + while (fsize > 0) {
> + count = s->ring_end - s->ring_pos;
> + if (count > fsize)
> + count = fsize;
> + memcpy(buf, s->ring_pos, count);
> + s->ring_pos += count;
> + if (s->ring_pos == s->ring_end)
> + s->ring_pos = s->ring_start;
> + buf += count;
> + fsize -= count;
> + }
>  
> - err = uaudio_chan_open(sc, ch);
> - if (err) {
> - uaudio_chan_free_buffers(sc, ch);
> - return (EIO);
>   }
>  
> - ch->intr = intr;
> - ch->arg = arg;
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug >= 2) {
> + getmicrotime(&tv);
> + printf("uaudio_pdata_xfer: %llu.%06lu: "
> +    "%luB, %u fr, remain = 0x%x, offs = %d\n",
> +    tv.tv_sec, tv.tv_usec,
> +    buf - xfer->buf, xfer->nframes, s->remain, s->ring_offs);
> + }
> +#endif
>  
> - s = splusb();
> - for (i = 0; i < UAUDIO_NCHANBUFS; i++)
> - uaudio_chan_ptransfer(ch);
> - if (ch->sync_pipe) {
> - for (i = 0; i < UAUDIO_NSYNCBUFS; i++)
> - uaudio_chan_psync_transfer(ch);
> + /* this can't happen, debug only */
> + if (xfer->nframes == 0) {
> + printf("%s: zero frame play xfer\n", DEVNAME(sc));
> + return;
>   }
> - splx(s);
>  
> - return (0);
> + usbd_setup_isoc_xfer(xfer->usb_xfer, s->data_pipe, sc,
> +    xfer->sizes, xfer->nframes, USBD_NO_COPY, uaudio_pdata_intr);
> +
> + err = usbd_transfer(xfer->usb_xfer);
> + if (err != USBD_IN_PROGRESS)
> + printf("%s: uaudio_pdata_xfer: err = %d\n", DEVNAME(sc), err);
> +
> + if (++s->data_nextxfer == UAUDIO_NXFERS)
> + s->data_nextxfer = 0;
> +
>  }
>  
> -/* Set up a pipe for a channel. */
> -usbd_status
> -uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch)
> +/*
> + * Callback called by the USB driver upon completion of play data transfer.
> + */
> +void
> +uaudio_pdata_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status)
>  {
> - struct as_info *as = &sc->sc_alts[ch->altidx];
> - int endpt = as->edesc->bEndpointAddress;
> - usbd_status err;
> +#ifdef UAUDIO_DEBUG
> + struct timeval tv;
> +#endif
> + struct uaudio_softc *sc = arg;
> + struct uaudio_stream *s = &sc->pstream;
> + struct uaudio_xfer *xfer;
>  
> - DPRINTF(("%s: endpt=0x%02x, speed=%d, alt=%d\n",
> - __func__, endpt, ch->sample_rate, as->alt));
> + if (status != 0) {
> + DPRINTF("uaudio_pdata_intr: xfer status = %d\n", status);
> + return;
> + }
>  
> - /* Set alternate interface corresponding to the mode. */
> - err = usbd_set_interface(as->ifaceh, as->alt);
> - if (err) {
> - DPRINTF(("%s: usbd_set_interface failed\n", __func__));
> - return (err);
> + xfer = s->data_xfers + s->data_nextxfer;
> + if (xfer->usb_xfer != usb_xfer) {
> + DPRINTF("uaudio_pdata_intr: wrong xfer\n");
> + return;
>   }
>  
> - /*
> - * If just one sampling rate is supported,
> - * no need to call uaudio_set_speed().
> - * Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request.
> - */
> - if (as->asf1desc->bSamFreqType != 1) {
> - err = uaudio_set_speed(sc, endpt, ch->sample_rate);
> - if (err)
> - DPRINTF(("%s: set_speed failed err=%s\n",
> - __func__, usbd_errstr(err)));
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug >= 2) {
> + getmicrotime(&tv);
> + printf("uaudio_pdata_intr: %llu.%06lu: %uB, status = %d\n",
> +    tv.tv_sec, tv.tv_usec, xfer->size, status);
>   }
> +#endif
> + uaudio_pdata_xfer(sc);
>  
> - if (sc->sc_quirks & UAUDIO_FLAG_EMU0202)
> - uaudio_set_speed_emu0202(ch);
> + mtx_enter(&audio_lock);
> + s->intr(s->arg);
> + mtx_leave(&audio_lock);
> +}
>  
> - ch->pipe = 0;
> - ch->sync_pipe = 0;
> - DPRINTF(("%s: create pipe to 0x%02x\n", __func__, endpt));
> - err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->pipe);
> - if (err) {
> - printf("%s: error creating pipe: err=%s endpt=0x%02x\n",
> -    __func__, usbd_errstr(err), endpt);
> - return err;
> - }
> - if (as->edesc1 != NULL) {
> - endpt = as->edesc1->bEndpointAddress;
> - DPRINTF(("%s: create sync-pipe to 0x%02x\n", __func__, endpt));
> - err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->sync_pipe);
> - if (err) {
> - printf("%s: error creating sync-pipe: err=%s endpt=0x%02x\n",
> -    __func__, usbd_errstr(err), endpt);
> - }
> +/*
> + * Submit a play sync transfer to the USB driver.
> + */
> +void
> +uaudio_psync_xfer(struct uaudio_softc *sc)
> +{
> +#ifdef UAUDIO_DEBUG
> + struct timeval tv;
> +#endif
> + struct uaudio_stream *s = &sc->pstream;
> + struct uaudio_xfer *xfer;
> + unsigned int i;
> + int err;
> +
> + xfer = s->sync_xfers + s->sync_nextxfer;
> + xfer->nframes = 1;
> +
> + for (i = 0; i < xfer->nframes; i++)
> + xfer->sizes[i] = sc->sync_pktsz;
> +
> + xfer->size = xfer->nframes * sc->sync_pktsz;
> +
> +#ifdef UAUDIO_DEBUG
> + memset(xfer->buf, 0xd0, sc->sync_pktsz * xfer->nframes);
> +#endif
> +
> + usbd_setup_isoc_xfer(xfer->usb_xfer, s->sync_pipe, sc,
> +    xfer->sizes, xfer->nframes,
> +    USBD_NO_COPY | USBD_SHORT_XFER_OK,
> +    uaudio_psync_intr);
> +
> + err = usbd_transfer(xfer->usb_xfer);
> + if (err != USBD_IN_PROGRESS)
> + printf("%s: uaudio_psync_xfer: err = %d\n", DEVNAME(sc), err);
> +
> + if (++s->sync_nextxfer == UAUDIO_NXFERS)
> + s->sync_nextxfer = 0;
> +
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug >= 2) {
> + getmicrotime(&tv);
> + printf("uaudio_psync_xfer: %llu.%06lu: %dB, %d fr\n",
> +    tv.tv_sec, tv.tv_usec, sc->sync_pktsz, xfer->nframes);
>   }
> - return err;
> +#endif
>  }
>  
> +/*
> + * Callback called by the USB driver upon completion of play sync transfer.
> + */
>  void
> -uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch)
> +uaudio_psync_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status)
>  {
> - struct as_info *as = &sc->sc_alts[ch->altidx];
> +#ifdef UAUDIO_DEBUG
> + struct timeval tv;
> +#endif
> + struct uaudio_softc *sc = arg;
> + struct uaudio_stream *s = &sc->pstream;
> + struct uaudio_xfer *xfer;
> + unsigned char *buf;
> + unsigned int i;
> + int32_t val;
>  
> - as->sc_busy = 0;
> - if (sc->sc_nullalt >= 0) {
> - DPRINTF(("%s: set null alt=%d\n",
> - __func__, sc->sc_nullalt));
> - usbd_set_interface(as->ifaceh, sc->sc_nullalt);
> + if (status != 0) {
> + DPRINTF("uaudio_psync_intr: xfer status = %d\n", status);
> + return;
>   }
> - if (ch->pipe) {
> - usbd_abort_pipe(ch->pipe);
> - usbd_close_pipe(ch->pipe);
> +
> + xfer = s->sync_xfers + s->sync_nextxfer;
> + if (xfer->usb_xfer != usb_xfer) {
> + DPRINTF("uaudio_psync_intr: wrong xfer\n");
> + return;
>   }
> - if (ch->sync_pipe) {
> - usbd_abort_pipe(ch->sync_pipe);
> - usbd_close_pipe(ch->sync_pipe);
> +
> + buf = xfer->buf;
> + for (i = 0; i < xfer->nframes; i++) {
> + if (xfer->sizes[i] == sc->sync_pktsz) {
> + val = buf[0] | buf[1] << 8 | buf[2] << 16;
> + if (sc->sync_pktsz == 4)
> + val |= xfer->buf[3] << 24;
> + else
> + val <<= 2;
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug >= 2) {
> + getmicrotime(&tv);
> + printf("uaudio_psync_intr: %llu.%06lu: "
> +    "spf: %08x\n",
> +    tv.tv_sec, tv.tv_usec, val);
> + }
> +#endif
> + if (val > s->spf_max)
> + s->spf = s->spf_max;
> + else if (val < s->spf_min)
> + s->spf = s->spf_min;
> + else
> + s->spf = val;
> + }
> + buf += sc->sync_pktsz;
>   }
> +
> + uaudio_psync_xfer(sc);
>  }
>  
> -usbd_status
> -uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch)
> +/*
> + * Submit a rec data transfer to the USB driver.
> + */
> +void
> +uaudio_rdata_xfer(struct uaudio_softc *sc)
>  {
> - struct as_info *as = &sc->sc_alts[ch->altidx];
> - struct usbd_xfer *xfer;
> - void *buf;
> - int i, size;
> -
> - DPRINTF(("%s: max_bytes_per_frame=%d nframes=%d\n", __func__,
> -    ch->max_bytes_per_frame, ch->nframes));
> +#ifdef UAUDIO_DEBUG
> + struct timeval tv;
> +#endif
> + struct uaudio_stream *s = &sc->rstream;
> + struct uaudio_alt *a = sc->params->ralt;
> + struct uaudio_xfer *xfer;
> + unsigned int fsize, bpf;
> + int done;
> + int err;
> +
> + xfer = s->data_xfers + s->data_nextxfer;
> + bpf = a->bps * a->nch;
> + xfer->nframes = 0;
> + done = s->ring_offs;
>  
> - size = ch->max_bytes_per_frame * ch->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;
> + while (1) {
> + /*
> + * if we crossed the next block boundary, we're done
> + */
> + if (done >= s->safe_blksz) {
> + done:
> + xfer->size = done - s->ring_offs;
> + s->ring_offs = done - s->ring_blksz;
> + break;
>   }
> - ch->chanbufs[i].buffer = buf;
> - ch->chanbufs[i].chan = ch;
> - }
> - if (as->edesc1 != NULL) {
> - size = (ch->hi_speed ? 4 : 3) * ch->nsync_frames;
> - for (i = 0; i < UAUDIO_NSYNCBUFS; i++) {
> - xfer = usbd_alloc_xfer(sc->sc_udev);
> - if (xfer == 0)
> - goto bad_sync;
> - ch->syncbufs[i].xfer = xfer;
> - buf = usbd_alloc_buffer(xfer, size);
> - if (buf == 0) {
> - i++;
> - goto bad_sync;
> - }
> - ch->syncbufs[i].buffer = buf;
> - ch->syncbufs[i].chan = ch;
> +
> + /*
> + * this can't happen, debug only
> + */
> + if (xfer->nframes == s->nframes_max) {
> + printf("%s: too many frames for rec xfer: "
> +    "done = %d, blksz = %d\n",
> +    DEVNAME(sc), done, s->ring_blksz);
> + goto done;
>   }
> - }
>  
> - return (USBD_NORMAL_COMPLETION);
> + /*
> + * estimate next block using s->spf, but allow
> + * transfers up to maxpkt
> + */
> + s->remain += s->spf;
> + fsize = (s->remain >> 16) * bpf;
> + s->remain &= 0xffff;
> + done += fsize;
> + xfer->sizes[xfer->nframes] = s->maxpkt;
> + xfer->nframes++;
>  
> -bad:
> - while (--i >= 0)
> - /* implicit buffer free */
> - usbd_free_xfer(ch->chanbufs[i].xfer);
> - return (USBD_NOMEM);
> + }
>  
> -bad_sync:
> - while (--i >= 0)
> - /* implicit buffer free */
> - usbd_free_xfer(ch->syncbufs[i].xfer);
> - return (USBD_NOMEM);
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug >= 2) {
> + getmicrotime(&tv);
> + printf("uaudio_rdata_xfer: %llu.%06lu: "
> +    "%u fr, %dB (max %d), offs = %d\n",
> +    tv.tv_sec, tv.tv_usec,
> +    xfer->nframes, xfer->size,
> +    s->maxpkt * xfer->nframes, s->ring_offs);
> + }
> +#endif
>  
> -}
> + /* this can't happen, debug only */
> + if (xfer->nframes == 0) {
> + printf("%s: zero frame rec xfer\n", DEVNAME(sc));
> + return;
> + }
>  
> -void
> -uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch)
> -{
> - struct as_info *as = &sc->sc_alts[ch->altidx];
> - int i;
> +#ifdef UAUDIO_DEBUG
> + memset(xfer->buf, 0xd0, s->maxpkt * xfer->nframes);
> +#endif
> + usbd_setup_isoc_xfer(xfer->usb_xfer, s->data_pipe, sc,
> +    xfer->sizes, xfer->nframes, USBD_NO_COPY | USBD_SHORT_XFER_OK,
> +    uaudio_rdata_intr);
> +
> + err = usbd_transfer(xfer->usb_xfer);
> + if (err != USBD_IN_PROGRESS)
> + printf("%s: uaudio_rdata_xfer: err = %d\n", DEVNAME(sc), err);
>  
> - for (i = 0; i < UAUDIO_NCHANBUFS; i++)
> - usbd_free_xfer(ch->chanbufs[i].xfer);
> - if (as->edesc1 != NULL) {
> - for (i = 0; i < UAUDIO_NSYNCBUFS; i++)
> - usbd_free_xfer(ch->syncbufs[i].xfer);
> - }
> + if (++s->data_nextxfer == UAUDIO_NXFERS)
> + s->data_nextxfer = 0;
>  }
>  
> -/* Called at splusb() */
> +/*
> + * Callback called by the USB driver upon completion of rec data transfer.
> + */
>  void
> -uaudio_chan_ptransfer(struct chan *ch)
> +uaudio_rdata_intr(struct usbd_xfer *usb_xfer, void *arg, usbd_status status)
>  {
> - struct chanbuf *cb;
> - u_char *pos;
> - int i, n, size, residue, total;
> +#ifdef UAUDIO_DEBUG
> + struct timeval tv;
> +#endif
> + struct uaudio_softc *sc = arg;
> + struct uaudio_stream *s = &sc->rstream;
> + struct uaudio_alt *a = sc->params->ralt;
> + struct uaudio_xfer *xfer;
> + unsigned char *buf, *framebuf;
> + unsigned int count, fsize, nframes, bpf;
> + unsigned int data_size, null_size;
>  
> - if (usbd_is_dying(ch->sc->sc_udev))
> + if (status != 0) {
> + DPRINTF("uaudio_rdata_intr: xfer status = %d\n", status);
>   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 < ch->nframes; i++) {
> - size = ch->bytes_per_frame;
> - residue += ch->fraction;
> - if (residue >= ch->frac_denom) {
> - if ((ch->sc->sc_altflags & UA_NOFRAC) == 0)
> - size += ch->sample_size;
> - residue -= ch->frac_denom;
> - }
> - cb->sizes[i] = size;
> - total += size;
> + xfer = s->data_xfers + s->data_nextxfer;
> + if (xfer->usb_xfer != usb_xfer) {
> + DPRINTF("uaudio_rdata_intr: wrong xfer\n");
> + return;
>   }
> - ch->residue = residue;
> - cb->size = total;
>  
> - /*
> - * Transfer data from upper layer buffer to channel buffer.  Be sure
> - * to let the upper layer know each time a block is moved, so it can
> - * add more.
> - */
> - pos = cb->buffer;
> - while (total > 0) {
> - n = min(total, ch->end - ch->cur);
> - n = min(n, ch->blksize - ch->transferred);
> - memcpy(pos, ch->cur, n);
> - total -= n;
> - pos += n;
> - ch->cur += n;
> - if (ch->cur >= ch->end)
> - ch->cur = ch->start;
> + bpf = a->bps * a->nch;
> + framebuf = xfer->buf;
> + nframes = 0;
> + null_size = 0;
> + data_size = 0;
> + for (nframes = 0; nframes < xfer->nframes; nframes++) {
> + fsize = xfer->sizes[nframes];
> + if (fsize > 0) {
> + data_size += fsize;
> + s->locked = 1;
> + } else if (!s->locked) {
> + /* treat initial null packets as silence */
> + fsize = (s->spf >> 16) * bpf;
> + memset(framebuf, 0, fsize);
> + null_size += fsize;
> + }
>  
> - ch->transferred += n;
> - /* Call back to upper layer */
> - if (ch->transferred >= ch->blksize) {
> - DPRINTFN(5,("%s: call %p(%p)\n",
> -    __func__, ch->intr, ch->arg));
> - mtx_enter(&audio_lock);
> - ch->intr(ch->arg);
> - mtx_leave(&audio_lock);
> - ch->transferred -= ch->blksize;
> + /*
> + * fill ring from frame buffer, handling
> + * boundary conditions
> + */
> + buf = framebuf;
> + while (fsize > 0) {
> + count = s->ring_end - s->ring_pos;
> + if (count > fsize)
> + count = fsize;
> + memcpy(s->ring_pos, buf, count);
> + s->ring_pos += count;
> + if (s->ring_pos == s->ring_end)
> + s->ring_pos = s->ring_start;
> + buf += count;
> + fsize -= count;
>   }
> +
> + framebuf += s->maxpkt;
>   }
>  
> + s->ring_offs += data_size + null_size - xfer->size;
> +
>  #ifdef UAUDIO_DEBUG
> - if (uaudiodebug > 8) {
> - DPRINTF(("%s: buffer=%p, residue=0.%03d\n",
> - __func__, cb->buffer, ch->residue));
> - for (i = 0; i < ch->nframes; i++) {
> - DPRINTF(("   [%d] length %d\n", i, cb->sizes[i]));
> - }
> + if (uaudio_debug >= 2) {
> + getmicrotime(&tv);
> + printf("uaudio_rdata_intr: %llu.%06lu: "
> +    "%u fr, %uB + %uB = %uB of %uB, offs -> %d\n",
> +    tv.tv_sec, tv.tv_usec,
> +    nframes, data_size, null_size, data_size + null_size,
> +    xfer->size, s->ring_offs);
>   }
>  #endif
> + uaudio_rdata_xfer(sc);
>  
> - DPRINTFN(5,("%s: transfer xfer=%p\n", __func__, cb->xfer));
> - usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, ch->nframes,
> -    USBD_NO_COPY | USBD_SHORT_XFER_OK, uaudio_chan_pintr);
> -
> - (void)usbd_transfer(cb->xfer);
> + mtx_enter(&audio_lock);
> + s->intr(s->arg);
> + mtx_leave(&audio_lock);
>  }
>  
> +/*
> + * Start simultaneously playback and recording, unless trigger_input()
> + * and trigger_output() were not both called yet.
> + */
>  void
> -uaudio_chan_pintr(struct usbd_xfer *xfer, void *priv,
> -  usbd_status status)
> +uaudio_trigger(struct uaudio_softc *sc)
>  {
> - struct chanbuf *cb = priv;
> - struct chan *ch = cb->chan;
> - u_int32_t count;
> + int i, s;
>  
> - /* Return if we are aborting. */
> - if (status == USBD_CANCELLED)
> + if (sc->mode != sc->trigger_mode)
>   return;
>  
> - usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
> - DPRINTFN(5,("%s: count=%d, transferred=%d\n",
> -    __func__, count, ch->transferred));
> -#ifdef UAUDIO_DEBUG
> - if (count != cb->size) {
> - printf("%s: count(%d) != size(%d)\n",
> -       __func__, count, cb->size);
> + DPRINTF("uaudio_trigger: starting\n");
> + s = splusb();
> + for (i = 0; i < UAUDIO_NXFERS; i++) {
> + if (sc->mode & AUMODE_PLAY) {
> + if (sc->pstream.sync_pipe)
> + uaudio_psync_xfer(sc);
> + uaudio_pdata_xfer(sc);
> + }
> + if (sc->mode & AUMODE_RECORD)
> + uaudio_rdata_xfer(sc);
>   }
> -#endif
> -
> - /* start next transfer */
> - uaudio_chan_ptransfer(ch);
> + splx(s);
>  }
>  
> -/* Called at splusb() */
>  void
> -uaudio_chan_psync_transfer(struct chan *ch)
> +uaudio_print(struct uaudio_softc *sc)
>  {
> - struct syncbuf *sb;
> - int i, size, total = 0;
> + struct uaudio_unit *u;
> + struct uaudio_mixent *m;
> + struct uaudio_params *p;
> + int pchan = 0, rchan = 0, async = 0;
> + int nctl = 0;
> +
> + for (u = sc->unit_list; u != NULL; u = u->unit_next) {
> + for (m = u->mixent_list; m != NULL; m = m->next)
> + nctl++;
> + }
> +
> + for (p = sc->params_list; p != NULL; p = p->next) {
> + if (p->palt && p->palt->nch > pchan)
> + pchan = p->palt->nch;
> + if (p->ralt && p->ralt->nch > rchan)
> + rchan = p->ralt->nch;
> + if (p->palt && p->palt->sync)
> + async = 1;
> + if (p->ralt && p->ralt->sync)
> + async = 1;
> + }
> +
> + printf("%s: class v%d, %s, %s, channels: %d play, %d rec, %d ctls\n",
> +    DEVNAME(sc),
> +    sc->version >> 8,
> +    sc->ufps == 1000 ? "full-speed" : "high-speed",
> +    async ? "async" : "sync",
> +    pchan, rchan, nctl);
> +}
>  
> - if (usbd_is_dying(ch->sc->sc_udev))
> - return;
> +int
> +uaudio_match(struct device *parent, void *match, void *aux)
> +{
> + struct usb_attach_arg *arg = aux;
> + struct usb_interface_descriptor *idesc;
>  
> - /* Pick the next sync buffer. */
> - sb = &ch->syncbufs[ch->cursyncbuf];
> - if (++ch->cursyncbuf >= UAUDIO_NSYNCBUFS)
> - ch->cursyncbuf = 0;
> + if (arg->iface == NULL || arg->device == NULL)
> + return UMATCH_NONE;
>  
> - size = ch->hi_speed ? 4 : 3;
> - for (i = 0; i < ch->nsync_frames; i++) {
> - sb->sizes[i] = size;
> - sb->offsets[i] = total;
> - total += size;
> + idesc = usbd_get_interface_descriptor(arg->iface);
> + if (idesc == NULL) {
> + DPRINTF("uaudio_match: couldn't get idesc\n");
> + return UMATCH_NONE;
>   }
> - sb->size = total;
>  
> - DPRINTFN(5,("%s: transfer xfer=%p\n", __func__, sb->xfer));
> - usbd_setup_isoc_xfer(sb->xfer, ch->sync_pipe, sb, sb->sizes,
> -    ch->nsync_frames, USBD_NO_COPY | USBD_SHORT_XFER_OK,
> -    uaudio_chan_psync_intr);
> + if (idesc->bInterfaceClass != UICLASS_AUDIO ||
> +    idesc->bInterfaceSubClass != UISUBCLASS_AUDIOSTREAM)
> + return UMATCH_NONE;
>  
> - (void)usbd_transfer(sb->xfer);
> + return UMATCH_VENDOR_PRODUCT_CONF_IFACE;
>  }
>  
>  void
> -uaudio_chan_psync_intr(struct usbd_xfer *xfer, void *priv,
> -    usbd_status status)
> +uaudio_attach(struct device *parent, struct device *self, void *aux)
>  {
> - struct syncbuf *sb = priv;
> - struct chan *ch = sb->chan;
> - u_int32_t count, tmp;
> - u_int32_t freq, freq_w, freq_f;
> - int i, pos, size;
> + struct uaudio_softc *sc = (struct uaudio_softc *)self;
> + struct usb_attach_arg *arg = aux;
> + struct usb_config_descriptor *cdesc;
> + struct uaudio_blob desc;
> +
> + /*
> + * this device has audio AC or AS or MS interface, get the
> + * full config descriptor and attach audio devices
> + */
>  
> - /* Return if we are aborting. */
> - if (status == USBD_CANCELLED)
> + cdesc = usbd_get_config_descriptor(arg->device);
> + if (cdesc == NULL)
>   return;
>  
> - usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
> - DPRINTFN(5,("%s: count=%d\n", __func__, count));
> + desc.rptr = (unsigned char *)cdesc;
> + desc.wptr = desc.rptr + UGETW(cdesc->wTotalLength);
>  
> - size = ch->hi_speed ? 4 : 3;
> - for (i = 0; count > 0 && i < ch->nsync_frames; i++) {
> - if (sb->sizes[i] != size)
> - continue;
> - count -= size;
> - pos = sb->offsets[i];
> - if (ch->hi_speed) {
> - /* 16.16 (12.13) -> 16.16 (12.16) */
> - freq = sb->buffer[pos+3] << 24 |
> -    sb->buffer[pos+2] << 16 |
> -    sb->buffer[pos+1] << 8 |
> -    sb->buffer[pos];
> - } else {
> - /* 10.14 (10.10) -> 16.16 (10.16) */
> - freq = sb->buffer[pos+2] << 18 |
> -    sb->buffer[pos+1] << 10 |
> -    sb->buffer[pos] << 2;
> - }
> - freq_w = (freq >> 16) & (ch->hi_speed ? 0x0fff : 0x03ff);
> - freq_f = freq & 0xffff;
> - DPRINTFN(5,("%s: freq = %d %d/%d\n", __func__, freq_w, freq_f,
> -    ch->frac_denom));
> - tmp = freq_w * ch->sample_size;
> - if (tmp + (freq_f ? ch->sample_size : 0) >
> -    ch->max_bytes_per_frame) {
> - DPRINTF(("%s: packet size request too large: %d/%d/%d\n",
> -    __func__, tmp, ch->max_bytes_per_frame, ch->maxpktsize));
> - } else {
> - ch->bytes_per_frame = tmp;
> - ch->fraction = freq_f;
> - }
> + sc->udev = arg->device;
> + sc->unit_list = NULL;
> + sc->names = NULL;
> + sc->alts = NULL;
> + sc->params_list = NULL;
> + sc->clock = NULL;
> + sc->params = NULL;
> + sc->rate = 0;
> + sc->mode = 0;
> + sc->trigger_mode = 0;
> +
> + switch (sc->udev->speed) {
> + case USB_SPEED_LOW:
> + case USB_SPEED_FULL:
> + sc->ufps = 1000;
> + sc->sync_pktsz = 3;
> + break;
> + case USB_SPEED_HIGH:
> + sc->ufps = 8000;
> + sc->sync_pktsz = 4;
> + break;
> + default:
> + /* XXX: what about USB_SPEED_SUPER? */
> + return;
>   }
>  
> - /* start next transfer */
> - uaudio_chan_psync_transfer(ch);
> + if (!uaudio_process_conf(sc, &desc))
> + return;
> +
> +#ifdef UAUDIO_DEBUG
> + if (uaudio_debug)
> + uaudio_conf_print(sc);
> +#endif
> + /* print a nice uaudio attach line */
> + uaudio_print(sc);
> +
> + audio_attach_mi(&uaudio_hw_if, sc, &sc->dev);
>  }
>  
> -/* Called at splusb() */
> -void
> -uaudio_chan_rtransfer(struct chan *ch)
> +int
> +uaudio_detach(struct device *self, int flags)
>  {
> - struct chanbuf *cb;
> - int i, size, total;
> + struct uaudio_softc *sc = (struct uaudio_softc *)self;
> + struct uaudio_unit *unit;
> + struct uaudio_params *params;
> + struct uaudio_alt *alt;
> + struct uaudio_name *name;
> + struct uaudio_mixent *mixent;
> + int rv;
>  
> - if (usbd_is_dying(ch->sc->sc_udev))
> - return;
> + rv = config_detach_children(self, flags);
>  
> - /* Pick the next channel buffer. */
> - cb = &ch->chanbufs[ch->curchanbuf];
> - if (++ch->curchanbuf >= UAUDIO_NCHANBUFS)
> - ch->curchanbuf = 0;
> + while ((alt = sc->alts) != NULL) {
> + sc->alts = alt->next;
> + free(alt, M_DEVBUF, sizeof(struct uaudio_alt));
> + }
>  
> - /* Compute the size of each frame in the next transfer. */
> - total = 0;
> - for (i = 0; i < ch->nframes; i++) {
> - size = ch->bytes_per_frame;
> - cb->sizes[i] = size;
> - cb->offsets[i] = total;
> - total += size;
> + while ((params = sc->params_list) != NULL) {
> + sc->params_list = params->next;
> + free(params, M_DEVBUF, sizeof(struct uaudio_params));
>   }
> - cb->size = total;
>  
> -#ifdef UAUDIO_DEBUG
> - if (uaudiodebug > 8) {
> - DPRINTF(("%s: buffer=%p, residue=0.%03d\n",
> - __func__, cb->buffer, ch->residue));
> - for (i = 0; i < ch->nframes; i++) {
> - DPRINTF(("   [%d] length %d\n", i, cb->sizes[i]));
> + while ((unit = sc->unit_list) != NULL) {
> + sc->unit_list = unit->unit_next;
> + while ((mixent = unit->mixent_list) != NULL) {
> + unit->mixent_list = mixent->next;
> + uaudio_ranges_clear(&mixent->ranges);
> + free(mixent, M_DEVBUF, sizeof(struct uaudio_mixent));
>   }
> + uaudio_ranges_clear(&unit->rates);
> + free(unit, M_DEVBUF, sizeof(struct uaudio_unit));
>   }
> -#endif
>  
> - DPRINTFN(5,("%s: transfer xfer=%p\n", __func__, cb->xfer));
> - usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, ch->nframes,
> -    USBD_NO_COPY | USBD_SHORT_XFER_OK, uaudio_chan_rintr);
> + while ((name = sc->names)) {
> + sc->names = name->next;
> + free(name, M_DEVBUF, sizeof(struct uaudio_name));
> + }
>  
> - (void)usbd_transfer(cb->xfer);
> + return rv;
>  }
>  
> -void
> -uaudio_chan_rintr(struct usbd_xfer *xfer, void *priv,
> -  usbd_status status)
> +int
> +uaudio_open(void *self, int flags)
>  {
> - struct chanbuf *cb = priv;
> - struct chan *ch = cb->chan;
> - u_int16_t pos;
> - u_int32_t count;
> - int i, n, frsize;
> -
> - /* Return if we are aborting. */
> - if (status == USBD_CANCELLED)
> - return;
> + struct uaudio_softc *sc = self;
> + struct uaudio_params *p;
>  
> - usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
> - DPRINTFN(5,("%s: count=%d, transferred=%d\n",
> -    __func__, count, ch->transferred));
> + if (usbd_is_dying(sc->udev))
> + return EIO;
>  
> - /* count < cb->size is normal for asynchronous source */
> -#ifdef DIAGNOSTIC
> - if (count > cb->size) {
> - printf("%s: count(%d) > size(%d)\n",
> -       __func__, count, cb->size);
> - }
> -#endif
> + flags &= (FREAD | FWRITE);
>  
> - /*
> - * Transfer data from channel buffer to upper layer buffer, taking
> - * care of wrapping the upper layer buffer.
> - */
> - for (i = 0; i < ch->nframes; i++) {
> - frsize = cb->sizes[i];
> - pos = cb->offsets[i];
> - while (frsize > 0) {
> - n = min(frsize, ch->end - ch->cur);
> - n = min(n, ch->blksize - ch->transferred);
> - memcpy(ch->cur, cb->buffer + pos, n);
> - frsize -= n;
> - pos += n;
> - ch->cur += n;
> - if (ch->cur >= ch->end)
> - ch->cur = ch->start;
> -
> - ch->transferred += n;
> - /* Call back to upper layer */
> - if (ch->transferred >= ch->blksize) {
> - DPRINTFN(5,("%s: call %p(%p)\n",
> -    __func__, ch->intr, ch->arg));
> - mtx_enter(&audio_lock);
> - ch->intr(ch->arg);
> - mtx_leave(&audio_lock);
> - ch->transferred -= ch->blksize;
> - }
> - if (count < n)
> - printf("%s: count < n\n", __func__);
> - else
> - count -= n;
> + for (p = sc->params_list; p != NULL; p = p->next) {
> + switch (flags) {
> + case FWRITE:
> + if (!p->palt)
> + break;
> + sc->mode = AUMODE_PLAY;
> + return 0;
> + case FREAD:
> + if (!p->ralt)
> + break;
> + sc->mode = AUMODE_RECORD;
> + return 0;
> + case FREAD | FWRITE:
> + if (!(p->ralt && p->palt))
> + break;
> + sc->mode = AUMODE_RECORD | AUMODE_PLAY;
> + return 0;
>   }
>   }
> - if (count != 0) {
> - printf("%s: transfer count - frame total = %d\n",
> -    __func__, count);
> - }
>  
> - /* start next transfer */
> - uaudio_chan_rtransfer(ch);
> + return ENXIO;
>  }
>  
>  void
> -uaudio_chan_init(struct chan *ch, int mode, int altidx,
> -    const struct audio_params *param)
> +uaudio_close(void *self)
> +{
> + struct uaudio_softc *sc = self;
> +
> + sc->mode = 0;
> +}
> +
> +int
> +uaudio_set_params(void *self, int setmode, int usemode,
> +    struct audio_params *ap, struct audio_params *ar)
>  {
> - struct as_info *ai = &ch->sc->sc_alts[altidx];
> - int samples_per_frame, ival, use_maxpkt = 0;
> + struct uaudio_softc *sc = (struct uaudio_softc *)self;
> + struct uaudio_params *p, *best_mode, *best_rate, *best_nch;
> + int rate, rateindex;
>  
> - if (ai->attributes & UA_SED_MAXPACKETSONLY) {
> - DPRINTF(("%s: alt %d needs maxpktsize packets\n",
> -    __func__, altidx));
> - use_maxpkt = 1;
> +#ifdef DIAGNOSTIC
> + if (setmode != usemode || setmode != sc->mode) {
> + printf("%s: bad call to uaudio_set_params()\n", DEVNAME(sc));
> + return EINVAL;
> + }
> + if (sc->mode == 0) {
> + printf("%s: uaudio_set_params(): not open\n", DEVNAME(sc));
> + return EINVAL;
>   }
> - else if (mode == AUMODE_RECORD) {
> - DPRINTF(("%s: using maxpktsize packets for record channel\n",
> -    __func__));
> - use_maxpkt = 1;
> +#endif
> + /*
> + * audio(4) layer requests equal play and record rates
> + */
> + rate = (sc->mode & AUMODE_PLAY) ? ap->sample_rate : ar->sample_rate;
> + rateindex = uaudio_rates_indexof(~0, rate);
> +
> + DPRINTF("uaudio_set_params: rate %d -> %d (index %d)\n",
> +    rate, uaudio_rates[rateindex], rateindex);
> +
> + best_mode = best_rate = best_nch = NULL;
> +
> + for (p = sc->params_list; p != NULL; p = p->next) {
> +
> + /* test if params match the requested mode */
> + if (sc->mode & AUMODE_PLAY) {
> + if (p->palt == NULL)
> + continue;
> + }
> + if (sc->mode & AUMODE_RECORD) {
> + if (p->ralt == NULL)
> + continue;
> + }
> + if (best_mode == NULL)
> + best_mode = p;
> +
> + /* test if params match the requested rate */
> + if ((uaudio_getrates(sc, p) & (1 << rateindex)) == 0)
> + continue;
> + if (best_rate == NULL)
> + best_rate = p;
> +
> + /* test if params match the requested channel counts */
> + if (sc->mode & AUMODE_PLAY) {
> + if (p->palt->nch != ap->channels)
> + continue;
> + }
> + if (sc->mode & AUMODE_RECORD) {
> + if (p->ralt->nch != ar->channels)
> + continue;
> + }
> + if (best_nch == NULL)
> + best_nch = p;
> +
> + /* test if params match the requested precision */
> + if (sc->mode & AUMODE_PLAY) {
> + if (p->palt->bits != ap->precision)
> + continue;
> + }
> + if (sc->mode & AUMODE_RECORD) {
> + if (p->ralt->bits != ar->precision)
> + continue;
> + }
> +
> + /* everything matched, we're done */
> + break;
>   }
>  
> - ch->altidx = altidx;
> - ch->maxpktsize = UGETW(ai->edesc->wMaxPacketSize);
> - ch->sample_rate = param->sample_rate;
> - ch->sample_size = param->channels * param->bps;
> - ch->usb_fps = USB_FRAMES_PER_SECOND;
> - ch->hi_speed = ch->sc->sc_udev->speed == USB_SPEED_HIGH;
> - if (ch->hi_speed) {
> - ch->usb_fps *= 8;
> - /*
> - * Polling interval is considered a frame, as opposed to
> - * micro-frame being a frame.
> - */
> - ival = ch->sc->sc_alts[altidx].edesc->bInterval;
> - if (ival > 0 && ival <= 4)
> - ch->usb_fps >>= (ival - 1);
> - DPRINTF(("%s: detected USB high-speed with ival %d\n",
> -    __func__, ival));
> + if (p == NULL) {
> + if (best_nch)
> + p = best_nch;
> + else if (best_rate)
> + p = best_rate;
> + else if (best_mode)
> + p = best_mode;
> + else
> + return ENOTTY;
>   }
>  
>   /*
> - * Use UAUDIO_MIN_FRAMES here, so uaudio_round_blocksize() can
> - * make sure the blocksize duration will be > 1 USB frame.
> - */
> - samples_per_frame = ch->sample_rate / ch->usb_fps;
> - if (!use_maxpkt) {
> - ch->fraction = ch->sample_rate % ch->usb_fps;
> - if (samples_per_frame * ch->sample_size > ch->maxpktsize) {
> - DPRINTF(("%s: packet size %d too big, max %d\n",
> -    __func__, ch->bytes_per_frame, ch->maxpktsize));
> - samples_per_frame = ch->maxpktsize / ch->sample_size;
> - }
> - ch->bytes_per_frame = samples_per_frame * ch->sample_size;
> - ch->nframes = UAUDIO_MIN_FRAMES;
> - } else {
> - ch->fraction = 0;
> - ch->bytes_per_frame = ch->maxpktsize;
> - ch->nframes = UAUDIO_MIN_FRAMES * samples_per_frame *
> -    ch->sample_size / ch->maxpktsize;
> - }
> - if (ch->nframes > UAUDIO_MAX_FRAMES)
> - ch->nframes = UAUDIO_MAX_FRAMES;
> - else if (ch->nframes < 1)
> - ch->nframes = 1;
> -
> - ch->max_bytes_per_frame = ch->bytes_per_frame;
> - if (!use_maxpkt)
> - ch->max_bytes_per_frame += ch->sample_size;
> - if (ch->max_bytes_per_frame > ch->maxpktsize)
> - ch->max_bytes_per_frame = ch->maxpktsize;
> -
> - ch->residue = 0;
> - ch->frac_denom = ch->usb_fps;
> - if (ai->edesc1 != NULL) {
> - /*
> - * The lower 16-bits of the sync request represent
> - * fractional samples.  Scale up the fraction here once
> - * so all fractions are using the same denominator.
> - */
> - ch->frac_denom = 1 << 16;
> - ch->fraction = (ch->fraction * ch->frac_denom) / ch->usb_fps;
> + * Recalculate rate index, because the choosen parameters
> + * may not support the requested one
> + */
> + rateindex = uaudio_rates_indexof(uaudio_getrates(sc, p), rate);
>  
> - /*
> - * Have to set nsync_frames somewhere.  We can request
> - * a lot of sync data; the device will reply when it's
> - * ready, with empty frames meaning to keep using the
> - * current rate.
> - */
> - ch->nsync_frames = UAUDIO_MAX_FRAMES;
> + sc->params = p;
> + sc->rate = uaudio_rates[rateindex];
> + sc->nsamp_per_ufr = sc->rate / USB_FRAMES_PER_SECOND;
> +
> + DPRINTF("uaudio_set_params: rate = %u, spf = %u\n",
> +    sc->rate, sc->nsamp_per_ufr);
> +
> + if (sc->mode & AUMODE_PLAY) {
> + ap->sample_rate = sc->rate;
> + ap->precision = p->palt->bits;
> + ap->encoding = AUDIO_ENCODING_SLINEAR_LE;
> + ap->bps = p->palt->bps;
> + ap->msb = 1;
> + ap->channels = p->palt->nch;
> + }
> + if (sc->mode & AUMODE_RECORD) {
> + ar->sample_rate = sc->rate;
> + ar->precision = p->ralt->bits;
> + ar->encoding = AUDIO_ENCODING_SLINEAR_LE;
> + ar->bps = p->ralt->bps;
> + ar->msb = 1;
> + ar->channels = p->ralt->nch;
>   }
> - DPRINTF(("%s: residual sample fraction: %d/%d\n", __func__,
> -    ch->fraction, ch->frac_denom));
> +
> + return 0;
>  }
>  
> -void
> -uaudio_chan_set_param(struct chan *ch, u_char *start, u_char *end, int blksize)
> +int
> +uaudio_round_blocksize(void *self, int blksz)
>  {
> - ch->start = start;
> - ch->end = end;
> - ch->cur = start;
> - ch->transferred = 0;
> - ch->curchanbuf = 0;
> - ch->blksize = blksize;
> + struct uaudio_softc *sc = self;
> + struct uaudio_alt *a;
> + unsigned int bpu, rbpf, pbpf;
> + unsigned int blksz_max;
>  
>   /*
> - * Recompute nframes based on blksize, but make sure nframes
> - * is not longer in time duration than blksize.
> + * We don't know if we're called for the play or record
> + * direction. But as the number of play and record channels
> + * may differ, we have to calculate the smallest possible
> + * block size multiplier suitable for both playback and
> + * recording. This the LCM of the play and record number of
> + * channels multiplied by what would be the mono block size.
>   */
> - ch->nframes = ch->blksize * ch->usb_fps /
> -    (ch->bytes_per_frame * ch->usb_fps +
> -    ch->sample_size * ch->fraction);
> - if (ch->nframes > UAUDIO_MAX_FRAMES)
> - ch->nframes = UAUDIO_MAX_FRAMES;
> - else if (ch->nframes < 1)
> - ch->nframes = 1;
>  
> - ch->reqms = ch->bytes_per_frame / ch->sample_size *
> -    ch->nframes * 1000 / ch->sample_rate;
> + if (sc->mode & AUMODE_PLAY) {
> + a = sc->params->palt;
> + pbpf = a->bps * a->nch;
> + } else
> + pbpf = 1;
> +
> + if (sc->mode & AUMODE_RECORD) {
> + a = sc->params->ralt;
> + rbpf = a->bps * a->nch;
> + } else
> + rbpf = 1;
> +
> + bpu = sc->nsamp_per_ufr * rbpf * pbpf / uaudio_gcd(pbpf, rbpf);
> +
> + /*
> + * XXX: usb support is broken for large play xfers (playback
> + * stutters in full-duplex mode).  It appears that full-duplex
> + * with 25ms blocks kinda works on uhci and ehci (on root hub
> + * only). So, just limit the block size until usb gets fixed.
> + */
> + blksz_max = (pbpf > rbpf ? pbpf : rbpf) * sc->rate * 25 / 1000;
> + if (blksz > blksz_max)
> + blksz = blksz_max;
>  
> - DPRINTF(("%s: alt=%d blk=%d maxpkt=%u bpf=%u rate=%u nframes=%u reqms=%u\n",
> -    __func__, ch->altidx, ch->blksize, ch->maxpktsize,
> -    ch->bytes_per_frame, ch->sample_rate, ch->nframes, ch->reqms));
> + blksz -= blksz % bpu;
> + if (blksz == 0)
> + blksz = bpu;
> + return blksz;
>  }
>  
>  int
> -uaudio_match_alt_rate(void *addr, int alt, int rate)
> +uaudio_trigger_output(void *self, void *start, void *end, int blksz,
> +    void (*intr)(void *), void *arg, struct audio_params *param)
>  {
> - struct uaudio_softc *sc = addr;
> - const struct usb_audio_streaming_type1_descriptor *a1d;
> - int i, j, r;
> + struct uaudio_softc *sc = self;
> + int err;
>  
> - a1d = sc->sc_alts[alt].asf1desc;
> - if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
> - if ((UA_SAMP_LO(a1d) <= rate) &&
> -    (UA_SAMP_HI(a1d) >= rate)) {
> - return rate;
> - } else {
> - if (UA_SAMP_LO(a1d) > rate)
> - return UA_SAMP_LO(a1d);
> - else
> - return UA_SAMP_HI(a1d);
> - }
> - } else {
> - for (i = 0; i < 100; i++) {
> - for (j = 0; j < a1d->bSamFreqType; j++) {
> - r = UA_GETSAMP(a1d, j);
> - if ((r - (500 * i) <= rate) &&
> -    (r + (500 * i) >= rate))
> - return r;
> - }
> - }
> - /* assumes rates are listed in order from lowest to highest */
> - if (rate < UA_GETSAMP(a1d, 0))
> - j = 0;
> - else
> - j = a1d->bSamFreqType - 1;
> - return UA_GETSAMP(a1d, j);
> - }
> - DPRINTF(("%s: could not match rate\n", __func__));
> - return rate;
> + err = uaudio_stream_open(sc,
> +    AUMODE_PLAY, start, end, blksz, intr, arg);
> + if (err)
> + return err;
> +
> + sc->trigger_mode |= AUMODE_PLAY;
> + uaudio_trigger(sc);
> + return 0;
>  }
>  
>  int
> -uaudio_match_alt(void *addr, struct audio_params *p, int mode)
> +uaudio_trigger_input(void *self, void *start, void *end, int blksz,
> +    void (*intr)(void *), void *arg, struct audio_params *param)
>  {
> - struct uaudio_softc *sc = addr;
> - const struct usb_audio_streaming_type1_descriptor *a1d;
> - int i, j, dir, rate;
> - int alts_eh, alts_ch, ualt;
> -
> - DPRINTF(("%s: mode=%s rate=%ld ch=%d pre=%d bps=%d enc=%d\n",
> -    __func__, mode == AUMODE_RECORD ? "rec" : "play", p->sample_rate,
> -    p->channels, p->precision, p->bps, p->encoding));
> + struct uaudio_softc *sc = self;
> + int err;
>  
> - alts_eh = 0;
> - for (i = 0; i < sc->sc_nalts; i++) {
> - dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress);
> - if ((mode == AUMODE_RECORD && dir != UE_DIR_IN) ||
> -    (mode == AUMODE_PLAY && dir == UE_DIR_IN))
> - continue;
> - DPRINTFN(6,("%s: matched %s alt %d for direction\n", __func__,
> -    mode == AUMODE_RECORD ? "rec" : "play", i));
> - if (sc->sc_alts[i].encoding != p->encoding)
> - continue;
> - a1d = sc->sc_alts[i].asf1desc;
> - if (a1d->bBitResolution != p->precision)
> - continue;
> - alts_eh |= 1 << i;
> - DPRINTFN(6,("%s: matched %s alt %d for enc/pre\n", __func__,
> -    mode == AUMODE_RECORD ? "rec" : "play", i));
> - }
> - if (alts_eh == 0) {
> - DPRINTF(("%s: could not match dir/enc/prec\n", __func__));
> - return -1;
> - }
> + err = uaudio_stream_open(sc,
> +    AUMODE_RECORD, start, end, blksz, intr, arg);
> + if (err)
> + return err;
>  
> - alts_ch = 0;
> - for (i = 0; i < 3; i++) {
> - for (j = 0; j < sc->sc_nalts; j++) {
> - if (!(alts_eh & (1 << j)))
> - continue;
> - a1d = sc->sc_alts[j].asf1desc;
> - if (a1d->bNrChannels == p->channels) {
> - alts_ch |= 1 << j;
> - DPRINTFN(6,("%s: matched alt %d for channels\n",
> -    __func__, j));
> - }
> - }
> - if (alts_ch)
> - break;
> - if (p->channels == 2)
> - p->channels = 1;
> - else
> - p->channels = 2;
> - }
> - if (!alts_ch) {
> - /* just use the first alt that matched the encoding */
> - for (i = 0; i < sc->sc_nalts; i++)
> - if (alts_eh & (1 << i))
> - break;
> - alts_ch = 1 << i;
> - a1d = sc->sc_alts[i].asf1desc;
> - p->channels = a1d->bNrChannels;
> - }
> + sc->trigger_mode |= AUMODE_RECORD;
> + uaudio_trigger(sc);
> + return 0;
> +}
>  
> - ualt = -1;
> - for (i = 0; i < sc->sc_nalts; i++) {
> - if (alts_ch & (1 << i)) {
> - rate = uaudio_match_alt_rate(sc, i, p->sample_rate);
> - if (rate - 50 <= p->sample_rate &&
> -    rate + 50 >= p->sample_rate) {
> - DPRINTFN(6,("%s: alt %d matched rate %ld with %d\n",
> -    __func__, i, p->sample_rate, rate));
> - p->sample_rate = rate;
> - break;
> - }
> - }
> - }
> - if (i < sc->sc_nalts) {
> - ualt = i;
> - } else {
> - for (i = 0; i < sc->sc_nalts; i++) {
> - if (alts_ch & (1 << i)) {
> - ualt = i;
> - p->sample_rate = uaudio_match_alt_rate(sc,
> -    i, p->sample_rate);
> - break;
> - }
> - }
> - }
> +int
> +uaudio_halt_output(void *self)
> +{
> + struct uaudio_softc *sc = (struct uaudio_softc *)self;
>  
> - return ualt;
> + uaudio_stream_close(sc, AUMODE_PLAY);
> + sc->trigger_mode &= ~AUMODE_PLAY;
> + return 0;
>  }
>  
>  int
> -uaudio_set_params(void *addr, int setmode, int usemode,
> -    struct audio_params *play, struct audio_params *rec)
> +uaudio_halt_input(void *self)
>  {
> - struct uaudio_softc *sc = addr;
> - int flags = sc->sc_altflags;
> - int i;
> - int paltidx = -1, raltidx = -1;
> - struct audio_params *p;
> - int mode;
> -
> - if (usbd_is_dying(sc->sc_udev))
> - return (EIO);
> -
> - if (((usemode & AUMODE_PLAY) && sc->sc_playchan.pipe != NULL) ||
> -    ((usemode & AUMODE_RECORD) && sc->sc_recchan.pipe != NULL))
> - return (EBUSY);
> -
> - if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1)
> - sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 0;
> - if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1)
> - sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 0;
> -
> - /* Some uaudio devices are unidirectional.  Don't try to find a
> -   matching mode for the unsupported direction. */
> - setmode &= sc->sc_mode;
> -
> - for (mode = AUMODE_RECORD; mode != -1;
> -     mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
> - if ((setmode & mode) == 0)
> - continue;
> + struct uaudio_softc *sc = (struct uaudio_softc *)self;
>  
> - p = (mode == AUMODE_PLAY) ? play : rec;
> + uaudio_stream_close(sc, AUMODE_RECORD);
> + sc->trigger_mode &= ~AUMODE_RECORD;
> + return 0;
> +}
>  
> - switch (p->precision) {
> - case 24:
> - if (!(flags & HAS_24)) {
> - if (flags & HAS_16)
> - p->precision = 16;
> - else
> - p->precision = 8;
> - }
> - break;
> - case 16:
> - if (!(flags & HAS_16)) {
> - if (flags & HAS_24)
> - p->precision = 24;
> - else
> - p->precision = 8;
> - }
> - break;
> - case 8:
> - if (!(flags & HAS_8) && !(flags & HAS_8U)) {
> - if (flags & HAS_16)
> - p->precision = 16;
> - else
> - p->precision = 24;
> - }
> - break;
> - }
> +int
> +uaudio_get_props(void *self)
> +{
> + return AUDIO_PROP_FULLDUPLEX;
> +}
>  
> - i = uaudio_match_alt(sc, p, mode);
> - if (i < 0) {
> - DPRINTF(("%s: uaudio_match_alt failed for %s\n",
> -    __func__, mode == AUMODE_RECORD ? "rec" : "play"));
> - continue;
> +int
> +uaudio_get_port(void *arg, struct mixer_ctrl *ctl)
> +{
> + struct uaudio_softc *sc = arg;
> + struct uaudio_unit *u;
> + struct uaudio_mixent *m;
> + unsigned char req_buf[4];
> + struct uaudio_blob p;
> + int i, nch, val;
> +
> + if (!uaudio_mixer_byindex(sc, ctl->dev, &u, &m))
> + return ENOENT;
> +
> + switch (m->type) {
> + case UAUDIO_MIX_SW:
> + p.rptr = p.wptr = req_buf;
> + if (!uaudio_req(sc,
> + UT_READ_CLASS_INTERFACE,
> + UAUDIO_V1_REQ_GET_CUR,
> + m->req_sel,
> + m->chan < 0 ? 0 : m->chan,
> + sc->ctl_ifnum,
> + u->id,
> + req_buf,
> + 1))
> + return EIO;
> + p.wptr++;
> + if (!uaudio_getnum(&p, 1, &val))
> + return EIO;
> + ctl->un.ord = !!val;
> + break;
> + case UAUDIO_MIX_NUM:
> + nch = uaudio_mixer_nchan(m, NULL);
> + ctl->un.value.num_channels = nch;
> + for (i = 0; i < nch; i++) {
> + p.rptr = p.wptr = req_buf;
> + if (!uaudio_req(sc,
> + UT_READ_CLASS_INTERFACE,
> + UAUDIO_V1_REQ_GET_CUR,
> + m->req_sel,
> + m->chan < 0 ? 0 : i + 1,
> + sc->ctl_ifnum,
> + u->id,
> + req_buf,
> + 2))
> + return EIO;
> + p.wptr += 2;
> + if (!uaudio_getnum(&p, 2, &val))
> + return EIO;
> + ctl->un.value.level[i] =
> +    uaudio_ranges_decode(&m->ranges,
> + uaudio_sign_expand(val, 2));
> + m = m->next;
>   }
> -
> - p->bps = sc->sc_alts[i].asf1desc->bSubFrameSize;
> - p->msb = 1;
> -
> - if (mode == AUMODE_PLAY)
> - paltidx = i;
> - else
> - raltidx = i;
> + break;
> + case UAUDIO_MIX_ENUM:
> + /* XXX: not used yet */
> + break;
>   }
> + return 0;
> +}
>  
> - if (setmode & AUMODE_PLAY) {
> - if (paltidx == -1) {
> - DPRINTF(("%s: did not find alt for playback\n",
> -    __func__));
> - return (EINVAL);
> - }
> - /* XXX abort transfer if currently happening? */
> - uaudio_chan_init(&sc->sc_playchan, AUMODE_PLAY, paltidx, play);
> - }
> - if (setmode & AUMODE_RECORD) {
> - if (raltidx == -1) {
> - DPRINTF(("%s: did not find alt for recording\n",
> -    __func__));
> - return (EINVAL);
> +int
> +uaudio_set_port(void *arg, struct mixer_ctrl *ctl)
> +{
> + struct uaudio_softc *sc = arg;
> + struct uaudio_unit *u;
> + struct uaudio_mixent *m;
> + unsigned char req_buf[4];
> + unsigned int val;
> + int i, nch;
> +
> + if (!uaudio_mixer_byindex(sc, ctl->dev, &u, &m))
> + return ENOENT;
> +
> + switch (m->type) {
> + case UAUDIO_MIX_SW:
> + if (ctl->un.ord < 0 || ctl->un.ord > 1)
> + return EINVAL;
> + req_buf[0] = ctl->un.ord;
> + if (!uaudio_req(sc,
> + UT_WRITE_CLASS_INTERFACE,
> + UAUDIO_V1_REQ_SET_CUR,
> + m->req_sel,
> + m->chan < 0 ? 0 : m->chan,
> + sc->ctl_ifnum,
> + u->id,
> + req_buf,
> + 1))
> + return EIO;
> + break;
> + case UAUDIO_MIX_NUM:
> + nch = uaudio_mixer_nchan(m, NULL);
> + ctl->un.value.num_channels = nch;
> + for (i = 0; i < nch; i++) {
> + val = uaudio_ranges_encode(&m->ranges,
> +    ctl->un.value.level[i]);
> + DPRINTF("uaudio_set_port: ch %d, ctl %d, num val %d\n",
> +    i, ctl->un.value.level[i], val);
> + req_buf[0] = val;
> + req_buf[1] = val >> 8;
> + if (!uaudio_req(sc,
> + UT_WRITE_CLASS_INTERFACE,
> + UAUDIO_V1_REQ_SET_CUR,
> + m->req_sel,
> + m->chan < 0 ? 0 : i + 1,
> + sc->ctl_ifnum,
> + u->id,
> + req_buf,
> + 2))
> + return EIO;
> + m = m->next;
>   }
> - /* XXX abort transfer if currently happening? */
> - uaudio_chan_init(&sc->sc_recchan, AUMODE_RECORD, raltidx, rec);
> + break;
> + case UAUDIO_MIX_ENUM:
> + /* XXX: not used yet */
> + break;
>   }
> -
> - if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1)
> - sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 1;
> - if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1)
> - sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 1;
> -
> - DPRINTF(("%s: use altidx=p%d/r%d, altno=p%d/r%d\n", __func__,
> - sc->sc_playchan.altidx, sc->sc_recchan.altidx,
> - (sc->sc_playchan.altidx >= 0)
> -   ?sc->sc_alts[sc->sc_playchan.altidx].idesc->bAlternateSetting
> -   : -1,
> - (sc->sc_recchan.altidx >= 0)
> -   ? sc->sc_alts[sc->sc_recchan.altidx].idesc->bAlternateSetting
> -   : -1));
> -
> - return (0);
> + return 0;
>  }
>  
> -usbd_status
> -uaudio_set_speed(struct uaudio_softc *sc, int endpt, u_int speed)
> +int
> +uaudio_query_devinfo(void *arg, struct mixer_devinfo *devinfo)
>  {
> - usb_device_request_t req;
> - u_int8_t data[3];
> + struct uaudio_softc *sc = arg;
> + struct uaudio_unit *u;
> + struct uaudio_mixent *m;
>  
> - DPRINTFN(5,("%s: endpt=%d speed=%u\n", __func__, 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));
> -}
> + devinfo->next = -1;
> + devinfo->prev = -1;
> + switch (devinfo->index) {
> + case UAUDIO_CLASS_REC:
> + strlcpy(devinfo->label.name, AudioCrecord, MAX_AUDIO_DEV_LEN);
> + devinfo->type = AUDIO_MIXER_CLASS;
> + devinfo->mixer_class = -1;
> + return 0;
> + case UAUDIO_CLASS_IN:
> + strlcpy(devinfo->label.name, AudioCinputs, MAX_AUDIO_DEV_LEN);
> + devinfo->type = AUDIO_MIXER_CLASS;
> + devinfo->mixer_class = -1;
> + return 0;
> + case UAUDIO_CLASS_OUT:
> + strlcpy(devinfo->label.name, AudioCoutputs, MAX_AUDIO_DEV_LEN);
> + devinfo->type = AUDIO_MIXER_CLASS;
> + devinfo->mixer_class = -1;
> + return 0;
> + }
>  
> -void
> -uaudio_set_speed_emu0202(struct chan *ch)
> -{
> - usb_device_request_t req;
> - int rates[6] = { 44100, 48000, 88200, 96000, 176400, 192000 };
> - int i;
> - u_int8_t data[1];
> + /*
> + * find the unit & mixent structure for the given index
> + */
> + if (!uaudio_mixer_byindex(sc, devinfo->index, &u, &m))
> + return ENOENT;
>  
> - for (i = 0; i < 6; i++)
> - if (rates[i] >= ch->sample_rate)
> - break;
> - if (i >= 6) {
> - DPRINTF(("%s: unhandled rate %d\n", __func__, ch->sample_rate));
> - i = 0;
> + if (strcmp(m->fname, "level") == 0) {
> + /*
> + * mixer(4) interface doesn't give a names to level
> + * controls
> + */
> + strlcpy(devinfo->label.name, u->name, MAX_AUDIO_DEV_LEN);
> + } else {
> + snprintf(devinfo->label.name,
> +    MAX_AUDIO_DEV_LEN, "%s_%s", u->name, m->fname);
>   }
>  
> - req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
> - req.bRequest = SET_CUR;
> - USETW2(req.wValue, 0x03, 0);
> - USETW2(req.wIndex, 12, ch->sc->sc_ac_iface);
> - USETW(req.wLength, 1);
> - data[0] = i;
> -
> - usbd_do_request(ch->sc->sc_udev, &req, data);
> + devinfo->mixer_class = u->mixer_class;
> + switch (m->type) {
> + case UAUDIO_MIX_SW:
> + devinfo->type = AUDIO_MIXER_ENUM;
> + devinfo->un.e.num_mem = 2;
> + devinfo->un.e.member[0].ord = 0;
> + strlcpy(devinfo->un.e.member[0].label.name, "off",
> +    MAX_AUDIO_DEV_LEN);
> + devinfo->un.e.member[1].ord = 1;
> + strlcpy(devinfo->un.e.member[1].label.name, "on",
> +    MAX_AUDIO_DEV_LEN);
> + break;
> + case UAUDIO_MIX_NUM:
> + devinfo->type = AUDIO_MIXER_VALUE;
> + devinfo->un.v.num_channels = uaudio_mixer_nchan(m, NULL);
> + devinfo->un.v.delta = 1;
> + break;
> + case UAUDIO_MIX_ENUM:
> + /* XXX: not used yet */
> + devinfo->type = AUDIO_MIXER_ENUM;
> + devinfo->un.e.num_mem = 0;
> + break;
> + }
> + return 0;
>  }
> Index: uaudioreg.h
> ===================================================================
> RCS file: /cvs/src/sys/dev/usb/uaudioreg.h,v
> retrieving revision 1.15
> diff -u -p -u -p -r1.15 uaudioreg.h
> --- uaudioreg.h 15 Apr 2013 09:23:02 -0000 1.15
> +++ uaudioreg.h 31 Dec 2018 15:17:23 -0000
> @@ -1,386 +0,0 @@
> -/* $OpenBSD: uaudioreg.h,v 1.15 2013/04/15 09:23:02 mglocker Exp $ */
> -/* $NetBSD: uaudioreg.h,v 1.11 2002/10/23 02:32:37 christos Exp $ */
> -
> -/*
> - * Copyright (c) 1999 The NetBSD Foundation, Inc.
> - * All rights reserved.
> - *
> - * This code is derived from software contributed to The NetBSD Foundation
> - * by Lennart Augustsson ([hidden email]) 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.
> - *
> - * 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_CONFIG 0x22
> -#define UDESC_CS_STRING 0x23
> -#define UDESC_CS_INTERFACE 0x24
> -#define UDESC_CS_ENDPOINT 0x25
> -
> -#define UDESCSUB_AC_HEADER 1
> -#define UDESCSUB_AC_INPUT 2
> -#define UDESCSUB_AC_OUTPUT 3
> -#define UDESCSUB_AC_MIXER 4
> -#define UDESCSUB_AC_SELECTOR 5
> -#define UDESCSUB_AC_FEATURE 6
> -#define UDESCSUB_AC_PROCESSING 7
> -#define UDESCSUB_AC_EXTENSION 8
> -
> -/* The first fields are identical to usb_endpoint_descriptor_t */
> -struct usb_endpoint_descriptor_audio {
> - 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;
> -} __packed;
> -#define USB_ENDPOINT_DESCRIPTOR_AUDIO_SIZE 9
> -
> -struct usb_audio_control_descriptor {
> - uByte bLength;
> - uByte bDescriptorType;
> - uByte bDescriptorSubtype;
> - uWord bcdADC;
> - uWord wTotalLength;
> - uByte bInCollection;
> - uByte baInterfaceNr[1];
> -} __packed;
> -
> -struct usb_audio_streaming_interface_descriptor {
> - uByte bLength;
> - uByte bDescriptorType;
> - uByte bDescriptorSubtype;
> - uByte bTerminalLink;
> - uByte bDelay;
> - uWord wFormatTag;
> -} __packed;
> -
> -struct usb_audio_streaming_endpoint_descriptor {
> - uByte bLength;
> - uByte bDescriptorType;
> - uByte bDescriptorSubtype;
> - uByte bmAttributes;
> -#define UA_SED_FREQ_CONTROL 0x01
> -#define UA_SED_PITCH_CONTROL 0x02
> -#define UA_SED_MAXPACKETSONLY 0x80
> - uByte bLockDelayUnits;
> - uWord wLockDelay;
> -} __packed;
> -
> -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)
> -} __packed;
> -
> -struct usb_audio_cluster {
> - uByte bNrChannels;
> - uWord wChannelConfig;
> - uByte iChannelNames;
> -} __packed;
> -
> -/* Shared by all units and terminals */
> -struct usb_audio_unit {
> - uByte bLength;
> - uByte bDescriptorType;
> - uByte bDescriptorSubtype;
> - uByte bUnitId;
> -};
> -
> -/* 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;
> -} __packed;
> -
> -/* UDESCSUB_AC_OUTPUT */
> -struct usb_audio_output_terminal {
> - uByte bLength;
> - uByte bDescriptorType;
> - uByte bDescriptorSubtype;
> - uByte bTerminalId;
> - uWord wTerminalType;
> - uByte bAssocTerminal;
> - uByte bSourceId;
> - uByte iTerminal;
> -} __packed;
> -
> -/* 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 */
> -} __packed;
> -struct usb_audio_mixer_unit_1 {
> - uByte bNrChannels;
> - uWord wChannelConfig;
> - uByte iChannelNames;
> - uByte bmControls[255]; /* [bNrChannels] */
> - /*uByte iMixer;*/
> -} __packed;
> -
> -/* UDESCSUB_AC_SELECTOR */
> -struct usb_audio_selector_unit {
> - uByte bLength;
> - uByte bDescriptorType;
> - uByte bDescriptorSubtype;
> - uByte bUnitId;
> - uByte bNrInPins;
> - uByte baSourceId[255]; /* [bNrInPins] */
> - /* uByte iSelector; */
> -} __packed;
> -
> -/* 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; */
> -} __packed;
> -
> -/* 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 */
> -} __packed;
> -struct usb_audio_processing_unit_1{
> - uByte bNrChannels;
> - uWord wChannelConfig;
> - uByte iChannelNames;
> - uByte bControlSize;
> - uByte bmControls[255]; /* [bControlSize] */
> -#define UA_PROC_ENABLE_MASK 1
> -} __packed;
> -
> -struct usb_audio_processing_unit_updown {
> - uByte iProcessing;
> - uByte bNrModes;
> - uWord waModes[255]; /* [bNrModes] */
> -} __packed;
> -
> -/* 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 */
> -} __packed;
> -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;*/
> -} __packed;
> -
> -/* 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 UA_FMT_MPEG 0x1001
> -#define UA_FMT_AC3 0x1002
> -
> -#define SAMPLING_FREQ_CONTROL 0x01
> -#define PITCH_CONTROL 0x02
> -
> -#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
>


--
Sascha Paunovic <[hidden email]>

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

alexh
In reply to this post by Alexandre Ratchov-2
Hi,


Alexandre Ratchov-2 wrote
> If you have an audio device that is class compliant (aka vendor claims
> it's "driverless" on MacOS) *and* one of the above host/hub/device
> combinations then I'd be very interested in test reports. Especially
> I'd like to know about possible regressions.

I tested with a Focusrite Scarlett 2i4 which should be a class compliant
v2.0 device, but I get "only one AC iface allowed".

Here is my dmesg:





--
Sent from: http://openbsd-archive.7691.n7.nabble.com/openbsd-dev-tech-f151936.html

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

Alexandre Ratchov-2
On Thu, Apr 25, 2019 at 02:49:35AM -0700, alexh wrote:

> Hi,
>
>
> Alexandre Ratchov-2 wrote
> > If you have an audio device that is class compliant (aka vendor claims
> > it's "driverless" on MacOS) *and* one of the above host/hub/device
> > combinations then I'd be very interested in test reports. Especially
> > I'd like to know about possible regressions.
>
> I tested with a Focusrite Scarlett 2i4 which should be a class compliant
> v2.0 device, but I get "only one AC iface allowed".
>
> Here is my dmesg:
>

Could you install the sysutils/usbutils package and send me the output
of "lsusb -vv" please?

Thanks.

Reply | Threaded
Open this post in threaded view
|

Re: new USB audio class v2.0 driver

alexh
On 4/26/19 7:08 PM, Alexandre Ratchov wrote:

> On Thu, Apr 25, 2019 at 02:49:35AM -0700, alexh wrote:
>> Hi,
>>
>>
>> Alexandre Ratchov-2 wrote
>>> If you have an audio device that is class compliant (aka vendor claims
>>> it's "driverless" on MacOS) *and* one of the above host/hub/device
>>> combinations then I'd be very interested in test reports. Especially
>>> I'd like to know about possible regressions.
>>
>> I tested with a Focusrite Scarlett 2i4 which should be a class compliant
>> v2.0 device, but I get "only one AC iface allowed".
>>
>> Here is my dmesg:
>>
>
> Could you install the sysutils/usbutils package and send me the output
> of "lsusb -vv" please?
>
> Thanks.
>
Sure, it is attached. Thanks!


lsusb.txt (74K) Download Attachment