Author: hselasky
Date: Fri Nov  9 07:29:11 2012
New Revision: 242822
URL: http://svnweb.freebsd.org/changeset/base/242822

Log:
  MFC r242127, r240078, r240609, r241988, r242129, r242223, r242438,
  r242453, r242455 and r242458:
  
  Add full support for Fast Track Ultra 8R from M-audio.
  
  Implement support for USB Audio v2.0.
  
  Remove some redundant USB audio v1.0 debug data. Use lsusb instead.
  
  Implement support for the so-called USB feedback endpoint for USB
  audio devices.
  
  Export all mixer nodes into dev.pcm.X.mixer.Y sysctl nodes.
  
  PR:   usb/171254

Modified:
  stable/9/sys/dev/sound/usb/uaudio.c
  stable/9/sys/dev/sound/usb/uaudioreg.h
  stable/9/sys/dev/usb/quirk/usb_quirk.c
  stable/9/sys/dev/usb/usbdevs
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/dev/   (props changed)

Modified: stable/9/sys/dev/sound/usb/uaudio.c
==============================================================================
--- stable/9/sys/dev/sound/usb/uaudio.c Fri Nov  9 07:12:31 2012        
(r242821)
+++ stable/9/sys/dev/sound/usb/uaudio.c Fri Nov  9 07:29:11 2012        
(r242822)
@@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_request.h>
 
 #define        USB_DEBUG_VAR uaudio_debug
 #include <dev/usb/usb_debug.h>
@@ -115,22 +116,39 @@ SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, def
 #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)
 
+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];
@@ -149,6 +167,9 @@ 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;
 };
@@ -161,12 +182,10 @@ struct uaudio_chan {
        struct mtx *pcm_mtx;            /* 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;
+       struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1];
+       union uaudio_asf1d p_asf1d;
+       union uaudio_sed p_sed;
        const usb_endpoint_descriptor_audio_t *p_ed1;
-       const usb_endpoint_descriptor_audio_t *p_ed2;
        const struct uaudio_format *p_fmt;
 
        uint8_t *buf;                   /* pointer to buffer */
@@ -192,6 +211,13 @@ struct uaudio_chan {
        uint8_t valid;
        uint8_t iface_index;
        uint8_t iface_alt_index;
+       uint8_t channels;
+
+       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 */
@@ -242,13 +268,23 @@ struct umidi_chan {
        uint8_t single_command;
 };
 
+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;
+};
+
 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_search_result sc_mixer_clocks;
 
+       struct mtx *sc_mixer_lock;
        struct usb_device *sc_udev;
        struct usb_xfer *sc_mixer_xfer[1];
        struct uaudio_mixer_node *sc_mixer_root;
@@ -273,24 +309,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;
@@ -303,7 +343,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"},
@@ -321,6 +361,24 @@ static const struct uaudio_format uaudio
        {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
@@ -341,23 +399,23 @@ 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 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);
+/* ==== 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_mixer_add_mixer(struct uaudio_softc *,
                    const struct uaudio_terminal_node *, int);
 static void    uaudio_mixer_add_selector(struct uaudio_softc *,
@@ -378,25 +436,56 @@ static uint16_t   uaudio_mixer_determine_c
                    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 *);
@@ -413,13 +502,10 @@ static int        umidi_detach(device_t dev);
 #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,
@@ -439,10 +525,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,
@@ -462,6 +558,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
@@ -555,7 +661,8 @@ uaudio_probe(device_t dev)
        /* lookup non-standard device */
 
        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);
        }
 
@@ -614,30 +721,43 @@ 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",
+               device_printf(dev, "Play: %d Hz, %d ch, %s format, "
+                   "2x8ms buffer.\n",
                    sc->sc_play_chan.sample_rate,
-                   sc->sc_play_chan.p_asf1d->bNrChannels,
+                   sc->sc_play_chan.channels,
                    sc->sc_play_chan.p_fmt->description);
        } else {
                device_printf(dev, "No playback.\n");
        }
 
        if (sc->sc_rec_chan.valid) {
-               device_printf(dev, "Record: %d Hz, %d ch, %s format.\n",
+               device_printf(dev, "Record: %d Hz, %d ch, %s format, "
+                   "2x8ms buffer.\n",
                    sc->sc_rec_chan.sample_rate,
-                   sc->sc_rec_chan.p_asf1d->bNrChannels,
+                   sc->sc_rec_chan.channels,
                    sc->sc_rec_chan.p_fmt->description);
        } else {
                device_printf(dev, "No recording.\n");
@@ -679,6 +799,10 @@ uaudio_attach(device_t dev)
                DPRINTF("child attach failed\n");
                goto detach;
        }
+
+       /* reload all mixer settings */
+       uaudio_mixer_reload_all(sc);
+
        return (0);                     /* success */
 
 detach:
@@ -714,9 +838,8 @@ uaudio_attach_sub(device_t dev, kobj_cla
                 */
                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;
 
        snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
@@ -738,6 +861,8 @@ uaudio_attach_sub(device_t dev, kobj_cla
        }
        pcm_setstatus(dev, status);
 
+       uaudio_mixer_register_sysctl(sc, dev);
+
        return (0);                     /* success */
 
 detach:
@@ -779,9 +904,9 @@ uaudio_detach(device_t dev)
         * any.
         */
        if (sc->sc_play_chan.valid)
-               usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS);
+               usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 
1);
        if (sc->sc_rec_chan.valid)
