kernel/usb4bsd: Update uaudio driver to FreeBSD r276701
authorSascha Wildner <saw@online.de>
Fri, 9 Jan 2015 17:18:21 +0000 (18:18 +0100)
committerMarkus Pfeiffer <markus.pfeiffer@morphism.de>
Sun, 22 Feb 2015 22:33:11 +0000 (22:33 +0000)
etc/devd/usb.conf
sys/bus/u4b/audio/Makefile
sys/bus/u4b/audio/uaudio.c
sys/bus/u4b/audio/uaudio.h
sys/bus/u4b/audio/uaudio_pcm.c
sys/bus/u4b/audio/uaudioreg.h

index f406768..02c57ee 100644 (file)
@@ -291,6 +291,14 @@ nomatch 32 {
        action "kldload -n uslcom";
 };
 
+nomatch 32 {
+       match "bus" "uhub[0-9]+";
+       match "mode" "host";
+       match "vendor" "0x0499";
+       match "product" "(0x1000|0x1001|0x1002|0x1003|0x1004|0x1005|0x1006|0x1007|0x1008|0x1009|0x100a|0x100c|0x100d|0x100e|0x100f|0x1010|0x1011|0x1012|0x1013|0x1014|0x1015|0x1016|0x1017|0x1018|0x1019|0x101a|0x101b|0x101c|0x101d|0x101e|0x101f|0x1020|0x1021|0x1022|0x1023|0x1024|0x1025|0x1026|0x1027|0x1028|0x1029|0x102a|0x102b|0x102e|0x1030|0x1031|0x1032|0x1033|0x1034|0x1035|0x1036|0x1037|0x1038|0x1039|0x103a|0x103b|0x103c|0x103d|0x103e|0x103f|0x1040|0x1041|0x1042|0x1043|0x1044|0x1045|0x104e|0x104f|0x1050|0x1051|0x1052|0x1053|0x1054|0x1055|0x1056|0x1057|0x1058|0x1059|0x105a|0x105b|0x105c|0x105d|0x1503|0x2000|0x2001|0x2002|0x2003|0x5000|0x5001|0x5002|0x5003|0x5004|0x5005|0x5006|0x5007|0x5008|0x5009|0x500a|0x500b|0x500c|0x500d|0x500e|0x500f|0x7000|0x7010)";
+       action "kldload -n uaudio";
+};
+
 nomatch 32 {
        match "bus" "uhub[0-9]+";
        match "mode" "host";
@@ -3401,6 +3409,22 @@ nomatch 32 {
        action "kldload -n uhid";
 };
 
+nomatch 32 {
+       match "bus" "uhub[0-9]+";
+       match "mode" "host";
+       match "intclass" "0x01";
+       match "intsubclass" "0x01";
+       action "kldload -n uaudio";
+};
+
+nomatch 32 {
+       match "bus" "uhub[0-9]+";
+       match "mode" "host";
+       match "intclass" "0x01";
+       match "intsubclass" "0x03";
+       action "kldload -n uaudio";
+};
+
 nomatch 32 {
        match "bus" "uhub[0-9]+";
        match "mode" "host";
@@ -3423,5 +3447,5 @@ nomatch 32 {
        action "kldload -n umass";
 };
 
-# 1538 USB entries processed
+# 1643 USB entries processed
 
index 8d4ee34..5100e29 100644 (file)
@@ -1,6 +1,8 @@
-KMOD=   uaudio
+# $FreeBSD: head/sys/modules/sound/driver/uaudio/Makefile 266006 2014-05-14 07:33:06Z hselasky $
 
-SRCS=   opt_bus.h opt_usb.h opt_snd.h device_if.h bus_if.h usb_if.h usbdevs.h \
-       mixer_if.h feeder_if.h channel_if.h uaudio.c uaudio_pcm.c
+KMOD=  uaudio
+SRCS=  bus_if.h device_if.h mixer_if.h usb_if.h
+SRCS+= opt_usb.h opt_bus.h opt_snd.h feeder_if.h channel_if.h usbdevs.h
+SRCS+= uaudio_pcm.c uaudio.c 
 
-.include <bsd.kmod.mk>  
+.include <bsd.kmod.mk>
index 039661d..c7bb431 100644 (file)
@@ -1,5 +1,5 @@
 /*     $NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $ */
-/*     $FreeBSD$ */
+/*     $FreeBSD: head/sys/dev/sound/usb/uaudio.c 276701 2015-01-05 15:04:17Z hselasky $ */
 
 /*-
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -65,6 +65,9 @@
 #include <bus/u4b/usb.h>
 #include <bus/u4b/usbdi.h>
 #include <bus/u4b/usbdi_util.h>
+#include <bus/u4b/usbhid.h>
+#include <bus/u4b/usb_request.h>
+#include <bus/u4b/usb_process.h>
 
 #define        USB_DEBUG_VAR uaudio_debug
 #include <bus/u4b/usb_debug.h>
@@ -73,9 +76,7 @@
 
 #include <sys/reboot.h>                        /* for bootverbose */
 
-#ifdef HAVE_KERNEL_OPTION_HEADERS
 #include "opt_snd.h"
-#endif
 
 #include <dev/sound/pcm/sound.h>
 #include <bus/u4b/audio/uaudioreg.h>
@@ -109,22 +110,40 @@ SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW,
 #endif
 
 #define        UAUDIO_NFRAMES          64      /* must be factor of 8 due HS-USB */
-#define        UAUDIO_NCHANBUFS        2       /* number of outstanding request */
-#define        UAUDIO_RECURSE_LIMIT   24       /* rounds */
+#define        UAUDIO_NCHANBUFS        2       /* number of outstanding request */
+#define        UAUDIO_RECURSE_LIMIT    255     /* rounds */
 
 #define        MAKE_WORD(h,l) (((h) << 8) | (l))
 #define        BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1)
 #define        UAUDIO_MAX_CHAN(x) (x)
+#define        MIX(sc) ((sc)->sc_mixer_node)
+
+union uaudio_asid {
+       const struct usb_audio_streaming_interface_descriptor *v1;
+       const struct usb_audio20_streaming_interface_descriptor *v2;
+};
+
+union uaudio_asf1d {
+       const struct usb_audio_streaming_type1_descriptor *v1;
+       const struct usb_audio20_streaming_type1_descriptor *v2;
+};
+
+union uaudio_sed {
+       const struct usb_audio_streaming_endpoint_descriptor *v1;
+       const struct usb_audio20_streaming_endpoint_descriptor *v2;
+};
 
 struct uaudio_mixer_node {
+       const char *name;
+
        int32_t minval;
        int32_t maxval;
-#define        MIX_MAX_CHAN 8
+#define        MIX_MAX_CHAN 16
        int32_t wValue[MIX_MAX_CHAN];   /* using nchan */
        uint32_t mul;
        uint32_t ctl;
 
-       uint16_t wData[MIX_MAX_CHAN];   /* using nchan */
+       int wData[MIX_MAX_CHAN];        /* using nchan */
        uint16_t wIndex;
 
        uint8_t update[(MIX_MAX_CHAN + 7) / 8];
@@ -143,25 +162,42 @@ struct uaudio_mixer_node {
 #define        MAX_SELECTOR_INPUT_PIN 256
        uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN];
        uint8_t class;
+       uint8_t val_default;
+
+       uint8_t desc[64];
 
        struct uaudio_mixer_node *next;
 };
 
+struct uaudio_configure_msg {
+       struct usb_proc_msg hdr;
+       struct uaudio_softc *sc;
+};
+
+#define        CHAN_MAX_ALT 24
+
+struct uaudio_chan_alt {
+       union uaudio_asf1d p_asf1d;
+       union uaudio_sed p_sed;
+       const usb_endpoint_descriptor_audio_t *p_ed1;
+       const struct uaudio_format *p_fmt;
+       const struct usb_config *usb_cfg;
+       uint32_t sample_rate;   /* in Hz */
+       uint16_t sample_size;
+       uint8_t iface_index;
+       uint8_t iface_alt_index;
+       uint8_t channels;
+       uint8_t enable_sync;
+};
+
 struct uaudio_chan {
        struct pcmchan_caps pcm_cap;    /* capabilities */
-
+       struct uaudio_chan_alt usb_alt[CHAN_MAX_ALT];
        struct snd_dbuf *pcm_buf;
-       const struct usb_config *usb_cfg;
        struct lock *pcm_lock;          /* lock protecting this structure */
        struct uaudio_softc *priv_sc;
        struct pcm_channel *pcm_ch;
-       struct usb_xfer *xfer[UAUDIO_NCHANBUFS];
-       const struct usb_audio_streaming_interface_descriptor *p_asid;
-       const struct usb_audio_streaming_type1_descriptor *p_asf1d;
-       const struct usb_audio_streaming_endpoint_descriptor *p_sed;
-       const usb_endpoint_descriptor_audio_t *p_ed1;
-       const usb_endpoint_descriptor_audio_t *p_ed2;
-       const struct uaudio_format *p_fmt;
+       struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1];
 
        uint8_t *buf;                   /* pointer to buffer */
        uint8_t *start;                 /* upper layer buffer start */
@@ -169,26 +205,33 @@ struct uaudio_chan {
        uint8_t *cur;                   /* current position in upper layer
                                         * buffer */
 
-       uint32_t intr_size;             /* in bytes */
        uint32_t intr_frames;           /* in units */
-       uint32_t sample_rate;
        uint32_t frames_per_second;
        uint32_t sample_rem;
        uint32_t sample_curr;
+       uint32_t max_buf;
 
-       uint32_t format;
        uint32_t pcm_format[2];
 
        uint16_t bytes_per_frame[2];
 
-       uint16_t sample_size;
-
-       uint8_t valid;
-       uint8_t iface_index;
-       uint8_t iface_alt_index;
+       uint8_t num_alt;
+       uint8_t cur_alt;
+       uint8_t set_alt;
+       uint8_t operation;
+#define        CHAN_OP_NONE 0
+#define        CHAN_OP_START 1
+#define        CHAN_OP_STOP 2
+#define        CHAN_OP_DRAIN 3
+
+       uint8_t last_sync_time;
+       uint8_t last_sync_state;
+#define        UAUDIO_SYNC_NONE 0
+#define        UAUDIO_SYNC_MORE 1
+#define        UAUDIO_SYNC_LESS 2
 };
 
-#define        UMIDI_CABLES_MAX   16           /* units */
+#define        UMIDI_EMB_JACK_MAX   16         /* units */
 #define        UMIDI_TX_FRAMES    256          /* units */
 #define        UMIDI_TX_BUFFER    (UMIDI_TX_FRAMES * 4)        /* bytes */
 
@@ -219,7 +262,7 @@ struct umidi_sub_chan {
 
 struct umidi_chan {
 
-       struct umidi_sub_chan sub[UMIDI_CABLES_MAX];
+       struct umidi_sub_chan sub[UMIDI_EMB_JACK_MAX];
        struct lock lock;
 
        struct usb_xfer *xfer[UMIDI_N_TRANSFER];
@@ -231,20 +274,54 @@ struct umidi_chan {
        uint8_t write_open_refcount;
 
        uint8_t curr_cable;
-       uint8_t max_cable;
+       uint8_t max_emb_jack;
        uint8_t valid;
        uint8_t single_command;
 };
 
-struct uaudio_softc {
-       struct lock sc_lock;
+struct uaudio_search_result {
+       uint8_t bit_input[(256 + 7) / 8];
+       uint8_t bit_output[(256 + 7) / 8];
+       uint8_t recurse_level;
+       uint8_t id_max;
+       uint8_t is_input;
+};
+
+enum {
+       UAUDIO_HID_RX_TRANSFER,
+       UAUDIO_HID_N_TRANSFER,
+};
+
+struct uaudio_hid {
+       struct usb_xfer *xfer[UAUDIO_HID_N_TRANSFER];
+       struct hid_location volume_up_loc;
+       struct hid_location volume_down_loc;
+       struct hid_location mute_loc;
+       uint32_t flags;
+#define        UAUDIO_HID_VALID                0x0001
+#define        UAUDIO_HID_HAS_ID               0x0002
+#define        UAUDIO_HID_HAS_VOLUME_UP        0x0004
+#define        UAUDIO_HID_HAS_VOLUME_DOWN      0x0008
+#define        UAUDIO_HID_HAS_MUTE             0x0010
+       uint8_t iface_index;
+       uint8_t volume_up_id;
+       uint8_t volume_down_id;
+       uint8_t mute_id;
+};
 
+struct uaudio_softc {
        struct sbuf sc_sndstat;
        struct sndcard_func sc_sndcard_func;
        struct uaudio_chan sc_rec_chan;
        struct uaudio_chan sc_play_chan;
        struct umidi_chan sc_midi_chan;
+       struct uaudio_hid sc_hid;
+       struct uaudio_search_result sc_mixer_clocks;
+       struct uaudio_mixer_node sc_mixer_node;
+       struct uaudio_configure_msg sc_config_msg[2];
 
+       struct lock *sc_mixer_lock;
+       struct snd_mixer *sc_mixer_dev;
        struct usb_device *sc_udev;
        struct usb_xfer *sc_mixer_xfer[1];
        struct uaudio_mixer_node *sc_mixer_root;
@@ -269,24 +346,28 @@ struct uaudio_softc {
        uint8_t sc_uq_au_vendor_class:1;
 };
 
-struct uaudio_search_result {
-       uint8_t bit_input[(256 + 7) / 8];
-       uint8_t bit_output[(256 + 7) / 8];
-       uint8_t bit_visited[(256 + 7) / 8];
-       uint8_t recurse_level;
-       uint8_t id_max;
-};
-
 struct uaudio_terminal_node {
        union {
                const struct usb_descriptor *desc;
-               const struct usb_audio_input_terminal *it;
-               const struct usb_audio_output_terminal *ot;
-               const struct usb_audio_mixer_unit_0 *mu;
-               const struct usb_audio_selector_unit *su;
-               const struct usb_audio_feature_unit *fu;
-               const struct usb_audio_processing_unit_0 *pu;
-               const struct usb_audio_extension_unit_0 *eu;
+               const struct usb_audio_input_terminal *it_v1;
+               const struct usb_audio_output_terminal *ot_v1;
+               const struct usb_audio_mixer_unit_0 *mu_v1;
+               const struct usb_audio_selector_unit *su_v1;
+               const struct usb_audio_feature_unit *fu_v1;
+               const struct usb_audio_processing_unit_0 *pu_v1;
+               const struct usb_audio_extension_unit_0 *eu_v1;
+               const struct usb_audio20_clock_source_unit *csrc_v2;
+               const struct usb_audio20_clock_selector_unit_0 *csel_v2;
+               const struct usb_audio20_clock_multiplier_unit *cmul_v2;
+               const struct usb_audio20_input_terminal *it_v2;
+               const struct usb_audio20_output_terminal *ot_v2;
+               const struct usb_audio20_mixer_unit_0 *mu_v2;
+               const struct usb_audio20_selector_unit *su_v2;
+               const struct usb_audio20_feature_unit *fu_v2;
+               const struct usb_audio20_sample_rate_unit *ru_v2;
+               const struct usb_audio20_processing_unit_0 *pu_v2;
+               const struct usb_audio20_extension_unit_0 *eu_v2;
+               const struct usb_audio20_effect_unit *ef_v2;
        }       u;
        struct uaudio_search_result usr;
        struct uaudio_terminal_node *root;
@@ -299,7 +380,7 @@ struct uaudio_format {
        const char *description;
 };
 
-static const struct uaudio_format uaudio_formats[] = {
+static const struct uaudio_format uaudio10_formats[] = {
 
        {UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
        {UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
@@ -317,6 +398,24 @@ static const struct uaudio_format uaudio_formats[] = {
        {0, 0, 0, NULL}
 };
 
+static const struct uaudio_format uaudio20_formats[] = {
+
+       {UA20_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
+       {UA20_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
+       {UA20_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
+       {UA20_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
+
+       {UA20_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
+       {UA20_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
+       {UA20_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
+       {UA20_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
+
+       {UA20_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
+       {UA20_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
+
+       {0, 0, 0, NULL}
+};
+
 #define        UAC_OUTPUT      0
 #define        UAC_INPUT       1
 #define        UAC_EQUAL       2
@@ -337,23 +436,26 @@ static device_attach_t uaudio_attach;
 static device_detach_t uaudio_detach;
 
 static usb_callback_t uaudio_chan_play_callback;
+static usb_callback_t uaudio_chan_play_sync_callback;
 static usb_callback_t uaudio_chan_record_callback;
+static usb_callback_t uaudio_chan_record_sync_callback;
 static usb_callback_t uaudio_mixer_write_cfg_callback;
 static usb_callback_t umidi_bulk_read_callback;
 static usb_callback_t umidi_bulk_write_callback;
+static usb_callback_t uaudio_hid_rx_callback;
+
+static usb_proc_callback_t uaudio_configure_msg;
+
+/* ==== USB mixer ==== */
+
+static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS);
+static void uaudio_mixer_ctl_free(struct uaudio_softc *);
+static void uaudio_mixer_register_sysctl(struct uaudio_softc *, device_t);
+static void uaudio_mixer_reload_all(struct uaudio_softc *);
+static void uaudio_mixer_controls_create_ftu(struct uaudio_softc *);
+
+/* ==== USB audio v1.0 ==== */
 
-static void    uaudio_chan_fill_info_sub(struct uaudio_softc *,
-                   struct usb_device *, uint32_t, uint8_t, uint8_t);
-static void    uaudio_chan_fill_info(struct uaudio_softc *,
-                   struct usb_device *);
-static void    uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
-                   struct uaudio_mixer_node *);
-static void    uaudio_mixer_add_ctl(struct uaudio_softc *,
-                   struct uaudio_mixer_node *);
-static void    uaudio_mixer_add_input(struct uaudio_softc *,
-                   const struct uaudio_terminal_node *, int);
-static void    uaudio_mixer_add_output(struct uaudio_softc *,
-                   const struct uaudio_terminal_node *, int);
 static void    uaudio_mixer_add_mixer(struct uaudio_softc *,
                    const struct uaudio_terminal_node *, int);
 static void    uaudio_mixer_add_selector(struct uaudio_softc *,
@@ -374,25 +476,56 @@ static uint16_t   uaudio_mixer_determine_class(const struct uaudio_terminal_node *
                    struct uaudio_mixer_node *);
 static uint16_t        uaudio_mixer_feature_name(const struct uaudio_terminal_node *,
                    struct uaudio_mixer_node *);
-static const struct uaudio_terminal_node *uaudio_mixer_get_input(
-                   const struct uaudio_terminal_node *, uint8_t);
-static const struct uaudio_terminal_node *uaudio_mixer_get_output(
-                   const struct uaudio_terminal_node *, uint8_t);
 static void    uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *,
                    const uint8_t *, uint8_t, struct uaudio_search_result *);
-static void    uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *,
-                   uint8_t, uint8_t, struct uaudio_search_result *);
+static const void *uaudio_mixer_verify_desc(const void *, uint32_t);
+static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t);
+static int     uaudio_mixer_get(struct usb_device *, uint16_t, uint8_t,
+                   struct uaudio_mixer_node *);
+
+/* ==== USB audio v2.0 ==== */
+
+static void    uaudio20_mixer_add_mixer(struct uaudio_softc *,
+                   const struct uaudio_terminal_node *, int);
+static void    uaudio20_mixer_add_selector(struct uaudio_softc *,
+                   const struct uaudio_terminal_node *, int);
+static void    uaudio20_mixer_add_feature(struct uaudio_softc *,
+                   const struct uaudio_terminal_node *, int);
+static struct  usb_audio20_cluster uaudio20_mixer_get_cluster(uint8_t,
+                   const struct uaudio_terminal_node *);
+static uint16_t        uaudio20_mixer_determine_class(const struct uaudio_terminal_node *,
+                   struct uaudio_mixer_node *);
+static uint16_t        uaudio20_mixer_feature_name(const struct uaudio_terminal_node *,
+                   struct uaudio_mixer_node *);
+static void    uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *,
+                   const uint8_t *, uint8_t, struct uaudio_search_result *);
+static const void *uaudio20_mixer_verify_desc(const void *, uint32_t);
+static usb_error_t uaudio20_set_speed(struct usb_device *, uint8_t,
+                   uint8_t, uint32_t);
+
+/* USB audio v1.0 and v2.0 */
+
+static void    uaudio_chan_fill_info_sub(struct uaudio_softc *,
+                   struct usb_device *, uint32_t, uint8_t, uint8_t);
+static void    uaudio_chan_fill_info(struct uaudio_softc *,
+                   struct usb_device *);
+static void    uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
+                   struct uaudio_mixer_node *);
+static void    uaudio_mixer_add_ctl(struct uaudio_softc *,
+                   struct uaudio_mixer_node *);
 static void    uaudio_mixer_fill_info(struct uaudio_softc *,
                    struct usb_device *, void *);
-static uint16_t        uaudio_mixer_get(struct usb_device *, uint8_t,
-                   struct uaudio_mixer_node *);
 static void    uaudio_mixer_ctl_set(struct uaudio_softc *,
                    struct uaudio_mixer_node *, uint8_t, int32_t val);
-static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t);
 static int     uaudio_mixer_signext(uint8_t, int);
 static int     uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val);
-static const void *uaudio_mixer_verify_desc(const void *, uint32_t);
 static void    uaudio_mixer_init(struct uaudio_softc *);
+static const struct uaudio_terminal_node *uaudio_mixer_get_input(
+                   const struct uaudio_terminal_node *, uint8_t);
+static const struct uaudio_terminal_node *uaudio_mixer_get_output(
+                   const struct uaudio_terminal_node *, uint8_t);
+static void    uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *,
+                   uint8_t, uint8_t, struct uaudio_search_result *);
 static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t);
 static struct  umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *);
 static void    umidi_start_read(struct usb_fifo *);
@@ -405,17 +538,17 @@ static void       umidi_close(struct usb_fifo *, int);
 static void    umidi_init(device_t dev);
 static int     umidi_probe(device_t dev);
 static int     umidi_detach(device_t dev);
+static int     uaudio_hid_probe(struct uaudio_softc *sc,
+                   struct usb_attach_arg *uaa);
+static void    uaudio_hid_detach(struct uaudio_softc *sc);
 
 #ifdef USB_DEBUG
 static void    uaudio_chan_dump_ep_desc(
                    const usb_endpoint_descriptor_audio_t *);
-static void    uaudio_mixer_dump_cluster(uint8_t,
-                   const struct uaudio_terminal_node *);
-static const char *uaudio_mixer_get_terminal_name(uint16_t);
 #endif
 
 static const struct usb_config
-       uaudio_cfg_record[UAUDIO_NCHANBUFS] = {
+       uaudio_cfg_record[UAUDIO_NCHANBUFS + 1] = {
        [0] = {
                .type = UE_ISOCHRONOUS,
                .endpoint = UE_ADDR_ANY,
@@ -435,10 +568,20 @@ static const struct usb_config
                .flags = {.short_xfer_ok = 1,},
                .callback = &uaudio_chan_record_callback,
        },
+
+       [2] = {
+               .type = UE_ISOCHRONOUS,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_OUT,
+               .bufsize = 0,   /* use "wMaxPacketSize * frames" */
+               .frames = 1,
+               .flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
+               .callback = &uaudio_chan_record_sync_callback,
+       },
 };
 
 static const struct usb_config
-       uaudio_cfg_play[UAUDIO_NCHANBUFS] = {
+       uaudio_cfg_play[UAUDIO_NCHANBUFS + 1] = {
        [0] = {
                .type = UE_ISOCHRONOUS,
                .endpoint = UE_ADDR_ANY,
@@ -458,6 +601,16 @@ static const struct usb_config
                .flags = {.short_xfer_ok = 1,},
                .callback = &uaudio_chan_play_callback,
        },
+
+       [2] = {
+               .type = UE_ISOCHRONOUS,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_IN,
+               .bufsize = 0,   /* use "wMaxPacketSize * frames" */
+               .frames = 1,
+               .flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
+               .callback = &uaudio_chan_play_sync_callback,
+       },
 };
 
 static const struct usb_config
@@ -512,6 +665,18 @@ static const struct usb_config
        },
 };
 
+static const struct usb_config
+       uaudio_hid_config[UAUDIO_HID_N_TRANSFER] = {
+       [UAUDIO_HID_RX_TRANSFER] = {
+               .type = UE_INTERRUPT,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_IN,
+               .bufsize = 0,   /* use wMaxPacketSize */
+               .flags = {.short_xfer_ok = 1,},
+               .callback = &uaudio_hid_rx_callback,
+       },
+};
+
 static devclass_t uaudio_devclass;
 
 static device_method_t uaudio_methods[] = {
@@ -531,6 +696,115 @@ static driver_t uaudio_driver = {
        .size = sizeof(struct uaudio_softc),
 };
 
+/* The following table is derived from Linux's quirks-table.h */ 
+static const STRUCT_USB_HOST_ID uaudio_vendor_midi[] = {
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1000, 0) }, /* UX256 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1001, 0) }, /* MU1000 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1002, 0) }, /* MU2000 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1003, 0) }, /* MU500 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1004, 3) }, /* UW500 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1005, 0) }, /* MOTIF6 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1006, 0) }, /* MOTIF7 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1007, 0) }, /* MOTIF8 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1008, 0) }, /* UX96 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1009, 0) }, /* UX16 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x100a, 3) }, /* EOS BX */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x100c, 0) }, /* UC-MX */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x100d, 0) }, /* UC-KX */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x100e, 0) }, /* S08 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x100f, 0) }, /* CLP-150 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1010, 0) }, /* CLP-170 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1011, 0) }, /* P-250 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1012, 0) }, /* TYROS */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1013, 0) }, /* PF-500 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1014, 0) }, /* S90 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1015, 0) }, /* MOTIF-R */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1016, 0) }, /* MDP-5 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1017, 0) }, /* CVP-204 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1018, 0) }, /* CVP-206 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1019, 0) }, /* CVP-208 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x101a, 0) }, /* CVP-210 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x101b, 0) }, /* PSR-1100 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x101c, 0) }, /* PSR-2100 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x101d, 0) }, /* CLP-175 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x101e, 0) }, /* PSR-K1 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x101f, 0) }, /* EZ-J24 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1020, 0) }, /* EZ-250i */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1021, 0) }, /* MOTIF ES 6 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1022, 0) }, /* MOTIF ES 7 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1023, 0) }, /* MOTIF ES 8 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1024, 0) }, /* CVP-301 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1025, 0) }, /* CVP-303 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1026, 0) }, /* CVP-305 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1027, 0) }, /* CVP-307 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1028, 0) }, /* CVP-309 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1029, 0) }, /* CVP-309GP */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x102a, 0) }, /* PSR-1500 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x102b, 0) }, /* PSR-3000 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x102e, 0) }, /* ELS-01/01C */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1030, 0) }, /* PSR-295/293 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1031, 0) }, /* DGX-205/203 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1032, 0) }, /* DGX-305 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1033, 0) }, /* DGX-505 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1034, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1035, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1036, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1037, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1038, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1039, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x103a, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x103b, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x103c, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x103d, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x103e, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x103f, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1040, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1041, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1042, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1043, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1044, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1045, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x104e, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x104f, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1050, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1051, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1052, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1053, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1054, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1055, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1056, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1057, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1058, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1059, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x105a, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x105b, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x105c, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x105d, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x1503, 3) }, /* MOX6/MOX8 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x2000, 0) }, /* DGP-7 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x2001, 0) }, /* DGP-5 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x2002, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x2003, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5000, 0) }, /* CS1D */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5001, 0) }, /* DSP1D */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5002, 0) }, /* DME32 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5003, 0) }, /* DM2000 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5004, 0) }, /* 02R96 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5005, 0) }, /* ACU16-C */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5006, 0) }, /* NHB32-C */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5007, 0) }, /* DM1000 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5008, 0) }, /* 01V96 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x5009, 0) }, /* SPX2000 */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x500a, 0) }, /* PM5D */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x500b, 0) }, /* DME64N */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x500c, 0) }, /* DME24N */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x500d, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x500e, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x500f, 0) }, /* NULL */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x7000, 0) }, /* DTX */
+       { USB_VPI(USB_VENDOR_YAMAHA, 0x7010, 0) }, /* UB99 */
+};
+
 static const STRUCT_USB_HOST_ID __used uaudio_devs[] = {
        /* Generic USB audio class match */
        {USB_IFACE_CLASS(UICLASS_AUDIO),
@@ -548,10 +822,16 @@ uaudio_probe(device_t dev)
        if (uaa->usb_mode != USB_MODE_HOST)
                return (ENXIO);
 
-       /* lookup non-standard device */
+       /* lookup non-standard device(s) */
+
+       if (usbd_lookup_id_by_uaa(uaudio_vendor_midi,
+           sizeof(uaudio_vendor_midi), uaa) == 0) {
+               return (BUS_PROBE_SPECIFIC);
+       }
 
        if (uaa->info.bInterfaceClass != UICLASS_AUDIO) {
-               if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0)
+               if (uaa->info.bInterfaceClass != UICLASS_VENDOR ||
+                   usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0)
                        return (ENXIO);
        }
 
@@ -581,15 +861,18 @@ uaudio_attach(device_t dev)
        struct usb_attach_arg *uaa = device_get_ivars(dev);
        struct uaudio_softc *sc = device_get_softc(dev);
        struct usb_interface_descriptor *id;
+       usb_error_t err;
        device_t child;
 
-       lockinit(&sc->sc_lock, "uaudio", 0, 0);
-
        sc->sc_play_chan.priv_sc = sc;
        sc->sc_rec_chan.priv_sc = sc;
        sc->sc_udev = uaa->device;
        sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
        sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
+       sc->sc_config_msg[0].hdr.pm_callback = &uaudio_configure_msg;
+       sc->sc_config_msg[0].sc = sc;
+       sc->sc_config_msg[1].hdr.pm_callback = &uaudio_configure_msg;
+       sc->sc_config_msg[1].sc = sc;
 
        if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR))
                sc->sc_uq_audio_swap_lr = 1;
