Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 9 Nov 2012 07:29:11 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r242822 - in stable/9/sys/dev: sound/usb usb usb/quirk
Message-ID:  <201211090729.qA97TBOj021065@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201211090729.qA97TBOj021065>