-               usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS);
+               usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 
1);
 
        if (bus_generic_detach(dev) != 0) {
                DPRINTF("detach failed!\n");
@@ -791,6 +916,10 @@ uaudio_detach(device_t dev)
 
        umidi_detach(dev);
 
+       /* free mixer data */
+
+       uaudio_mixer_ctl_free(sc);
+
        return (0);
 }
 
@@ -857,28 +986,88 @@ uaudio_record_fix_fs(usb_endpoint_descri
        }
 }
 
+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;
-       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;
+       union uaudio_asid asid = { NULL };
+       union uaudio_asf1d asf1d = { NULL };
+       union uaudio_sed sed = { NULL };
        usb_endpoint_descriptor_audio_t *ed1 = NULL;
-       const usb_endpoint_descriptor_audio_t *ed2 = 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;
+       const struct uaudio_format *p_fmt = NULL;
        struct uaudio_chan *chan;
        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 uma_if_class;
 
@@ -923,171 +1112,264 @@ uaudio_chan_fill_info_sub(struct uaudio_
                                        sc->sc_midi_chan.valid = 1;
                                }
                        }
-                       asid = NULL;
-                       asf1d = NULL;
+                       asid.v1 = NULL;
+                       asf1d.v1 = NULL;
                        ed1 = NULL;
-                       ed2 = NULL;
-                       sed = NULL;
+                       sed.v1 = NULL;
                }
-               if ((desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+
+               if (audio_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);
+                       }
+
+                       /*
+                        * Don't collect any USB audio descriptors if
+                        * this is not an USB audio stream interface.
+                        */
+                       continue;
+               }
+
+               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. */
+               if (audio_rev >= UAUDIO_VERSION_30) {
+                       goto next_ep;
+               } else if (audio_rev >= UAUDIO_VERSION_20) {
 
-                       wFormat = UGETW(asid->wFormatTag);
-                       bChannels = UAUDIO_MAX_CHAN(asf1d->bNrChannels);
-                       bBitResolution = asf1d->bBitResolution;
+                       uint32_t dwFormat;
+                       uint8_t bSubslotSize;
 
-                       if (asf1d->bSamFreqType == 0) {
+                       dwFormat = UGETDW(asid.v2->bmFormats);
+                       bChannels = asid.v2->bNrChannels;
+                       bBitResolution = asf1d.v2->bBitResolution;
+                       bSubslotSize = asf1d.v2->bSubslotSize;
+
+                       if (bBitResolution != (bSubslotSize * 8)) {
+                               DPRINTF("Invalid bSubslotSize\n");
+                               goto next_ep;
+                       }
+
+                       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->bBitResolution;
+
+                       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)) {
+                       if ((bChannels != channels) ||
+                           (bBitResolution != bit_resolution)) {
+                               DPRINTF("Wrong number of channels\n");
+                               goto next_ep;
+                       }
+               }
 
-                               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 ((chan->valid == 0) && usbd_get_iface(udev, 
curidx)) {
+               if (chan->valid != 0 ||
+                   usbd_get_iface(udev, curidx) == NULL) {
+                       DPRINTF("Channel already exists or "
+                           "interface is not valid\n");
+                       goto next_ep;
+               }
 
-                                       chan->valid = 1;
+               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->sample_rate = rate;
+               chan->p_asf1d = asf1d;
+               chan->p_ed1 = ed1;
+               chan->p_fmt = p_fmt;
+               chan->p_sed = sed;
+               chan->iface_index = curidx;
+               chan->iface_alt_index = alt_index;
 
-                                       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);
-                                       }
-                               }
-                       }
-                       audio_if = 0;
-                       continue;
-               }
+               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(channels) *
+                   p_fmt->bPrecision) / 8;
+               chan->channels = channels;
+
+               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));
+               }
+
+               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;
        }
 }
 
@@ -1137,7 +1419,7 @@ uaudio_chan_fill_info(struct uaudio_soft
                         * disable surround setups on FULL-speed USB
                         * by default
                         */
-                       channels = 2;
+                       channels = 4;
                        break;
                default:
                        channels = 16;
@@ -1177,72 +1459,186 @@ done:
 }
 
 static void
-uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
+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;
-       uint32_t total;
-       uint32_t blockcount;
-       uint32_t n;
-       uint32_t offset;
+       uint8_t buf[4];
+       uint64_t temp;
+       int len;
        int actlen;
-       int sumlen;
-
-       usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
+       int nframes;
 
-       if (ch->end == ch->start) {
-               DPRINTF("no buffer!\n");
-               return;
-       }
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
 
        switch (USB_GET_STATE(xfer)) {
        case USB_ST_TRANSFERRED:
-tr_transferred:
-               if (actlen < sumlen) {
-                       DPRINTF("short transfer, "
-                           "%d of %d bytes\n", actlen, sumlen);
-               }
-               chn_intr(ch->pcm_ch);
 
-       case USB_ST_SETUP:

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to