@@ -612,33 +895,88 @@ uaudio_attach(device_t dev)
 
        id = usbd_get_interface_descriptor(uaa->iface);
 
-       uaudio_chan_fill_info(sc, uaa->device);
-
+       /* must fill mixer info before channel info */
        uaudio_mixer_fill_info(sc, uaa->device, id);
 
+       /* fill channel info */
+       uaudio_chan_fill_info(sc, uaa->device);
+
        DPRINTF("audio rev %d.%02x\n",
            sc->sc_audio_rev >> 8,
            sc->sc_audio_rev & 0xff);
 
+       if (sc->sc_mixer_count == 0) {
+               if (uaa->info.idVendor == USB_VENDOR_MAUDIO &&
+                   (uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA ||
+                   uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA8R)) {
+                       DPRINTF("Generating mixer descriptors\n");
+                       uaudio_mixer_controls_create_ftu(sc);
+               }
+       }
+
        DPRINTF("%d mixer controls\n",
            sc->sc_mixer_count);
 
-       if (sc->sc_play_chan.valid) {
-               device_printf(dev, "Play: %d Hz, %d ch, %s format\n",
-                   sc->sc_play_chan.sample_rate,
-                   sc->sc_play_chan.p_asf1d->bNrChannels,
-                   sc->sc_play_chan.p_fmt->description);
+       if (sc->sc_play_chan.num_alt > 0) {
+               uint8_t x;
+
+               /*
+                * Need to set a default alternate interface, else
+                * some USB audio devices might go into an infinte
+                * re-enumeration loop:
+                */
+               err = usbd_set_alt_interface_index(sc->sc_udev,
+                   sc->sc_play_chan.usb_alt[0].iface_index,
+                   sc->sc_play_chan.usb_alt[0].iface_alt_index);
+               if (err) {
+                       DPRINTF("setting of alternate index failed: %s!\n",
+                           usbd_errstr(err));
+               }
+               for (x = 0; x != sc->sc_play_chan.num_alt; x++) {
+                       device_printf(dev, "Play: %d Hz, %d ch, %s format, "
+                           "2x8ms buffer.\n",
+                           sc->sc_play_chan.usb_alt[x].sample_rate,
+                           sc->sc_play_chan.usb_alt[x].channels,
+                           sc->sc_play_chan.usb_alt[x].p_fmt->description);
+               }
        } else {
-               device_printf(dev, "No playback!\n");
+               device_printf(dev, "No playback.\n");
        }
 
-       if (sc->sc_rec_chan.valid) {
-               device_printf(dev, "Record: %d Hz, %d ch, %s format\n",
-                   sc->sc_rec_chan.sample_rate,
-                   sc->sc_rec_chan.p_asf1d->bNrChannels,
-                   sc->sc_rec_chan.p_fmt->description);
+       if (sc->sc_rec_chan.num_alt > 0) {
+               uint8_t x;
+
+               /*
+                * Need to set a default alternate interface, else
+                * some USB audio devices might go into an infinte
+                * re-enumeration loop:
+                */
+               err = usbd_set_alt_interface_index(sc->sc_udev,
+                   sc->sc_rec_chan.usb_alt[0].iface_index,
+                   sc->sc_rec_chan.usb_alt[0].iface_alt_index);
+               if (err) {
+                       DPRINTF("setting of alternate index failed: %s!\n",
+                           usbd_errstr(err));
+               }
+               for (x = 0; x != sc->sc_rec_chan.num_alt; x++) {
+                       device_printf(dev, "Record: %d Hz, %d ch, %s format, "
+                           "2x8ms buffer.\n",
+                           sc->sc_rec_chan.usb_alt[x].sample_rate,
+                           sc->sc_rec_chan.usb_alt[x].channels,
+                           sc->sc_rec_chan.usb_alt[x].p_fmt->description);
+               }
        } else {
-               device_printf(dev, "No recording!\n");
+               device_printf(dev, "No recording.\n");
+       }
+
+       if (sc->sc_midi_chan.valid == 0) {
+               if (usbd_lookup_id_by_uaa(uaudio_vendor_midi,
+                   sizeof(uaudio_vendor_midi), uaa) == 0) {
+                       sc->sc_midi_chan.iface_index =
+                           (uint8_t)uaa->driver_info;
+                       sc->sc_midi_chan.iface_alt_index = 0;
+                       sc->sc_midi_chan.valid = 1;
+               }
        }
 
        if (sc->sc_midi_chan.valid) {
@@ -646,9 +984,9 @@ uaudio_attach(device_t dev)
                if (umidi_probe(dev)) {
                        goto detach;
                }
-               device_printf(dev, "MIDI sequencer\n");
+               device_printf(dev, "MIDI sequencer.\n");
        } else {
-               device_printf(dev, "No midi sequencer\n");
+               device_printf(dev, "No MIDI sequencer.\n");
        }
 
        DPRINTF("doing child attach\n");
@@ -657,18 +995,36 @@ uaudio_attach(device_t dev)
 
        sc->sc_sndcard_func.func = SCF_PCM;
 
-       child = device_add_child(dev, "pcm", -1);
+       /*
+        * Only attach a PCM device if we have a playback, recording
+        * or mixer device present:
+        */
+       if (sc->sc_play_chan.num_alt > 0 ||
+           sc->sc_rec_chan.num_alt > 0 ||
+           sc->sc_mix_info) {
+               child = device_add_child(dev, "pcm", -1);
 
-       if (child == NULL) {
-               DPRINTF("out of memory\n");
-               goto detach;
+               if (child == NULL) {
+                       DPRINTF("out of memory\n");
+                       goto detach;
+               }
+               device_set_ivars(child, &sc->sc_sndcard_func);
        }
-       device_set_ivars(child, &sc->sc_sndcard_func);
 
        if (bus_generic_attach(dev)) {
                DPRINTF("child attach failed\n");
                goto detach;
        }
+
+       if (uaudio_hid_probe(sc, uaa) == 0) {
+               device_printf(dev, "HID volume keys found.\n");
+       } else {
+               device_printf(dev, "No HID volume keys found.\n");
+       }
+
+       /* reload all mixer settings */
+       uaudio_mixer_reload_all(sc);
+
        return (0);                     /* success */
 
 detach:
@@ -704,33 +1060,33 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas
                 */
                uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL);
        }
-       if (mixer_init(dev, mixer_class, sc)) {
+       if (mixer_init(dev, mixer_class, sc))
                goto detach;
-       }
        sc->sc_mixer_init = 1;
 
+       mixer_hwvol_init(dev);
+
        ksnprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
 
        if (pcm_register(dev, sc,
-           sc->sc_play_chan.valid ? 1 : 0,
-           sc->sc_rec_chan.valid ? 1 : 0)) {
+           (sc->sc_play_chan.num_alt > 0) ? 1 : 0,
+           (sc->sc_rec_chan.num_alt > 0) ? 1 : 0)) {
                goto detach;
        }
 
-#if 0 /* XXX */
        uaudio_pcm_setflags(dev, SD_F_MPSAFE);
-#endif
-       uaudio_pcm_setflags(dev, 0);
        sc->sc_pcm_registered = 1;
 
-       if (sc->sc_play_chan.valid) {
+       if (sc->sc_play_chan.num_alt > 0) {
                pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc);
        }
-       if (sc->sc_rec_chan.valid) {
+       if (sc->sc_rec_chan.num_alt > 0) {
                pcm_addchan(dev, PCMDIR_REC, chan_class, sc);
        }
        pcm_setstatus(dev, status);
 
+       uaudio_mixer_register_sysctl(sc, dev);
+
        return (0);                     /* success */
 
 detach:
@@ -771,10 +1127,17 @@ uaudio_detach(device_t dev)
         * will time out and close opened /dev/dspX.Y device(s), if
         * any.
         */
-       if (sc->sc_play_chan.valid)
-               usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS);
-       if (sc->sc_rec_chan.valid)
-               usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS);
+       usb_proc_explore_lock(sc->sc_udev);
+       sc->sc_play_chan.operation = CHAN_OP_DRAIN;
+       sc->sc_rec_chan.operation = CHAN_OP_DRAIN;
+       usb_proc_explore_mwait(sc->sc_udev,
+           &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
+       usb_proc_explore_unlock(sc->sc_udev);
+
+       usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
+       usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);
+
+       uaudio_hid_detach(sc);
 
        if (bus_generic_detach(dev) != 0) {
                DPRINTF("detach failed!\n");
@@ -783,97 +1146,359 @@ uaudio_detach(device_t dev)
        sc->sc_sndstat_valid = 0;
 
        umidi_detach(dev);
-       lockuninit(&sc->sc_lock);
+
+       /* free mixer data */
+
+       uaudio_mixer_ctl_free(sc);
 
        return (0);
 }
 
-/*========================================================================*
- * AS - Audio Stream - routines
- *========================================================================*/
-
-#ifdef USB_DEBUG
-static void
-uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed)
+static uint32_t
+uaudio_get_buffer_size(struct uaudio_chan *ch, uint8_t alt)
 {
-       if (ed) {
-               DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n"
-                   "bEndpointAddress=%d bmAttributes=0x%x \n"
-                   "wMaxPacketSize=%d bInterval=%d \n"
-                   "bRefresh=%d bSynchAddress=%d\n",
-                   ed, ed->bLength, ed->bDescriptorType,
-                   ed->bEndpointAddress, ed->bmAttributes,
-                   UGETW(ed->wMaxPacketSize), ed->bInterval,
-                   UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0,
-                   UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0);
-       }
+       struct uaudio_chan_alt *chan_alt = &ch->usb_alt[alt];
+       /* We use 2 times 8ms of buffer */
+       uint32_t buf_size = (((chan_alt->sample_rate * (UAUDIO_NFRAMES / 8)) +
+           1000 - 1) / 1000) * chan_alt->sample_size;
+       return (buf_size);
 }
 
-#endif
-
-/*
- * The following is a workaround for broken no-name USB audio devices
- * sold by dealextreme called "3D sound". The problem is that the
- * manufacturer computed wMaxPacketSize is too small to hold the
- * actual data sent. In other words the device sometimes sends more
- * data than it actually reports it can send in a single isochronous
- * packet.
- */
 static void
-uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep,
-    uint32_t xps, uint32_t add)
+uaudio_configure_msg_sub(struct uaudio_softc *sc,
+    struct uaudio_chan *chan, int dir)
 {
-       uint32_t mps;
+       struct uaudio_chan_alt *chan_alt;
+       uint32_t frames;
+       uint32_t buf_size;
+       uint16_t fps;
+       uint8_t set_alt;
+       uint8_t fps_shift;
+       uint8_t operation;
+       usb_error_t err;
 
-       mps = UGETW(ep->wMaxPacketSize);
+       if (chan->num_alt <= 0)
+               return;
 
-       /*
-        * If the device indicates it can send more data than what the
-        * sample rate indicates, we apply the workaround.
-        */
-       if (mps > xps) {
+       DPRINTF("\n");
 
-               /* allow additional data */
-               xps += add;
+       usb_proc_explore_lock(sc->sc_udev);
+       operation = chan->operation;
+       chan->operation = CHAN_OP_NONE;
+       usb_proc_explore_unlock(sc->sc_udev);
 
-               /* check against the maximum USB 1.x length */
-               if (xps > 1023)
-                       xps = 1023;
+       lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
+       if (chan->cur_alt != chan->set_alt)
+               set_alt = chan->set_alt;
+       else
+               set_alt = CHAN_MAX_ALT;
+       lockmgr(chan->pcm_lock, LK_RELEASE);
 
-               /* check if we should do an update */
-               if (mps < xps) {
-                       /* simply update the wMaxPacketSize field */
-                       USETW(ep->wMaxPacketSize, xps);
-                       DPRINTF("Workaround: Updated wMaxPacketSize "
-                           "from %d to %d bytes.\n",
-                           (int)mps, (int)xps);
-               }
+       if (set_alt >= chan->num_alt)
+               goto done;
+
+       chan_alt = chan->usb_alt + set_alt;
+
+       usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
+
+       err = usbd_set_alt_interface_index(sc->sc_udev,
+           chan_alt->iface_index, chan_alt->iface_alt_index);
+       if (err) {
+               DPRINTF("setting of alternate index failed: %s!\n",
+                   usbd_errstr(err));
+               goto error;
        }
-}
 
-static void
-uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
-    uint32_t rate, uint8_t channels, uint8_t bit_resolution)
-{
-       struct usb_descriptor *desc = NULL;
-       const struct usb_audio_streaming_interface_descriptor *asid = NULL;
-       const struct usb_audio_streaming_type1_descriptor *asf1d = NULL;
-       const struct usb_audio_streaming_endpoint_descriptor *sed = NULL;
-       usb_endpoint_descriptor_audio_t *ed1 = NULL;
-       const usb_endpoint_descriptor_audio_t *ed2 = NULL;
-       struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
-       struct usb_interface_descriptor *id;
-       const struct uaudio_format *p_fmt;
+       /*
+        * Only set the sample rate if the channel reports that it
+        * supports the frequency control.
+        */
+
+       if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
+               /* FALLTHROUGH */
+       } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
+               unsigned int x;
+         
+               for (x = 0; x != 256; x++) {
+                       if (dir == PCMDIR_PLAY) {
+                               if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
+                                   (1 << (x % 8)))) {
+                                       continue;
+                               }
+                       } else {
+                               if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
+                                   (1 << (x % 8)))) {
+                                       continue;
+                               }
+                       }
+
+                       if (uaudio20_set_speed(sc->sc_udev,
+                           sc->sc_mixer_iface_no, x, chan_alt->sample_rate)) {
+                               /*
+                                * If the endpoint is adaptive setting
+                                * the speed may fail.
+                                */
+                               DPRINTF("setting of sample rate failed! "
+                                   "(continuing anyway)\n");
+                       }
+               }
+       } else if (chan_alt->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) {
+               if (uaudio_set_speed(sc->sc_udev,
+                   chan_alt->p_ed1->bEndpointAddress, chan_alt->sample_rate)) {
+                       /*
+                        * If the endpoint is adaptive setting the
+                        * speed may fail.
+                        */
+                       DPRINTF("setting of sample rate failed! "
+                           "(continuing anyway)\n");
+               }
+       }
+       if (usbd_transfer_setup(sc->sc_udev, &chan_alt->iface_index, chan->xfer,
+           chan_alt->usb_cfg, UAUDIO_NCHANBUFS + 1, chan, chan->pcm_lock)) {
+               DPRINTF("could not allocate USB transfers!\n");
+               goto error;
+       }
+
+       fps = usbd_get_isoc_fps(sc->sc_udev);
+
+       if (fps < 8000) {
+               /* FULL speed USB */
+               frames = 8;
+       } else {
+               /* HIGH speed USB */
+               frames = UAUDIO_NFRAMES;
+       }
+
+       fps_shift = usbd_xfer_get_fps_shift(chan->xfer[0]);
+
+       /* down shift number of frames per second, if any */
+       fps >>= fps_shift;
+       frames >>= fps_shift;
+
+       /* bytes per frame should not be zero */
+       chan->bytes_per_frame[0] =
+           ((chan_alt->sample_rate / fps) * chan_alt->sample_size);
+       chan->bytes_per_frame[1] =
+           (((chan_alt->sample_rate + fps - 1) / fps) * chan_alt->sample_size);
+
+       /* setup data rate dithering, if any */
+       chan->frames_per_second = fps;
+       chan->sample_rem = chan_alt->sample_rate % fps;
+       chan->sample_curr = 0;
+       chan->frames_per_second = fps;
+
+       /* compute required buffer size */
+       buf_size = (chan->bytes_per_frame[1] * frames);
+
+       if (buf_size > (chan->end - chan->start)) {
+               DPRINTF("buffer size is too big\n");
+               goto error;
+       }
+
+       chan->intr_frames = frames;
+
+       DPRINTF("fps=%d sample_rem=%d\n", (int)fps, (int)chan->sample_rem);
+
+       if (chan->intr_frames == 0) {
+               DPRINTF("frame shift is too high!\n");
+               goto error;
+       }
+
+       lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
+       chan->cur_alt = set_alt;
+       lockmgr(chan->pcm_lock, LK_RELEASE);
+
+done:
+#if (UAUDIO_NCHANBUFS != 2)
+#error "please update code"
+#endif
+       switch (operation) {
+       case CHAN_OP_START:
+               lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
+               usbd_transfer_start(chan->xfer[0]);
+               usbd_transfer_start(chan->xfer[1]);
+               lockmgr(chan->pcm_lock, LK_RELEASE);
+               break;
+       case CHAN_OP_STOP:
+               lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
+               usbd_transfer_stop(chan->xfer[0]);
+               usbd_transfer_stop(chan->xfer[1]);
+               lockmgr(chan->pcm_lock, LK_RELEASE);
+               break;
+       default:
+               break;
+       }
+       return;
+
+error:
+       usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
+
+       lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
+       chan->cur_alt = CHAN_MAX_ALT;
+       lockmgr(chan->pcm_lock, LK_RELEASE);
+}
+
+static void
+uaudio_configure_msg(struct usb_proc_msg *pm)
+{
+       struct uaudio_softc *sc = ((struct uaudio_configure_msg *)pm)->sc;
+
+       usb_proc_explore_unlock(sc->sc_udev);
+       uaudio_configure_msg_sub(sc, &sc->sc_play_chan, PCMDIR_PLAY);
+       uaudio_configure_msg_sub(sc, &sc->sc_rec_chan, PCMDIR_REC);
+       usb_proc_explore_lock(sc->sc_udev);
+}
+
+/*========================================================================*
+ * AS - Audio Stream - routines
+ *========================================================================*/
+
+#ifdef USB_DEBUG
+static void
+uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed)
+{
+       if (ed) {
+               DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n"
+                   "bEndpointAddress=%d bmAttributes=0x%x \n"
+                   "wMaxPacketSize=%d bInterval=%d \n"
+                   "bRefresh=%d bSynchAddress=%d\n",
+                   ed, ed->bLength, ed->bDescriptorType,
+                   ed->bEndpointAddress, ed->bmAttributes,
+                   UGETW(ed->wMaxPacketSize), ed->bInterval,
+                   UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0,
+                   UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0);
+       }
+}
+
+#endif
+
+/*
+ * The following is a workaround for broken no-name USB audio devices
+ * sold by dealextreme called "3D sound". The problem is that the
+ * manufacturer computed wMaxPacketSize is too small to hold the
+ * actual data sent. In other words the device sometimes sends more
+ * data than it actually reports it can send in a single isochronous
+ * packet.
+ */
+static void
+uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep,
+    uint32_t xps, uint32_t add)
+{
+       uint32_t mps;
+
+       mps = UGETW(ep->wMaxPacketSize);
+
+       /*
+        * If the device indicates it can send more data than what the
+        * sample rate indicates, we apply the workaround.
+        */
+       if (mps > xps) {
+
+               /* allow additional data */
+               xps += add;
+
+               /* check against the maximum USB 1.x length */
+               if (xps > 1023)
+                       xps = 1023;
+
+               /* check if we should do an update */
+               if (mps < xps) {
+                       /* simply update the wMaxPacketSize field */
+                       USETW(ep->wMaxPacketSize, xps);
+                       DPRINTF("Workaround: Updated wMaxPacketSize "
+                           "from %d to %d bytes.\n",
+                           (int)mps, (int)xps);
+               }
+       }
+}
+
+static usb_error_t
+uaudio20_check_rate(struct usb_device *udev, uint8_t iface_no,
+    uint8_t clockid, uint32_t rate)
+{
+       struct usb_device_request req;
+       usb_error_t error;
+       uint8_t data[255];
+       uint16_t actlen;
+       uint16_t rates;
+       uint16_t x;
+
+       DPRINTFN(6, "ifaceno=%d clockid=%d rate=%u\n",
+           iface_no, clockid, rate);
+
+       req.bmRequestType = UT_READ_CLASS_INTERFACE;
+       req.bRequest = UA20_CS_RANGE;
+       USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
+       USETW2(req.wIndex, clockid, iface_no);
+       USETW(req.wLength, 255);
+
+        error = usbd_do_request_flags(udev, NULL, &req, data,
+           USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT);
+
+       if (error != 0 || actlen < 2)
+               return (USB_ERR_INVAL);
+
+       rates = data[0] | (data[1] << 8);
+       actlen = (actlen - 2) / 12;
+
+       if (rates > actlen) {
+               DPRINTF("Too many rates\n");
+               rates = actlen;
+       }
+
+       for (x = 0; x != rates; x++) {
+               uint32_t min = UGETDW(data + 2 + (12 * x));
+               uint32_t max = UGETDW(data + 6 + (12 * x));
+               uint32_t res = UGETDW(data + 10 + (12 * x));
+
+               if (res == 0) {
+                       DPRINTF("Zero residue\n");
+                       res = 1;
+               }
+
+               if (min > max) {
+                       DPRINTF("Swapped max and min\n");
+                       uint32_t temp;
+                       temp = min;
+                       min = max;
+                       max = temp;
+               }
+
+               if (rate >= min && rate <= max &&
+                   (((rate - min) % res) == 0)) {
+                       return (0);
+               }
+       }
+       return (USB_ERR_INVAL);
+}
+
+static void
+uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
+    uint32_t rate, uint8_t channels, uint8_t bit_resolution)
+{
+       struct usb_descriptor *desc = NULL;
+       union uaudio_asid asid = { NULL };
+       union uaudio_asf1d asf1d = { NULL };
+       union uaudio_sed sed = { NULL };
+       struct usb_midi_streaming_endpoint_descriptor *msid = NULL;
+       usb_endpoint_descriptor_audio_t *ed1 = NULL;
+       const struct usb_audio_control_descriptor *acdp = NULL;
+       struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
+       struct usb_interface_descriptor *id;
+       const struct uaudio_format *p_fmt = NULL;
        struct uaudio_chan *chan;
+       struct uaudio_chan_alt *chan_alt;
+       uint32_t format;
        uint16_t curidx = 0xFFFF;
        uint16_t lastidx = 0xFFFF;
        uint16_t alt_index = 0;
-       uint16_t wFormat;
+       uint16_t audio_rev = 0;
+       uint16_t x;
        uint8_t ep_dir;
        uint8_t bChannels;
        uint8_t bBitResolution;
-       uint8_t x;
        uint8_t audio_if = 0;
+       uint8_t midi_if = 0;
        uint8_t uma_if_class;
 
        while ((desc = usb_desc_foreach(cd, desc))) {
@@ -892,12 +1517,25 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
                                alt_index++;
                        }
 
+                       if ((!(sc->sc_hid.flags & UAUDIO_HID_VALID)) &&
+                           (id->bInterfaceClass == UICLASS_HID) &&
+                           (id->bInterfaceSubClass == 0) &&
+                           (id->bInterfaceProtocol == 0) &&
+                           (alt_index == 0) &&
+                           usbd_get_iface(udev, curidx) != NULL) {
+                               DPRINTF("Found HID interface at %d\n",
+                                   curidx);
+                               sc->sc_hid.flags |= UAUDIO_HID_VALID;
+                               sc->sc_hid.iface_index = curidx;
+                       }
+
                        uma_if_class =
                            ((id->bInterfaceClass == UICLASS_AUDIO) ||
                            ((id->bInterfaceClass == UICLASS_VENDOR) &&
                            (sc->sc_uq_au_vendor_class != 0)));
 
-                       if ((uma_if_class != 0) && (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) {
+                       if ((uma_if_class != 0) &&
+                           (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) {
                                audio_if = 1;
                        } else {
                                audio_if = 0;
@@ -909,186 +1547,355 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
                                /*
                                 * XXX could allow multiple MIDI interfaces
                                 */
+                               midi_if = 1;
 
                                if ((sc->sc_midi_chan.valid == 0) &&
-                                   usbd_get_iface(udev, curidx)) {
+                                   (usbd_get_iface(udev, curidx) != NULL)) {
                                        sc->sc_midi_chan.iface_index = curidx;
                                        sc->sc_midi_chan.iface_alt_index = alt_index;
                                        sc->sc_midi_chan.valid = 1;
                                }
+                       } else {
+                               midi_if = 0;
                        }
-                       asid = NULL;
-                       asf1d = NULL;
+                       asid.v1 = NULL;
+                       asf1d.v1 = NULL;
                        ed1 = NULL;
-                       ed2 = NULL;
-                       sed = NULL;
+                       sed.v1 = NULL;
+               }
+
+               if (audio_if == 0) {
+                       if (midi_if == 0) {
+                               if ((acdp == NULL) &&
+                                   (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+                                   (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) &&
+                                   (desc->bLength >= sizeof(*acdp))) {
+                                       acdp = (void *)desc;
+                                       audio_rev = UGETW(acdp->bcdADC);
+                               }
+                       } else {
+                               msid = (void *)desc;
+
+                               /* get the maximum number of embedded jacks in use, if any */
+                               if (msid->bLength >= sizeof(*msid) &&
+                                   msid->bDescriptorType == UDESC_CS_ENDPOINT &&
+                                   msid->bDescriptorSubtype == MS_GENERAL &&
+                                   msid->bNumEmbMIDIJack > sc->sc_midi_chan.max_emb_jack) {
+                                       sc->sc_midi_chan.max_emb_jack = msid->bNumEmbMIDIJack;
+                               }
+                       }
+                       /*
+                        * Don't collect any USB audio descriptors if
+                        * this is not an USB audio stream interface.
+                        */
+                       continue;
                }
-               if ((desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+
+               if ((acdp != NULL) &&
+                   (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
                    (desc->bDescriptorSubtype == AS_GENERAL) &&
-                   (desc->bLength >= sizeof(*asid))) {
-                       if (asid == NULL) {
-                               asid = (void *)desc;
+                   (asid.v1 == NULL)) {
+                       if (audio_rev >= UAUDIO_VERSION_30) {
+                               /* FALLTHROUGH */
+                       } else if (audio_rev >= UAUDIO_VERSION_20) {
+                               if (desc->bLength >= sizeof(*asid.v2)) {
+                                       asid.v2 = (void *)desc;
+                               }
+                       } else {
+                               if (desc->bLength >= sizeof(*asid.v1)) {
+                                       asid.v1 = (void *)desc;
+                               }
                        }
                }
-               if ((desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+               if ((acdp != NULL) &&
+                   (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
                    (desc->bDescriptorSubtype == FORMAT_TYPE) &&
-                   (desc->bLength >= sizeof(*asf1d))) {
-                       if (asf1d == NULL) {
-                               asf1d = (void *)desc;
-                               if (asf1d->bFormatType != FORMAT_TYPE_I) {
-                                       DPRINTFN(11, "ignored bFormatType = %d\n",
-                                           asf1d->bFormatType);
-                                       asf1d = NULL;
-                                       continue;
-                               }
-                               if (asf1d->bLength < (sizeof(*asf1d) +
-                                   ((asf1d->bSamFreqType == 0) ? 6 :
-                                   (asf1d->bSamFreqType * 3)))) {
-                                       DPRINTFN(11, "'asf1d' descriptor is too short\n");
-                                       asf1d = NULL;
-                                       continue;
+                   (asf1d.v1 == NULL)) {
+                       if (audio_rev >= UAUDIO_VERSION_30) {
+                               /* FALLTHROUGH */
+                       } else if (audio_rev >= UAUDIO_VERSION_20) {
+                               if (desc->bLength >= sizeof(*asf1d.v2))
+                                       asf1d.v2 = (void *)desc;
+                       } else {
+                               if (desc->bLength >= sizeof(*asf1d.v1)) {
+                                       asf1d.v1 = (void *)desc;
+
+                                       if (asf1d.v1->bFormatType != FORMAT_TYPE_I) {
+                                               DPRINTFN(11, "ignored bFormatType = %d\n",
+                                                   asf1d.v1->bFormatType);
+                                               asf1d.v1 = NULL;
+                                               continue;
+                                       }
+                                       if (desc->bLength < (sizeof(*asf1d.v1) +
+                                           ((asf1d.v1->bSamFreqType == 0) ? 6 :
+                                           (asf1d.v1->bSamFreqType * 3)))) {
+                                               DPRINTFN(11, "invalid descriptor, "
+                                                   "too short\n");
+                                               asf1d.v1 = NULL;
+                                               continue;
+                                       }
                                }
                        }
                }
                if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
-                   (desc->bLength >= UEP_MINSIZE)) {
-                       if (ed1 == NULL) {
-                               ed1 = (void *)desc;
-                               if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) {
-                                       ed1 = NULL;
-                               }
+                   (desc->bLength >= UEP_MINSIZE) &&
+                   (ed1 == NULL)) {
+                       ed1 = (void *)desc;
+                       if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) {
+                               ed1 = NULL;
+                               continue;
                        }
                }
-               if ((desc->bDescriptorType == UDESC_CS_ENDPOINT) &&
+               if ((acdp != NULL) &&
+                   (desc->bDescriptorType == UDESC_CS_ENDPOINT) &&
                    (desc->bDescriptorSubtype == AS_GENERAL) &&
-                   (desc->bLength >= sizeof(*sed))) {
-                       if (sed == NULL) {
-                               sed = (void *)desc;
+                   (sed.v1 == NULL)) {
+                       if (audio_rev >= UAUDIO_VERSION_30) {
+                               /* FALLTHROUGH */
+                       } else if (audio_rev >= UAUDIO_VERSION_20) {
+                               if (desc->bLength >= sizeof(*sed.v2))
+                                       sed.v2 = (void *)desc;
+                       } else {
+                               if (desc->bLength >= sizeof(*sed.v1))
+                                       sed.v1 = (void *)desc;
                        }
                }
-               if (audio_if && asid && asf1d && ed1 && sed) {
+               if (asid.v1 == NULL || asf1d.v1 == NULL ||
+                   ed1 == NULL || sed.v1 == NULL) {
+                       /* need more descriptors */
+                       continue;
+               }
 
-                       ep_dir = UE_GET_DIR(ed1->bEndpointAddress);
+               ep_dir = UE_GET_DIR(ed1->bEndpointAddress);
 
-                       /* We ignore sync endpoint information until further. */
+               /* We ignore sync endpoint information until further. */
 
-                       wFormat = UGETW(asid->wFormatTag);
-                       bChannels = UAUDIO_MAX_CHAN(asf1d->bNrChannels);
-                       bBitResolution = asf1d->bBitResolution;
+               if (audio_rev >= UAUDIO_VERSION_30) {
+                       goto next_ep;
+               } else if (audio_rev >= UAUDIO_VERSION_20) {
 
-                       if (asf1d->bSamFreqType == 0) {
+                       uint32_t dwFormat;
+
+                       dwFormat = UGETDW(asid.v2->bmFormats);
+                       bChannels = asid.v2->bNrChannels;
+                       bBitResolution = asf1d.v2->bSubslotSize * 8;
+
+                       if ((bChannels != channels) ||
+                           (bBitResolution != bit_resolution)) {
+                               DPRINTF("Wrong number of channels\n");
+                               goto next_ep;
+                       }
+
+                       for (p_fmt = uaudio20_formats;
+                           p_fmt->wFormat != 0; p_fmt++) {
+                               if ((p_fmt->wFormat & dwFormat) &&
+                                   (p_fmt->bPrecision == bBitResolution))
+                                       break;
+                       }
+
+                       if (p_fmt->wFormat == 0) {
+                               DPRINTF("Unsupported audio format\n");
+                               goto next_ep;
+                       }
+
+                       for (x = 0; x != 256; x++) {
+                               if (ep_dir == UE_DIR_OUT) {
+                                       if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
+                                           (1 << (x % 8)))) {
+                                               continue;
+                                       }
+                               } else {
+                                       if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
+                                           (1 << (x % 8)))) {
+                                               continue;
+                                       }
+                               }
+
+                               DPRINTF("Checking clock ID=%d\n", x);
+
+                               if (uaudio20_check_rate(udev,
+                                   sc->sc_mixer_iface_no, x, rate)) {
+                                       DPRINTF("Unsupported sampling "
+                                           "rate, id=%d\n", x);
+                                       goto next_ep;
+                               }
+                       }
+               } else {
+                       uint16_t wFormat;
+
+                       wFormat = UGETW(asid.v1->wFormatTag);
+                       bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels);
+                       bBitResolution = asf1d.v1->bSubFrameSize * 8;
+
+                       if (asf1d.v1->bSamFreqType == 0) {
                                DPRINTFN(16, "Sample rate: %d-%dHz\n",
-                                   UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+                                   UA_SAMP_LO(asf1d.v1),
+                                   UA_SAMP_HI(asf1d.v1));
 
-                               if ((rate >= UA_SAMP_LO(asf1d)) &&
-                                   (rate <= UA_SAMP_HI(asf1d))) {
+                               if ((rate >= UA_SAMP_LO(asf1d.v1)) &&
+                                   (rate <= UA_SAMP_HI(asf1d.v1)))
                                        goto found_rate;
-                               }
                        } else {
 
-                               for (x = 0; x < asf1d->bSamFreqType; x++) {
+                               for (x = 0; x < asf1d.v1->bSamFreqType; x++) {
                                        DPRINTFN(16, "Sample rate = %dHz\n",
-                                           UA_GETSAMP(asf1d, x));
+                                           UA_GETSAMP(asf1d.v1, x));
 
-                                       if (rate == UA_GETSAMP(asf1d, x)) {
+                                       if (rate == UA_GETSAMP(asf1d.v1, x))
                                                goto found_rate;
-                                       }
                                }
                        }
-
-                       audio_if = 0;
-                       continue;
+                       goto next_ep;
 
        found_rate:
-
-                       for (p_fmt = uaudio_formats;
-                           p_fmt->wFormat;
-                           p_fmt++) {
+                       for (p_fmt = uaudio10_formats;
+                           p_fmt->wFormat != 0; p_fmt++) {
                                if ((p_fmt->wFormat == wFormat) &&
-                                   (p_fmt->bPrecision == bBitResolution)) {
-                                       goto found_format;
-                               }
+                                   (p_fmt->bPrecision == bBitResolution))
+                                       break;
+                       }
+                       if (p_fmt->wFormat == 0) {
+                               DPRINTF("Unsupported audio format\n");
+                               goto next_ep;
                        }
 
-                       audio_if = 0;
-                       continue;
-
-       found_format:
+                       if ((bChannels != channels) ||
+                           (bBitResolution != bit_resolution)) {
+                               DPRINTF("Wrong number of channels\n");
+                               goto next_ep;
+                       }
+               }
 
-                       if ((bChannels == channels) &&
-                           (bBitResolution == bit_resolution)) {
+               chan = (ep_dir == UE_DIR_IN) ?
+                   &sc->sc_rec_chan : &sc->sc_play_chan;
 
-                               chan = (ep_dir == UE_DIR_IN) ?
-                                   &sc->sc_rec_chan :
-                                   &sc->sc_play_chan;
+               if (usbd_get_iface(udev, curidx) == NULL) {
+                       DPRINTF("Interface is not valid\n");
+                       goto next_ep;
+               }
+               if (chan->num_alt == CHAN_MAX_ALT) {
+                       DPRINTF("Too many alternate settings\n");
+                       goto next_ep;
+               }
+               chan->set_alt = 0;
+               chan->cur_alt = CHAN_MAX_ALT;
 
-                               if ((chan->valid == 0) && usbd_get_iface(udev, curidx)) {
+               chan_alt = &chan->usb_alt[chan->num_alt++];
 
-                                       chan->valid = 1;
 #ifdef USB_DEBUG
-                                       uaudio_chan_dump_ep_desc(ed1);
-                                       uaudio_chan_dump_ep_desc(ed2);
-
-                                       if (sed->bmAttributes & UA_SED_FREQ_CONTROL) {
-                                               DPRINTFN(2, "FREQ_CONTROL\n");
-                                       }
-                                       if (sed->bmAttributes & UA_SED_PITCH_CONTROL) {
-                                               DPRINTFN(2, "PITCH_CONTROL\n");
-                                       }
+               uaudio_chan_dump_ep_desc(ed1);
 #endif
-                                       DPRINTF("Sample rate = %dHz, channels = %d, "
-                                           "bits = %d, format = %s\n", rate, channels,
-                                           bit_resolution, p_fmt->description);
-
-                                       chan->sample_rate = rate;
-                                       chan->p_asid = asid;
-                                       chan->p_asf1d = asf1d;
-                                       chan->p_ed1 = ed1;
-                                       chan->p_ed2 = ed2;
-                                       chan->p_fmt = p_fmt;
-                                       chan->p_sed = sed;
-                                       chan->iface_index = curidx;
-                                       chan->iface_alt_index = alt_index;
-
-                                       if (ep_dir == UE_DIR_IN)
-                                               chan->usb_cfg =
-                                                   uaudio_cfg_record;
-                                       else
-                                               chan->usb_cfg =
-                                                   uaudio_cfg_play;
-
-                                       chan->sample_size = ((
-                                           UAUDIO_MAX_CHAN(chan->p_asf1d->bNrChannels) *
-                                           chan->p_asf1d->bBitResolution) / 8);
-
-                                       if (ep_dir == UE_DIR_IN &&
-                                           usbd_get_speed(udev) == USB_SPEED_FULL) {
-                                               uaudio_record_fix_fs(ed1,
-                                                   chan->sample_size * (rate / 1000),
-                                                   chan->sample_size * (rate / 4000));
-                                       }
+               DPRINTF("Sample rate = %dHz, channels = %d, "
+                   "bits = %d, format = %s\n", rate, channels,
+                   bit_resolution, p_fmt->description);
+
+               chan_alt->sample_rate = rate;
+               chan_alt->p_asf1d = asf1d;
+               chan_alt->p_ed1 = ed1;
+               chan_alt->p_fmt = p_fmt;
+               chan_alt->p_sed = sed;
+               chan_alt->iface_index = curidx;
+               chan_alt->iface_alt_index = alt_index;
+
+               if (UEP_HAS_SYNCADDR(ed1) && ed1->bSynchAddress != 0) {
+                       DPRINTF("Sync endpoint will be used, if present\n");
+                       chan_alt->enable_sync = 1;
+               } else {
+                       DPRINTF("Sync endpoint will not be used\n");
+                       chan_alt->enable_sync = 0;
+               }
 
-                                       if (sc->sc_sndstat_valid) {
-                                               sbuf_printf(&sc->sc_sndstat, "\n\t"
-                                                   "mode %d.%d:(%s) %dch, %d/%dbit, %s, %dHz",
-                                                   curidx, alt_index,
-                                                   (ep_dir == UE_DIR_IN) ? "input" : "output",
-                                                   asf1d->bNrChannels, asf1d->bBitResolution,
-                                                   asf1d->bSubFrameSize * 8,
-                                                   p_fmt->description, rate);
-                                       }
-                               }
+               usbd_set_parent_iface(sc->sc_udev, curidx,
+                   sc->sc_mixer_iface_index);
+
+               if (ep_dir == UE_DIR_IN)
+                       chan_alt->usb_cfg = uaudio_cfg_record;
+               else
+                       chan_alt->usb_cfg = uaudio_cfg_play;
+
+               chan_alt->sample_size = (UAUDIO_MAX_CHAN(channels) *
+                   p_fmt->bPrecision) / 8;
+               chan_alt->channels = channels;
+
+               if (ep_dir == UE_DIR_IN &&
+                   usbd_get_speed(udev) == USB_SPEED_FULL) {
+                       uaudio_record_fix_fs(ed1,
+                           chan_alt->sample_size * (rate / 1000),
+                           chan_alt->sample_size * (rate / 4000));
+               }
+
+               /* setup play/record format */
+
+               format = chan_alt->p_fmt->freebsd_fmt;
+
+               switch (chan_alt->channels) {
+               case 2:
+                       /* stereo */
+                       format = SND_FORMAT(format, 2, 0);
+                       break;
+               case 1:
+                       /* mono */
+                       format = SND_FORMAT(format, 1, 0);
+                       break;
+               default:
+                       /* surround and more */
+                       format = feeder_matrix_default_format(
+                           SND_FORMAT(format, chan_alt->channels, 0));
+                       break;
+               }
+
+               /* check if format is not supported */
+               if (format == 0) {
+                       DPRINTF("The selected audio format is not supported\n");
+                       chan->num_alt--;
+                       goto next_ep;
+               }
+               if (chan->num_alt > 1) {
+                       /* we only accumulate one format at different sample rates */
+                       if (chan->pcm_format[0] != format) {
+                               DPRINTF("Multiple formats is not supported\n");
+                               chan->num_alt--;
+                               goto next_ep;
+                       }
+                       /* ignore if duplicate sample rate entry */
+                       if (rate == chan->usb_alt[chan->num_alt - 2].sample_rate) {
+                               DPRINTF("Duplicate sample rate detected\n");
+                               chan->num_alt--;
+                               goto next_ep;
                        }
-                       audio_if = 0;
-                       continue;
                }
+               chan->pcm_cap.fmtlist = chan->pcm_format;
+               chan->pcm_cap.fmtlist[0] = format;
+
+               if (rate < chan->pcm_cap.minspeed || chan->pcm_cap.minspeed == 0)
+                       chan->pcm_cap.minspeed = rate;
+               if (rate > chan->pcm_cap.maxspeed || chan->pcm_cap.maxspeed == 0)
+                       chan->pcm_cap.maxspeed = rate;
+
+               if (sc->sc_sndstat_valid != 0) {
+                       sbuf_printf(&sc->sc_sndstat, "\n\t"
+                           "mode %d.%d:(%s) %dch, %dbit, %s, %dHz",
+                           curidx, alt_index,
+                           (ep_dir == UE_DIR_IN) ? "input" : "output",
+                                   channels, p_fmt->bPrecision,
+                                   p_fmt->description, rate);
+               }
+
+       next_ep:
+               sed.v1 = NULL;
+               ed1 = NULL;
        }
 }
 
 /* This structure defines all the supported rates. */
 
-static const uint32_t uaudio_rate_list[] = {
+static const uint32_t uaudio_rate_list[CHAN_MAX_ALT] = {
+       384000,
+       352800,
+       192000,
+       176400,
        96000,
+       88200,
        88000,
        80000,
        72000,
@@ -1131,7 +1938,7 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
                         * disable surround setups on FULL-speed USB
                         * by default
                         */
-                       channels = 2;
+                       channels = 4;
                        break;
                default:
                        channels = 16;
@@ -1153,20 +1960,93 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
                                uaudio_chan_fill_info_sub(sc, udev, rate, x, y);
 
                        /* try find a matching rate, if any */
-                       for (z = 0; uaudio_rate_list[z]; z++) {
+                       for (z = 0; uaudio_rate_list[z]; z++)
                                uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y);
-
-                               if (sc->sc_rec_chan.valid &&
-                                   sc->sc_play_chan.valid) {
-                                       goto done;
-                               }
-                       }
                }
        }
-
-done:
-       if (sc->sc_sndstat_valid) {
+       if (sc->sc_sndstat_valid)
                sbuf_finish(&sc->sc_sndstat);
+}
+
+static void
+uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+       struct uaudio_chan *ch = usbd_xfer_softc(xfer);
+       struct usb_page_cache *pc;
+       uint64_t sample_rate = ch->usb_alt[ch->cur_alt].sample_rate;
+       uint8_t buf[4];
+       uint64_t temp;
+       int len;
+       int actlen;
+       int nframes;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+
+               DPRINTFN(6, "transferred %d bytes\n", actlen);
+
+               if (nframes == 0)
+                       break;
+               len = usbd_xfer_frame_len(xfer, 0);
+               if (len == 0)
+                       break;
+               if (len > sizeof(buf))
+                       len = sizeof(buf);
+
+               memset(buf, 0, sizeof(buf));
+
+               pc = usbd_xfer_get_frame(xfer, 0);
+               usbd_copy_out(pc, 0, buf, len);
+
+               temp = UGETDW(buf);
+
+               DPRINTF("Value = 0x%08x\n", (int)temp);
+
+               /* auto-detect SYNC format */
+
+               if (len == 4)
+                       temp &= 0x0fffffff;
+
+               /* check for no data */
+
+               if (temp == 0)
+                       break;
+
+               /* correctly scale value */
+
+               temp = (temp * 125ULL) - 64;
+
+               /* auto adjust */
+
+               while (temp < (sample_rate - (sample_rate / 4)))
+                       temp *= 2;
+
+               while (temp > (sample_rate + (sample_rate / 2)))
+                       temp /= 2;
+
+               /* compare */
+
+               DPRINTF("Comparing %d < %d\n",
+                   (int)temp, (int)sample_rate);
+
+               if (temp == sample_rate)
+                       ch->last_sync_state = UAUDIO_SYNC_NONE;
+               else if (temp > sample_rate)
+                       ch->last_sync_state = UAUDIO_SYNC_MORE;
+               else
+                       ch->last_sync_state = UAUDIO_SYNC_LESS;
+               break;
+
+       case USB_ST_SETUP:
+               usbd_xfer_set_frames(xfer, 1);
+               usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_framelen(xfer));
+               usbd_transfer_submit(xfer);
+               break;
+
+       default:                        /* Error */
+               break;
        }
 }
 
@@ -1175,6 +2055,8 @@ uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
 {
        struct uaudio_chan *ch = usbd_xfer_softc(xfer);
        struct usb_page_cache *pc;
+       uint32_t sample_size = ch->usb_alt[ch->cur_alt].sample_size;
+       uint32_t mfl;
        uint32_t total;
        uint32_t blockcount;
        uint32_t n;
@@ -1198,12 +2080,20 @@ tr_transferred:
                }
                chn_intr(ch->pcm_ch);
 
+               /* start SYNC transfer, if any */
+               if (ch->usb_alt[ch->cur_alt].enable_sync != 0) {
+                       if ((ch->last_sync_time++ & 7) == 0)
+                               usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]);
+               }
+
        case USB_ST_SETUP:
-               if (ch->bytes_per_frame[1] > usbd_xfer_max_framelen(xfer)) {
+               mfl = usbd_xfer_max_framelen(xfer);
+
+               if (ch->bytes_per_frame[1] > mfl) {
                        DPRINTF("bytes per transfer, %d, "
                            "exceeds maximum, %d!\n",
                            ch->bytes_per_frame[1],
-                           usbd_xfer_max_framelen(xfer));
+                           mfl);
                        break;
                }
 
@@ -1217,15 +2107,37 @@ tr_transferred:
 
                /* setup frame lengths */
                for (n = 0; n != blockcount; n++) {
+                       uint32_t frame_len;
+
                        ch->sample_curr += ch->sample_rem;
                        if (ch->sample_curr >= ch->frames_per_second) {
                                ch->sample_curr -= ch->frames_per_second;
-                               usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[1]);
-                               total += ch->bytes_per_frame[1];
+                               frame_len = ch->bytes_per_frame[1];
                        } else {
-                               usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[0]);
-                               total += ch->bytes_per_frame[0];
+                               frame_len = ch->bytes_per_frame[0];
                        }
+
+                       if (n == (blockcount - 1)) {
+                               switch (ch->last_sync_state) {
+                               case UAUDIO_SYNC_MORE:
+                                       DPRINTFN(6, "sending one sample more\n");
+                                       if ((frame_len + sample_size) <= mfl)
+                                               frame_len += sample_size;
+                                       ch->last_sync_state = UAUDIO_SYNC_NONE;
+                                       break;
+                               case UAUDIO_SYNC_LESS:
+                                       DPRINTFN(6, "sending one sample less\n");
+                                       if (frame_len >= sample_size)
+                                               frame_len -= sample_size;
+                                       ch->last_sync_state = UAUDIO_SYNC_NONE;
+                                       break;
+                               default:
+                                       break;
+                               }
+                       }
+
+                       usbd_xfer_set_frame_len(xfer, n, frame_len);
+                       total += frame_len;
                }
 
                DPRINTFN(6, "transfer %d bytes\n", total);
@@ -1261,20 +2173,26 @@ tr_transferred:
        }
 }
 
+static void
+uaudio_chan_record_sync_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+       /* TODO */
+}
+
 static void
 uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
 {
        struct uaudio_chan *ch = usbd_xfer_softc(xfer);
        struct usb_page_cache *pc;
-       uint32_t n;
-       uint32_t m;
-       uint32_t blockcount;
        uint32_t offset0;
        uint32_t offset1;
        uint32_t mfl;
+       int m;
+       int n;
        int len;
        int actlen;
        int nframes;
+       int blockcount;
 
        usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
        mfl = usbd_xfer_max_framelen(xfer);
@@ -1301,9 +2219,9 @@ uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
 
                                m = (ch->end - ch->cur);
 
-                               if (m > len) {
+                               if (m > len)
                                        m = len;
-                               }
+
                                usbd_copy_out(pc, offset1, ch->cur, m);
 
                                len -= m;
@@ -1347,164 +2265,41 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
        struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
            &sc->sc_play_chan : &sc->sc_rec_chan);
        uint32_t buf_size;
-       uint32_t frames;
-       uint32_t format;
-       uint16_t fps;
-       uint8_t endpoint;
-       uint8_t blocks;
-       uint8_t iface_index;
-       uint8_t alt_index;
-       uint8_t fps_shift;
-       usb_error_t err;
-
-       fps = usbd_get_isoc_fps(sc->sc_udev);
-
-       if (fps < 8000) {
-               /* FULL speed USB */
-               frames = 8;
-       } else {
-               /* HIGH speed USB */
-               frames = UAUDIO_NFRAMES;
-       }
-
-       /* setup play/record format */
-
-       ch->pcm_cap.fmtlist = ch->pcm_format;
-
-       ch->pcm_format[0] = 0;
-       ch->pcm_format[1] = 0;
-
-       ch->pcm_cap.minspeed = ch->sample_rate;
-       ch->pcm_cap.maxspeed = ch->sample_rate;
+       uint8_t x;
 
-       /* setup mutex and PCM channel */
+       /* store mutex and PCM channel */
 
        ch->pcm_ch = c;
        ch->pcm_lock = c->lock;
 
-       format = ch->p_fmt->freebsd_fmt;
-
-#ifdef XXX
-       switch (ch->p_asf1d->bNrChannels) {
-       case 2:
-               /* stereo */
-               format = SND_FORMAT(format, 2, 0);
-               break;
-       case 1:
-               /* mono */
-               format = SND_FORMAT(format, 1, 0);
-               break;
-       default:
-               /* surround and more */
-               format = feeder_matrix_default_format(
-                   SND_FORMAT(format, ch->p_asf1d->bNrChannels, 0));
-               break;
-       }
-#endif
-       /*
-        * KLUDGE:
-        * Add support at least for 2 channels
-        */
-       if (ch->p_asf1d->bNrChannels == 2) format |= AFMT_STEREO;
-
-       ch->pcm_cap.fmtlist[0] = format;
-       ch->pcm_cap.fmtlist[1] = 0;
-
-       /* check if format is not supported */
-
-       if (format == 0) {
-               DPRINTF("The selected audio format is not supported\n");
-               goto error;
-       }
-
-       /* set alternate interface corresponding to the mode */
-
-       endpoint = ch->p_ed1->bEndpointAddress;
-       iface_index = ch->iface_index;
-       alt_index = ch->iface_alt_index;
-
-       DPRINTF("endpoint=0x%02x, speed=%d, iface=%d alt=%d\n",
-           endpoint, ch->sample_rate, iface_index, alt_index);
-
-       err = usbd_set_alt_interface_index(sc->sc_udev, iface_index, alt_index);
-       if (err) {
-               DPRINTF("setting of alternate index failed: %s!\n",
-                   usbd_errstr(err));
-               goto error;
-       }
-       usbd_set_parent_iface(sc->sc_udev, iface_index,
-           sc->sc_mixer_iface_index);
-
-       /*
-        * Only set the sample rate if the channel reports that it
-        * supports the frequency control.
-        */
-       if (ch->p_sed->bmAttributes & UA_SED_FREQ_CONTROL) {
-               if (uaudio_set_speed(sc->sc_udev, endpoint, ch->sample_rate)) {
-                       /*
-                        * If the endpoint is adaptive setting the speed may
-                        * fail.
-                        */
-                       DPRINTF("setting of sample rate failed! (continuing anyway)\n");
-               }
-       }
-       if (usbd_transfer_setup(sc->sc_udev, &iface_index, ch->xfer,
-           ch->usb_cfg, UAUDIO_NCHANBUFS, ch, ch->pcm_lock)) {
-               DPRINTF("could not allocate USB transfers!\n");
-               goto error;
-       }
-
-       fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]);
-
-       /* down shift number of frames per second, if any */
-       fps >>= fps_shift;
-       frames >>= fps_shift;
-
-       /* bytes per frame should not be zero */
-       ch->bytes_per_frame[0] = ((ch->sample_rate / fps) * ch->sample_size);
-       ch->bytes_per_frame[1] = (((ch->sample_rate + fps - 1) / fps) * ch->sample_size);
-
-       /* setup data rate dithering, if any */
-       ch->frames_per_second = fps;
-       ch->sample_rem = ch->sample_rate % fps;
-       ch->sample_curr = 0;
-       ch->frames_per_second = fps;
-
-       /* compute required buffer size */
-       buf_size = (ch->bytes_per_frame[1] * frames);
-
-       ch->intr_size = buf_size;
-       ch->intr_frames = frames;
+       /* compute worst case buffer */
 
-       DPRINTF("fps=%d sample_rem=%d\n", fps, ch->sample_rem);
-
-       if (ch->intr_frames == 0) {
-               DPRINTF("frame shift is too high!\n");
-               goto error;
+       buf_size = 0;
+       for (x = 0; x != ch->num_alt; x++) {
+               uint32_t temp = uaudio_get_buffer_size(ch, x);
+               if (temp > buf_size)
+                       buf_size = temp;
        }
 
-       /* setup double buffering */
+       /* allow double buffering */
        buf_size *= 2;
-       blocks = 2;
+
+       DPRINTF("Worst case buffer is %d bytes\n", (int)buf_size);
 
        ch->buf = kmalloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
-       if (ch->buf == NULL)
-               goto error;
        if (sndbuf_setup(b, ch->buf, buf_size) != 0)
                goto error;
-       if (sndbuf_resize(b, blocks, ch->intr_size)) 
-               goto error;
 
        ch->start = ch->buf;
        ch->end = ch->buf + buf_size;
        ch->cur = ch->buf;
        ch->pcm_buf = b;
+       ch->max_buf = buf_size;
 
        if (ch->pcm_lock == NULL) {
                DPRINTF("ERROR: PCM channels does not have a mutex!\n");
                goto error;
        }
-
        return (ch);
 
 error:
@@ -1519,9 +2314,9 @@ uaudio_chan_free(struct uaudio_chan *ch)
                kfree(ch->buf, M_DEVBUF);
                ch->buf = NULL;
        }
-       usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS);
+       usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS + 1);
 
-       ch->valid = 0;
+       ch->num_alt = 0;
 
        return (0);
 }
@@ -1529,7 +2324,15 @@ uaudio_chan_free(struct uaudio_chan *ch)
 int
 uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
 {
-       return (ch->intr_size);
+       uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
+
+       sndbuf_setup(ch->pcm_buf, ch->buf, temp);
+
+       ch->start = ch->buf;
+       ch->end = ch->buf + temp;
+       ch->cur = ch->buf;
+
+       return (temp / 2);
 }
 
 int
@@ -1542,10 +2345,23 @@ uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
 int
 uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
 {
-       if (speed != ch->sample_rate) {
-               DPRINTF("rate conversion required\n");
+       uint8_t x;
+
+       for (x = 0; x < ch->num_alt; x++) {
+               if (ch->usb_alt[x].sample_rate < speed) {
+                       /* sample rate is too low */
+                       break;
+               }
        }
-       return (ch->sample_rate);
+
+       if (x != 0)
+               x--;
+
+       ch->set_alt = x;
+
+       DPRINTF("Selecting alt %d\n", (int)x);
+
+       return (ch->usb_alt[x].sample_rate);
 }
 
 int
@@ -1560,7 +2376,6 @@ uaudio_chan_getcaps(struct uaudio_chan *ch)
        return (&ch->pcm_cap);
 }
 
-#ifdef XXXDF
 static struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = {
        .id = SND_CHN_MATRIX_DRV,
        .channels = 2,
@@ -1605,26 +2420,36 @@ uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format)
 
        return (feeder_matrix_format_map(format));
 }
-#endif 
+
 int
 uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
 {
-       ch->format = format;
+       DPRINTF("Selecting format 0x%08x\n", (unsigned int)format);
        return (0);
 }
 
 int
 uaudio_chan_start(struct uaudio_chan *ch)
 {
-       ch->cur = ch->start;
+       struct uaudio_softc *sc = ch->priv_sc;
+       int do_start = 0;
+
+       usb_proc_explore_lock(sc->sc_udev);
+       if (ch->operation != CHAN_OP_DRAIN) {
+               if (ch->cur_alt == ch->set_alt &&
+                   ch->operation == CHAN_OP_NONE) {
+                       /* save doing the explore task */
+                       do_start = 1;
+               } else {
+                       ch->operation = CHAN_OP_START;
+                       (void)usb_proc_explore_msignal(sc->sc_udev,
+                           &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
+               }
+       }
+       usb_proc_explore_unlock(sc->sc_udev);
 
-#if (UAUDIO_NCHANBUFS != 2)
-#error "please update code"
-#endif
-       if (ch->xfer[0]) {
+       if (do_start) {
                usbd_transfer_start(ch->xfer[0]);
-       }
-       if (ch->xfer[1]) {
                usbd_transfer_start(ch->xfer[1]);
        }
        return (0);
@@ -1633,11 +2458,27 @@ uaudio_chan_start(struct uaudio_chan *ch)
 int
 uaudio_chan_stop(struct uaudio_chan *ch)
 {
-#if (UAUDIO_NCHANBUFS != 2)
-#error "please update code"
-#endif
-       usbd_transfer_stop(ch->xfer[0]);
-       usbd_transfer_stop(ch->xfer[1]);
+       struct uaudio_softc *sc = ch->priv_sc;
+       int do_stop = 0;
+
+       usb_proc_explore_lock(sc->sc_udev);
+       if (ch->operation != CHAN_OP_DRAIN) {
+               if (ch->cur_alt == ch->set_alt &&
+                   ch->operation == CHAN_OP_NONE) {
+                       /* save doing the explore task */
+                       do_stop = 1;
+               } else {
+                       ch->operation = CHAN_OP_STOP;
+                       (void)usb_proc_explore_msignal(sc->sc_udev,
+                           &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
+               }
+       }
+       usb_proc_explore_unlock(sc->sc_udev);
+
+       if (do_stop) {
+               usbd_transfer_stop(ch->xfer[0]);
+               usbd_transfer_stop(ch->xfer[1]);
+       }
        return (0);
 }
 
@@ -1645,19 +2486,333 @@ uaudio_chan_stop(struct uaudio_chan *ch)
  * AC - Audio Controller - routines
  *========================================================================*/
 
+static int
+uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+       struct uaudio_softc *sc;
+       struct uaudio_mixer_node *pmc;
+       int hint;
+       int error;
+       int temp = 0;
+       int chan = 0;
+
+       sc = (struct uaudio_softc *)oidp->oid_arg1;
+       hint = oidp->oid_arg2;
+
+       if (sc->sc_mixer_lock == NULL)
+               return (ENXIO);
+
+       /* lookup mixer node */
+
+       lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE);
+       for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) {
+               for (chan = 0; chan != (int)pmc->nchan; chan++) {
+                       if (pmc->wValue[chan] != -1 &&
+                           pmc->wValue[chan] == hint) {
+                               temp = pmc->wData[chan];
+                               goto found;
+                       }
+               }
+       }
+found:
+       lockmgr(sc->sc_mixer_lock, LK_RELEASE);
+
+       error = sysctl_handle_int(oidp, &temp, 0, req);
+       if (error != 0 || req->newptr == NULL)
+               return (error);
+
+       /* update mixer value */
+
+       lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE);
+       if (pmc != NULL &&
+           temp >= pmc->minval &&
+           temp <= pmc->maxval) {
+
+               pmc->wData[chan] = temp;
+               pmc->update[(chan / 8)] |= (1 << (chan % 8));
+
+               /* start the transfer, if not already started */
+               usbd_transfer_start(sc->sc_mixer_xfer[0]);
+       }
+       lockmgr(sc->sc_mixer_lock, LK_RELEASE);
+
+       return (0);
+}
+
+static void
+uaudio_mixer_ctl_free(struct uaudio_softc *sc)
+{
+       struct uaudio_mixer_node *p_mc;
+
+       while ((p_mc = sc->sc_mixer_root) != NULL) {
+               sc->sc_mixer_root = p_mc->next;
+               kfree(p_mc, M_USBDEV);
+       }
+}
+
+static void
+uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev)
+{
+       struct uaudio_mixer_node *pmc;
+       struct sysctl_oid *mixer_tree;
+       struct sysctl_oid *control_tree;
+       char buf[32];
+       int chan;
+       int n;
+
+       mixer_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
+           SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "mixer",
+           CTLFLAG_RD, NULL, "");
+
+       if (mixer_tree == NULL)
+               return;
+
+       for (n = 0, pmc = sc->sc_mixer_root; pmc != NULL;
+           pmc = pmc->next, n++) {
+
+               for (chan = 0; chan < pmc->nchan; chan++) {
+
+                       if (pmc->nchan > 1) {
+                               ksnprintf(buf, sizeof(buf), "%s_%d_%d",
+                                   pmc->name, n, chan);
+                       } else {
+                               ksnprintf(buf, sizeof(buf), "%s_%d",
+                                   pmc->name, n);
+                       }
+
+                       control_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
+                           SYSCTL_CHILDREN(mixer_tree), OID_AUTO, buf,
+                           CTLFLAG_RD, NULL, "Mixer control nodes");
+
+                       if (control_tree == NULL)
+                               continue;
+
+                       SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+                           SYSCTL_CHILDREN(control_tree),
+                           OID_AUTO, "val", CTLTYPE_INT | CTLFLAG_RW, sc,
+                           pmc->wValue[chan],
+                           uaudio_mixer_sysctl_handler, "I", "Current value");
+
+                       SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+                           SYSCTL_CHILDREN(control_tree),
+                           OID_AUTO, "min", CTLFLAG_RD, 0, pmc->minval,
+                           "Minimum value");
+
+                       SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+                           SYSCTL_CHILDREN(control_tree),
+                           OID_AUTO, "max", CTLFLAG_RD, 0, pmc->maxval,
+                           "Maximum value");
+
+                       SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev),
+                           SYSCTL_CHILDREN(control_tree),
+                           OID_AUTO, "desc", CTLFLAG_RD, pmc->desc, 0,
+                           "Description");
+               }
+       }
+}
+
+/* M-Audio FastTrack Ultra Mixer Description */
+/* Origin: Linux USB Audio driver */
+static void
+uaudio_mixer_controls_create_ftu(struct uaudio_softc *sc)
+{
+       int chx;
+       int chy;
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+       MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
+       MIX(sc).wValue[0] = MAKE_WORD(8, 0);
+       MIX(sc).class = UAC_OUTPUT;
+       MIX(sc).type = MIX_UNSIGNED_16;
+       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+       MIX(sc).name = "effect";
+       MIX(sc).minval = 0;
+       MIX(sc).maxval = 7;
+       MIX(sc).mul = 7;
+       MIX(sc).nchan = 1;
+       MIX(sc).update[0] = 1;
+       strlcpy(MIX(sc).desc, "Room1,2,3,Hall1,2,Plate,Delay,Echo", sizeof(MIX(sc).desc));
+       uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+       MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no);
+
+       for (chx = 0; chx != 8; chx++) {
+               for (chy = 0; chy != 8; chy++) {
+
+                       MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1);
+                       MIX(sc).type = MIX_SIGNED_16;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+                       MIX(sc).name = "mix_rec";
+                       MIX(sc).nchan = 1;
+                       MIX(sc).update[0] = 1;
+                       MIX(sc).val_default = 0;
+                       ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
+                           "AIn%d - Out%d Record Volume", chy + 1, chx + 1);
+
+                       uaudio_mixer_add_ctl(sc, &MIX(sc));
+
+                       MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1 + 8);
+                       MIX(sc).type = MIX_SIGNED_16;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+                       MIX(sc).name = "mix_play";
+                       MIX(sc).nchan = 1;
+                       MIX(sc).update[0] = 1;
+                       MIX(sc).val_default = (chx == chy) ? 2 : 0;
+                       ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
+                           "DIn%d - Out%d Playback Volume", chy + 1, chx + 1);
+
+                       uaudio_mixer_add_ctl(sc, &MIX(sc));
+               }
+       }
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+       MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
+       MIX(sc).wValue[0] = MAKE_WORD(2, 0);
+       MIX(sc).class = UAC_OUTPUT;
+       MIX(sc).type = MIX_SIGNED_8;
+       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+       MIX(sc).name = "effect_vol";
+       MIX(sc).nchan = 1;
+       MIX(sc).update[0] = 1;
+       MIX(sc).minval = 0;
+       MIX(sc).maxval = 0x7f;
+       MIX(sc).mul = 0x7f;
+       MIX(sc).nchan = 1;
+       MIX(sc).update[0] = 1;
+       strlcpy(MIX(sc).desc, "Effect Volume", sizeof(MIX(sc).desc));
+       uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+       MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
+       MIX(sc).wValue[0] = MAKE_WORD(3, 0);
+       MIX(sc).class = UAC_OUTPUT;
+       MIX(sc).type = MIX_SIGNED_16;
+       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+       MIX(sc).name = "effect_dur";
+       MIX(sc).nchan = 1;
+       MIX(sc).update[0] = 1;
+       MIX(sc).minval = 0;
+       MIX(sc).maxval = 0x7f00;
+       MIX(sc).mul = 0x7f00;
+       MIX(sc).nchan = 1;
+       MIX(sc).update[0] = 1;
+       strlcpy(MIX(sc).desc, "Effect Duration", sizeof(MIX(sc).desc));
+       uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+       MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
+       MIX(sc).wValue[0] = MAKE_WORD(4, 0);
+       MIX(sc).class = UAC_OUTPUT;
+       MIX(sc).type = MIX_SIGNED_8;
+       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+       MIX(sc).name = "effect_fb";
+       MIX(sc).nchan = 1;
+       MIX(sc).update[0] = 1;
+       MIX(sc).minval = 0;
+       MIX(sc).maxval = 0x7f;
+       MIX(sc).mul = 0x7f;
+       MIX(sc).nchan = 1;
+       MIX(sc).update[0] = 1;
+       strlcpy(MIX(sc).desc, "Effect Feedback Volume", sizeof(MIX(sc).desc));
+       uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+       MIX(sc).wIndex = MAKE_WORD(7, sc->sc_mixer_iface_no);
+       for (chy = 0; chy != 4; chy++) {
+
+               MIX(sc).wValue[0] = MAKE_WORD(7, chy + 1);
+               MIX(sc).type = MIX_SIGNED_16;
+               MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+               MIX(sc).name = "effect_ret";
+               MIX(sc).nchan = 1;
+               MIX(sc).update[0] = 1;
+               ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
+                   "Effect Return %d Volume", chy + 1);
+
+               uaudio_mixer_add_ctl(sc, &MIX(sc));
+       }
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+       MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no);
+
+       for (chy = 0; chy != 8; chy++) {
+               MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1);
+               MIX(sc).type = MIX_SIGNED_16;
+               MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+               MIX(sc).name = "effect_send";
+               MIX(sc).nchan = 1;
+               MIX(sc).update[0] = 1;
+               ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
+                   "Effect Send AIn%d Volume", chy + 1);
+
+               uaudio_mixer_add_ctl(sc, &MIX(sc));
+
+               MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1 + 8);
+               MIX(sc).type = MIX_SIGNED_16;
+               MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+               MIX(sc).name = "effect_send";
+               MIX(sc).nchan = 1;
+               MIX(sc).update[0] = 1;
+               ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
+                   "Effect Send DIn%d Volume", chy + 1);
+
+               uaudio_mixer_add_ctl(sc, &MIX(sc));
+       }
+}
+
+static void
+uaudio_mixer_reload_all(struct uaudio_softc *sc)
+{
+       struct uaudio_mixer_node *pmc;
+       int chan;
+
+       if (sc->sc_mixer_lock == NULL)
+               return;
+
+       lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE);
+       for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) {
+               /* use reset defaults for non-oss controlled settings */
+               if (pmc->ctl == SOUND_MIXER_NRDEVICES)
+                       continue;
+               for (chan = 0; chan < pmc->nchan; chan++)
+                       pmc->update[chan / 8] |= (1 << (chan % 8));
+       }
+       usbd_transfer_start(sc->sc_mixer_xfer[0]);
+
+       /* start HID volume keys, if any */
+       usbd_transfer_start(sc->sc_hid.xfer[0]);
+       lockmgr(sc->sc_mixer_lock, LK_RELEASE);
+}
+
 static void
 uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
 {
        struct uaudio_mixer_node *p_mc_new =
            kmalloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK);
-
-       if (p_mc_new != NULL) {
-               memcpy(p_mc_new, mc, sizeof(*p_mc_new));
-               p_mc_new->next = sc->sc_mixer_root;
-               sc->sc_mixer_root = p_mc_new;
-               sc->sc_mixer_count++;
-       } else {
-               DPRINTF("out of memory\n");
+       int ch;
+
+       memcpy(p_mc_new, mc, sizeof(*p_mc_new));
+       p_mc_new->next = sc->sc_mixer_root;
+       sc->sc_mixer_root = p_mc_new;
+       sc->sc_mixer_count++;
+
+       /* set default value for all channels */
+       for (ch = 0; ch < p_mc_new->nchan; ch++) {
+               switch (p_mc_new->val_default) {
+               case 1:
+                       /* 50% */
+                       p_mc_new->wData[ch] = (p_mc_new->maxval + p_mc_new->minval) / 2;
+                       break;
+               case 2:
+                       /* 100% */
+                       p_mc_new->wData[ch] = p_mc_new->maxval;
+                       break;
+               default:
+                       /* 0% */
+                       p_mc_new->wData[ch] = p_mc_new->minval;
+                       break;
+               }
        }
 }
 
@@ -1681,13 +2836,10 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
 
                /* determine min and max values */
 
-               mc->minval = uaudio_mixer_get(sc->sc_udev, GET_MIN, mc);
-
-               mc->minval = uaudio_mixer_signext(mc->type, mc->minval);
-
-               mc->maxval = uaudio_mixer_get(sc->sc_udev, GET_MAX, mc);
-
-               mc->maxval = uaudio_mixer_signext(mc->type, mc->maxval);
+               mc->minval = uaudio_mixer_get(sc->sc_udev,
+                   sc->sc_audio_rev, GET_MIN, mc);
+               mc->maxval = uaudio_mixer_get(sc->sc_udev,
+                   sc->sc_audio_rev, GET_MAX, mc);
 
                /* check if max and min was swapped */
 
@@ -1697,72 +2849,120 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
                        mc->minval = res;
                }
 
-               /* compute value range */
-               mc->mul = mc->maxval - mc->minval;
-               if (mc->mul == 0)
-                       mc->mul = 1;
+               /* compute value range */
+               mc->mul = mc->maxval - mc->minval;
+               if (mc->mul == 0)
+                       mc->mul = 1;
+
+               /* compute value alignment */
+               res = uaudio_mixer_get(sc->sc_udev,
+                   sc->sc_audio_rev, GET_RES, mc);
+
+               DPRINTF("Resolution = %d\n", (int)res);
+       }
+
+       uaudio_mixer_add_ctl_sub(sc, mc);
+
+#ifdef USB_DEBUG
+       if (uaudio_debug > 2) {
+               uint8_t i;
+
+               for (i = 0; i < mc->nchan; i++) {
+                       DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]);
+               }
+               DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' "
+                   "min=%d max=%d\n",
+                   mc->wIndex, mc->type, mc->ctl,
+                   mc->minval, mc->maxval);
+       }
+#endif
+}
+
+static void
+uaudio_mixer_add_mixer(struct uaudio_softc *sc,
+    const struct uaudio_terminal_node *iot, int id)
+{
+       const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu_v1;
+       const struct usb_audio_mixer_unit_1 *d1;
+
+       uint32_t bno;                   /* bit number */
+       uint32_t p;                     /* bit number accumulator */
+       uint32_t mo;                    /* matching outputs */
+       uint32_t mc;                    /* matching channels */
+       uint32_t ichs;                  /* input channels */
+       uint32_t ochs;                  /* output channels */
+       uint32_t c;
+       uint32_t chs;                   /* channels */
+       uint32_t i;
+       uint32_t o;
+
+       DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
+           d0->bUnitId, d0->bNrInPins);
+
+       /* compute the number of input channels */
+
+       ichs = 0;
+       for (i = 0; i < d0->bNrInPins; i++) {
+               ichs += uaudio_mixer_get_cluster(
+                   d0->baSourceId[i], iot).bNrChannels;
+       }
+
+       d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
+
+       /* and the number of output channels */
+
+       ochs = d1->bNrChannels;
 
-               /* compute value alignment */
-               res = uaudio_mixer_get(sc->sc_udev, GET_RES, mc);
+       DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
 
-               DPRINTF("Resolution = %d\n", (int)res);
-       }
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
 
-       uaudio_mixer_add_ctl_sub(sc, mc);
+       MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+       uaudio_mixer_determine_class(&iot[id], &MIX(sc));
+       MIX(sc).type = MIX_SIGNED_16;
 
-#ifdef USB_DEBUG
-       if (uaudio_debug > 2) {
-               uint8_t i;
+       if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL)
+               return;
 
-               for (i = 0; i < mc->nchan; i++) {
-                       DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]);
+       for (p = i = 0; i < d0->bNrInPins; i++) {
+               chs = uaudio_mixer_get_cluster(
+                   d0->baSourceId[i], iot).bNrChannels;
+               mc = 0;
+               for (c = 0; c < chs; c++) {
+                       mo = 0;
+                       for (o = 0; o < ochs; o++) {
+                               bno = ((p + c) * ochs) + o;
+                               if (BIT_TEST(d1->bmControls, bno))
+                                       mo++;
+                       }
+                       if (mo == 1)
+                               mc++;
                }
-               DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' "
-                   "min=%d max=%d\n",
-                   mc->wIndex, mc->type, mc->ctl,
-                   mc->minval, mc->maxval);
-       }
-#endif
-}
-
-static void
-uaudio_mixer_add_input(struct uaudio_softc *sc,
-    const struct uaudio_terminal_node *iot, int id)
-{
-#ifdef USB_DEBUG
-       const struct usb_audio_input_terminal *d = iot[id].u.it;
-
-       DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x "
-           "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d "
-           "iChannelNames=%d\n",
-           d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
-           d->bNrChannels, UGETW(d->wChannelConfig),
-           d->iChannelNames);
-#endif
-}
+               if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
 
-static void
-uaudio_mixer_add_output(struct uaudio_softc *sc,
-    const struct uaudio_terminal_node *iot, int id)
-{
-#ifdef USB_DEBUG
-       const struct usb_audio_output_terminal *d = iot[id].u.ot;
+                       /* repeat bit-scan */
 
-       DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x "
-           "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n",
-           d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
-           d->bSourceId, d->iTerminal);
-#endif
+                       mc = 0;
+                       for (c = 0; c < chs; c++) {
+                               for (o = 0; o < ochs; o++) {
+                                       bno = ((p + c) * ochs) + o;
+                                       if (BIT_TEST(d1->bmControls, bno))
+                                               MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
+                               }
+                       }
+                       MIX(sc).nchan = chs;
+                       uaudio_mixer_add_ctl(sc, &MIX(sc));
+               }
+               p += chs;
+       }
 }
 
 static void
-uaudio_mixer_add_mixer(struct uaudio_softc *sc,
+uaudio20_mixer_add_mixer(struct uaudio_softc *sc,
     const struct uaudio_terminal_node *iot, int id)
 {
-       struct uaudio_mixer_node mix;
-
-       const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu;
-       const struct usb_audio_mixer_unit_1 *d1;
+       const struct usb_audio20_mixer_unit_0 *d0 = iot[id].u.mu_v2;
+       const struct usb_audio20_mixer_unit_1 *d1;
 
        uint32_t bno;                   /* bit number */
        uint32_t p;                     /* bit number accumulator */
@@ -1782,8 +2982,8 @@ uaudio_mixer_add_mixer(struct uaudio_softc *sc,
 
        ichs = 0;
        for (i = 0; i < d0->bNrInPins; i++) {
-               ichs += (uaudio_mixer_get_cluster(d0->baSourceId[i], iot)
-                   .bNrChannels);
+               ichs += uaudio20_mixer_get_cluster(
+                   d0->baSourceId[i], iot).bNrChannels;
        }
 
        d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
@@ -1794,29 +2994,28 @@ uaudio_mixer_add_mixer(struct uaudio_softc *sc,
 
        DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
 
-       memset(&mix, 0, sizeof(mix));
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
 
-       mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
-       uaudio_mixer_determine_class(&iot[id], &mix);
-       mix.type = MIX_SIGNED_16;
+       MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+       uaudio20_mixer_determine_class(&iot[id], &MIX(sc));
+       MIX(sc).type = MIX_SIGNED_16;
 
-       if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) {
+       if (uaudio20_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL)
                return;
-       }
+
        for (p = i = 0; i < d0->bNrInPins; i++) {
-               chs = uaudio_mixer_get_cluster(d0->baSourceId[i], iot).bNrChannels;
+               chs = uaudio20_mixer_get_cluster(
+                   d0->baSourceId[i], iot).bNrChannels;
                mc = 0;
                for (c = 0; c < chs; c++) {
                        mo = 0;
                        for (o = 0; o < ochs; o++) {
                                bno = ((p + c) * ochs) + o;
-                               if (BIT_TEST(d1->bmControls, bno)) {
+                               if (BIT_TEST(d1->bmControls, bno))
                                        mo++;
-                               }
                        }
-                       if (mo == 1) {
+                       if (mo == 1)
                                mc++;
-                       }
                }
                if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
 
@@ -1826,15 +3025,12 @@ uaudio_mixer_add_mixer(struct uaudio_softc *sc,
                        for (c = 0; c < chs; c++) {
                                for (o = 0; o < ochs; o++) {
                                        bno = ((p + c) * ochs) + o;
-                                       if (BIT_TEST(d1->bmControls, bno)) {
-                                               mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
-                                       }
+                                       if (BIT_TEST(d1->bmControls, bno))
+                                               MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
                                }
                        }
-                       mix.nchan = chs;
-                       uaudio_mixer_add_ctl(sc, &mix);
-               } else {
-                       /* XXX */
+                       MIX(sc).nchan = chs;
+                       uaudio_mixer_add_ctl(sc, &MIX(sc));
                }
                p += chs;
        }
@@ -1844,52 +3040,107 @@ static void
 uaudio_mixer_add_selector(struct uaudio_softc *sc,
     const struct uaudio_terminal_node *iot, int id)
 {
-       const struct usb_audio_selector_unit *d = iot[id].u.su;
-       struct uaudio_mixer_node mix;
+       const struct usb_audio_selector_unit *d = iot[id].u.su_v1;
        uint16_t i;
 
        DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
            d->bUnitId, d->bNrInPins);
 
-       if (d->bNrInPins == 0) {
+       if (d->bNrInPins == 0)
                return;
-       }
-       memset(&mix, 0, sizeof(mix));
-
-       mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
-       mix.wValue[0] = MAKE_WORD(0, 0);
-       uaudio_mixer_determine_class(&iot[id], &mix);
-       mix.nchan = 1;
-       mix.type = MIX_SELECTOR;
 
-       mix.ctl = SOUND_MIXER_NRDEVICES;
-       mix.minval = 1;
-       mix.maxval = d->bNrInPins;
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+
+       MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
+       MIX(sc).wValue[0] = MAKE_WORD(0, 0);
+       uaudio_mixer_determine_class(&iot[id], &MIX(sc));
+       MIX(sc).nchan = 1;
+       MIX(sc).type = MIX_SELECTOR;
+       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+       MIX(sc).minval = 1;
+       MIX(sc).maxval = d->bNrInPins;
+       MIX(sc).name = "selector";
+
+       i = d->baSourceId[d->bNrInPins];
+       if (i == 0 ||
+           usbd_req_get_string_any(sc->sc_udev, NULL,
+           MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
+               MIX(sc).desc[0] = 0;
+       }
 
-       if (mix.maxval > MAX_SELECTOR_INPUT_PIN) {
-               mix.maxval = MAX_SELECTOR_INPUT_PIN;
+       if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN) {
+               MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN;
        }
-       mix.mul = (mix.maxval - mix.minval);
+       MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval);
        for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) {
-               mix.slctrtype[i] = SOUND_MIXER_NRDEVICES;
+               MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES;
+       }
+
+       for (i = 0; i < MIX(sc).maxval; i++) {
+               MIX(sc).slctrtype[i] = uaudio_mixer_feature_name(
+                   &iot[d->baSourceId[i]], &MIX(sc));
+       }
+
+       MIX(sc).class = 0;                      /* not used */
+
+       uaudio_mixer_add_ctl(sc, &MIX(sc));
+}
+
+static void
+uaudio20_mixer_add_selector(struct uaudio_softc *sc,
+    const struct uaudio_terminal_node *iot, int id)
+{
+       const struct usb_audio20_selector_unit *d = iot[id].u.su_v2;
+       uint16_t i;
+
+       DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
+           d->bUnitId, d->bNrInPins);
+
+       if (d->bNrInPins == 0)
+               return;
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+
+       MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
+       MIX(sc).wValue[0] = MAKE_WORD(0, 0);
+       uaudio20_mixer_determine_class(&iot[id], &MIX(sc));
+       MIX(sc).nchan = 1;
+       MIX(sc).type = MIX_SELECTOR;
+       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+       MIX(sc).minval = 1;
+       MIX(sc).maxval = d->bNrInPins;
+       MIX(sc).name = "selector";
+
+       i = d->baSourceId[d->bNrInPins];
+       if (i == 0 ||
+           usbd_req_get_string_any(sc->sc_udev, NULL,
+           MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
+               MIX(sc).desc[0] = 0;
        }
 
-       for (i = 0; i < mix.maxval; i++) {
-               mix.slctrtype[i] = uaudio_mixer_feature_name
-                   (&iot[d->baSourceId[i]], &mix);
+       if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN)
+               MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN;
+
+       MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval);
+       for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++)
+               MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES;
+
+       for (i = 0; i < MIX(sc).maxval; i++) {
+               MIX(sc).slctrtype[i] = uaudio20_mixer_feature_name(
+                   &iot[d->baSourceId[i]], &MIX(sc));
        }
 
-       mix.class = 0;                  /* not used */
+       MIX(sc).class = 0;                      /* not used */
 
-       uaudio_mixer_add_ctl(sc, &mix);
+       uaudio_mixer_add_ctl(sc, &MIX(sc));
 }
 
 static uint32_t
 uaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit *d,
-    uint8_t index)
+    uint8_t i)
 {
        uint32_t temp = 0;
-       uint32_t offset = (index * d->bControlSize);
+       uint32_t offset = (i * d->bControlSize);
 
        if (d->bControlSize > 0) {
                temp |= d->bmaControls[offset];
@@ -1910,8 +3161,7 @@ static void
 uaudio_mixer_add_feature(struct uaudio_softc *sc,
     const struct uaudio_terminal_node *iot, int id)
 {
-       const struct usb_audio_feature_unit *d = iot[id].u.fu;
-       struct uaudio_mixer_node mix;
+       const struct usb_audio_feature_unit *d = iot[id].u.fu_v1;
        uint32_t fumask;
        uint32_t mmask;
        uint32_t cmask;
@@ -1921,18 +3171,18 @@ uaudio_mixer_add_feature(struct uaudio_softc *sc,
        uint8_t ctl;
        uint8_t i;
 
-       if (d->bControlSize == 0) {
+       if (d->bControlSize == 0)
                return;
-       }
-       memset(&mix, 0, sizeof(mix));
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
 
        nchan = (d->bLength - 7) / d->bControlSize;
        mmask = uaudio_mixer_feature_get_bmaControls(d, 0);
        cmask = 0;
 
-       if (nchan == 0) {
+       if (nchan == 0)
                return;
-       }
+
        /* figure out what we can control */
 
        for (chan = 1; chan < nchan; chan++) {
@@ -1945,7 +3195,14 @@ uaudio_mixer_add_feature(struct uaudio_softc *sc,
        if (nchan > MIX_MAX_CHAN) {
                nchan = MIX_MAX_CHAN;
        }
-       mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
+       MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
+
+       i = d->bmaControls[d->bControlSize];
+       if (i == 0 ||
+           usbd_req_get_string_any(sc->sc_udev, NULL,
+           MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
+               MIX(sc).desc[0] = 0;
+       }
 
        for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) {
 
@@ -1955,80 +3212,225 @@ uaudio_mixer_add_feature(struct uaudio_softc *sc,
                    ctl, fumask);
 
                if (mmask & fumask) {
-                       mix.nchan = 1;
-                       mix.wValue[0] = MAKE_WORD(ctl, 0);
+                       MIX(sc).nchan = 1;
+                       MIX(sc).wValue[0] = MAKE_WORD(ctl, 0);
                } else if (cmask & fumask) {
-                       mix.nchan = nchan - 1;
+                       MIX(sc).nchan = nchan - 1;
                        for (i = 1; i < nchan; i++) {
                                if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask)
-                                       mix.wValue[i - 1] = MAKE_WORD(ctl, i);
+                                       MIX(sc).wValue[i - 1] = MAKE_WORD(ctl, i);
                                else
-                                       mix.wValue[i - 1] = -1;
+                                       MIX(sc).wValue[i - 1] = -1;
                        }
                } else {
                        continue;
                }
 
-               mixernumber = uaudio_mixer_feature_name(&iot[id], &mix);
+               mixernumber = uaudio_mixer_feature_name(&iot[id], &MIX(sc));
 
                switch (ctl) {
                case MUTE_CONTROL:
-                       mix.type = MIX_ON_OFF;
-                       mix.ctl = SOUND_MIXER_NRDEVICES;
+                       MIX(sc).type = MIX_ON_OFF;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+                       MIX(sc).name = "mute";
                        break;
 
                case VOLUME_CONTROL:
-                       mix.type = MIX_SIGNED_16;
-                       mix.ctl = mixernumber;
+                       MIX(sc).type = MIX_SIGNED_16;
+                       MIX(sc).ctl = mixernumber;
+                       MIX(sc).name = "vol";
                        break;
 
                case BASS_CONTROL:
-                       mix.type = MIX_SIGNED_8;
-                       mix.ctl = SOUND_MIXER_BASS;
+                       MIX(sc).type = MIX_SIGNED_8;
+                       MIX(sc).ctl = SOUND_MIXER_BASS;
+                       MIX(sc).name = "bass";
                        break;
 
                case MID_CONTROL:
-                       mix.type = MIX_SIGNED_8;
-                       mix.ctl = SOUND_MIXER_NRDEVICES;        /* XXXXX */
+                       MIX(sc).type = MIX_SIGNED_8;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;    /* XXXXX */
+                       MIX(sc).name = "mid";
                        break;
 
                case TREBLE_CONTROL:
-                       mix.type = MIX_SIGNED_8;
-                       mix.ctl = SOUND_MIXER_TREBLE;
+                       MIX(sc).type = MIX_SIGNED_8;
+                       MIX(sc).ctl = SOUND_MIXER_TREBLE;
+                       MIX(sc).name = "treble";
                        break;
 
                case GRAPHIC_EQUALIZER_CONTROL:
                        continue;       /* XXX don't add anything */
-                       break;
 
                case AGC_CONTROL:
-                       mix.type = MIX_ON_OFF;
-                       mix.ctl = SOUND_MIXER_NRDEVICES;        /* XXXXX */
+                       MIX(sc).type = MIX_ON_OFF;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;    /* XXXXX */
+                       MIX(sc).name = "agc";
                        break;
 
                case DELAY_CONTROL:
-                       mix.type = MIX_UNSIGNED_16;
-                       mix.ctl = SOUND_MIXER_NRDEVICES;        /* XXXXX */
+                       MIX(sc).type = MIX_UNSIGNED_16;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;    /* XXXXX */
+                       MIX(sc).name = "delay";
                        break;
 
                case BASS_BOOST_CONTROL:
-                       mix.type = MIX_ON_OFF;
-                       mix.ctl = SOUND_MIXER_NRDEVICES;        /* XXXXX */
+                       MIX(sc).type = MIX_ON_OFF;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;    /* XXXXX */
+                       MIX(sc).name = "boost";
                        break;
 
                case LOUDNESS_CONTROL:
-                       mix.type = MIX_ON_OFF;
-                       mix.ctl = SOUND_MIXER_LOUD;     /* Is this correct ? */
+                       MIX(sc).type = MIX_ON_OFF;
+                       MIX(sc).ctl = SOUND_MIXER_LOUD; /* Is this correct ? */
+                       MIX(sc).name = "loudness";
                        break;
 
                default:
-                       mix.type = MIX_UNKNOWN;
+                       MIX(sc).type = MIX_UNKNOWN;
+                       break;
+               }
+
+               if (MIX(sc).type != MIX_UNKNOWN)
+                       uaudio_mixer_add_ctl(sc, &MIX(sc));
+       }
+}
+
+static void
+uaudio20_mixer_add_feature(struct uaudio_softc *sc,
+    const struct uaudio_terminal_node *iot, int id)
+{
+       const struct usb_audio20_feature_unit *d = iot[id].u.fu_v2;
+       uint32_t ctl;
+       uint32_t mmask;
+       uint32_t cmask;
+       uint16_t mixernumber;
+       uint8_t nchan;
+       uint8_t chan;
+       uint8_t i;
+       uint8_t what;
+
+       if (UGETDW(d->bmaControls[0]) == 0)
+               return;
+
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
+
+       nchan = (d->bLength - 6) / 4;
+       mmask = UGETDW(d->bmaControls[0]);
+       cmask = 0;
+
+       if (nchan == 0)
+               return;
+
+       /* figure out what we can control */
+
+       for (chan = 1; chan < nchan; chan++)
+               cmask |= UGETDW(d->bmaControls[chan]);
+
+       if (nchan > MIX_MAX_CHAN)
+               nchan = MIX_MAX_CHAN;
+
+       MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
+
+       i = d->bmaControls[nchan][0];
+       if (i == 0 ||
+           usbd_req_get_string_any(sc->sc_udev, NULL,
+           MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
+               MIX(sc).desc[0] = 0;
+       }
+
+       for (ctl = 3; ctl != 0; ctl <<= 2) {
+
+               mixernumber = uaudio20_mixer_feature_name(&iot[id], &MIX(sc));
+
+               switch (ctl) {
+               case (3 << 0):
+                       MIX(sc).type = MIX_ON_OFF;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
+                       MIX(sc).name = "mute";
+                       what = MUTE_CONTROL;
                        break;
+               case (3 << 2): 
+                       MIX(sc).type = MIX_SIGNED_16;
+                       MIX(sc).ctl = mixernumber;
+                       MIX(sc).name = "vol";
+                       what = VOLUME_CONTROL;
+                       break;
+               case (3 << 4):
+                       MIX(sc).type = MIX_SIGNED_8;
+                       MIX(sc).ctl = SOUND_MIXER_BASS;
+                       MIX(sc).name = "bass";
+                       what = BASS_CONTROL;
+                       break;
+               case (3 << 6):
+                       MIX(sc).type = MIX_SIGNED_8;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;    /* XXXXX */
+                       MIX(sc).name = "mid";
+                       what = MID_CONTROL;
+                       break;
+               case (3 << 8):
+                       MIX(sc).type = MIX_SIGNED_8;
+                       MIX(sc).ctl = SOUND_MIXER_TREBLE;
+                       MIX(sc).name = "treble";
+                       what = TREBLE_CONTROL;
+                       break;
+               case (3 << 12):
+                       MIX(sc).type = MIX_ON_OFF;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;    /* XXXXX */
+                       MIX(sc).name = "agc";
+                       what = AGC_CONTROL;
+                       break;
+               case (3 << 14):
+                       MIX(sc).type = MIX_UNSIGNED_16;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;    /* XXXXX */
+                       MIX(sc).name = "delay";
+                       what = DELAY_CONTROL;
+                       break;
+               case (3 << 16):
+                       MIX(sc).type = MIX_ON_OFF;
+                       MIX(sc).ctl = SOUND_MIXER_NRDEVICES;    /* XXXXX */
+                       MIX(sc).name = "boost";
+                       what = BASS_BOOST_CONTROL;
+                       break;
+               case (3 << 18):
+                       MIX(sc).type = MIX_ON_OFF;
+                       MIX(sc).ctl = SOUND_MIXER_LOUD; /* Is this correct ? */
+                       MIX(sc).name = "loudness";
+                       what = LOUDNESS_CONTROL;
+                       break;
+               case (3 << 20):
+                       MIX(sc).type = MIX_SIGNED_16;
+                       MIX(sc).ctl = mixernumber;
+                       MIX(sc).name = "igain";
+                       what = INPUT_GAIN_CONTROL;
+                       break;
+               case (3 << 22):
+                       MIX(sc).type = MIX_SIGNED_16;
+                       MIX(sc).ctl = mixernumber;
+                       MIX(sc).name = "igainpad";
+                       what = INPUT_GAIN_PAD_CONTROL;
+                       break;
+               default:
+                       continue;
                }
 
-               if (mix.type != MIX_UNKNOWN) {
-                       uaudio_mixer_add_ctl(sc, &mix);
+               if ((mmask & ctl) == ctl) {
+                       MIX(sc).nchan = 1;
+                       MIX(sc).wValue[0] = MAKE_WORD(what, 0);
+               } else if ((cmask & ctl) == ctl) {
+                       MIX(sc).nchan = nchan - 1;
+                       for (i = 1; i < nchan; i++) {
+                               if ((UGETDW(d->bmaControls[i]) & ctl) == ctl)
+                                       MIX(sc).wValue[i - 1] = MAKE_WORD(what, i);
+                               else
+                                       MIX(sc).wValue[i - 1] = -1;
+                       }
+               } else {
+                       continue;
                }
+
+               if (MIX(sc).type != MIX_UNKNOWN)
+                       uaudio_mixer_add_ctl(sc, &MIX(sc));
        }
 }
 
@@ -2036,12 +3438,11 @@ static void
 uaudio_mixer_add_processing_updown(struct uaudio_softc *sc,
     const struct uaudio_terminal_node *iot, int id)
 {
-       const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu;
+       const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1;
        const struct usb_audio_processing_unit_1 *d1 =
-       (const void *)(d0->baSourceId + d0->bNrInPins);
+           (const void *)(d0->baSourceId + d0->bNrInPins);
        const struct usb_audio_processing_unit_updown *ud =
-       (const void *)(d1->bmControls + d1->bControlSize);
-       struct uaudio_mixer_node mix;
+           (const void *)(d1->bmControls + d1->bControlSize);
        uint8_t i;
 
        if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) {
@@ -2058,33 +3459,32 @@ uaudio_mixer_add_processing_updown(struct uaudio_softc *sc,
                DPRINTF("no mode select\n");
                return;
        }
-       memset(&mix, 0, sizeof(mix));
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
 
-       mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
-       mix.nchan = 1;
-       mix.wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0);
-       uaudio_mixer_determine_class(&iot[id], &mix);
-       mix.type = MIX_ON_OFF;          /* XXX */
+       MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+       MIX(sc).nchan = 1;
+       MIX(sc).wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0);
+       uaudio_mixer_determine_class(&iot[id], &MIX(sc));
+       MIX(sc).type = MIX_ON_OFF;              /* XXX */
 
        for (i = 0; i < ud->bNrModes; i++) {
                DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i]));
                /* XXX */
        }
 
-       uaudio_mixer_add_ctl(sc, &mix);
+       uaudio_mixer_add_ctl(sc, &MIX(sc));
 }
 
 static void
 uaudio_mixer_add_processing(struct uaudio_softc *sc,
     const struct uaudio_terminal_node *iot, int id)
 {
-       const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu;
+       const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1;
        const struct usb_audio_processing_unit_1 *d1 =
-       (const void *)(d0->baSourceId + d0->bNrInPins);
-       struct uaudio_mixer_node mix;
+           (const void *)(d0->baSourceId + d0->bNrInPins);
        uint16_t ptype;
 
-       memset(&mix, 0, sizeof(mix));
+       memset(&MIX(sc), 0, sizeof(MIX(sc)));
 
        ptype = UGETW(d0->wProcessType);
 
@@ -2095,12 +3495,12 @@ uaudio_mixer_add_processing(struct uaudio_softc *sc,
                return;
        }
        if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) {
-               mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
-               mix.nchan = 1;
-               mix.wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0);
-               uaudio_mixer_determine_class(&iot[id], &mix);
-               mix.type = MIX_ON_OFF;
-               uaudio_mixer_add_ctl(sc, &mix);
+               MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+               MIX(sc).nchan = 1;
+               MIX(sc).wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0);
+               uaudio_mixer_determine_class(&iot[id], &MIX(sc));
+               MIX(sc).type = MIX_ON_OFF;
+               uaudio_mixer_add_ctl(sc, &MIX(sc));
        }
        switch (ptype) {
        case UPDOWNMIX_PROCESS:
@@ -2123,10 +3523,9 @@ static void
 uaudio_mixer_add_extension(struct uaudio_softc *sc,
     const struct uaudio_terminal_node *iot, int id)
 {
-       const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu;
+       const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu_v1;
        const struct usb_audio_extension_unit_1 *d1 =
-       (const void *)(d0->baSourceId + d0->bNrInPins);
-       struct uaudio_mixer_node mix;
+           (const void *)(d0->baSourceId + d0->bNrInPins);
 
        DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
            d0->bUnitId, d0->bNrInPins);
@@ -2139,15 +3538,15 @@ uaudio_mixer_add_extension(struct uaudio_softc *sc,
        }
        if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) {
 
-               memset(&mix, 0, sizeof(mix));
+               memset(&MIX(sc), 0, sizeof(MIX(sc)));
 
-               mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
-               mix.nchan = 1;
-               mix.wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0);
-               uaudio_mixer_determine_class(&iot[id], &mix);
-               mix.type = MIX_ON_OFF;
+               MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+               MIX(sc).nchan = 1;
+               MIX(sc).wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0);
+               uaudio_mixer_determine_class(&iot[id], &MIX(sc));
+               MIX(sc).type = MIX_ON_OFF;
 
-               uaudio_mixer_add_ctl(sc, &mix);
+               uaudio_mixer_add_ctl(sc, &MIX(sc));
        }
 }
 
@@ -2208,11 +3607,16 @@ uaudio_mixer_verify_desc(const void *arg, uint32_t len)
                if (u.desc->bLength < len) {
                        goto error;
                }
-               len += u.su->bNrInPins;
+               len += u.su->bNrInPins + 1;
                break;
 
        case UDESCSUB_AC_FEATURE:
-               len += (sizeof(*u.fu) + 1);
+               len += sizeof(*u.fu) + 1;
+
+               if (u.desc->bLength < len)
+                       goto error;
+
+               len += u.fu->bControlSize;
                break;
 
        case UDESCSUB_AC_PROCESSING:
@@ -2247,24 +3651,177 @@ uaudio_mixer_verify_desc(const void *arg, uint32_t len)
 
                if (u.desc->bLength < len) {
                        goto error;
-               }
-               e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
+               }
+               e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
+
+               len += sizeof(*e1);
+
+               if (u.desc->bLength < len) {
+                       goto error;
+               }
+               len += e1->bControlSize;
+               break;
+
+       default:
+               goto error;
+       }
+
+       if (u.desc->bLength < len) {
+               goto error;
+       }
+       return (u.desc);
+
+error:
+       if (u.desc) {
+               DPRINTF("invalid descriptor, type=%d, "
+                   "sub_type=%d, len=%d of %d bytes\n",
+                   u.desc->bDescriptorType,
+                   u.desc->bDescriptorSubtype,
+                   u.desc->bLength, len);
+       }
+       return (NULL);
+}
+
+static const void *
+uaudio20_mixer_verify_desc(const void *arg, uint32_t len)
+{
+       const struct usb_audio20_mixer_unit_1 *d1;
+       const struct usb_audio20_extension_unit_1 *e1;
+       const struct usb_audio20_processing_unit_1 *u1;
+       const struct usb_audio20_clock_selector_unit_1 *c1;
+
+       union {
+               const struct usb_descriptor *desc;
+               const struct usb_audio20_clock_source_unit *csrc;
+               const struct usb_audio20_clock_selector_unit_0 *csel;
+               const struct usb_audio20_clock_multiplier_unit *cmul;
+               const struct usb_audio20_input_terminal *it;
+               const struct usb_audio20_output_terminal *ot;
+               const struct usb_audio20_mixer_unit_0 *mu;
+               const struct usb_audio20_selector_unit *su;
+               const struct usb_audio20_feature_unit *fu;
+               const struct usb_audio20_sample_rate_unit *ru;
+               const struct usb_audio20_processing_unit_0 *pu;
+               const struct usb_audio20_extension_unit_0 *eu;
+               const struct usb_audio20_effect_unit *ef;
+       }     u;
+
+       u.desc = arg;
+
+       if (u.desc == NULL)
+               goto error;
+
+       if (u.desc->bDescriptorType != UDESC_CS_INTERFACE)
+               goto error;
+
+       switch (u.desc->bDescriptorSubtype) {
+       case UDESCSUB_AC_INPUT:
+               len += sizeof(*u.it);
+               break;
+
+       case UDESCSUB_AC_OUTPUT:
+               len += sizeof(*u.ot);
+               break;
+
+       case UDESCSUB_AC_MIXER:
+               len += sizeof(*u.mu);
+
+               if (u.desc->bLength < len)
+                       goto error;
+               len += u.mu->bNrInPins;
+
+               if (u.desc->bLength < len)
+                       goto error;
+
+               d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins);
+
+               len += sizeof(*d1) + d1->bNrChannels;
+               break;
+
+       case UDESCSUB_AC_SELECTOR:
+               len += sizeof(*u.su);
+
+               if (u.desc->bLength < len)
+                       goto error;
+
+               len += u.su->bNrInPins + 1;
+               break;
+
+       case UDESCSUB_AC_FEATURE:
+               len += sizeof(*u.fu) + 1;
+               break;
+
+       case UDESCSUB_AC_EFFECT:
+               len += sizeof(*u.ef) + 4;
+               break;
+
+       case UDESCSUB_AC_PROCESSING_V2:
+               len += sizeof(*u.pu);
+
+               if (u.desc->bLength < len)
+                       goto error;
+
+               len += u.pu->bNrInPins;
+
+               if (u.desc->bLength < len)
+                       goto error;
+
+               u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins);
+
+               len += sizeof(*u1);
+               break;
+
+       case UDESCSUB_AC_EXTENSION_V2:
+               len += sizeof(*u.eu);
+
+               if (u.desc->bLength < len)
+                       goto error;
+
+               len += u.eu->bNrInPins;
+
+               if (u.desc->bLength < len)
+                       goto error;
+
+               e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
+
+               len += sizeof(*e1);
+               break;
+
+       case UDESCSUB_AC_CLOCK_SRC:
+               len += sizeof(*u.csrc);
+               break;
+
+       case UDESCSUB_AC_CLOCK_SEL:
+               len += sizeof(*u.csel);
+
+               if (u.desc->bLength < len)
+                       goto error;
 
-               len += sizeof(*e1);
+               len += u.csel->bNrInPins;
 
-               if (u.desc->bLength < len) {
+               if (u.desc->bLength < len)
                        goto error;
-               }
-               len += e1->bControlSize;
+
+               c1 = (const void *)(u.csel->baCSourceId + u.csel->bNrInPins);
+
+               len += sizeof(*c1);
+               break;
+
+       case UDESCSUB_AC_CLOCK_MUL:
+               len += sizeof(*u.cmul);
+               break;
+
+       case UDESCSUB_AC_SAMPLE_RT:
+               len += sizeof(*u.ru);
                break;
 
        default:
                goto error;
        }
 
-       if (u.desc->bLength < len) {
+       if (u.desc->bLength < len)
                goto error;
-       }
+
        return (u.desc);
 
 error:
@@ -2278,35 +3835,6 @@ error:
        return (NULL);
 }
 
-#ifdef USB_DEBUG
-static void
-uaudio_mixer_dump_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
-{
-       static const char *channel_names[16] = {
-               "LEFT", "RIGHT", "CENTER", "LFE",
-               "LEFT_SURROUND", "RIGHT_SURROUND", "LEFT_CENTER", "RIGHT_CENTER",
-               "SURROUND", "LEFT_SIDE", "RIGHT_SIDE", "TOP",
-               "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15",
-       };
-       uint16_t cc;
-       uint8_t i;
-       const struct usb_audio_cluster cl = uaudio_mixer_get_cluster(id, iot);
-
-       cc = UGETW(cl.wChannelConfig);
-
-       DPRINTF("cluster: bNrChannels=%u iChannelNames=%u wChannelConfig="
-           "0x%04x:\n", cl.iChannelNames, cl.bNrChannels, cc);
-
-       for (i = 0; cc; i++) {
-               if (cc & 1) {
-                       DPRINTF(" - %s\n", channel_names[i]);
-               }
-               cc >>= 1;
-       }
-}
-
-#endif
-
 static struct usb_audio_cluster
 uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
 {
@@ -2321,43 +3849,43 @@ uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
                }
                switch (dp->bDescriptorSubtype) {
                case UDESCSUB_AC_INPUT:
-                       r.bNrChannels = iot[id].u.it->bNrChannels;
-                       r.wChannelConfig[0] = iot[id].u.it->wChannelConfig[0];
-                       r.wChannelConfig[1] = iot[id].u.it->wChannelConfig[1];
-                       r.iChannelNames = iot[id].u.it->iChannelNames;
+                       r.bNrChannels = iot[id].u.it_v1->bNrChannels;
+                       r.wChannelConfig[0] = iot[id].u.it_v1->wChannelConfig[0];
+                       r.wChannelConfig[1] = iot[id].u.it_v1->wChannelConfig[1];
+                       r.iChannelNames = iot[id].u.it_v1->iChannelNames;
                        goto done;
 
                case UDESCSUB_AC_OUTPUT:
-                       id = iot[id].u.ot->bSourceId;
+                       id = iot[id].u.ot_v1->bSourceId;
                        break;
 
                case UDESCSUB_AC_MIXER:
                        r = *(const struct usb_audio_cluster *)
-                           &iot[id].u.mu->baSourceId[iot[id].u.mu->
-                           bNrInPins];
+                           &iot[id].u.mu_v1->baSourceId[
+                           iot[id].u.mu_v1->bNrInPins];
                        goto done;
 
                case UDESCSUB_AC_SELECTOR:
-                       if (iot[id].u.su->bNrInPins > 0) {
+                       if (iot[id].u.su_v1->bNrInPins > 0) {
                                /* XXX This is not really right */
-                               id = iot[id].u.su->baSourceId[0];
+                               id = iot[id].u.su_v1->baSourceId[0];
                        }
                        break;
 
                case UDESCSUB_AC_FEATURE:
-                       id = iot[id].u.fu->bSourceId;
+                       id = iot[id].u.fu_v1->bSourceId;
                        break;
 
                case UDESCSUB_AC_PROCESSING:
                        r = *((const struct usb_audio_cluster *)
-                           &iot[id].u.pu->baSourceId[iot[id].u.pu->
-                           bNrInPins]);
+                           &iot[id].u.pu_v1->baSourceId[
+                           iot[id].u.pu_v1->bNrInPins]);
                        goto done;
 
                case UDESCSUB_AC_EXTENSION:
                        r = *((const struct usb_audio_cluster *)
-                           &iot[id].u.eu->baSourceId[iot[id].u.eu->
-                           bNrInPins]);
+                           &iot[id].u.eu_v1->baSourceId[
+                           iot[id].u.eu_v1->bNrInPins]);
                        goto done;
 
                default:
@@ -2371,108 +3899,80 @@ done:
        return (r);
 }
 
-#ifdef USB_DEBUG
+static struct usb_audio20_cluster
+uaudio20_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
+{
+       struct usb_audio20_cluster r;
+       const struct usb_descriptor *dp;
+       uint8_t i;
 
-struct uaudio_tt_to_string {
-       uint16_t terminal_type;
-       const char *desc;
-};
+       for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) {    /* avoid infinite loops */
+               dp = iot[id].u.desc;
+               if (dp == NULL)
+                       goto error;
 
-static const struct uaudio_tt_to_string uaudio_tt_to_string[] = {
+               switch (dp->bDescriptorSubtype) {
+               case UDESCSUB_AC_INPUT:
+                       r.bNrChannels = iot[id].u.it_v2->bNrChannels;
+                       r.bmChannelConfig[0] = iot[id].u.it_v2->bmChannelConfig[0];
+                       r.bmChannelConfig[1] = iot[id].u.it_v2->bmChannelConfig[1];
+                       r.bmChannelConfig[2] = iot[id].u.it_v2->bmChannelConfig[2];
+                       r.bmChannelConfig[3] = iot[id].u.it_v2->bmChannelConfig[3];
+                       r.iChannelNames = iot[id].u.it_v2->iTerminal;
+                       goto done;
 
-       /* USB terminal types */
-       {UAT_UNDEFINED, "UAT_UNDEFINED"},
-       {UAT_STREAM, "UAT_STREAM"},
-       {UAT_VENDOR, "UAT_VENDOR"},
+               case UDESCSUB_AC_OUTPUT:
+                       id = iot[id].u.ot_v2->bSourceId;
+                       break;
 
-       /* input terminal types */
-       {UATI_UNDEFINED, "UATI_UNDEFINED"},
-       {UATI_MICROPHONE, "UATI_MICROPHONE"},
-       {UATI_DESKMICROPHONE, "UATI_DESKMICROPHONE"},
-       {UATI_PERSONALMICROPHONE, "UATI_PERSONALMICROPHONE"},
-       {UATI_OMNIMICROPHONE, "UATI_OMNIMICROPHONE"},
-       {UATI_MICROPHONEARRAY, "UATI_MICROPHONEARRAY"},
-       {UATI_PROCMICROPHONEARR, "UATI_PROCMICROPHONEARR"},
+               case UDESCSUB_AC_MIXER:
+                       r = *(const struct usb_audio20_cluster *)
+                           &iot[id].u.mu_v2->baSourceId[
+                           iot[id].u.mu_v2->bNrInPins];
+                       goto done;
 
-       /* output terminal types */
-       {UATO_UNDEFINED, "UATO_UNDEFINED"},
-       {UATO_SPEAKER, "UATO_SPEAKER"},
-       {UATO_HEADPHONES, "UATO_HEADPHONES"},
-       {UATO_DISPLAYAUDIO, "UATO_DISPLAYAUDIO"},
-       {UATO_DESKTOPSPEAKER, "UATO_DESKTOPSPEAKER"},
-       {UATO_ROOMSPEAKER, "UATO_ROOMSPEAKER"},
-       {UATO_COMMSPEAKER, "UATO_COMMSPEAKER"},
-       {UATO_SUBWOOFER, "UATO_SUBWOOFER"},
+               case UDESCSUB_AC_SELECTOR:
+                       if (iot[id].u.su_v2->bNrInPins > 0) {
+                               /* XXX This is not really right */
+                               id = iot[id].u.su_v2->baSourceId[0];
+                       }
+                       break;
 
-       /* bidir terminal types */
-       {UATB_UNDEFINED, "UATB_UNDEFINED"},
-       {UATB_HANDSET, "UATB_HANDSET"},
-       {UATB_HEADSET, "UATB_HEADSET"},
-       {UATB_SPEAKERPHONE, "UATB_SPEAKERPHONE"},
-       {UATB_SPEAKERPHONEESUP, "UATB_SPEAKERPHONEESUP"},
-       {UATB_SPEAKERPHONEECANC, "UATB_SPEAKERPHONEECANC"},
+               case UDESCSUB_AC_SAMPLE_RT:
+                       id = iot[id].u.ru_v2->bSourceId;
+                       break;
 
-       /* telephony terminal types */
-       {UATT_UNDEFINED, "UATT_UNDEFINED"},
-       {UATT_PHONELINE, "UATT_PHONELINE"},
-       {UATT_TELEPHONE, "UATT_TELEPHONE"},
-       {UATT_DOWNLINEPHONE, "UATT_DOWNLINEPHONE"},
+               case UDESCSUB_AC_EFFECT:
+                       id = iot[id].u.ef_v2->bSourceId;
+                       break;
 
-       /* external terminal types */
-       {UATE_UNDEFINED, "UATE_UNDEFINED"},
-       {UATE_ANALOGCONN, "UATE_ANALOGCONN"},
-       {UATE_LINECONN, "UATE_LINECONN"},
-       {UATE_LEGACYCONN, "UATE_LEGACYCONN"},
-       {UATE_DIGITALAUIFC, "UATE_DIGITALAUIFC"},
-       {UATE_SPDIF, "UATE_SPDIF"},
-       {UATE_1394DA, "UATE_1394DA"},
-       {UATE_1394DV, "UATE_1394DV"},
+               case UDESCSUB_AC_FEATURE:
+                       id = iot[id].u.fu_v2->bSourceId;
+                       break;
 
-       /* embedded function terminal types */
-       {UATF_UNDEFINED, "UATF_UNDEFINED"},
-       {UATF_CALIBNOISE, "UATF_CALIBNOISE"},
-       {UATF_EQUNOISE, "UATF_EQUNOISE"},
-       {UATF_CDPLAYER, "UATF_CDPLAYER"},
-       {UATF_DAT, "UATF_DAT"},
-       {UATF_DCC, "UATF_DCC"},
-       {UATF_MINIDISK, "UATF_MINIDISK"},
-       {UATF_ANALOGTAPE, "UATF_ANALOGTAPE"},
-       {UATF_PHONOGRAPH, "UATF_PHONOGRAPH"},
-       {UATF_VCRAUDIO, "UATF_VCRAUDIO"},
-       {UATF_VIDEODISCAUDIO, "UATF_VIDEODISCAUDIO"},
-       {UATF_DVDAUDIO, "UATF_DVDAUDIO"},
-       {UATF_TVTUNERAUDIO, "UATF_TVTUNERAUDIO"},
-       {UATF_SATELLITE, "UATF_SATELLITE"},
-       {UATF_CABLETUNER, "UATF_CABLETUNER"},
-       {UATF_DSS, "UATF_DSS"},
-       {UATF_RADIORECV, "UATF_RADIORECV"},
-       {UATF_RADIOXMIT, "UATF_RADIOXMIT"},
-       {UATF_MULTITRACK, "UATF_MULTITRACK"},
-       {UATF_SYNTHESIZER, "UATF_SYNTHESIZER"},
-
-       /* unknown */
-       {0x0000, "UNKNOWN"},
-};
+               case UDESCSUB_AC_PROCESSING_V2:
+                       r = *((const struct usb_audio20_cluster *)
+                           &iot[id].u.pu_v2->baSourceId[
+                           iot[id].u.pu_v2->bNrInPins]);
+                       goto done;
 
-static const char *
-uaudio_mixer_get_terminal_name(uint16_t terminal_type)
-{
-       const struct uaudio_tt_to_string *uat = uaudio_tt_to_string;
+               case UDESCSUB_AC_EXTENSION_V2:
+                       r = *((const struct usb_audio20_cluster *)
+                           &iot[id].u.eu_v2->baSourceId[
+                           iot[id].u.eu_v2->bNrInPins]);
+                       goto done;
 
-       while (uat->terminal_type) {
-               if (uat->terminal_type == terminal_type) {
-                       break;
+               default:
+                       goto error;
                }
-               uat++;
        }
-       if (uat->terminal_type == 0) {
-               DPRINTF("unknown terminal type (0x%04x)", terminal_type);
-       }
-       return (uat->desc);
+error:
+       DPRINTF("Bad data!\n");
+       memset(&r, 0, sizeof(r));
+done:
+       return (r);
 }
 
-#endif
-
 static uint16_t
 uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot,
     struct uaudio_mixer_node *mix)
@@ -2492,8 +3992,63 @@ uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot,
         * one output terminal:
         */
        if (output[0] && (!output[1])) {
-               terminal_type = UGETW(output[0]->u.ot->wTerminalType);
+               terminal_type =
+                   UGETW(output[0]->u.ot_v1->wTerminalType);
+       }
+       /*
+        * If the only output terminal is USB,
+        * the class is UAC_RECORD.
+        */
+       if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
+
+               mix->class = UAC_RECORD;
+               if (input[0] && (!input[1])) {
+                       terminal_type =
+                           UGETW(input[0]->u.it_v1->wTerminalType);
+               } else {
+                       terminal_type = 0;
+               }
+               goto done;
+       }
+       /*
+        * if the unit is connected to just
+        * one input terminal, the
+        * class is UAC_INPUT:
+        */
+       if (input[0] && (!input[1])) {
+               mix->class = UAC_INPUT;
+               terminal_type =
+                   UGETW(input[0]->u.it_v1->wTerminalType);
+               goto done;
        }
+       /*
+        * Otherwise, the class is UAC_OUTPUT.
+        */
+       mix->class = UAC_OUTPUT;
+done:
+       return (terminal_type);
+}
+
+static uint16_t
+uaudio20_mixer_determine_class(const struct uaudio_terminal_node *iot,
+    struct uaudio_mixer_node *mix)
+{
+       uint16_t terminal_type = 0x0000;
+       const struct uaudio_terminal_node *input[2];
+       const struct uaudio_terminal_node *output[2];
+
+       input[0] = uaudio_mixer_get_input(iot, 0);
+       input[1] = uaudio_mixer_get_input(iot, 1);
+
+       output[0] = uaudio_mixer_get_output(iot, 0);
+       output[1] = uaudio_mixer_get_output(iot, 1);
+
+       /*
+        * check if there is only
+        * one output terminal:
+        */
+       if (output[0] && (!output[1]))
+               terminal_type = UGETW(output[0]->u.ot_v2->wTerminalType);
        /*
         * If the only output terminal is USB,
         * the class is UAC_RECORD.
@@ -2502,7 +4057,8 @@ uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot,
 
                mix->class = UAC_RECORD;
                if (input[0] && (!input[1])) {
-                       terminal_type = UGETW(input[0]->u.it->wTerminalType);
+                       terminal_type =
+                           UGETW(input[0]->u.it_v2->wTerminalType);
                } else {
                        terminal_type = 0;
                }
@@ -2515,7 +4071,8 @@ uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot,
         */
        if (input[0] && (!input[1])) {
                mix->class = UAC_INPUT;
-               terminal_type = UGETW(input[0]->u.it->wTerminalType);
+               terminal_type =
+                   UGETW(input[0]->u.it_v2->wTerminalType);
                goto done;
        }
        /*
@@ -2631,125 +4188,391 @@ uaudio_mixer_feature_name(const struct uaudio_terminal_node *iot,
                uat++;
        }
 
-       DPRINTF("terminal_type=%s (0x%04x) -> %d\n",
-           uaudio_mixer_get_terminal_name(terminal_type),
+       DPRINTF("terminal_type=0x%04x -> %d\n",
+           terminal_type, uat->feature);
+
+       return (uat->feature);
+}
+
+static uint16_t
+uaudio20_mixer_feature_name(const struct uaudio_terminal_node *iot,
+    struct uaudio_mixer_node *mix)
+{
+       const struct uaudio_tt_to_feature *uat;
+       uint16_t terminal_type = uaudio20_mixer_determine_class(iot, mix);
+
+       if ((mix->class == UAC_RECORD) && (terminal_type == 0))
+               return (SOUND_MIXER_IMIX);
+       
+       for (uat = uaudio_tt_to_feature; uat->terminal_type != 0; uat++) {
+               if (uat->terminal_type == terminal_type)
+                       break;
+       }
+
+       DPRINTF("terminal_type=0x%04x -> %d\n",
            terminal_type, uat->feature);
 
        return (uat->feature);
 }
 
 static const struct uaudio_terminal_node *
-uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t index)
+uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t i)
+{
+       struct uaudio_terminal_node *root = iot->root;
+       uint8_t n;
+
+       n = iot->usr.id_max;
+       do {
+               if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) {
+                       if (!i--)
+                               return (root + n);
+               }
+       } while (n--);
+
+       return (NULL);
+}
+
+static const struct uaudio_terminal_node *
+uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t i)
 {
        struct uaudio_terminal_node *root = iot->root;
        uint8_t n;
 
-       n = iot->usr.id_max;
-       do {
-               if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) {
-                       if (!index--) {
-                               return (root + n);
+       n = iot->usr.id_max;
+       do {
+               if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) {
+                       if (!i--)
+                               return (root + n);
+               }
+       } while (n--);
+
+       return (NULL);
+}
+
+static void
+uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
+    const uint8_t *p_id, uint8_t n_id,
+    struct uaudio_search_result *info)
+{
+       struct uaudio_terminal_node *iot;
+       uint8_t n;
+       uint8_t i;
+       uint8_t is_last;
+
+top:
+       for (n = 0; n < n_id; n++) {
+
+               i = p_id[n];
+
+               if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
+                       DPRINTF("avoided going into a circle at id=%d!\n", i);
+                       return;
+               }
+
+               info->recurse_level++;
+
+               iot = (root + i);
+
+               if (iot->u.desc == NULL)
+                       continue;
+
+               is_last = ((n + 1) == n_id);
+
+               switch (iot->u.desc->bDescriptorSubtype) {
+               case UDESCSUB_AC_INPUT:
+                       info->bit_input[i / 8] |= (1 << (i % 8));
+                       break;
+
+               case UDESCSUB_AC_FEATURE:
+                       if (is_last) {
+                               p_id = &iot->u.fu_v1->bSourceId;
+                               n_id = 1;
+                               goto top;
+                       }
+                       uaudio_mixer_find_inputs_sub(
+                           root, &iot->u.fu_v1->bSourceId, 1, info);
+                       break;
+
+               case UDESCSUB_AC_OUTPUT:
+                       if (is_last) {
+                               p_id = &iot->u.ot_v1->bSourceId;
+                               n_id = 1;
+                               goto top;
+                       }
+                       uaudio_mixer_find_inputs_sub(
+                           root, &iot->u.ot_v1->bSourceId, 1, info);
+                       break;
+
+               case UDESCSUB_AC_MIXER:
+                       if (is_last) {
+                               p_id = iot->u.mu_v1->baSourceId;
+                               n_id = iot->u.mu_v1->bNrInPins;
+                               goto top;
+                       }
+                       uaudio_mixer_find_inputs_sub(
+                           root, iot->u.mu_v1->baSourceId,
+                           iot->u.mu_v1->bNrInPins, info);
+                       break;
+
+               case UDESCSUB_AC_SELECTOR:
+                       if (is_last) {
+                               p_id = iot->u.su_v1->baSourceId;
+                               n_id = iot->u.su_v1->bNrInPins;
+                               goto top;
+                       }
+                       uaudio_mixer_find_inputs_sub(
+                           root, iot->u.su_v1->baSourceId,
+                           iot->u.su_v1->bNrInPins, info);
+                       break;
+
+               case UDESCSUB_AC_PROCESSING:
+                       if (is_last) {
+                               p_id = iot->u.pu_v1->baSourceId;
+                               n_id = iot->u.pu_v1->bNrInPins;
+                               goto top;
+                       }
+                       uaudio_mixer_find_inputs_sub(
+                           root, iot->u.pu_v1->baSourceId,
+                           iot->u.pu_v1->bNrInPins, info);
+                       break;
+
+               case UDESCSUB_AC_EXTENSION:
+                       if (is_last) {
+                               p_id = iot->u.eu_v1->baSourceId;
+                               n_id = iot->u.eu_v1->bNrInPins;
+                               goto top;
+                       }
+                       uaudio_mixer_find_inputs_sub(
+                           root, iot->u.eu_v1->baSourceId,
+                           iot->u.eu_v1->bNrInPins, info);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}
+
+static void
+uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
+    const uint8_t *p_id, uint8_t n_id,
+    struct uaudio_search_result *info)
+{
+       struct uaudio_terminal_node *iot;
+       uint8_t n;
+       uint8_t i;
+       uint8_t is_last;
+
+top:
+       for (n = 0; n < n_id; n++) {
+
+               i = p_id[n];
+
+               if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
+                       DPRINTF("avoided going into a circle at id=%d!\n", i);
+                       return;
+               }
+
+               info->recurse_level++;
+
+               iot = (root + i);
+
+               if (iot->u.desc == NULL)
+                       continue;
+
+               is_last = ((n + 1) == n_id);
+
+               switch (iot->u.desc->bDescriptorSubtype) {
+               case UDESCSUB_AC_INPUT:
+                       info->bit_input[i / 8] |= (1 << (i % 8));
+                       break;
+
+               case UDESCSUB_AC_OUTPUT:
+                       if (is_last) {
+                               p_id = &iot->u.ot_v2->bSourceId;
+                               n_id = 1;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_inputs_sub(
+                           root, &iot->u.ot_v2->bSourceId, 1, info);
+                       break;
+
+               case UDESCSUB_AC_MIXER:
+                       if (is_last) {
+                               p_id = iot->u.mu_v2->baSourceId;
+                               n_id = iot->u.mu_v2->bNrInPins;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_inputs_sub(
+                           root, iot->u.mu_v2->baSourceId,
+                           iot->u.mu_v2->bNrInPins, info);
+                       break;
+
+               case UDESCSUB_AC_SELECTOR:
+                       if (is_last) {
+                               p_id = iot->u.su_v2->baSourceId;
+                               n_id = iot->u.su_v2->bNrInPins;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_inputs_sub(
+                           root, iot->u.su_v2->baSourceId,
+                           iot->u.su_v2->bNrInPins, info);
+                       break;
+
+               case UDESCSUB_AC_SAMPLE_RT:
+                       if (is_last) {
+                               p_id = &iot->u.ru_v2->bSourceId;
+                               n_id = 1;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_inputs_sub(
+                           root, &iot->u.ru_v2->bSourceId,
+                           1, info);
+                       break;
+
+               case UDESCSUB_AC_EFFECT:
+                       if (is_last) {
+                               p_id = &iot->u.ef_v2->bSourceId;
+                               n_id = 1;
+                               goto top;
                        }
-               }
-       } while (n--);
+                       uaudio20_mixer_find_inputs_sub(
+                           root, &iot->u.ef_v2->bSourceId,
+                           1, info);
+                       break;
 
-       return (NULL);
-}
+               case UDESCSUB_AC_FEATURE:
+                       if (is_last) {
+                               p_id = &iot->u.fu_v2->bSourceId;
+                               n_id = 1;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_inputs_sub(
+                           root, &iot->u.fu_v2->bSourceId, 1, info);
+                       break;
 
-static const struct uaudio_terminal_node *
-uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t index)
-{
-       struct uaudio_terminal_node *root = iot->root;
-       uint8_t n;
+               case UDESCSUB_AC_PROCESSING_V2:
+                       if (is_last) {
+                               p_id = iot->u.pu_v2->baSourceId;
+                               n_id = iot->u.pu_v2->bNrInPins;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_inputs_sub(
+                           root, iot->u.pu_v2->baSourceId,
+                           iot->u.pu_v2->bNrInPins, info);
+                       break;
 
-       n = iot->usr.id_max;
-       do {
-               if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) {
-                       if (!index--) {
-                               return (root + n);
+               case UDESCSUB_AC_EXTENSION_V2:
+                       if (is_last) {
+                               p_id = iot->u.eu_v2->baSourceId;
+                               n_id = iot->u.eu_v2->bNrInPins;
+                               goto top;
                        }
+                       uaudio20_mixer_find_inputs_sub(
+                           root, iot->u.eu_v2->baSourceId,
+                           iot->u.eu_v2->bNrInPins, info);
+                       break;
+               default:
+                       break;
                }
-       } while (n--);
-
-       return (NULL);
+       }
 }
 
 static void
-uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
+uaudio20_mixer_find_clocks_sub(struct uaudio_terminal_node *root,
     const uint8_t *p_id, uint8_t n_id,
     struct uaudio_search_result *info)
 {
        struct uaudio_terminal_node *iot;
        uint8_t n;
        uint8_t i;
+       uint8_t is_last;
+       uint8_t id;
 
-       if (info->recurse_level >= UAUDIO_RECURSE_LIMIT) {
-               return;
-       }
-       info->recurse_level++;
-
+top:
        for (n = 0; n < n_id; n++) {
 
                i = p_id[n];
 
-               if (info->bit_visited[i / 8] & (1 << (i % 8))) {
-                       /* don't go into a circle */
+               if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
                        DPRINTF("avoided going into a circle at id=%d!\n", i);
-                       continue;
-               } else {
-                       info->bit_visited[i / 8] |= (1 << (i % 8));
+                       return;
                }
 
+               info->recurse_level++;
+
                iot = (root + i);
 
-               if (iot->u.desc == NULL) {
+               if (iot->u.desc == NULL)
                        continue;
-               }
+
+               is_last = ((n + 1) == n_id);
+
                switch (iot->u.desc->bDescriptorSubtype) {
                case UDESCSUB_AC_INPUT:
-                       info->bit_input[i / 8] |= (1 << (i % 8));
-                       break;
-
-               case UDESCSUB_AC_FEATURE:
-                       uaudio_mixer_find_inputs_sub
-                           (root, &iot->u.fu->bSourceId, 1, info);
+                       info->is_input = 1;
+                       if (is_last) {
+                               p_id = &iot->u.it_v2->bCSourceId;
+                               n_id = 1;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_clocks_sub(root,
+                           &iot->u.it_v2->bCSourceId, 1, info);
                        break;
 
                case UDESCSUB_AC_OUTPUT:
-                       uaudio_mixer_find_inputs_sub
-                           (root, &iot->u.ot->bSourceId, 1, info);
+                       info->is_input = 0;
+                       if (is_last) {
+                               p_id = &iot->u.ot_v2->bCSourceId;
+                               n_id = 1;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_clocks_sub(root,
+                           &iot->u.ot_v2->bCSourceId, 1, info);
                        break;
 
-               case UDESCSUB_AC_MIXER:
-                       uaudio_mixer_find_inputs_sub
-                           (root, iot->u.mu->baSourceId,
-                           iot->u.mu->bNrInPins, info);
+               case UDESCSUB_AC_CLOCK_SEL:
+                       if (is_last) {
+                               p_id = iot->u.csel_v2->baCSourceId;
+                               n_id = iot->u.csel_v2->bNrInPins;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_clocks_sub(root,
+                           iot->u.csel_v2->baCSourceId,
+                           iot->u.csel_v2->bNrInPins, info);
                        break;
 
-               case UDESCSUB_AC_SELECTOR:
-                       uaudio_mixer_find_inputs_sub
-                           (root, iot->u.su->baSourceId,
-                           iot->u.su->bNrInPins, info);
+               case UDESCSUB_AC_CLOCK_MUL:
+                       if (is_last) {
+                               p_id = &iot->u.cmul_v2->bCSourceId;
+                               n_id = 1;
+                               goto top;
+                       }
+                       uaudio20_mixer_find_clocks_sub(root,
+                           &iot->u.cmul_v2->bCSourceId,
+                           1, info);
                        break;
 
-               case UDESCSUB_AC_PROCESSING:
-                       uaudio_mixer_find_inputs_sub
-                           (root, iot->u.pu->baSourceId,
-                           iot->u.pu->bNrInPins, info);
-                       break;
+               case UDESCSUB_AC_CLOCK_SRC:
 
-               case UDESCSUB_AC_EXTENSION:
-                       uaudio_mixer_find_inputs_sub
-                           (root, iot->u.eu->baSourceId,
-                           iot->u.eu->bNrInPins, info);
+                       id = iot->u.csrc_v2->bClockId;
+
+                       switch (info->is_input) {
+                       case 0:
+                               info->bit_output[id / 8] |= (1 << (id % 8));
+                               break;
+                       case 1:
+                               info->bit_input[id / 8] |= (1 << (id % 8));
+                               break;
+                       default:
+                               break;
+                       }
                        break;
 
-               case UDESCSUB_AC_HEADER:
                default:
                        break;
                }
        }
-       info->recurse_level--;
 }
 
 static void
@@ -2778,8 +4601,8 @@ uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id,
 }
 
 static void
-uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev,
-    void *desc)
+uaudio_mixer_fill_info(struct uaudio_softc *sc,
+    struct usb_device *udev, void *desc)
 {
        const struct usb_audio_control_descriptor *acdp;
        struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
@@ -2813,15 +4636,6 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev,
        DPRINTFN(3, "found AC header, vers=%03x, len=%d\n",
            sc->sc_audio_rev, wTotalLen);
 
-       if (sc->sc_audio_rev != UAUDIO_VERSION) {
-
-               if (sc->sc_uq_bad_adc) {
-
-               } else {
-                       DPRINTF("invalid audio version\n");
-                       goto done;
-               }
-       }
        iot = kmalloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP,
            M_WAITOK | M_ZERO);
 
@@ -2835,13 +4649,17 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev,
                        wTotalLen -= dp->bLength;
                }
 
-               au = uaudio_mixer_verify_desc(dp, 0);
+               if (sc->sc_audio_rev >= UAUDIO_VERSION_30)
+                       au = NULL;
+               else if (sc->sc_audio_rev >= UAUDIO_VERSION_20)
+                       au = uaudio20_mixer_verify_desc(dp, 0);
+               else
+                       au = uaudio_mixer_verify_desc(dp, 0);
 
                if (au) {
                        iot[au->bUnitId].u.desc = (const void *)au;
-                       if (au->bUnitId > ID_max) {
+                       if (au->bUnitId > ID_max)
                                ID_max = au->bUnitId;
-                       }
                }
        }
 
@@ -2853,7 +4671,21 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev,
         */
        i = ID_max;
        do {
-               uaudio_mixer_find_inputs_sub(iot, &i, 1, &((iot + i)->usr));
+               if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
+                       /* FALLTHROUGH */
+               } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
+                       uaudio20_mixer_find_inputs_sub(iot,
+                           &i, 1, &((iot + i)->usr));
+
+                       sc->sc_mixer_clocks.is_input = 255;
+                       sc->sc_mixer_clocks.recurse_level = 0;
+
+                       uaudio20_mixer_find_clocks_sub(iot,
+                           &i, 1, &sc->sc_mixer_clocks);
+               } else {
+                       uaudio_mixer_find_inputs_sub(iot,
+                           &i, 1, &((iot + i)->usr));
+               }
        } while (i--);
 
        /*
@@ -2862,7 +4694,8 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev,
         */
        i = ID_max;
        do {
-               uaudio_mixer_find_outputs_sub(iot, i, ID_max, &((iot + i)->usr));
+               uaudio_mixer_find_outputs_sub(iot,
+                   i, ID_max, &((iot + i)->usr));
        } while (i--);
 
        /* set "id_max" and "root" */
@@ -2873,106 +4706,59 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev,
                (iot + i)->root = iot;
        } while (i--);
 
-#ifdef USB_DEBUG
+       /*
+        * Scan the config to create a linked list of "mixer" nodes:
+        */
+
        i = ID_max;
        do {
-               uint8_t j;
+               dp = iot[i].u.desc;
 
-               if (iot[i].u.desc == NULL) {
+               if (dp == NULL)
                        continue;
-               }
-               DPRINTF("id %d:\n", i);
-
-               switch (iot[i].u.desc->bDescriptorSubtype) {
-               case UDESCSUB_AC_INPUT:
-                       DPRINTF(" - AC_INPUT type=%s\n",
-                           uaudio_mixer_get_terminal_name
-                           (UGETW(iot[i].u.it->wTerminalType)));
-                       uaudio_mixer_dump_cluster(i, iot);
-                       break;
-
-               case UDESCSUB_AC_OUTPUT:
-                       DPRINTF(" - AC_OUTPUT type=%s "
-                           "src=%d\n", uaudio_mixer_get_terminal_name
-                           (UGETW(iot[i].u.ot->wTerminalType)),
-                           iot[i].u.ot->bSourceId);
-                       break;
-
-               case UDESCSUB_AC_MIXER:
-                       DPRINTF(" - AC_MIXER src:\n");
-                       for (j = 0; j < iot[i].u.mu->bNrInPins; j++) {
-                               DPRINTF("   - %d\n", iot[i].u.mu->baSourceId[j]);
-                       }
-                       uaudio_mixer_dump_cluster(i, iot);
-                       break;
-
-               case UDESCSUB_AC_SELECTOR:
-                       DPRINTF(" - AC_SELECTOR src:\n");
-                       for (j = 0; j < iot[i].u.su->bNrInPins; j++) {
-                               DPRINTF("   - %d\n", iot[i].u.su->baSourceId[j]);
-                       }
-                       break;
 
-               case UDESCSUB_AC_FEATURE:
-                       DPRINTF(" - AC_FEATURE src=%d\n", iot[i].u.fu->bSourceId);
-                       break;
+               DPRINTFN(11, "id=%d subtype=%d\n",
+                   i, dp->bDescriptorSubtype);
 
-               case UDESCSUB_AC_PROCESSING:
-                       DPRINTF(" - AC_PROCESSING src:\n");
-                       for (j = 0; j < iot[i].u.pu->bNrInPins; j++) {
-                               DPRINTF("   - %d\n", iot[i].u.pu->baSourceId[j]);
-                       }
-                       uaudio_mixer_dump_cluster(i, iot);
-                       break;
+               if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
+                       continue;
+               } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
 
-               case UDESCSUB_AC_EXTENSION:
-                       DPRINTF(" - AC_EXTENSION src:\n");
-                       for (j = 0; j < iot[i].u.eu->bNrInPins; j++) {
-                               DPRINTF("%d ", iot[i].u.eu->baSourceId[j]);
-                       }
-                       uaudio_mixer_dump_cluster(i, iot);
-                       break;
+                       switch (dp->bDescriptorSubtype) {
+                       case UDESCSUB_AC_HEADER:
+                               DPRINTF("unexpected AC header\n");
+                               break;
 
-               default:
-                       DPRINTF("unknown audio control (subtype=%d)\n",
-                           iot[i].u.desc->bDescriptorSubtype);
-               }
+                       case UDESCSUB_AC_INPUT:
+                       case UDESCSUB_AC_OUTPUT:
+                       case UDESCSUB_AC_PROCESSING_V2:
+                       case UDESCSUB_AC_EXTENSION_V2:
+                       case UDESCSUB_AC_EFFECT:
+                       case UDESCSUB_AC_CLOCK_SRC:
+                       case UDESCSUB_AC_CLOCK_SEL:
+                       case UDESCSUB_AC_CLOCK_MUL:
+                       case UDESCSUB_AC_SAMPLE_RT:
+                               break;
 
-               DPRINTF("Inputs to this ID are:\n");
+                       case UDESCSUB_AC_MIXER:
+                               uaudio20_mixer_add_mixer(sc, iot, i);
+                               break;
 
-               j = ID_max;
-               do {
-                       if (iot[i].usr.bit_input[j / 8] & (1 << (j % 8))) {
-                               DPRINTF("  -- ID=%d\n", j);
-                       }
-               } while (j--);
+                       case UDESCSUB_AC_SELECTOR:
+                               uaudio20_mixer_add_selector(sc, iot, i);
+                               break;
 
-               DPRINTF("Outputs from this ID are:\n");
+                       case UDESCSUB_AC_FEATURE:
+                               uaudio20_mixer_add_feature(sc, iot, i);
+                               break;
 
-               j = ID_max;
-               do {
-                       if (iot[i].usr.bit_output[j / 8] & (1 << (j % 8))) {
-                               DPRINTF("  -- ID=%d\n", j);
+                       default:
+                               DPRINTF("bad AC desc subtype=0x%02x\n",
+                                   dp->bDescriptorSubtype);
+                               break;
                        }
-               } while (j--);
-
-       } while (i--);
-#endif
-
-       /*
-        * scan the config to create a linked
-        * list of "mixer" nodes:
-        */
-
-       i = ID_max;
-       do {
-               dp = iot[i].u.desc;
-
-               if (dp == NULL) {
                        continue;
                }
-               DPRINTFN(11, "id=%d subtype=%d\n",
-                   i, dp->bDescriptorSubtype);
 
                switch (dp->bDescriptorSubtype) {
                case UDESCSUB_AC_HEADER:
@@ -2980,11 +4766,7 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev,
                        break;
 
                case UDESCSUB_AC_INPUT:
-                       uaudio_mixer_add_input(sc, iot, i);
-                       break;
-
                case UDESCSUB_AC_OUTPUT:
-                       uaudio_mixer_add_output(sc, iot, i);
                        break;
 
                case UDESCSUB_AC_MIXER:
@@ -3016,42 +4798,76 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb_device *udev,
        } while (i--);
 
 done:
-       if (iot) {
-               kfree(iot, M_TEMP);
-       }
+       kfree(iot, M_TEMP);
 }
 
-static uint16_t
-uaudio_mixer_get(struct usb_device *udev, uint8_t what,
-    struct uaudio_mixer_node *mc)
+static int
+uaudio_mixer_get(struct usb_device *udev, uint16_t audio_rev,
+    uint8_t what, struct uaudio_mixer_node *mc)
 {
        struct usb_device_request req;
-       uint16_t val;
-       uint16_t len = MIX_SIZE(mc->type);
-       uint8_t data[4];
+       int val;
+       uint8_t data[2 + (2 * 3)];
        usb_error_t err;
 
-       if (mc->wValue[0] == -1) {
+       if (mc->wValue[0] == -1)
+               return (0);
+
+       if (audio_rev >= UAUDIO_VERSION_30)
                return (0);
+       else if (audio_rev >= UAUDIO_VERSION_20) {
+               if (what == GET_CUR) {
+                       req.bRequest = UA20_CS_CUR;
+                       USETW(req.wLength, 2);
+               } else {
+                       req.bRequest = UA20_CS_RANGE;
+                       USETW(req.wLength, 8);
+               }
+       } else {
+               uint16_t len = MIX_SIZE(mc->type);
+
+               req.bRequest = what;
+               USETW(req.wLength, len);
        }
+
        req.bmRequestType = UT_READ_CLASS_INTERFACE;
-       req.bRequest = what;
        USETW(req.wValue, mc->wValue[0]);
        USETW(req.wIndex, mc->wIndex);
-       USETW(req.wLength, len);
+
+       memset(data, 0, sizeof(data));
 
        err = usbd_do_request(udev, NULL, &req, data);
        if (err) {
                DPRINTF("err=%s\n", usbd_errstr(err));
                return (0);
        }
-       if (len < 1) {
-               data[0] = 0;
-       }
-       if (len < 2) {
-               data[1] = 0;
+
+       if (audio_rev >= UAUDIO_VERSION_30) {
+               val = 0;
+       } else if (audio_rev >= UAUDIO_VERSION_20) {
+               switch (what) {
+               case GET_CUR:
+                       val = (data[0] | (data[1] << 8));
+                       break;
+               case GET_MIN:
+                       val = (data[2] | (data[3] << 8));
+                       break;
+               case GET_MAX:
+                       val = (data[4] | (data[5] << 8));
+                       break;
+               case GET_RES:
+                       val = (data[6] | (data[7] << 8));
+                       break;
+               default:
+                       val = 0;
+                       break;
+               }
+       } else {
+               val = (data[0] | (data[1] << 8));
        }
-       val = (data[0] | (data[1] << 8));
+
+       if (what == GET_CUR || what == GET_MIN || what == GET_MAX)
+               val = uaudio_mixer_signext(mc->type, val);
 
        DPRINTFN(3, "val=%d\n", val);
 
@@ -3088,8 +4904,6 @@ tr_setup:
                while (mc) {
                        while (sc->sc_mixer_chan < mc->nchan) {
 
-                               len = MIX_SIZE(mc->type);
-
                                chan = sc->sc_mixer_chan;
 
                                sc->sc_mixer_chan++;
@@ -3102,17 +4916,24 @@ tr_setup:
                                if (update) {
 
                                        req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
-                                       req.bRequest = SET_CUR;
                                        USETW(req.wValue, mc->wValue[chan]);
                                        USETW(req.wIndex, mc->wIndex);
-                                       USETW(req.wLength, len);
 
-                                       if (len > 0) {
-                                               buf[0] = (mc->wData[chan] & 0xFF);
-                                       }
-                                       if (len > 1) {
-                                               buf[1] = (mc->wData[chan] >> 8) & 0xFF;
+                                       if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
+                                               return;
+                                       } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
+                                               len = 2;
+                                               req.bRequest = UA20_CS_CUR;
+                                               USETW(req.wLength, len);
+                                       } else {
+                                               len = MIX_SIZE(mc->type);
+                                               req.bRequest = SET_CUR;
+                                               USETW(req.wLength, len);
                                        }
+
+                                       buf[0] = (mc->wData[chan] & 0xFF);
+                                       buf[1] = (mc->wData[chan] >> 8) & 0xFF;
+
                                        pc = usbd_xfer_get_frame(xfer, 0);
                                        usbd_copy_in(pc, 0, &req, sizeof(req));
                                        pc = usbd_xfer_get_frame(xfer, 1);
@@ -3166,6 +4987,29 @@ uaudio_set_speed(struct usb_device *udev, uint8_t endpt, uint32_t speed)
        return (usbd_do_request(udev, NULL, &req, data));
 }
 
+static usb_error_t
+uaudio20_set_speed(struct usb_device *udev, uint8_t iface_no,
+    uint8_t clockid, uint32_t speed)
+{
+       struct usb_device_request req;
+       uint8_t data[4];
+
+       DPRINTFN(6, "ifaceno=%d clockid=%d speed=%u\n",
+           iface_no, clockid, speed);
+
+       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+       req.bRequest = UA20_CS_CUR;
+       USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
+       USETW2(req.wIndex, clockid, iface_no);
+       USETW(req.wLength, 4);
+       data[0] = speed;
+       data[1] = speed >> 8;
+       data[2] = speed >> 16;
+       data[3] = speed >> 24;
+
+       return (usbd_do_request(udev, NULL, &req, data));
+}
+
 static int
 uaudio_mixer_signext(uint8_t type, int val)
 {
@@ -3257,9 +5101,12 @@ uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m)
 {
        DPRINTF("\n");
 
+       sc->sc_mixer_lock = mixer_get_lock(m);
+       sc->sc_mixer_dev = m;
+
        if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index,
            sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc,
-           &sc->sc_lock)) {
+           sc->sc_mixer_lock)) {
                DPRINTFN(0, "could not allocate USB "
                    "transfer for audio mixer!\n");
                return (ENOMEM);
@@ -3280,6 +5127,8 @@ uaudio_mixer_uninit_sub(struct uaudio_softc *sc)
 
        usbd_transfer_unsetup(sc->sc_mixer_xfer, 1);
 
+       sc->sc_mixer_lock = NULL;
+
        return (0);
 }
 
@@ -3290,13 +5139,13 @@ uaudio_mixer_set(struct uaudio_softc *sc, unsigned type,
        struct uaudio_mixer_node *mc;
        int chan;
 
-       for (mc = sc->sc_mixer_root; mc;
-           mc = mc->next) {
+       for (mc = sc->sc_mixer_root; mc != NULL; mc = mc->next) {
 
                if (mc->ctl == type) {
                        for (chan = 0; chan < mc->nchan; chan++) {
                                uaudio_mixer_ctl_set(sc, mc, chan,
-                                   (int)((chan == 0 ? left : right) * 255) / 100);
+                                   (int)((chan == 0 ? left : right) *
+                                   255) / 100);
                        }
                }
        }
@@ -3386,8 +5235,7 @@ umidi_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
                         */
                        sub = &chan->sub[cn];
 
-                       if ((cmd_len != 0) &&
-                           (cn < chan->max_cable) &&
+                       if ((cmd_len != 0) && (cn < chan->max_emb_jack) &&
                            (sub->read_open != 0)) {
 
                                /* Send data to the application */
@@ -3623,7 +5471,7 @@ tr_setup:
                        }
 
                        chan->curr_cable++;
-                       if (chan->curr_cable >= chan->max_cable)
+                       if (chan->curr_cable >= chan->max_emb_jack)
                                chan->curr_cable = 0;
 
                        if (chan->curr_cable == start_cable) {
@@ -3660,7 +5508,7 @@ umidi_sub_by_fifo(struct usb_fifo *fifo)
        struct umidi_sub_chan *sub;
        uint32_t n;
 
-       for (n = 0; n < UMIDI_CABLES_MAX; n++) {
+       for (n = 0; n < UMIDI_EMB_JACK_MAX; n++) {
                sub = &chan->sub[n];
                if ((sub->fifo.fp[USB_FIFO_RX] == fifo) ||
                    (sub->fifo.fp[USB_FIFO_TX] == fifo)) {
@@ -3746,7 +5594,6 @@ umidi_open(struct usb_fifo *fifo, int fflags)
                }
                /* clear stall first */
                lockmgr(&chan->lock, LK_EXCLUSIVE);
-               usbd_xfer_set_stall(chan->xfer[UMIDI_TX_TRANSFER]);
                chan->write_open_refcount++;
                sub->write_open = 1;
 
@@ -3825,12 +5672,31 @@ umidi_probe(device_t dev)
                DPRINTF("error=%s\n", usbd_errstr(error));
                goto detach;
        }
-       if ((chan->max_cable > UMIDI_CABLES_MAX) ||
-           (chan->max_cable == 0)) {
-               chan->max_cable = UMIDI_CABLES_MAX;
+
+       /*
+        * Some USB MIDI device makers couldn't resist using
+        * wMaxPacketSize = 4 for RX and TX BULK endpoints, although
+        * that size is an unsupported value for FULL speed BULK
+        * endpoints. The same applies to some HIGH speed MIDI devices
+        * which are using a wMaxPacketSize different from 512 bytes.
+        *
+        * Refer to section 5.8.3 in USB 2.0 PDF: Cite: "All Host
+        * Controllers are required to have support for 8-, 16-, 32-,
+        * and 64-byte maximum packet sizes for full-speed bulk
+        * endpoints and 512 bytes for high-speed bulk endpoints."
+        */
+       if (usbd_xfer_maxp_was_clamped(chan->xfer[UMIDI_TX_TRANSFER]))
+               chan->single_command = 1;
+
+       if (chan->single_command != 0)
+               device_printf(dev, "Single command MIDI quirk enabled\n");
+
+       if ((chan->max_emb_jack == 0) ||
+           (chan->max_emb_jack > UMIDI_EMB_JACK_MAX)) {
+               chan->max_emb_jack = UMIDI_EMB_JACK_MAX;
        }
 
-       for (n = 0; n < chan->max_cable; n++) {
+       for (n = 0; n < chan->max_emb_jack; n++) {
 
                sub = &chan->sub[n];
 
@@ -3845,9 +5711,6 @@ umidi_probe(device_t dev)
 
        lockmgr(&chan->lock, LK_EXCLUSIVE);
 
-       /* clear stall first */
-       usbd_xfer_set_stall(chan->xfer[UMIDI_RX_TRANSFER]);
-
        /*
         * NOTE: At least one device will not work properly unless the
         * BULK IN pipe is open all the time. This might have to do
@@ -3871,9 +5734,8 @@ umidi_detach(device_t dev)
        struct umidi_chan *chan = &sc->sc_midi_chan;
        uint32_t n;
 
-       for (n = 0; n < UMIDI_CABLES_MAX; n++) {
+       for (n = 0; n < UMIDI_EMB_JACK_MAX; n++)
                usb_fifo_detach(&chan->sub[n].fifo);
-       }
 
        lockmgr(&chan->lock, LK_EXCLUSIVE);
 
@@ -3888,7 +5750,171 @@ umidi_detach(device_t dev)
        return (0);
 }
 
-DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, NULL);
+static void
+uaudio_hid_rx_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+       struct uaudio_softc *sc = usbd_xfer_softc(xfer);
+       const uint8_t *buffer = usbd_xfer_get_frame_buffer(xfer, 0);
+       struct snd_mixer *m;
+       uint8_t id;
+       int actlen;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               DPRINTF("actlen=%d\n", actlen);
+
+               if (actlen != 0 &&
+                   (sc->sc_hid.flags & UAUDIO_HID_HAS_ID)) {
+                       id = *buffer;
+                       buffer++;
+                       actlen--;
+               } else {
+                       id = 0;
+               }
+
+               m = sc->sc_mixer_dev;
+
+               if ((sc->sc_hid.flags & UAUDIO_HID_HAS_MUTE) &&
+                   (sc->sc_hid.mute_id == id) &&
+                   hid_get_data(buffer, actlen,
+                   &sc->sc_hid.mute_loc)) {
+
+                       DPRINTF("Mute toggle\n");
+
+                       mixer_hwvol_mute_locked(m);
+               }
+
+               if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_UP) &&
+                   (sc->sc_hid.volume_up_id == id) &&
+                   hid_get_data(buffer, actlen,
+                   &sc->sc_hid.volume_up_loc)) {
+
+                       DPRINTF("Volume Up\n");
+
+                       mixer_hwvol_step_locked(m, 1, 1);
+               }
+
+               if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_DOWN) &&
+                   (sc->sc_hid.volume_down_id == id) &&
+                   hid_get_data(buffer, actlen,
+                   &sc->sc_hid.volume_down_loc)) {
+
+                       DPRINTF("Volume Down\n");
+
+                       mixer_hwvol_step_locked(m, -1, -1);
+               }
+
+       case USB_ST_SETUP:
+tr_setup:
+               /* check if we can put more data into the FIFO */
+               usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+               usbd_transfer_submit(xfer);
+               break;
+
+       default:                        /* Error */
+
+               DPRINTF("error=%s\n", usbd_errstr(error));
+
+               if (error != USB_ERR_CANCELLED) {
+                       /* try to clear stall first */
+                       usbd_xfer_set_stall(xfer);
+                       goto tr_setup;
+               }
+               break;
+       }
+}
+
+static int
+uaudio_hid_probe(struct uaudio_softc *sc,
+    struct usb_attach_arg *uaa)
+{
+       void *d_ptr;
+       uint32_t flags;
+       uint16_t d_len;
+       uint8_t id;
+       int error;
+
+       if (!(sc->sc_hid.flags & UAUDIO_HID_VALID))
+               return (-1);
+
+       if (sc->sc_mixer_lock == NULL)
+               return (-1);
+
+       /* Get HID descriptor */
+       error = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
+           &d_len, M_TEMP, sc->sc_hid.iface_index);
+
+       if (error) {
+               DPRINTF("error reading report description\n");
+               return (-1);
+       }
+
+       /* check if there is an ID byte */
+       hid_report_size(d_ptr, d_len, hid_input, &id);
+
+       if (id != 0)
+               sc->sc_hid.flags |= UAUDIO_HID_HAS_ID;
+
+       if (hid_locate(d_ptr, d_len,
+           HID_USAGE2(HUP_CONSUMER, 0xE9 /* Volume Increment */),
+           hid_input, 0, &sc->sc_hid.volume_up_loc, &flags,
+           &sc->sc_hid.volume_up_id)) {
+               if (flags & HIO_VARIABLE)
+                       sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_UP;
+               DPRINTFN(1, "Found Volume Up key\n");
+       }
+
+       if (hid_locate(d_ptr, d_len,
+           HID_USAGE2(HUP_CONSUMER, 0xEA /* Volume Decrement */),
+           hid_input, 0, &sc->sc_hid.volume_down_loc, &flags,
+           &sc->sc_hid.volume_down_id)) {
+               if (flags & HIO_VARIABLE)
+                       sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_DOWN;
+               DPRINTFN(1, "Found Volume Down key\n");
+       }
+
+       if (hid_locate(d_ptr, d_len,
+           HID_USAGE2(HUP_CONSUMER, 0xE2 /* Mute */),
+           hid_input, 0, &sc->sc_hid.mute_loc, &flags,
+           &sc->sc_hid.mute_id)) {
+               if (flags & HIO_VARIABLE)
+                       sc->sc_hid.flags |= UAUDIO_HID_HAS_MUTE;
+               DPRINTFN(1, "Found Mute key\n");
+       }
+
+       kfree(d_ptr, M_TEMP);
+
+       if (!(sc->sc_hid.flags & (UAUDIO_HID_HAS_VOLUME_UP |
+           UAUDIO_HID_HAS_VOLUME_DOWN |
+           UAUDIO_HID_HAS_MUTE))) {
+               DPRINTFN(1, "Did not find any volume related keys\n");
+               return (-1);
+       }
+
+       /* prevent the uhid driver from attaching */
+       usbd_set_parent_iface(uaa->device, sc->sc_hid.iface_index,
+           sc->sc_mixer_iface_index);
+
+       /* allocate USB transfers */
+       error = usbd_transfer_setup(uaa->device, &sc->sc_hid.iface_index,
+           sc->sc_hid.xfer, uaudio_hid_config, UAUDIO_HID_N_TRANSFER,
+           sc, sc->sc_mixer_lock);
+       if (error) {
+               DPRINTF("error=%s\n", usbd_errstr(error));
+               return (-1);
+       }
+       return (0);
+}
+
+static void
+uaudio_hid_detach(struct uaudio_softc *sc)
+{
+       usbd_transfer_unsetup(sc->sc_hid.xfer, UAUDIO_HID_N_TRANSFER);
+}
+
+DRIVER_MODULE_ORDERED(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, 0, SI_ORDER_ANY);
 MODULE_DEPEND(uaudio, usb, 1, 1, 1);
 MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
 MODULE_VERSION(uaudio, 1);
index 10c10de..44479df 100644 (file)
@@ -1,4 +1,4 @@
-/* $FreeBSD$ */
+/* $FreeBSD: head/sys/dev/sound/usb/uaudio.h 228484 2011-12-14 00:48:20Z hselasky $ */
 
 /*-
  * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
index b037240..f632404 100644 (file)
@@ -1,4 +1,4 @@
-/* $FreeBSD$ */
+/* $FreeBSD: head/sys/dev/sound/usb/uaudio_pcm.c 246128 2013-01-30 18:01:20Z sbz $ */
 
 /*-
  * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
@@ -27,9 +27,7 @@
  */
 
 
-#ifdef HAVE_KERNEL_OPTION_HEADERS
 #include "opt_snd.h"
-#endif
 
 #include <dev/sound/pcm/sound.h>
 #include <dev/sound/chip.h>
@@ -72,13 +70,11 @@ ua_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
        return (uaudio_chan_set_param_blocksize(data, blocksize));
 }
 
-#if 0
 static int
 ua_chan_setfragments(kobj_t obj, void *data, uint32_t blocksize, uint32_t blockcount)
 {
        return (uaudio_chan_set_param_fragments(data, blocksize, blockcount));
 }
-#endif
 
 static int
 ua_chan_trigger(kobj_t obj, void *data, int go)
@@ -105,13 +101,11 @@ ua_chan_getcaps(kobj_t obj, void *data)
        return (uaudio_chan_getcaps(data));
 }
 
-#if 0
 static struct pcmchan_matrix *
 ua_chan_getmatrix(kobj_t obj, void *data, uint32_t format)
 {
        return (uaudio_chan_getmatrix(data, format));
 }
-#endif
 
 static kobj_method_t ua_chan_methods[] = {
        KOBJMETHOD(channel_init, ua_chan_init),
@@ -119,15 +113,11 @@ static kobj_method_t ua_chan_methods[] = {
        KOBJMETHOD(channel_setformat, ua_chan_setformat),
        KOBJMETHOD(channel_setspeed, ua_chan_setspeed),
        KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize),
-#if 0
        KOBJMETHOD(channel_setfragments, ua_chan_setfragments),
-#endif
        KOBJMETHOD(channel_trigger, ua_chan_trigger),
        KOBJMETHOD(channel_getptr, ua_chan_getptr),
        KOBJMETHOD(channel_getcaps, ua_chan_getcaps),
-#if 0
        KOBJMETHOD(channel_getmatrix, ua_chan_getmatrix),
-#endif
        KOBJMETHOD_END
 };
 
@@ -146,7 +136,7 @@ ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right)
        struct lock *lock = mixer_get_lock(m);
        uint8_t do_unlock;
 
-       if (lockstatus(lock, curthread)) {
+       if (lockowned(lock)) {
                do_unlock = 0;
        } else {
                do_unlock = 1;
@@ -166,7 +156,7 @@ ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
        int retval;
        uint8_t do_unlock;
 
-       if (lockstatus(lock, curthread)) {
+       if (lockowned(lock)) {
                do_unlock = 0;
        } else {
                do_unlock = 1;
@@ -244,7 +234,7 @@ static driver_t ua_pcm_driver = {
        PCM_SOFTC_SIZE,
 };
 
-DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, NULL, NULL);
+DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, 0, 0);
 MODULE_DEPEND(ua_pcm, uaudio, 1, 1, 1);
 MODULE_DEPEND(ua_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
 MODULE_VERSION(ua_pcm, 1);
index eb2a9a4..362604c 100644 (file)
@@ -1,5 +1,5 @@
 /*     $NetBSD: uaudioreg.h,v 1.12 2004/11/05 19:08:29 kent Exp $      */
-/* $FreeBSD$ */
+/* $FreeBSD: head/sys/dev/sound/usb/uaudioreg.h 272254 2014-09-28 12:55:13Z hselasky $ */
 
 /*-
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#define        UAUDIO_VERSION          0x100
+#ifndef _UAUDIOREG_H_
+#define        _UAUDIOREG_H_
 
+#define        UAUDIO_VERSION          0x0100
+#define        UAUDIO_VERSION_20       0x0200
+#define        UAUDIO_VERSION_30       0x0300
+
+#define        UAUDIO_PROTOCOL_20      0x20
+
+#define        UDESC_CS_UNDEFINED      0x20
+#define        UDESC_CS_DEVICE         0x21
 #define        UDESC_CS_CONFIG         0x22
 #define        UDESC_CS_STRING         0x23
 #define        UDESC_CS_INTERFACE      0x24
 #define        UDESCSUB_AC_FEATURE     6
 #define        UDESCSUB_AC_PROCESSING  7
 #define        UDESCSUB_AC_EXTENSION   8
+/* ==== USB audio v2.0 ==== */
+#define        UDESCSUB_AC_EFFECT      7
+#define        UDESCSUB_AC_PROCESSING_V2 8
+#define        UDESCSUB_AC_EXTENSION_V2 9
+#define        UDESCSUB_AC_CLOCK_SRC   10
+#define        UDESCSUB_AC_CLOCK_SEL   11
+#define        UDESCSUB_AC_CLOCK_MUL   12
+#define        UDESCSUB_AC_SAMPLE_RT   13
 
 /* These macros check if the endpoint descriptor has additional fields */
 #define        UEP_MINSIZE     7
@@ -102,6 +119,13 @@ struct usb_audio_streaming_endpoint_descriptor {
        uWord   wLockDelay;
 } __packed;
 
+struct usb_midi_streaming_endpoint_descriptor {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bNumEmbMIDIJack;
+} __packed;
+
 struct usb_audio_streaming_type1_descriptor {
        uByte   bLength;
        uByte   bDescriptorType;
@@ -113,9 +137,9 @@ struct usb_audio_streaming_type1_descriptor {
        uByte   bSamFreqType;
 #define        UA_SAMP_CONTNUOUS 0
        uByte   tSamFreq[0];
-#define        UA_GETSAMP(p, n) (((p)->tSamFreq[((n)*3)+0]) |      \
+#define        UA_GETSAMP(p, n) ((uint32_t)((((p)->tSamFreq[((n)*3)+0]) | \
                          ((p)->tSamFreq[((n)*3)+1] << 8) | \
-                         ((p)->tSamFreq[((n)*3)+2] << 16))
+                         ((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;
@@ -250,6 +274,7 @@ struct usb_audio_extension_unit_0 {
        uByte   baSourceId[0];          /* [bNrInPins] */
        /* struct usb_audio_extension_unit_1 */
 } __packed;
+
 struct usb_audio_extension_unit_1 {
        uByte   bNrChannels;
        uWord   wChannelConfig;
@@ -348,14 +373,25 @@ struct usb_audio_extension_unit_1 {
 #define        DELAY_CONTROL   0x08
 #define        BASS_BOOST_CONTROL 0x09
 #define        LOUDNESS_CONTROL 0x0a
+/* ==== USB audio v2.0 ==== */
+#define        INPUT_GAIN_CONTROL 0x0b
+#define        INPUT_GAIN_PAD_CONTROL 0x0c
+#define        PHASE_INVERTER_CONTROL 0x0d
+#define        UNDERFLOW_CONTROL 0x0e
+#define        OVERFLOW_CONTROL 0x0f
+#define        LATENCY_CONTROL 0x10
 
 #define        FU_MASK(u) (1 << ((u)-1))
 
 #define        MASTER_CHAN     0
 
+#define        MS_GENERAL      1
 #define        AS_GENERAL      1
 #define        FORMAT_TYPE     2
 #define        FORMAT_SPECIFIC 3
+/* ==== USB audio v2.0 ==== */
+#define        FORMAT_ENCODER  3
+#define        FORMAT_DECODER  4
 
 #define        UA_FMT_PCM      1
 #define        UA_FMT_PCM8     2
@@ -402,3 +438,370 @@ struct usb_audio_extension_unit_1 {
 #define        DR_THRESHOLD_CONTROL                    4
 #define        DR_ATTACK_TIME_CONTROL                  5
 #define        DR_RELEASE_TIME_CONTROL         6
+
+/*------------------------------------------------------------------------*
+ * USB audio v2.0 definitions
+ *------------------------------------------------------------------------*/
+
+struct usb_audio20_streaming_interface_descriptor {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bTerminalLink;
+       uByte   bmControls;
+       uByte   bFormatType;
+       uDWord  bmFormats;
+       uByte   bNrChannels;
+       uDWord  bmChannelConfig;
+       uByte   iChannelNames;
+} __packed;
+
+struct usb_audio20_encoder_descriptor {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bEncoderID;
+       uByte   bEncoder;
+       uDWord  bmControls;
+       uByte   iParam1;
+       uByte   iParam2;
+       uByte   iParam3;
+       uByte   iParam4;
+       uByte   iParam5;
+       uByte   iParam6;
+       uByte   iParam7;
+       uByte   iParam8;
+       uByte   iEncoder;
+} __packed;
+
+struct usb_audio20_streaming_endpoint_descriptor {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bmAttributes;
+#define        UA20_MPS_ONLY           0x80
+       uByte   bmControls;
+#define        UA20_PITCH_CONTROL_MASK 0x03
+#define        UA20_DATA_OVERRUN_MASK  0x0C
+#define        UA20_DATA_UNDERRUN_MASK 0x30
+       uByte   bLockDelayUnits;
+       uWord   wLockDelay;
+} __packed;
+
+struct usb_audio20_feedback_endpoint_descriptor {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bEndpointAddress;
+       uByte   bmAttributes;
+       uWord   wMaxPacketSize;
+       uByte   bInterval;
+} __packed;
+
+#define        UA20_CS_CUR     0x01
+#define        UA20_CS_RANGE   0x02
+#define        UA20_CS_MEM     0x03
+
+struct usb_audio20_cur1_parameter {
+       uByte   bCur;
+} __packed;
+
+struct usb_audio20_ctl1_range_sub {
+       uByte   bMIN;
+       uByte   bMAX;
+       uByte   bRES;
+} __packed;
+
+struct usb_audio20_ctl1_range {
+       uWord   wNumSubRanges;
+       struct usb_audio20_ctl1_range_sub sub[1];
+} __packed;
+
+struct usb_audio20_cur2_parameter {
+       uWord   wCur;
+} __packed;
+
+struct usb_audio20_ctl2_range_sub {
+       uWord   wMIN;
+       uWord   wMAX;
+       uWord   wRES;
+} __packed;
+
+struct usb_audio20_ctl2_range {
+       uWord   wNumSubRanges;
+       struct usb_audio20_ctl2_range_sub sub[1];
+} __packed;
+
+struct usb_audio20_cur4_parameter {
+       uDWord  dCur;
+} __packed;
+
+struct usb_audio20_ctl4_range_sub {
+       uDWord  dMIN;
+       uDWord  dMAX;
+       uDWord  dRES;
+} __packed;
+
+struct usb_audio20_ctl4_range {
+       uWord   wNumSubRanges;
+       struct usb_audio20_ctl4_range_sub sub[1];
+} __packed;
+
+struct usb_audio20_cc_cluster_descriptor {
+       uByte   bNrChannels;
+       uDWord  bmChannelConfig;
+       uByte   iChannelNames;
+} __packed;
+
+struct usb_audio20_streaming_type1_descriptor {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bFormatType;
+       uByte   bSubslotSize;
+       uByte   bBitResolution;
+} __packed;
+
+#define        UA20_EERROR_NONE        0
+#define        UA20_EERROR_MEMORY      1
+#define        UA20_EERROR_BANDWIDTH   2
+#define        UA20_EERROR_CPU         3
+#define        UA20_EERROR_FORMATFR_ER 4
+#define        UA20_EERROR_FORMATFR_SM 5
+#define        UA20_EERROR_FORMATFR_LG 6
+#define        UA20_EERROR_DATAFORMAT  7
+#define        UA20_EERROR_NUMCHANNELS 8
+#define        UA20_EERROR_SAMPLERATE  9
+#define        UA20_EERROR_BITRATE     10
+#define        UA20_EERROR_PARAM       11
+#define        UA20_EERROR_NREADY      12
+#define        UA20_EERROR_BUSY        13
+
+struct usb_audio20_cc_alt_setting {
+       uByte   bControlSize;
+       uDWord  bmValidAltSettings;
+} __packed;
+
+struct usb_audio20_interrupt_message {
+       uByte   bInfo;
+       uByte   bAttribute;
+       uDWord  wValue;
+       uDWord  wIndex;
+} __packed;
+
+/* UDESCSUB_AC_CLOCK_SRC */
+struct usb_audio20_clock_source_unit {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bClockId;
+       uByte   bmAttributes;
+       uByte   bmControls;
+       uByte   bAssocTerminal;
+       uByte   iClockSource;
+} __packed;
+
+/* UDESCSUB_AC_CLOCK_SEL */
+struct usb_audio20_clock_selector_unit_0 {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bClockId;
+       uByte   bNrInPins;
+       uByte   baCSourceId[0];
+} __packed;
+
+struct usb_audio20_clock_selector_unit_1 {
+       uByte   bmControls;
+       uByte   iClockSelector;
+} __packed;
+
+/* UDESCSUB_AC_CLOCK_MUL */
+struct usb_audio20_clock_multiplier_unit {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bClockId;
+       uByte   bCSourceId;
+       uByte   bmControls;
+       uByte   iClockMultiplier;
+} __packed;
+
+/* UDESCSUB_AC_INPUT */
+struct usb_audio20_input_terminal {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bTerminalId;
+       uWord   wTerminalType;
+       uByte   bAssocTerminal;
+       uByte   bCSourceId;
+       uByte   bNrChannels;
+       uDWord  bmChannelConfig;
+       uByte   iCHannelNames;
+       uWord   bmControls;
+       uByte   iTerminal;
+} __packed;
+
+/* UDESCSUB_AC_OUTPUT */
+struct usb_audio20_output_terminal {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bTerminalId;
+       uWord   wTerminalType;
+       uByte   bAssocTerminal;
+       uByte   bSourceId;
+       uByte   bCSourceId;
+       uWord   bmControls;
+       uByte   iTerminal;
+} __packed;
+
+/* UDESCSUB_AC_MIXER */
+struct usb_audio20_mixer_unit_0 {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bUnitId;
+       uByte   bNrInPins;
+       uByte   baSourceId[0];
+       /* struct usb_audio20_mixer_unit_1 */
+} __packed;
+
+struct usb_audio20_mixer_unit_1 {
+       uByte   bNrChannels;
+       uDWord  bmChannelConfig;
+       uByte   iChannelNames;
+       uByte   bmControls[0];
+       /* uByte        iMixer; */
+} __packed;
+
+/* UDESCSUB_AC_SELECTOR */
+struct usb_audio20_selector_unit {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bUnitId;
+       uByte   bNrInPins;
+       uByte   baSourceId[0];
+       /* uByte        iSelector; */
+} __packed;
+
+/* UDESCSUB_AC_FEATURE */
+struct usb_audio20_feature_unit {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bUnitId;
+       uByte   bSourceId;
+       uDWord  bmaControls[0];
+       /* uByte        iFeature; */
+} __packed;
+
+/* UDESCSUB_AC_SAMPLE_RT */
+struct usb_audio20_sample_rate_unit {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bUnitId;
+       uByte   bSourceId;
+       uByte   bSourceInId;
+       uByte   bSourceOutId;
+       uByte   iSrc;
+} __packed;
+
+/* UDESCSUB_AC_EFFECT */
+struct usb_audio20_effect_unit {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bUnitId;
+       uWord   wEffectType;
+       uByte   bSourceId;
+       uDWord  bmaControls[0];
+       uByte   iEffects;
+} __packed;
+
+/* UDESCSUB_AC_PROCESSING_V2 */
+struct usb_audio20_processing_unit_0 {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bUnitId;
+       uWord   wProcessType;
+       uByte   bNrInPins;
+       uByte   baSourceId[0];          /* [bNrInPins] */
+} __packed;
+
+struct usb_audio20_processing_unit_1 {
+       uByte   bNrChannels;
+       uDWord  bmChannelConfig;
+       uByte   iChannelNames;
+       uWord   bmControls;
+       uByte   iProcessing;
+} __packed;
+
+/* UDESCSUB_AC_EXTENSION_V2 */
+struct usb_audio20_extension_unit_0 {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+       uByte   bUnitId;
+       uWord   wExtensionCode;
+       uByte   bNrInPins;
+       uByte   baSourceId[0];
+} __packed;
+
+struct usb_audio20_extension_unit_1 {
+       uByte   bNrChannels;
+       uDWord  bmChannelConfig;
+       uByte   iChannelNames;
+       uByte   bmControls;
+       uByte   iExtension;
+} __packed;
+
+struct usb_audio20_cluster {
+       uByte   bNrChannels;
+       uDWord  bmChannelConfig;
+       uByte   iChannelNames;
+} __packed;
+
+#define        UA20_TF_UNDEFINED               0x00
+#define        UA20_TF_DESKTOP_SPEAKER         0x01
+#define        UA20_TF_HOME_THEATER            0x02
+#define        UA20_TF_MICROPHONE              0x03
+#define        UA20_TF_HEADSET                 0x04
+#define        UA20_TF_TELEPHONE               0x05
+#define        UA20_TF_CONVERTER               0x06
+#define        UA20_TF_SOUND_RECORDER          0x07
+#define        UA20_TF_IO_BOX                  0x08
+#define        UA20_TF_MUSICAL_INSTRUMENT      0x09
+#define        UA20_TF_PRO_AUDIO               0x0A
+#define        UA20_TF_AV                      0x0B
+#define        UA20_TF_CONTROL_PANEL           0x0C
+#define        UA20_TF_OTHER                   0xFF
+
+#define        UA20_CS_SAM_FREQ_CONTROL        0x01
+#define        UA20_CS_CLOCK_VALID_CONTROL     0x02
+
+#define        UA20_TE_COPY_PROTECT_CONTROL    0x01
+#define        UA20_TE_CONNECTOR_CONTROL       0x02
+#define        UA20_TE_OVERLOAD_CONTROL        0x03
+#define        UA20_TE_CLUSTER_CONTROL         0x04
+#define        UA20_TE_UNDERFLOW_CONTROL       0x05
+#define        UA20_TE_OVERFLOW_CONTROL        0x06
+#define        UA20_TE_LATENCY_CONTROL         0x07
+
+#define        UA20_MU_MIXER_CONTROL           0x01
+#define        UA20_MU_CLUSTER_CONTROL         0x02
+#define        UA20_MU_UNDERFLOW_CONTROL       0x03
+#define        UA20_MU_OVERFLOW_CONTROL        0x04
+#define        UA20_MU_LATENCY_CONTROL         0x05
+
+#define        UA20_FMT_PCM    (1U << 0)
+#define        UA20_FMT_PCM8   (1U << 1)
+#define        UA20_FMT_FLOAT  (1U << 2)
+#define        UA20_FMT_ALAW   (1U << 3)
+#define        UA20_FMT_MULAW  (1U << 4)
+#define        UA20_FMT_RAW    (1U << 31)
+
+#endif                                 /* _UAUDIOREG_H_ */