Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 20 Dec 2004 03:41:57 +0900 (JST)
From:      Kazuhito HONDA <kazuhito@ph.noda.tus.ac.jp>
To:        FreeBSD-gnats-submit@FreeBSD.org
Cc:        kazuhito@ph.noda.tus.ac.jp
Subject:   kern/75274: Updating of USB audio codes (uaudio*.*) along recent NetBSD's
Message-ID:  <20041220.034157.846933450.kazuhito@ph.noda.tus.ac.jp>
Resent-Message-ID: <200412191850.iBJIoMaH001389@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         75274
>Category:       kern
>Synopsis:       Updating of USB audio codes (uaudio*.*) along recent NetBSD's
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Dec 19 18:50:22 GMT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Kazuhito HONDA
>Release:        FreeBSD 6.0-CURRENT i386
>Organization:
>Environment:
System: FreeBSD kaoru 6.0-CURRENT FreeBSD 6.0-CURRENT #191: Mon Dec 20 02:28:37 JST 2004 root@kaoru:/usr/obj/src/sys/i386/compile/KAORU.6.0B.0 i386

USB audio device: Sound Blaster Digital Music (SBDM, Creative Labs.)
>Description:
Codes for USB audio in FreeBSD have several problems.

1. Only one volume controllor even if the device has many volume controllor
2. Can't select a sound source for recording
3. Can't record
4. play in wrong sampling rate

For solving these problems, especially 1. 2., It is convenient 
to update FreeBSD uaudio codes along recent NetBSD uaudio codes.

Unfortunately USB audio device doesn't play 
if you don't set correct sampling rate 
of USB audio device after updating.
But, before updating, USB audio device play 
in wrong sampling rate for sound source 
if source sampling rate is not equal to device sampling rate.
I expect that the formaer is better.

>How-To-Repeat:

>Fix:
This patch is very long.
So I expect that the code check will be very hard.
I recommend that the checker will compare
the patched files with recent NetBSD's files,
and compare its difference with the difference 
between old FreeBSD files and old NetBSD files.

--- F_41-91.diff begins here ---
--- src/sys/dev/sound/usb/uaudio.c	Mon Dec 20 01:48:43 2004
+++ src/sys/dev/sound/usb/uaudio-91.c	Mon Dec 20 02:42:53 2004
@@ -1,5 +1,5 @@
-/*	$NetBSD: uaudio.c,v 1.41 2001/01/23 14:04:13 augustss Exp $	*/
-/*	$FreeBSD: src/sys/dev/sound/usb/uaudio.c,v 1.7 2002/08/25 01:32:22 bde Exp $: */
+/*	$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $	*/
+/*	$FreeBSD: src/sys/dev/sound/usb/uaudio-91.c,v $: */
 
 /*
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -39,11 +39,16 @@
  */
 
 /*
- * USB audio specs: http://www.usb.org/developers/data/devclass/audio10.pdf
- *                  http://www.usb.org/developers/data/devclass/frmts10.pdf
- *                  http://www.usb.org/developers/data/devclass/termt10.pdf
+ * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
+ *                  http://www.usb.org/developers/devclass_docs/frmts10.pdf
+ *                  http://www.usb.org/developers/devclass_docs/termt10.pdf
  */
 
+#include <sys/cdefs.h>
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $");
+#endif
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -66,11 +71,14 @@
 #include <sys/conf.h>
 #endif
 #include <sys/poll.h>
+#if defined(__FreeBSD__)
 #include <sys/sysctl.h>
+#endif
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
 #include <sys/audioio.h>
 #include <dev/audio_if.h>
+#include <dev/audiovar.h>
 #include <dev/mulaw.h>
 #include <dev/auconv.h>
 #elif defined(__FreeBSD__)
@@ -83,23 +91,39 @@
 #include <dev/usb/usbdi_util.h>
 #include <dev/usb/usb_quirks.h>
 
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <dev/usb/uaudioreg.h>
+#elif defined(__FreeBSD__)
 #include <dev/sound/usb/uaudioreg.h>
 #include <dev/sound/usb/uaudio.h>
+#endif
 
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+/* #define UAUDIO_DEBUG */
+#else
+/* #define USB_DEBUG */
+#endif
+/* #define UAUDIO_MULTIPLE_ENDPOINTS */
 #ifdef USB_DEBUG
-#define DPRINTF(x)	if (uaudiodebug) logprintf x
-#define DPRINTFN(n,x)	if (uaudiodebug>(n)) logprintf x
+#define DPRINTF(x)	do { if (uaudiodebug) logprintf x; } while (0)
+#define DPRINTFN(n,x)	do { if (uaudiodebug>(n)) logprintf x; } while (0)
 int	uaudiodebug = 0;
+#if defined(__FreeBSD__)
 SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio");
 SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW,
 	   &uaudiodebug, 0, "uaudio debug level");
+#endif
 #else
 #define DPRINTF(x)
 #define DPRINTFN(n,x)
 #endif
 
 #define UAUDIO_NCHANBUFS 6	/* number of outstanding request */
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#define UAUDIO_NFRAMES   10	/* ms of sound in each request */
+#elif defined(__FreeBSD__)
 #define UAUDIO_NFRAMES   20	/* ms of sound in each request */
+#endif
 
 
 #define MIX_MAX_CHAN 8
@@ -112,6 +136,7 @@ struct mixerctl {
 #define MIX_SIGNED_16	2
 #define MIX_UNSIGNED_16	3
 #define MIX_SIGNED_8	4
+#define MIX_SELECTOR	5
 #define MIX_SIZE(n) ((n) == MIX_SIGNED_16 || (n) == MIX_UNSIGNED_16 ? 2 : 1)
 #define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16)
 	int		minval, maxval;
@@ -130,22 +155,26 @@ struct mixerctl {
 struct as_info {
 	u_int8_t	alt;
 	u_int8_t	encoding;
+	u_int8_t	attributes; /* Copy of bmAttributes of
+				     * usb_audio_streaming_endpoint_descriptor
+				     */
 	usbd_interface_handle	ifaceh;
-	usb_interface_descriptor_t *idesc;
-	usb_endpoint_descriptor_audio_t *edesc;
-	struct usb_audio_streaming_type1_descriptor *asf1desc;
+	const usb_interface_descriptor_t *idesc;
+	const usb_endpoint_descriptor_audio_t *edesc;
+	const usb_endpoint_descriptor_audio_t *edesc1;
+	const struct usb_audio_streaming_type1_descriptor *asf1desc;
+	int		sc_busy;	/* currently used */
 };
 
 struct chan {
-	int	terminal;	/* terminal id */
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-	void	(*intr)(void *);	/* dma completion intr handler */
+	void	(*intr)(void *);	/* DMA completion intr handler */
 	void	*arg;		/* arg for intr() */
 #else
 	struct pcm_channel *pcm_ch;
 #endif
 	usbd_pipe_handle pipe;
-	int	dir;		/* direction */
+	usbd_pipe_handle sync_pipe;
 
 	u_int	sample_size;
 	u_int	sample_rate;
@@ -159,15 +188,16 @@ struct chan {
 	int	blksize;	/* chunk size to report up */
 	int	transferred;	/* transferred bytes not reported up */
 
-	char	nofrac;		/* don't do sample rate adjustment */
+	int	altidx;		/* currently used altidx */
 
 	int	curchanbuf;
 	struct chanbuf {
-		struct chan         *chan;
+		struct chan	*chan;
 		usbd_xfer_handle xfer;
-		u_char              *buffer;
-		u_int16_t           sizes[UAUDIO_NFRAMES];
-		u_int16_t	    size;
+		u_char		*buffer;
+		u_int16_t	sizes[UAUDIO_NFRAMES];
+		u_int16_t	offsets[UAUDIO_NFRAMES];
+		u_int16_t	size;
 	} chanbufs[UAUDIO_NCHANBUFS];
 
 	struct uaudio_softc *sc; /* our softc */
@@ -179,151 +209,194 @@ struct chan {
 };
 
 struct uaudio_softc {
-	USBBASEDEVICE sc_dev;		/* base device */
+	USBBASEDEVICE	sc_dev;		/* base device */
 	usbd_device_handle sc_udev;	/* USB device */
-
-	char	sc_dead;	/* The device is dead -- kill it */
-
-	int	sc_ac_iface;	/* Audio Control interface */
+	int		sc_ac_iface;	/* Audio Control interface */
 	usbd_interface_handle	sc_ac_ifaceh;
+	struct chan	sc_playchan;	/* play channel */
+	struct chan	sc_recchan;	/* record channel */
+	int		sc_nullalt;
+	int		sc_audio_rev;
+	struct as_info	*sc_alts;	/* alternate settings */
+	int		sc_nalts;	/* # of alternate settings */
+	int		sc_altflags;
+#define HAS_8		0x01
+#define HAS_16		0x02
+#define HAS_8U		0x04
+#define HAS_ALAW	0x08
+#define HAS_MULAW	0x10
+#define UA_NOFRAC	0x20		/* don't do sample rate adjustment */
+#define HAS_24		0x40
+	int		sc_mode;	/* play/record capability */
+	struct mixerctl *sc_ctls;	/* mixer controls */
+	int		sc_nctls;	/* # of mixer controls */
+	device_ptr_t	sc_audiodev;
+	char		sc_dying;
+};
 
-	struct chan sc_chan;
-
-	int	sc_curaltidx;
-
-	int	sc_nullalt;
-
-	int	sc_audio_rev;
-
-	struct as_info *sc_alts;
-	int	sc_nalts;
-	int	sc_props;
-
-	int	sc_altflags;
-#define HAS_8     0x01
-#define HAS_16    0x02
-#define HAS_8U    0x04
-#define HAS_ALAW  0x08
-#define HAS_MULAW 0x10
-
-	struct mixerctl *sc_ctls;
-	int	sc_nctls;
+struct terminal_list {
+	int size;
+	uint16_t terminals[1];
+};
+#define TERMINAL_LIST_SIZE(N)	(offsetof(struct terminal_list, terminals) \
+				+ sizeof(uint16_t) * (N))
 
-	device_ptr_t sc_audiodev;
-	char	sc_dying;
+struct io_terminal {
+	union {
+		const usb_descriptor_t *desc;
+		const struct usb_audio_input_terminal *it;
+		const struct usb_audio_output_terminal *ot;
+		const struct usb_audio_mixer_unit *mu;
+		const struct usb_audio_selector_unit *su;
+		const struct usb_audio_feature_unit *fu;
+		const struct usb_audio_processing_unit *pu;
+		const struct usb_audio_extension_unit *eu;
+	} d;
+	int inputs_size;
+	struct terminal_list **inputs; /* list of source input terminals */
+	struct terminal_list *output; /* list of destination output terminals */
+	int direct;		/* directly connected to an output terminal */
 };
 
-#define UAC_OUTPUT 0
-#define UAC_INPUT  1
-#define UAC_EQUAL  2
+#define UAC_OUTPUT	0
+#define UAC_INPUT	1
+#define UAC_EQUAL	2
+#define UAC_RECORD	3
+#define UAC_NCLASSES	4
+#if !defined(__FreeBSD__)
+#ifdef USB_DEBUG
+Static const char *uac_names[] = {
+	AudioCoutputs, AudioCinputs, AudioCequalization, AudioCrecord,
+};
+#endif
+#endif
 
-Static usbd_status	uaudio_identify_ac(struct uaudio_softc *sc,
-					   usb_config_descriptor_t *cdesc);
-Static usbd_status	uaudio_identify_as(struct uaudio_softc *sc,
-					   usb_config_descriptor_t *cdesc);
-Static usbd_status	uaudio_process_as(struct uaudio_softc *sc,
-			    char *buf, int *offsp, int size,
-			    usb_interface_descriptor_t *id);
+Static usbd_status uaudio_identify_ac
+	(struct uaudio_softc *, const usb_config_descriptor_t *);
+Static usbd_status uaudio_identify_as
+	(struct uaudio_softc *, const usb_config_descriptor_t *);
+Static usbd_status uaudio_process_as
+	(struct uaudio_softc *, const char *, int *, int,
+	 const usb_interface_descriptor_t *);
 
-Static void 		uaudio_add_alt(struct uaudio_softc *sc, 
-				       struct as_info *ai);
+Static void	uaudio_add_alt(struct uaudio_softc *, const struct as_info *);
 
-Static usb_interface_descriptor_t *uaudio_find_iface(char *buf,
-			    int size, int *offsp, int subtype);
+Static const usb_interface_descriptor_t *uaudio_find_iface
+	(const char *, int, int *, int);
 
-Static void		uaudio_mixer_add_ctl(struct uaudio_softc *sc,
-					     struct mixerctl *mp);
+Static void	uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *);
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-Static char 		*uaudio_id_name(struct uaudio_softc *sc,
-					usb_descriptor_t **dps, int id);
+Static char	*uaudio_id_name
+	(struct uaudio_softc *, const struct io_terminal *, int);
 #endif
 
-Static struct usb_audio_cluster uaudio_get_cluster(int id,
-						   usb_descriptor_t **dps);
-Static void		uaudio_add_input(struct uaudio_softc *sc,
-			    usb_descriptor_t *v, usb_descriptor_t **dps);
-Static void 		uaudio_add_output(struct uaudio_softc *sc,
-			    usb_descriptor_t *v, usb_descriptor_t **dps);
-Static void		uaudio_add_mixer(struct uaudio_softc *sc,
-			    usb_descriptor_t *v, usb_descriptor_t **dps);
-Static void		uaudio_add_selector(struct uaudio_softc *sc,
-			    usb_descriptor_t *v, usb_descriptor_t **dps);
-Static void		uaudio_add_feature(struct uaudio_softc *sc,
-			    usb_descriptor_t *v, usb_descriptor_t **dps);
-Static void		uaudio_add_processing_updown(struct uaudio_softc *sc,
-			         usb_descriptor_t *v, usb_descriptor_t **dps);
-Static void		uaudio_add_processing(struct uaudio_softc *sc,
-			    usb_descriptor_t *v, usb_descriptor_t **dps);
-Static void		uaudio_add_extension(struct uaudio_softc *sc,
-			    usb_descriptor_t *v, usb_descriptor_t **dps);
-Static usbd_status	uaudio_identify(struct uaudio_softc *sc, 
-			    usb_config_descriptor_t *cdesc);
+#ifdef USB_DEBUG
+Static void	uaudio_dump_cluster(const struct usb_audio_cluster *);
+#endif
+Static struct usb_audio_cluster uaudio_get_cluster
+	(int, const struct io_terminal *);
+Static void	uaudio_add_input
+	(struct uaudio_softc *, const struct io_terminal *, int);
+Static void	uaudio_add_output
+	(struct uaudio_softc *, const struct io_terminal *, int);
+Static void	uaudio_add_mixer
+	(struct uaudio_softc *, const struct io_terminal *, int);
+Static void	uaudio_add_selector
+	(struct uaudio_softc *, const struct io_terminal *, int);
+#ifdef USB_DEBUG
+Static const char *uaudio_get_terminal_name(int);
+#endif
+#if !defined(__FreeBSD__)
+Static int	uaudio_determine_class
+	(const struct io_terminal *, struct mixerctl *);
+Static const char *uaudio_feature_name
+	(const struct io_terminal *, struct mixerctl *);
+#endif
+Static void	uaudio_add_feature
+	(struct uaudio_softc *, const struct io_terminal *, int);
+Static void	uaudio_add_processing_updown
+	(struct uaudio_softc *, const struct io_terminal *, int);
+Static void	uaudio_add_processing
+	(struct uaudio_softc *, const struct io_terminal *, int);
+Static void	uaudio_add_extension
+	(struct uaudio_softc *, const struct io_terminal *, int);
+Static struct terminal_list *uaudio_merge_terminal_list
+	(const struct io_terminal *);
+Static struct terminal_list *uaudio_io_terminaltype
+	(int, struct io_terminal *, int);
+Static usbd_status uaudio_identify
+	(struct uaudio_softc *, const usb_config_descriptor_t *);
 
-Static int 		uaudio_signext(int type, int val);
+Static int	uaudio_signext(int, int);
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-Static int 		uaudio_value2bsd(struct mixerctl *mc, int val);
+Static int	uaudio_value2bsd(struct mixerctl *, int);
 #endif
-Static int 		uaudio_bsd2value(struct mixerctl *mc, int val);
-Static int 		uaudio_get(struct uaudio_softc *sc, int type,
-			    int which, int wValue, int wIndex, int len);
+Static int	uaudio_bsd2value(struct mixerctl *, int);
+Static int	uaudio_get(struct uaudio_softc *, int, int, int, int, int);
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-Static int		uaudio_ctl_get(struct uaudio_softc *sc, int which,
-			    struct mixerctl *mc, int chan);
+Static int	uaudio_ctl_get
+	(struct uaudio_softc *, int, struct mixerctl *, int);
 #endif
-Static void		uaudio_set(struct uaudio_softc *sc, int type,
-			    int which, int wValue, int wIndex, int l, int v);
-Static void 		uaudio_ctl_set(struct uaudio_softc *sc, int which,
-			    struct mixerctl *mc, int chan, int val);
+Static void	uaudio_set
+	(struct uaudio_softc *, int, int, int, int, int, int);
+Static void	uaudio_ctl_set
+	(struct uaudio_softc *, int, struct mixerctl *, int, int);
 
-Static usbd_status	uaudio_set_speed(struct uaudio_softc *, int, u_int);
+Static usbd_status uaudio_set_speed(struct uaudio_softc *, int, u_int);
 
-Static usbd_status	uaudio_chan_open(struct uaudio_softc *sc,
-					 struct chan *ch);
-Static void		uaudio_chan_close(struct uaudio_softc *sc,
-					  struct chan *ch);
-Static usbd_status	uaudio_chan_alloc_buffers(struct uaudio_softc *,
-						  struct chan *);
-Static void		uaudio_chan_free_buffers(struct uaudio_softc *,
-						 struct chan *);
+Static usbd_status uaudio_chan_open(struct uaudio_softc *, struct chan *);
+Static void	uaudio_chan_close(struct uaudio_softc *, struct chan *);
+Static usbd_status uaudio_chan_alloc_buffers
+	(struct uaudio_softc *, struct chan *);
+Static void	uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *);
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-Static void		uaudio_chan_set_param(struct chan *ch,
-			    struct audio_params *param, u_char *start, 
-			    u_char *end, int blksize);
+Static void	uaudio_chan_init
+	(struct chan *, int, const struct audio_params *, int);
+Static void	uaudio_chan_set_param(struct chan *, u_char *, u_char *, int);
 #endif
 
-Static void		uaudio_chan_ptransfer(struct chan *ch);
-Static void		uaudio_chan_pintr(usbd_xfer_handle xfer, 
-			    usbd_private_handle priv, usbd_status status);
+Static void	uaudio_chan_ptransfer(struct chan *);
+Static void	uaudio_chan_pintr
+	(usbd_xfer_handle, usbd_private_handle, usbd_status);
 
-Static void		uaudio_chan_rtransfer(struct chan *ch);
-Static void		uaudio_chan_rintr(usbd_xfer_handle xfer, 
-			    usbd_private_handle priv, usbd_status status);
+Static void	uaudio_chan_rtransfer(struct chan *);
+Static void	uaudio_chan_rintr
+	(usbd_xfer_handle, usbd_private_handle, usbd_status);
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-Static int		uaudio_open(void *, int);
-Static void		uaudio_close(void *);
-Static int		uaudio_drain(void *);
-Static int		uaudio_query_encoding(void *, struct audio_encoding *);
-Static int		uaudio_set_params(void *, int, int, 
-			    struct audio_params *, struct audio_params *);
-Static int		uaudio_round_blocksize(void *, int);
-Static int		uaudio_trigger_output(void *, void *, void *,
-					      int, void (*)(void *), void *,
-					      struct audio_params *);
-Static int		uaudio_trigger_input (void *, void *, void *,
-					      int, void (*)(void *), void *,
-					      struct audio_params *);
-Static int		uaudio_halt_in_dma(void *);
-Static int		uaudio_halt_out_dma(void *);
-Static int		uaudio_getdev(void *, struct audio_device *);
-Static int		uaudio_mixer_set_port(void *, mixer_ctrl_t *);
-Static int		uaudio_mixer_get_port(void *, mixer_ctrl_t *);
-Static int		uaudio_query_devinfo(void *, mixer_devinfo_t *);
-Static int		uaudio_get_props(void *);
+Static int	uaudio_open(void *, int);
+Static void	uaudio_close(void *);
+Static int	uaudio_drain(void *);
+Static int	uaudio_query_encoding(void *, struct audio_encoding *);
+Static void	uaudio_get_minmax_rates
+	(int, const struct as_info *, const struct audio_params *,
+	 int, u_long *, u_long *);
+Static int	uaudio_match_alt_sub
+	(int, const struct as_info *, const struct audio_params *, int, u_long);
+Static int	uaudio_match_alt_chan
+	(int, const struct as_info *, struct audio_params *, int);
+Static int	uaudio_match_alt
+	(int, const struct as_info *, struct audio_params *, int);
+Static int	uaudio_set_params
+	(void *, int, int, struct audio_params *, struct audio_params *);
+Static int	uaudio_round_blocksize(void *, int);
+Static int	uaudio_trigger_output
+	(void *, void *, void *, int, void (*)(void *), void *,
+	 struct audio_params *);
+Static int	uaudio_trigger_input
+	(void *, void *, void *, int, void (*)(void *), void *,
+	 struct audio_params *);
+Static int	uaudio_halt_in_dma(void *);
+Static int	uaudio_halt_out_dma(void *);
+Static int	uaudio_getdev(void *, struct audio_device *);
+Static int	uaudio_mixer_set_port(void *, mixer_ctrl_t *);
+Static int	uaudio_mixer_get_port(void *, mixer_ctrl_t *);
+Static int	uaudio_query_devinfo(void *, mixer_devinfo_t *);
+Static int	uaudio_get_props(void *);
 
-Static struct audio_hw_if uaudio_hw_if = {
+Static const struct audio_hw_if uaudio_hw_if = {
 	uaudio_open,
 	uaudio_close,
 	uaudio_drain,
@@ -350,6 +423,7 @@ Static struct audio_hw_if uaudio_hw_if =
 	uaudio_get_props,
 	uaudio_trigger_output,
 	uaudio_trigger_input,
+	NULL,
 };
 
 Static struct audio_device uaudio_device = {
@@ -360,7 +434,7 @@ Static struct audio_device uaudio_device
 
 #elif defined(__FreeBSD__)
 Static int	audio_attach_mi(device_t);
-Static void	uaudio_init_params(struct uaudio_softc * sc, struct chan *ch);
+Static int	uaudio_init_params(struct uaudio_softc * sc, struct chan *ch, int mode);
 
 /* for NetBSD compatibirity */
 #define	AUMODE_PLAY	0x01
@@ -397,13 +471,13 @@ USB_MATCH(uaudio)
 {
 	USB_MATCH_START(uaudio, uaa);
 	usb_interface_descriptor_t *id;
-	
+
 	if (uaa->iface == NULL)
 		return (UMATCH_NONE);
 
 	id = usbd_get_interface_descriptor(uaa->iface);
 	/* Trigger on the control interface. */
-	if (id == NULL || 
+	if (id == NULL ||
 	    id->bInterfaceClass != UICLASS_AUDIO ||
 	    id->bInterfaceSubClass != UISUBCLASS_AUDIOCONTROL ||
 	    (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_AUDIO))
@@ -421,8 +495,12 @@ USB_ATTACH(uaudio)
 	usbd_status err;
 	int i, j, found;
 
+#if defined(__FreeBSD__)
 	usbd_devinfo(uaa->device, 0, devinfo);
 	USB_ATTACH_SETUP;
+#else
+	usbd_devinfo(uaa->device, 0, devinfo, sizeof(devinfo));
+#endif
 
 #if !defined(__FreeBSD__)
 	printf(": %s\n", devinfo);
@@ -475,10 +553,12 @@ USB_ATTACH(uaudio)
 	printf("%s: audio rev %d.%02x\n", USBDEVNAME(sc->sc_dev),
 	       sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff);
 
-	sc->sc_chan.sc = sc;
+	sc->sc_playchan.sc = sc->sc_recchan.sc = sc;
+	sc->sc_playchan.altidx = -1;
+	sc->sc_recchan.altidx = -1;
 
 	if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_FRAC)
-		sc->sc_chan.nofrac = 1;
+		sc->sc_altflags |= UA_NOFRAC;
 
 #ifndef USB_DEBUG
 	if (bootverbose)
@@ -566,7 +646,7 @@ USB_DETACH(uaudio)
 #endif
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-int
+Static int
 uaudio_query_encoding(void *addr, struct audio_encoding *fp)
 {
 	struct uaudio_softc *sc = addr;
@@ -575,56 +655,56 @@ uaudio_query_encoding(void *addr, struct
 
 	if (sc->sc_dying)
 		return (EIO);
-    
+
 	if (sc->sc_nalts == 0 || flags == 0)
 		return (ENXIO);
 
 	idx = fp->index;
 	switch (idx) {
 	case 0:
-		strcpy(fp->name, AudioEulinear);
+		strlcpy(fp->name, AudioEulinear, sizeof(fp->name));
 		fp->encoding = AUDIO_ENCODING_ULINEAR;
 		fp->precision = 8;
 		fp->flags = flags&HAS_8U ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
 		return (0);
 	case 1:
-		strcpy(fp->name, AudioEmulaw);
+		strlcpy(fp->name, AudioEmulaw, sizeof(fp->name));
 		fp->encoding = AUDIO_ENCODING_ULAW;
 		fp->precision = 8;
 		fp->flags = flags&HAS_MULAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
 		return (0);
 	case 2:
-		strcpy(fp->name, AudioEalaw);
+		strlcpy(fp->name, AudioEalaw, sizeof(fp->name));
 		fp->encoding = AUDIO_ENCODING_ALAW;
 		fp->precision = 8;
 		fp->flags = flags&HAS_ALAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
 		return (0);
 	case 3:
-		strcpy(fp->name, AudioEslinear);
+		strlcpy(fp->name, AudioEslinear, sizeof(fp->name));
 		fp->encoding = AUDIO_ENCODING_SLINEAR;
 		fp->precision = 8;
 		fp->flags = flags&HAS_8 ? 0 : AUDIO_ENCODINGFLAG_EMULATED;
 		return (0);
-        case 4:
-		strcpy(fp->name, AudioEslinear_le);
+	case 4:
+		strlcpy(fp->name, AudioEslinear_le, sizeof(fp->name));
 		fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
 		fp->precision = 16;
 		fp->flags = 0;
 		return (0);
 	case 5:
-		strcpy(fp->name, AudioEulinear_le);
+		strlcpy(fp->name, AudioEulinear_le, sizeof(fp->name));
 		fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
 		fp->precision = 16;
 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
 		return (0);
 	case 6:
-		strcpy(fp->name, AudioEslinear_be);
+		strlcpy(fp->name, AudioEslinear_be, sizeof(fp->name));
 		fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
 		fp->precision = 16;
 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
 		return (0);
 	case 7:
-		strcpy(fp->name, AudioEulinear_be);
+		strlcpy(fp->name, AudioEulinear_be, sizeof(fp->name));
 		fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
 		fp->precision = 16;
 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
@@ -635,13 +715,13 @@ uaudio_query_encoding(void *addr, struct
 }
 #endif
 
-usb_interface_descriptor_t *
-uaudio_find_iface(char *buf, int size, int *offsp, int subtype)
+Static const usb_interface_descriptor_t *
+uaudio_find_iface(const char *buf, int size, int *offsp, int subtype)
 {
-	usb_interface_descriptor_t *d;
+	const usb_interface_descriptor_t *d;
 
 	while (*offsp < size) {
-		d = (void *)(buf + *offsp);
+		d = (const void *)(buf + *offsp);
 		*offsp += d->bLength;
 		if (d->bDescriptorType == UDESC_INTERFACE &&
 		    d->bInterfaceClass == UICLASS_AUDIO &&
@@ -651,29 +731,47 @@ uaudio_find_iface(char *buf, int size, i
 	return (NULL);
 }
 
-void
+Static void
 uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc)
 {
 	int res;
-	size_t len = sizeof(*mc) * (sc->sc_nctls + 1);
-	struct mixerctl *nmc = sc->sc_nctls == 0 ?
-	  malloc(len, M_USBDEV, M_NOWAIT) :
-	  realloc(sc->sc_ctls, len, M_USBDEV, M_NOWAIT);
+	size_t len;
+	struct mixerctl *nmc;
 
-	if(nmc == NULL){
+#if !defined(__FreeBSD__)
+	if (mc->class < UAC_NCLASSES) {
+		DPRINTF(("%s: adding %s.%s\n",
+			 __func__, uac_names[mc->class], mc->ctlname));
+	} else {
+		DPRINTF(("%s: adding %s\n", __func__, mc->ctlname));
+	}
+#endif
+	len = sizeof(*mc) * (sc->sc_nctls + 1);
+	nmc = malloc(len, M_USBDEV, M_NOWAIT);
+	if (nmc == NULL) {
 		printf("uaudio_mixer_add_ctl: no memory\n");
 		return;
 	}
+	/* Copy old data, if there was any */
+	if (sc->sc_nctls != 0) {
+		memcpy(nmc, sc->sc_ctls, sizeof(*mc) * (sc->sc_nctls));
+		free(sc->sc_ctls, M_USBDEV);
+	}
 	sc->sc_ctls = nmc;
 
 	mc->delta = 0;
-	if (mc->type != MIX_ON_OFF) {
+	if (mc->type == MIX_ON_OFF) {
+		mc->minval = 0;
+		mc->maxval = 1;
+	} else if (mc->type == MIX_SELECTOR) {
+		;
+	} else {
 		/* Determine min and max values. */
-		mc->minval = uaudio_signext(mc->type, 
-			uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE, 
-				   mc->wValue[0], mc->wIndex, 
+		mc->minval = uaudio_signext(mc->type,
+			uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE,
+				   mc->wValue[0], mc->wIndex,
 				   MIX_SIZE(mc->type)));
-		mc->maxval = 1 + uaudio_signext(mc->type, 
+		mc->maxval = 1 + uaudio_signext(mc->type,
 			uaudio_get(sc, GET_MAX, UT_READ_CLASS_INTERFACE,
 				   mc->wValue[0], mc->wIndex,
 				   MIX_SIZE(mc->type)));
@@ -684,10 +782,7 @@ uaudio_mixer_add_ctl(struct uaudio_softc
 				 mc->wValue[0], mc->wIndex,
 				 MIX_SIZE(mc->type));
 		if (res > 0)
-			mc->delta = (res * 256 + mc->mul/2) / mc->mul;
-	} else {
-		mc->minval = 0;
-		mc->maxval = 1;
+			mc->delta = (res * 255 + mc->mul/2) / mc->mul;
 	}
 
 	sc->sc_ctls[sc->sc_nctls++] = *mc;
@@ -714,67 +809,80 @@ uaudio_mixer_add_ctl(struct uaudio_softc
 }
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-char *
-uaudio_id_name(struct uaudio_softc *sc, usb_descriptor_t **dps, int id)
+Static char *
+uaudio_id_name(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
 {
 	static char buf[32];
-	sprintf(buf, "i%d", id);
+	snprintf(buf, sizeof(buf), "i%d", id);
 	return (buf);
 }
 #endif
 
-struct usb_audio_cluster
-uaudio_get_cluster(int id, usb_descriptor_t **dps)
+#ifdef USB_DEBUG
+Static void
+uaudio_dump_cluster(const struct usb_audio_cluster *cl)
+{
+	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",
+	};
+	int cc, i, first;
+
+	cc = UGETW(cl->wChannelConfig);
+	logprintf("cluster: bNrChannels=%u wChannelConfig=0x%.4x",
+		  cl->bNrChannels, cc);
+	first = TRUE;
+	for (i = 0; cc != 0; i++) {
+		if (cc & 1) {
+			logprintf("%c%s", first ? '<' : ',', channel_names[i]);
+			first = FALSE;
+		}
+		cc = cc >> 1;
+	}
+	logprintf("> iChannelNames=%u", cl->iChannelNames);
+}
+#endif
+
+Static struct usb_audio_cluster
+uaudio_get_cluster(int id, const struct io_terminal *iot)
 {
 	struct usb_audio_cluster r;
-	usb_descriptor_t *dp;
+	const usb_descriptor_t *dp;
 	int i;
 
 	for (i = 0; i < 25; i++) { /* avoid infinite loops */
-		dp = dps[id];
+		dp = iot[id].d.desc;
 		if (dp == 0)
 			goto bad;
 		switch (dp->bDescriptorSubtype) {
 		case UDESCSUB_AC_INPUT:
-#define p ((struct usb_audio_input_terminal *)dp)
-			r.bNrChannels = p->bNrChannels;
-			USETW(r.wChannelConfig, UGETW(p->wChannelConfig));
-			r.iChannelNames = p->iChannelNames;
-#undef p
+			r.bNrChannels = iot[id].d.it->bNrChannels;
+			USETW(r.wChannelConfig, UGETW(iot[id].d.it->wChannelConfig));
+			r.iChannelNames = iot[id].d.it->iChannelNames;
 			return (r);
 		case UDESCSUB_AC_OUTPUT:
-#define p ((struct usb_audio_output_terminal *)dp)
-			id = p->bSourceId;
-#undef p
+			id = iot[id].d.ot->bSourceId;
 			break;
 		case UDESCSUB_AC_MIXER:
-#define p ((struct usb_audio_mixer_unit *)dp)
-			r = *(struct usb_audio_cluster *)
-				&p->baSourceId[p->bNrInPins];
-#undef p
+			r = *(const struct usb_audio_cluster *)
+				&iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins];
 			return (r);
 		case UDESCSUB_AC_SELECTOR:
 			/* XXX This is not really right */
-#define p ((struct usb_audio_selector_unit *)dp)
-			id = p->baSourceId[0];
-#undef p
+			id = iot[id].d.su->baSourceId[0];
 			break;
 		case UDESCSUB_AC_FEATURE:
-#define p ((struct usb_audio_feature_unit *)dp)
-			id = p->bSourceId;
-#undef p
+			id = iot[id].d.fu->bSourceId;
 			break;
 		case UDESCSUB_AC_PROCESSING:
-#define p ((struct usb_audio_processing_unit *)dp)
-			r = *(struct usb_audio_cluster *)
-				&p->baSourceId[p->bNrInPins];
-#undef p
+			r = *(const struct usb_audio_cluster *)
+				&iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins];
 			return (r);
 		case UDESCSUB_AC_EXTENSION:
-#define p ((struct usb_audio_extension_unit *)dp)
-			r = *(struct usb_audio_cluster *)
-				&p->baSourceId[p->bNrInPins];
-#undef p
+			r = *(const struct usb_audio_cluster *)
+				&iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins];
 			return (r);
 		default:
 			goto bad;
@@ -787,13 +895,11 @@ uaudio_get_cluster(int id, usb_descripto
 
 }
 
-void
-uaudio_add_input(struct uaudio_softc *sc, usb_descriptor_t *v, 
-		 usb_descriptor_t **dps)
+Static void
+uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
 {
 #ifdef USB_DEBUG
-	struct usb_audio_input_terminal *d = 
-		(struct usb_audio_input_terminal *)v;
+	const struct usb_audio_input_terminal *d = iot[id].d.it;
 
 	DPRINTFN(2,("uaudio_add_input: bTerminalId=%d wTerminalType=0x%04x "
 		    "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d "
@@ -804,13 +910,11 @@ uaudio_add_input(struct uaudio_softc *sc
 #endif
 }
 
-void
-uaudio_add_output(struct uaudio_softc *sc, usb_descriptor_t *v,
-		  usb_descriptor_t **dps)
+Static void
+uaudio_add_output(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
 {
 #ifdef USB_DEBUG
-	struct usb_audio_output_terminal *d = 
-		(struct usb_audio_output_terminal *)v;
+	const struct usb_audio_output_terminal *d = iot[id].d.ot;
 
 	DPRINTFN(2,("uaudio_add_output: bTerminalId=%d wTerminalType=0x%04x "
 		    "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n",
@@ -819,33 +923,32 @@ uaudio_add_output(struct uaudio_softc *s
 #endif
 }
 
-void
-uaudio_add_mixer(struct uaudio_softc *sc, usb_descriptor_t *v,
-		 usb_descriptor_t **dps)
+Static void
+uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
 {
-	struct usb_audio_mixer_unit *d = (struct usb_audio_mixer_unit *)v;
-	struct usb_audio_mixer_unit_1 *d1;
+	const struct usb_audio_mixer_unit *d = iot[id].d.mu;
+	const struct usb_audio_mixer_unit_1 *d1;
 	int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k;
-	uByte *bm;
+	const uByte *bm;
 	struct mixerctl mix;
 
 	DPRINTFN(2,("uaudio_add_mixer: bUnitId=%d bNrInPins=%d\n",
 		    d->bUnitId, d->bNrInPins));
-	
+
 	/* Compute the number of input channels */
 	ichs = 0;
 	for (i = 0; i < d->bNrInPins; i++)
-		ichs += uaudio_get_cluster(d->baSourceId[i], dps).bNrChannels;
+		ichs += uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels;
 
 	/* and the number of output channels */
-	d1 = (struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins];
+	d1 = (const struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins];
 	ochs = d1->bNrChannels;
 	DPRINTFN(2,("uaudio_add_mixer: ichs=%d ochs=%d\n", ichs, ochs));
 
 	bm = d1->bmControls;
 	mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
 #if !defined(__FreeBSD__)
-	mix.class = -1;
+	uaudio_determine_class(&iot[id], &mix);
 #endif
 	mix.type = MIX_SIGNED_16;
 #if !defined(__FreeBSD__)	/* XXXXX */
@@ -854,7 +957,7 @@ uaudio_add_mixer(struct uaudio_softc *sc
 
 #define BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1)
 	for (p = i = 0; i < d->bNrInPins; i++) {
-		chs = uaudio_get_cluster(d->baSourceId[i], dps).bNrChannels;
+		chs = uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels;
 		mc = 0;
 		for (c = 0; c < chs; c++) {
 			mo = 0;
@@ -872,12 +975,13 @@ uaudio_add_mixer(struct uaudio_softc *sc
 				for (o = 0; o < ochs; o++) {
 					bno = (p + c) * ochs + o;
 					if (BIT(bno))
-						mix.wValue[k++] = 
+						mix.wValue[k++] =
 							MAKE(p+c+1, o+1);
 				}
 #if !defined(__FreeBSD__)
-			sprintf(mix.ctlname, "mix%d-%s", d->bUnitId,
-				uaudio_id_name(sc, dps, d->baSourceId[i]));
+			snprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-%s",
+			    d->bUnitId, uaudio_id_name(sc, iot,
+			    d->baSourceId[i]));
 #endif
 			mix.nchan = chs;
 			uaudio_mixer_add_ctl(sc, &mix);
@@ -890,34 +994,286 @@ uaudio_add_mixer(struct uaudio_softc *sc
 
 }
 
-void
-uaudio_add_selector(struct uaudio_softc *sc, usb_descriptor_t *v,
-		    usb_descriptor_t **dps)
+Static void
+uaudio_add_selector(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
 {
-#ifdef USB_DEBUG
-	struct usb_audio_selector_unit *d =
-		(struct usb_audio_selector_unit *)v;
+#if !defined(__FreeBSD__) || defined(USB_DEBUG)
+	const struct usb_audio_selector_unit *d = iot[id].d.su;
+#endif
+#if !defined(__FreeBSD__)
+	struct mixerctl mix;
+	int i, wp;
+#endif
 
 	DPRINTFN(2,("uaudio_add_selector: bUnitId=%d bNrInPins=%d\n",
 		    d->bUnitId, d->bNrInPins));
-#endif
+#if defined(__FreeBSD__)
 	printf("uaudio_add_selector: NOT IMPLEMENTED\n");
+#else
+	mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface);
+	mix.wValue[0] = MAKE(0, 0);
+	uaudio_determine_class(&iot[id], &mix);
+	mix.nchan = 1;
+	mix.type = MIX_SELECTOR;
+	mix.ctlunit = "";
+	mix.minval = 1;
+	mix.maxval = d->bNrInPins;
+	mix.mul = mix.maxval - mix.minval;
+	wp = snprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId);
+	for (i = 1; i <= d->bNrInPins; i++) {
+		wp += snprintf(mix.ctlname + wp, MAX_AUDIO_DEV_LEN - wp,
+			       "i%d", d->baSourceId[i - 1]);
+		if (wp > MAX_AUDIO_DEV_LEN - 1)
+			break;
+	}
+	uaudio_mixer_add_ctl(sc, &mix);
+#endif
 }
 
-void
-uaudio_add_feature(struct uaudio_softc *sc, usb_descriptor_t *v,
-		   usb_descriptor_t **dps)
+#ifdef USB_DEBUG
+Static const char *
+uaudio_get_terminal_name(int terminal_type)
 {
-	struct usb_audio_feature_unit *d = (struct usb_audio_feature_unit *)v;
+	static char buf[100];
+
+	switch (terminal_type) {
+	/* USB terminal types */
+	case UAT_UNDEFINED:	return "UAT_UNDEFINED";
+	case UAT_STREAM:	return "UAT_STREAM";
+	case UAT_VENDOR:	return "UAT_VENDOR";
+	/* input terminal types */
+	case UATI_UNDEFINED:	return "UATI_UNDEFINED";
+	case UATI_MICROPHONE:	return "UATI_MICROPHONE";
+	case UATI_DESKMICROPHONE:	return "UATI_DESKMICROPHONE";
+	case UATI_PERSONALMICROPHONE:	return "UATI_PERSONALMICROPHONE";
+	case UATI_OMNIMICROPHONE:	return "UATI_OMNIMICROPHONE";
+	case UATI_MICROPHONEARRAY:	return "UATI_MICROPHONEARRAY";
+	case UATI_PROCMICROPHONEARR:	return "UATI_PROCMICROPHONEARR";
+	/* output terminal types */
+	case UATO_UNDEFINED:	return "UATO_UNDEFINED";
+	case UATO_SPEAKER:	return "UATO_SPEAKER";
+	case UATO_HEADPHONES:	return "UATO_HEADPHONES";
+	case UATO_DISPLAYAUDIO:	return "UATO_DISPLAYAUDIO";
+	case UATO_DESKTOPSPEAKER:	return "UATO_DESKTOPSPEAKER";
+	case UATO_ROOMSPEAKER:	return "UATO_ROOMSPEAKER";
+	case UATO_COMMSPEAKER:	return "UATO_COMMSPEAKER";
+	case UATO_SUBWOOFER:	return "UATO_SUBWOOFER";
+	/* bidir terminal types */
+	case UATB_UNDEFINED:	return "UATB_UNDEFINED";
+	case UATB_HANDSET:	return "UATB_HANDSET";
+	case UATB_HEADSET:	return "UATB_HEADSET";
+	case UATB_SPEAKERPHONE:	return "UATB_SPEAKERPHONE";
+	case UATB_SPEAKERPHONEESUP:	return "UATB_SPEAKERPHONEESUP";
+	case UATB_SPEAKERPHONEECANC:	return "UATB_SPEAKERPHONEECANC";
+	/* telephony terminal types */
+	case UATT_UNDEFINED:	return "UATT_UNDEFINED";
+	case UATT_PHONELINE:	return "UATT_PHONELINE";
+	case UATT_TELEPHONE:	return "UATT_TELEPHONE";
+	case UATT_DOWNLINEPHONE:	return "UATT_DOWNLINEPHONE";
+	/* external terminal types */
+	case UATE_UNDEFINED:	return "UATE_UNDEFINED";
+	case UATE_ANALOGCONN:	return "UATE_ANALOGCONN";
+	case UATE_LINECONN:	return "UATE_LINECONN";
+	case UATE_LEGACYCONN:	return "UATE_LEGACYCONN";
+	case UATE_DIGITALAUIFC:	return "UATE_DIGITALAUIFC";
+	case UATE_SPDIF:	return "UATE_SPDIF";
+	case UATE_1394DA:	return "UATE_1394DA";
+	case UATE_1394DV:	return "UATE_1394DV";
+	/* embedded function terminal types */
+	case UATF_UNDEFINED:	return "UATF_UNDEFINED";
+	case UATF_CALIBNOISE:	return "UATF_CALIBNOISE";
+	case UATF_EQUNOISE:	return "UATF_EQUNOISE";
+	case UATF_CDPLAYER:	return "UATF_CDPLAYER";
+	case UATF_DAT:	return "UATF_DAT";
+	case UATF_DCC:	return "UATF_DCC";
+	case UATF_MINIDISK:	return "UATF_MINIDISK";
+	case UATF_ANALOGTAPE:	return "UATF_ANALOGTAPE";
+	case UATF_PHONOGRAPH:	return "UATF_PHONOGRAPH";
+	case UATF_VCRAUDIO:	return "UATF_VCRAUDIO";
+	case UATF_VIDEODISCAUDIO:	return "UATF_VIDEODISCAUDIO";
+	case UATF_DVDAUDIO:	return "UATF_DVDAUDIO";
+	case UATF_TVTUNERAUDIO:	return "UATF_TVTUNERAUDIO";
+	case UATF_SATELLITE:	return "UATF_SATELLITE";
+	case UATF_CABLETUNER:	return "UATF_CABLETUNER";
+	case UATF_DSS:	return "UATF_DSS";
+	case UATF_RADIORECV:	return "UATF_RADIORECV";
+	case UATF_RADIOXMIT:	return "UATF_RADIOXMIT";
+	case UATF_MULTITRACK:	return "UATF_MULTITRACK";
+	case UATF_SYNTHESIZER:	return "UATF_SYNTHESIZER";
+	default:
+		snprintf(buf, sizeof(buf), "unknown type (0x%.4x)", terminal_type);
+		return buf;
+	}
+}
+#endif
+
+#if !defined(__FreeBSD__)
+Static int
+uaudio_determine_class(const struct io_terminal *iot, struct mixerctl *mix)
+{
+	int terminal_type;
+
+	if (iot == NULL || iot->output == NULL) {
+		mix->class = UAC_OUTPUT;
+		return 0;
+	}
+	terminal_type = 0;
+	if (iot->output->size == 1)
+		terminal_type = iot->output->terminals[0];
+	/*
+	 * If the only output terminal is USB,
+	 * the class is UAC_RECORD.
+	 */
+	if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
+		mix->class = UAC_RECORD;
+		if (iot->inputs_size == 1
+		    && iot->inputs[0] != NULL
+		    && iot->inputs[0]->size == 1)
+			return iot->inputs[0]->terminals[0];
+		else
+			return 0;
+	}
+	/*
+	 * If the ultimate destination of the unit is just one output
+	 * terminal and the unit is connected to the output terminal
+	 * directly, the class is UAC_OUTPUT.
+	 */
+	if (terminal_type != 0 && iot->direct) {
+		mix->class = UAC_OUTPUT;
+		return terminal_type;
+	}
+	/*
+	 * If the unit is connected to just one input terminal,
+	 * the class is UAC_INPUT.
+	 */
+	if (iot->inputs_size == 1 && iot->inputs[0] != NULL
+	    && iot->inputs[0]->size == 1) {
+		mix->class = UAC_INPUT;
+		return iot->inputs[0]->terminals[0];
+	}
+	/*
+	 * Otherwise, the class is UAC_OUTPUT.
+	 */
+	mix->class = UAC_OUTPUT;
+	return terminal_type;
+}
+
+Static const char *
+uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix)
+{
+	int terminal_type;
+
+	terminal_type = uaudio_determine_class(iot, mix);
+	if (mix->class == UAC_RECORD && terminal_type == 0)
+		return AudioNmixerout;
+	DPRINTF(("%s: terminal_type=%s\n", __func__,
+		 uaudio_get_terminal_name(terminal_type)));
+	switch (terminal_type) {
+	case UAT_STREAM:
+		return AudioNdac;
+
+	case UATI_MICROPHONE:
+	case UATI_DESKMICROPHONE:
+	case UATI_PERSONALMICROPHONE:
+	case UATI_OMNIMICROPHONE:
+	case UATI_MICROPHONEARRAY:
+	case UATI_PROCMICROPHONEARR:
+		return AudioNmicrophone;
+
+	case UATO_SPEAKER:
+	case UATO_DESKTOPSPEAKER:
+	case UATO_ROOMSPEAKER:
+	case UATO_COMMSPEAKER:
+		return AudioNspeaker;
+
+	case UATO_HEADPHONES:
+		return AudioNheadphone;
+
+	case UATO_SUBWOOFER:
+		return AudioNlfe;
+
+	/* telephony terminal types */
+	case UATT_UNDEFINED:
+	case UATT_PHONELINE:
+	case UATT_TELEPHONE:
+	case UATT_DOWNLINEPHONE:
+		return "phone";
+
+	case UATE_ANALOGCONN:
+	case UATE_LINECONN:
+	case UATE_LEGACYCONN:
+		return AudioNline;
+
+	case UATE_DIGITALAUIFC:
+	case UATE_SPDIF:
+	case UATE_1394DA:
+	case UATE_1394DV:
+		return AudioNaux;
+
+	case UATF_CDPLAYER:
+		return AudioNcd;
+
+	case UATF_SYNTHESIZER:
+		return AudioNfmsynth;
+
+	case UATF_VIDEODISCAUDIO:
+	case UATF_DVDAUDIO:
+	case UATF_TVTUNERAUDIO:
+		return AudioNvideo;
+
+	case UAT_UNDEFINED:
+	case UAT_VENDOR:
+	case UATI_UNDEFINED:
+/* output terminal types */
+	case UATO_UNDEFINED:
+	case UATO_DISPLAYAUDIO:
+/* bidir terminal types */
+	case UATB_UNDEFINED:
+	case UATB_HANDSET:
+	case UATB_HEADSET:
+	case UATB_SPEAKERPHONE:
+	case UATB_SPEAKERPHONEESUP:
+	case UATB_SPEAKERPHONEECANC:
+/* external terminal types */
+	case UATE_UNDEFINED:
+/* embedded function terminal types */
+	case UATF_UNDEFINED:
+	case UATF_CALIBNOISE:
+	case UATF_EQUNOISE:
+	case UATF_DAT:
+	case UATF_DCC:
+	case UATF_MINIDISK:
+	case UATF_ANALOGTAPE:
+	case UATF_PHONOGRAPH:
+	case UATF_VCRAUDIO:
+	case UATF_SATELLITE:
+	case UATF_CABLETUNER:
+	case UATF_DSS:
+	case UATF_RADIORECV:
+	case UATF_RADIOXMIT:
+	case UATF_MULTITRACK:
+	case 0xffff:
+	default:
+		DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type));
+		return AudioNmaster;
+	}
+	return AudioNmaster;
+}
+#endif
+
+Static void
+uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
+{
+	const struct usb_audio_feature_unit *d = iot[id].d.fu;
 	uByte *ctls = d->bmaControls;
 	int ctlsize = d->bControlSize;
 	int nchan = (d->bLength - 7) / ctlsize;
-#if !defined(__FreeBSD__)
-	int srcId = d->bSourceId;
-#endif
 	u_int fumask, mmask, cmask;
 	struct mixerctl mix;
 	int chan, ctl, i, unit;
+#if !defined(__FreeBSD__)
+	const char *mixername;
+#endif
 
 #define GET(i) (ctls[(i)*ctlsize] | \
 		(ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0))
@@ -931,9 +1287,9 @@ uaudio_add_feature(struct uaudio_softc *
 	}
 
 #if !defined(__FreeBSD__)
-	DPRINTFN(1,("uaudio_add_feature: bUnitId=%d bSourceId=%d, "
-		    "%d channels, mmask=0x%04x, cmask=0x%04x\n", 
-		    d->bUnitId, srcId, nchan, mmask, cmask));
+	DPRINTFN(1,("uaudio_add_feature: bUnitId=%d, "
+		    "%d channels, mmask=0x%04x, cmask=0x%04x\n",
+		    d->bUnitId, nchan, mmask, cmask));
 #endif
 
 	if (nchan > MIX_MAX_CHAN)
@@ -961,7 +1317,7 @@ uaudio_add_feature(struct uaudio_softc *
 #undef GET
 
 #if !defined(__FreeBSD__)
-		mix.class = -1;	/* XXX */
+		mixername = uaudio_feature_name(&iot[id], &mix);
 #endif
 		switch (ctl) {
 		case MUTE_CONTROL:
@@ -969,10 +1325,9 @@ uaudio_add_feature(struct uaudio_softc *
 #if defined(__FreeBSD__)
 			mix.ctl = SOUND_MIXER_NRDEVICES;
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNmute);
 			mix.ctlunit = "";
+			snprintf(mix.ctlname, sizeof(mix.ctlname),
+				 "%s.%s", mixername, AudioNmute);
 #endif
 			break;
 		case VOLUME_CONTROL:
@@ -981,10 +1336,8 @@ uaudio_add_feature(struct uaudio_softc *
 			/* mix.ctl = SOUND_MIXER_VOLUME; */
 			mix.ctl = SOUND_MIXER_PCM;
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNmaster);
 			mix.ctlunit = AudioNvolume;
+			strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname));
 #endif
 			break;
 		case BASS_CONTROL:
@@ -992,10 +1345,9 @@ uaudio_add_feature(struct uaudio_softc *
 #if defined(__FreeBSD__)
 			mix.ctl = SOUND_MIXER_BASS;
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNbass);
 			mix.ctlunit = AudioNbass;
+			snprintf(mix.ctlname, sizeof(mix.ctlname),
+				 "%s.%s", mixername, AudioNbass);
 #endif
 			break;
 		case MID_CONTROL:
@@ -1003,10 +1355,9 @@ uaudio_add_feature(struct uaudio_softc *
 #if defined(__FreeBSD__)
 			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNmid);
 			mix.ctlunit = AudioNmid;
+			snprintf(mix.ctlname, sizeof(mix.ctlname),
+				 "%s.%s", mixername, AudioNmid);
 #endif
 			break;
 		case TREBLE_CONTROL:
@@ -1014,10 +1365,9 @@ uaudio_add_feature(struct uaudio_softc *
 #if defined(__FreeBSD__)
 			mix.ctl = SOUND_MIXER_TREBLE;
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNtreble);
 			mix.ctlunit = AudioNtreble;
+			snprintf(mix.ctlname, sizeof(mix.ctlname),
+				 "%s.%s", mixername, AudioNtreble);
 #endif
 			break;
 		case GRAPHIC_EQUALIZER_CONTROL:
@@ -1028,10 +1378,9 @@ uaudio_add_feature(struct uaudio_softc *
 #if defined(__FreeBSD__)
 			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNagc);
 			mix.ctlunit = "";
+			snprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s",
+				 mixername, AudioNagc);
 #endif
 			break;
 		case DELAY_CONTROL:
@@ -1039,10 +1388,9 @@ uaudio_add_feature(struct uaudio_softc *
 #if defined(__FreeBSD__)
 			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNdelay);
 			mix.ctlunit = "4 ms";
+			snprintf(mix.ctlname, sizeof(mix.ctlname),
+				 "%s.%s", mixername, AudioNdelay);
 #endif
 			break;
 		case BASS_BOOST_CONTROL:
@@ -1050,10 +1398,9 @@ uaudio_add_feature(struct uaudio_softc *
 #if defined(__FreeBSD__)
 			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNbassboost);
 			mix.ctlunit = "";
+			snprintf(mix.ctlname, sizeof(mix.ctlname),
+				 "%s.%s", mixername, AudioNbassboost);
 #endif
 			break;
 		case LOUDNESS_CONTROL:
@@ -1061,10 +1408,9 @@ uaudio_add_feature(struct uaudio_softc *
 #if defined(__FreeBSD__)
 			mix.ctl = SOUND_MIXER_LOUD;	/* Is this correct ? */
 #else
-			sprintf(mix.ctlname, "fea%d-%s-%s", unit,
-				uaudio_id_name(sc, dps, srcId), 
-				AudioNloudness);
 			mix.ctlunit = "";
+			snprintf(mix.ctlname, sizeof(mix.ctlname),
+				 "%s.%s", mixername, AudioNloudness);
 #endif
 			break;
 		}
@@ -1072,16 +1418,15 @@ uaudio_add_feature(struct uaudio_softc *
 	}
 }
 
-void
-uaudio_add_processing_updown(struct uaudio_softc *sc, usb_descriptor_t *v,
-			     usb_descriptor_t **dps)
+Static void
+uaudio_add_processing_updown(struct uaudio_softc *sc,
+			     const struct io_terminal *iot, int id)
 {
-	struct usb_audio_processing_unit *d = 
-	    (struct usb_audio_processing_unit *)v;
-	struct usb_audio_processing_unit_1 *d1 =
-	    (struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins];
-	struct usb_audio_processing_unit_updown *ud =
-	    (struct usb_audio_processing_unit_updown *)
+	const struct usb_audio_processing_unit *d = iot[id].d.pu;
+	const struct usb_audio_processing_unit_1 *d1 =
+	    (const struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins];
+	const struct usb_audio_processing_unit_updown *ud =
+	    (const struct usb_audio_processing_unit_updown *)
 		&d1->bmControls[d1->bControlSize];
 	struct mixerctl mix;
 	int i;
@@ -1098,12 +1443,12 @@ uaudio_add_processing_updown(struct uaud
 	mix.nchan = 1;
 	mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0);
 #if !defined(__FreeBSD__)
-	mix.class = -1;
+	uaudio_determine_class(&iot[id], &mix);
 #endif
 	mix.type = MIX_ON_OFF;	/* XXX */
 #if !defined(__FreeBSD__)
 	mix.ctlunit = "";
-	sprintf(mix.ctlname, "pro%d-mode", d->bUnitId);
+	snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d-mode", d->bUnitId);
 #endif
 
 	for (i = 0; i < ud->bNrModes; i++) {
@@ -1114,14 +1459,12 @@ uaudio_add_processing_updown(struct uaud
 	uaudio_mixer_add_ctl(sc, &mix);
 }
 
-void
-uaudio_add_processing(struct uaudio_softc *sc, usb_descriptor_t *v,
-		      usb_descriptor_t **dps)
+Static void
+uaudio_add_processing(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
 {
-	struct usb_audio_processing_unit *d = 
-	    (struct usb_audio_processing_unit *)v;
-	struct usb_audio_processing_unit_1 *d1 =
-	    (struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins];
+	const struct usb_audio_processing_unit *d = iot[id].d.pu;
+	const struct usb_audio_processing_unit_1 *d1 =
+	    (const struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins];
 	int ptype = UGETW(d->wProcessType);
 	struct mixerctl mix;
 
@@ -1133,19 +1476,20 @@ uaudio_add_processing(struct uaudio_soft
 		mix.nchan = 1;
 		mix.wValue[0] = MAKE(XX_ENABLE_CONTROL, 0);
 #if !defined(__FreeBSD__)
-		mix.class = -1;
+		uaudio_determine_class(&iot[id], &mix);
 #endif
 		mix.type = MIX_ON_OFF;
 #if !defined(__FreeBSD__)
 		mix.ctlunit = "";
-		sprintf(mix.ctlname, "pro%d.%d-enable", d->bUnitId, ptype);
+		snprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d.%d-enable",
+		    d->bUnitId, ptype);
 #endif
 		uaudio_mixer_add_ctl(sc, &mix);
 	}
 
 	switch(ptype) {
 	case UPDOWNMIX_PROCESS:
-		uaudio_add_processing_updown(sc, v, dps);
+		uaudio_add_processing_updown(sc, iot, id);
 		break;
 	case DOLBY_PROLOGIC_PROCESS:
 	case P3D_STEREO_EXTENDER_PROCESS:
@@ -1161,14 +1505,12 @@ uaudio_add_processing(struct uaudio_soft
 	}
 }
 
-void
-uaudio_add_extension(struct uaudio_softc *sc, usb_descriptor_t *v,
-		     usb_descriptor_t **dps)
+Static void
+uaudio_add_extension(struct uaudio_softc *sc, const struct io_terminal *iot, int id)
 {
-	struct usb_audio_extension_unit *d = 
-	    (struct usb_audio_extension_unit *)v;
-	struct usb_audio_extension_unit_1 *d1 =
-	    (struct usb_audio_extension_unit_1 *)&d->baSourceId[d->bNrInPins];
+	const struct usb_audio_extension_unit *d = iot[id].d.eu;
+	const struct usb_audio_extension_unit_1 *d1 =
+	    (const struct usb_audio_extension_unit_1 *)&d->baSourceId[d->bNrInPins];
 	struct mixerctl mix;
 
 	DPRINTFN(2,("uaudio_add_extension: bUnitId=%d bNrInPins=%d\n",
@@ -1182,19 +1524,206 @@ uaudio_add_extension(struct uaudio_softc
 		mix.nchan = 1;
 		mix.wValue[0] = MAKE(UA_EXT_ENABLE, 0);
 #if !defined(__FreeBSD__)
-		mix.class = -1;
+		uaudio_determine_class(&iot[id], &mix);
 #endif
 		mix.type = MIX_ON_OFF;
 #if !defined(__FreeBSD__)
 		mix.ctlunit = "";
-		sprintf(mix.ctlname, "ext%d-enable", d->bUnitId);
+		snprintf(mix.ctlname, sizeof(mix.ctlname), "ext%d-enable",
+		    d->bUnitId);
 #endif
 		uaudio_mixer_add_ctl(sc, &mix);
 	}
 }
 
-usbd_status
-uaudio_identify(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc)
+Static struct terminal_list*
+uaudio_merge_terminal_list(const struct io_terminal *iot)
+{
+	struct terminal_list *tml;
+	uint16_t *ptm;
+	int i, len;
+
+	len = 0;
+	if (iot->inputs == NULL)
+		return NULL;
+	for (i = 0; i < iot->inputs_size; i++) {
+		if (iot->inputs[i] != NULL)
+			len += iot->inputs[i]->size;
+	}
+	tml = malloc(TERMINAL_LIST_SIZE(len), M_TEMP, M_NOWAIT);
+	if (tml == NULL) {
+		printf("uaudio_merge_terminal_list: no memory\n");
+		return NULL;
+	}
+	tml->size = 0;
+	ptm = tml->terminals;
+	for (i = 0; i < iot->inputs_size; i++) {
+		if (iot->inputs[i] == NULL)
+			continue;
+		if (iot->inputs[i]->size > len)
+			break;
+		memcpy(ptm, iot->inputs[i]->terminals,
+		       iot->inputs[i]->size * sizeof(uint16_t));
+		tml->size += iot->inputs[i]->size;
+		ptm += iot->inputs[i]->size;
+		len -= iot->inputs[i]->size;
+	}
+	return tml;
+}
+
+Static struct terminal_list *
+uaudio_io_terminaltype(int outtype, struct io_terminal *iot, int id)
+{
+	struct terminal_list *tml;
+	struct io_terminal *it;
+	int src_id, i;
+
+	it = &iot[id];
+	if (it->output != NULL) {
+		/* already has outtype? */
+		for (i = 0; i < it->output->size; i++)
+			if (it->output->terminals[i] == outtype)
+				return uaudio_merge_terminal_list(it);
+		tml = malloc(TERMINAL_LIST_SIZE(it->output->size + 1),
+			     M_TEMP, M_NOWAIT);
+		if (tml == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return uaudio_merge_terminal_list(it);
+		}
+		memcpy(tml, it->output, TERMINAL_LIST_SIZE(it->output->size));
+		tml->terminals[it->output->size] = outtype;
+		tml->size++;
+		free(it->output, M_TEMP);
+		it->output = tml;
+		if (it->inputs != NULL) {
+			for (i = 0; i < it->inputs_size; i++)
+				if (it->inputs[i] != NULL)
+					free(it->inputs[i], M_TEMP);
+			free(it->inputs, M_TEMP);
+		}
+		it->inputs_size = 0;
+		it->inputs = NULL;
+	} else {		/* end `iot[id] != NULL' */
+		it->inputs_size = 0;
+		it->inputs = NULL;
+		it->output = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
+		if (it->output == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return NULL;
+		}
+		it->output->terminals[0] = outtype;
+		it->output->size = 1;
+		it->direct = FALSE;
+	}
+
+	switch (it->d.desc->bDescriptorSubtype) {
+	case UDESCSUB_AC_INPUT:
+		it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
+		if (it->inputs == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return NULL;
+		}
+		tml = malloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT);
+		if (tml == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			free(it->inputs, M_TEMP);
+			it->inputs = NULL;
+			return NULL;
+		}
+		it->inputs[0] = tml;
+		tml->terminals[0] = UGETW(it->d.it->wTerminalType);
+		tml->size = 1;
+		it->inputs_size = 1;
+		return uaudio_merge_terminal_list(it);
+	case UDESCSUB_AC_FEATURE:
+		src_id = it->d.fu->bSourceId;
+		it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
+		if (it->inputs == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return uaudio_io_terminaltype(outtype, iot, src_id);
+		}
+		it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id);
+		it->inputs_size = 1;
+		return uaudio_merge_terminal_list(it);
+	case UDESCSUB_AC_OUTPUT:
+		it->inputs = malloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT);
+		if (it->inputs == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return NULL;
+		}
+		src_id = it->d.ot->bSourceId;
+		it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id);
+		it->inputs_size = 1;
+		iot[src_id].direct = TRUE;
+		return NULL;
+	case UDESCSUB_AC_MIXER:
+		it->inputs_size = 0;
+		it->inputs = malloc(sizeof(struct terminal_list *)
+				    * it->d.mu->bNrInPins, M_TEMP, M_NOWAIT);
+		if (it->inputs == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return NULL;
+		}
+		for (i = 0; i < it->d.mu->bNrInPins; i++) {
+			src_id = it->d.mu->baSourceId[i];
+			it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
+							       src_id);
+			it->inputs_size++;
+		}
+		return uaudio_merge_terminal_list(it);
+	case UDESCSUB_AC_SELECTOR:
+		it->inputs_size = 0;
+		it->inputs = malloc(sizeof(struct terminal_list *)
+				    * it->d.su->bNrInPins, M_TEMP, M_NOWAIT);
+		if (it->inputs == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return NULL;
+		}
+		for (i = 0; i < it->d.su->bNrInPins; i++) {
+			src_id = it->d.su->baSourceId[i];
+			it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
+							       src_id);
+			it->inputs_size++;
+		}
+		return uaudio_merge_terminal_list(it);
+	case UDESCSUB_AC_PROCESSING:
+		it->inputs_size = 0;
+		it->inputs = malloc(sizeof(struct terminal_list *)
+				    * it->d.pu->bNrInPins, M_TEMP, M_NOWAIT);
+		if (it->inputs == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return NULL;
+		}
+		for (i = 0; i < it->d.pu->bNrInPins; i++) {
+			src_id = it->d.pu->baSourceId[i];
+			it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
+							       src_id);
+			it->inputs_size++;
+		}
+		return uaudio_merge_terminal_list(it);
+	case UDESCSUB_AC_EXTENSION:
+		it->inputs_size = 0;
+		it->inputs = malloc(sizeof(struct terminal_list *)
+				    * it->d.eu->bNrInPins, M_TEMP, M_NOWAIT);
+		if (it->inputs == NULL) {
+			printf("uaudio_io_terminaltype: no memory\n");
+			return NULL;
+		}
+		for (i = 0; i < it->d.eu->bNrInPins; i++) {
+			src_id = it->d.eu->baSourceId[i];
+			it->inputs[i] = uaudio_io_terminaltype(outtype, iot,
+							       src_id);
+			it->inputs_size++;
+		}
+		return uaudio_merge_terminal_list(it);
+	case UDESCSUB_AC_HEADER:
+	default:
+		return NULL;
+	}
+}
+
+Static usbd_status
+uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
 {
 	usbd_status err;
 
@@ -1204,46 +1733,55 @@ uaudio_identify(struct uaudio_softc *sc,
 	return (uaudio_identify_as(sc, cdesc));
 }
 
-void
-uaudio_add_alt(struct uaudio_softc *sc, struct as_info *ai)
+Static void
+uaudio_add_alt(struct uaudio_softc *sc, const struct as_info *ai)
 {
-	size_t len = sizeof(*ai) * (sc->sc_nalts + 1);
-	struct as_info *nai = sc->sc_nalts == 0 ?
-	  malloc(len, M_USBDEV, M_NOWAIT) :
-	  realloc(sc->sc_alts, len, M_USBDEV, M_NOWAIT);
+	size_t len;
+	struct as_info *nai;
 
+	len = sizeof(*ai) * (sc->sc_nalts + 1);
+	nai = malloc(len, M_USBDEV, M_NOWAIT);
 	if (nai == NULL) {
 		printf("uaudio_add_alt: no memory\n");
 		return;
 	}
-
+	/* Copy old data, if there was any */
+	if (sc->sc_nalts != 0) {
+		memcpy(nai, sc->sc_alts, sizeof(*ai) * (sc->sc_nalts));
+		free(sc->sc_alts, M_USBDEV);
+	}
 	sc->sc_alts = nai;
 	DPRINTFN(2,("uaudio_add_alt: adding alt=%d, enc=%d\n",
 		    ai->alt, ai->encoding));
 	sc->sc_alts[sc->sc_nalts++] = *ai;
 }
 
-usbd_status
-uaudio_process_as(struct uaudio_softc *sc, char *buf, int *offsp,
-		  int size, usb_interface_descriptor_t *id)
+Static usbd_status
+uaudio_process_as(struct uaudio_softc *sc, const char *buf, int *offsp,
+		  int size, const usb_interface_descriptor_t *id)
 #define offs (*offsp)
 {
-	struct usb_audio_streaming_interface_descriptor *asid;
-	struct usb_audio_streaming_type1_descriptor *asf1d;
-	usb_endpoint_descriptor_audio_t *ed;
-	struct usb_audio_streaming_endpoint_descriptor *sed;
+	const struct usb_audio_streaming_interface_descriptor *asid;
+	const struct usb_audio_streaming_type1_descriptor *asf1d;
+	const usb_endpoint_descriptor_audio_t *ed;
+	const usb_endpoint_descriptor_audio_t *epdesc1;
+	const struct usb_audio_streaming_endpoint_descriptor *sed;
 	int format, chan, prec, enc;
-	int dir, type;
+	int dir, type, sync;
 	struct as_info ai;
+	const char *format_str;
 
-	asid = (void *)(buf + offs);
+	asid = (const void *)(buf + offs);
 	if (asid->bDescriptorType != UDESC_CS_INTERFACE ||
 	    asid->bDescriptorSubtype != AS_GENERAL)
 		return (USBD_INVAL);
+	DPRINTF(("uaudio_process_as: asid: bTerminakLink=%d wFormatTag=%d\n",
+		 asid->bTerminalLink, UGETW(asid->wFormatTag)));
 	offs += asid->bLength;
 	if (offs > size)
 		return (USBD_INVAL);
-	asf1d = (void *)(buf + offs);
+
+	asf1d = (const void *)(buf + offs);
 	if (asf1d->bDescriptorType != UDESC_CS_INTERFACE ||
 	    asf1d->bDescriptorSubtype != FORMAT_TYPE)
 		return (USBD_INVAL);
@@ -1257,10 +1795,10 @@ uaudio_process_as(struct uaudio_softc *s
 		return (USBD_NORMAL_COMPLETION);
 	}
 
-	ed = (void *)(buf + offs);
+	ed = (const void *)(buf + offs);
 	if (ed->bDescriptorType != UDESC_ENDPOINT)
 		return (USBD_INVAL);
-	DPRINTF(("uaudio_process_as: endpoint bLength=%d bDescriptorType=%d "
+	DPRINTF(("uaudio_process_as: endpoint[0] bLength=%d bDescriptorType=%d "
 		 "bEndpointAddress=%d bmAttributes=0x%x wMaxPacketSize=%d "
 		 "bInterval=%d bRefresh=%d bSynchAddress=%d\n",
 		 ed->bLength, ed->bDescriptorType, ed->bEndpointAddress,
@@ -1279,78 +1817,166 @@ uaudio_process_as(struct uaudio_softc *s
 		type = UE_ISO_ASYNC;
 
 	/* We can't handle endpoints that need a sync pipe yet. */
-	if (dir == UE_DIR_IN ? type == UE_ISO_ADAPT : type == UE_ISO_ASYNC) {
-		printf("%s: ignored %sput endpoint of type %s\n",
-		       USBDEVNAME(sc->sc_dev),
-		       dir == UE_DIR_IN ? "in" : "out",
-		       dir == UE_DIR_IN ? "adaptive" : "async");
+	sync = FALSE;
+	if (dir == UE_DIR_IN && type == UE_ISO_ADAPT) {
+		sync = TRUE;
+#ifndef UAUDIO_MULTIPLE_ENDPOINTS
+		printf("%s: ignored input endpoint of type adaptive\n",
+		       USBDEVNAME(sc->sc_dev));
 		return (USBD_NORMAL_COMPLETION);
+#endif
 	}
-	
-	sed = (void *)(buf + offs);
+	if (dir != UE_DIR_IN && type == UE_ISO_ASYNC) {
+		sync = TRUE;
+#ifndef UAUDIO_MULTIPLE_ENDPOINTS
+		printf("%s: ignored output endpoint of type async\n",
+		       USBDEVNAME(sc->sc_dev));
+		return (USBD_NORMAL_COMPLETION);
+#endif
+	}
+
+	sed = (const void *)(buf + offs);
 	if (sed->bDescriptorType != UDESC_CS_ENDPOINT ||
 	    sed->bDescriptorSubtype != AS_GENERAL)
 		return (USBD_INVAL);
+	DPRINTF((" streadming_endpoint: offset=%d bLength=%d\n", offs, sed->bLength));
 	offs += sed->bLength;
 	if (offs > size)
 		return (USBD_INVAL);
-	
+
+	if (sync && id->bNumEndpoints <= 1) {
+		printf("%s: a sync-pipe endpoint but no other endpoint\n",
+		       USBDEVNAME(sc->sc_dev));
+		return USBD_INVAL;
+	}
+	if (!sync && id->bNumEndpoints > 1) {
+		printf("%s: non sync-pipe endpoint but multiple endpoints\n",
+		       USBDEVNAME(sc->sc_dev));
+		return USBD_INVAL;
+	}
+	epdesc1 = NULL;
+	if (id->bNumEndpoints > 1) {
+		epdesc1 = (const void*)(buf + offs);
+		if (epdesc1->bDescriptorType != UDESC_ENDPOINT)
+			return USBD_INVAL;
+		DPRINTF(("uaudio_process_as: endpoint[1] bLength=%d "
+			 "bDescriptorType=%d bEndpointAddress=%d "
+			 "bmAttributes=0x%x wMaxPacketSize=%d bInterval=%d "
+			 "bRefresh=%d bSynchAddress=%d\n",
+			 epdesc1->bLength, epdesc1->bDescriptorType,
+			 epdesc1->bEndpointAddress, epdesc1->bmAttributes,
+			 UGETW(epdesc1->wMaxPacketSize), epdesc1->bInterval,
+			 epdesc1->bRefresh, epdesc1->bSynchAddress));
+		offs += epdesc1->bLength;
+		if (offs > size)
+			return USBD_INVAL;
+		if (epdesc1->bSynchAddress != 0) {
+			printf("%s: invalid endpoint: bSynchAddress=0\n",
+			       USBDEVNAME(sc->sc_dev));
+			return USBD_INVAL;
+		}
+		if (UE_GET_XFERTYPE(epdesc1->bmAttributes) != UE_ISOCHRONOUS) {
+			printf("%s: invalid endpoint: bmAttributes=0x%x\n",
+			       USBDEVNAME(sc->sc_dev), epdesc1->bmAttributes);
+			return USBD_INVAL;
+		}
+		if (epdesc1->bEndpointAddress != ed->bSynchAddress) {
+			printf("%s: invalid endpoint addresses: "
+			       "ep[0]->bSynchAddress=0x%x "
+			       "ep[1]->bEndpointAddress=0x%x\n",
+			       USBDEVNAME(sc->sc_dev), ed->bSynchAddress,
+			       epdesc1->bEndpointAddress);
+			return USBD_INVAL;
+		}
+		/* UE_GET_ADDR(epdesc1->bEndpointAddress), and epdesc1->bRefresh */
+	}
+
 	format = UGETW(asid->wFormatTag);
 	chan = asf1d->bNrChannels;
 	prec = asf1d->bBitResolution;
-	if (prec != 8 && prec != 16) {
-#ifdef USB_DEBUG
+	if (prec != 8 && prec != 16 && prec != 24) {
 		printf("%s: ignored setting with precision %d\n",
 		       USBDEVNAME(sc->sc_dev), prec);
-#endif
 		return (USBD_NORMAL_COMPLETION);
 	}
 	switch (format) {
 	case UA_FMT_PCM:
-		sc->sc_altflags |= prec == 8 ? HAS_8 : HAS_16;
+		if (prec == 8) {
+			sc->sc_altflags |= HAS_8;
+		} else if (prec == 16) {
+			sc->sc_altflags |= HAS_16;
+		} else if (prec == 24) {
+			sc->sc_altflags |= HAS_24;
+		}
 		enc = AUDIO_ENCODING_SLINEAR_LE;
+		format_str = "pcm";
 		break;
 	case UA_FMT_PCM8:
 		enc = AUDIO_ENCODING_ULINEAR_LE;
 		sc->sc_altflags |= HAS_8U;
+		format_str = "pcm8";
 		break;
 	case UA_FMT_ALAW:
 		enc = AUDIO_ENCODING_ALAW;
 		sc->sc_altflags |= HAS_ALAW;
+		format_str = "alaw";
 		break;
 	case UA_FMT_MULAW:
 		enc = AUDIO_ENCODING_ULAW;
 		sc->sc_altflags |= HAS_MULAW;
+		format_str = "mulaw";
 		break;
+	case UA_FMT_IEEE_FLOAT:
 	default:
 		printf("%s: ignored setting with format %d\n",
 		       USBDEVNAME(sc->sc_dev), format);
 		return (USBD_NORMAL_COMPLETION);
 	}
-	DPRINTFN(1,("uaudio_identify: alt=%d enc=%d chan=%d prec=%d\n",
-		    id->bAlternateSetting, enc, chan, prec));
+#ifdef USB_DEBUG
+	printf("%s: %s: %dch, %d/%dbit, %s,", USBDEVNAME(sc->sc_dev),
+	       dir == UE_DIR_IN ? "recording" : "playback",
+	       chan, prec, asf1d->bSubFrameSize * 8, format_str);
+	if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
+		printf(" %d-%dHz\n", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+	} else {
+		int r;
+		printf(" %d", UA_GETSAMP(asf1d, 0));
+		for (r = 1; r < asf1d->bSamFreqType; r++)
+			printf(",%d", UA_GETSAMP(asf1d, r));
+		printf("Hz\n");
+	}
+#endif
 	ai.alt = id->bAlternateSetting;
 	ai.encoding = enc;
+	ai.attributes = sed->bmAttributes;
 	ai.idesc = id;
 	ai.edesc = ed;
+	ai.edesc1 = epdesc1;
 	ai.asf1desc = asf1d;
+	ai.sc_busy = 0;
 	uaudio_add_alt(sc, &ai);
-	sc->sc_chan.terminal = asid->bTerminalLink; /* XXX */
-	sc->sc_chan.dir |= dir == UE_DIR_OUT ? AUMODE_PLAY : AUMODE_RECORD;
+#ifdef USB_DEBUG
+	if (ai.attributes & UA_SED_FREQ_CONTROL)
+		DPRINTFN(1, ("uaudio_process_as:  FREQ_CONTROL\n"));
+	if (ai.attributes & UA_SED_PITCH_CONTROL)
+		DPRINTFN(1, ("uaudio_process_as:  PITCH_CONTROL\n"));
+#endif
+	sc->sc_mode |= (dir == UE_DIR_OUT) ? AUMODE_PLAY : AUMODE_RECORD;
+
 	return (USBD_NORMAL_COMPLETION);
 }
 #undef offs
-	
-usbd_status
-uaudio_identify_as(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc)
+
+Static usbd_status
+uaudio_identify_as(struct uaudio_softc *sc,
+		   const usb_config_descriptor_t *cdesc)
 {
-	usb_interface_descriptor_t *id;
-	usbd_status err;
-	char *buf;
+	const usb_interface_descriptor_t *id;
+	const char *buf;
 	int size, offs;
 
 	size = UGETW(cdesc->wTotalLength);
-	buf = (char *)cdesc;
+	buf = (const char *)cdesc;
 
 	/* Locate the AudioStreaming interface descriptor. */
 	offs = 0;
@@ -1358,13 +1984,10 @@ uaudio_identify_as(struct uaudio_softc *
 	if (id == NULL)
 		return (USBD_INVAL);
 
-	sc->sc_chan.terminal = -1;
-	sc->sc_chan.dir = 0;
-
 	/* Loop through all the alternate settings. */
 	while (offs <= size) {
-		DPRINTFN(2, ("uaudio_identify: interface %d\n",
-		    id->bInterfaceNumber));
+		DPRINTFN(2, ("uaudio_identify: interface=%d offset=%d\n",
+		    id->bInterfaceNumber, offs));
 		switch (id->bNumEndpoints) {
 		case 0:
 			DPRINTFN(2, ("uaudio_identify: AS null alt=%d\n",
@@ -1372,14 +1995,15 @@ uaudio_identify_as(struct uaudio_softc *
 			sc->sc_nullalt = id->bAlternateSetting;
 			break;
 		case 1:
-			err = uaudio_process_as(sc, buf, &offs, size, id);
+#ifdef UAUDIO_MULTIPLE_ENDPOINTS
+		case 2:
+#endif
+			uaudio_process_as(sc, buf, &offs, size, id);
 			break;
 		default:
-#ifdef USB_DEBUG
 			printf("%s: ignored audio interface with %d "
 			       "endpoints\n",
 			       USBDEVNAME(sc->sc_dev), id->bNumEndpoints);
-#endif
 			break;
 		}
 		id = uaudio_find_iface(buf, size, &offs,UISUBCLASS_AUDIOSTREAM);
@@ -1389,30 +2013,30 @@ uaudio_identify_as(struct uaudio_softc *
 	if (offs > size)
 		return (USBD_INVAL);
 	DPRINTF(("uaudio_identify_as: %d alts available\n", sc->sc_nalts));
-	if (sc->sc_chan.terminal < 0) {
-		printf("%s: no useable endpoint found\n", 
+
+	if (sc->sc_mode == 0) {
+		printf("%s: no usable endpoint found\n",
 		       USBDEVNAME(sc->sc_dev));
 		return (USBD_INVAL);
 	}
 
-#ifndef NO_RECORDING
-	if (sc->sc_chan.dir == (AUMODE_PLAY | AUMODE_RECORD))
-		sc->sc_props |= AUDIO_PROP_FULLDUPLEX;
-#endif
 	return (USBD_NORMAL_COMPLETION);
 }
 
-usbd_status
-uaudio_identify_ac(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc)
+Static usbd_status
+uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc)
 {
-	usb_interface_descriptor_t *id;
-	struct usb_audio_control_descriptor *acdp;
-	usb_descriptor_t *dp, *dps[256];
-	char *buf, *ibuf, *ibufend;
-	int size, offs, aclen, ndps, i;
+	struct io_terminal* iot;
+	const usb_interface_descriptor_t *id;
+	const struct usb_audio_control_descriptor *acdp;
+	const usb_descriptor_t *dp;
+	const struct usb_audio_output_terminal *pot;
+	struct terminal_list *tml;
+	const char *buf, *ibuf, *ibufend;
+	int size, offs, aclen, ndps, i, j;
 
 	size = UGETW(cdesc->wTotalLength);
-	buf = (char *)cdesc;
+	buf = (const char *)cdesc;
 
 	/* Locate the AudioControl interface descriptor. */
 	offs = 0;
@@ -1422,11 +2046,11 @@ uaudio_identify_ac(struct uaudio_softc *
 	if (offs + sizeof *acdp > size)
 		return (USBD_INVAL);
 	sc->sc_ac_iface = id->bInterfaceNumber;
-	DPRINTFN(2,("uaudio_identify: AC interface is %d\n", sc->sc_ac_iface));
+	DPRINTFN(2,("uaudio_identify_ac: AC interface is %d\n", sc->sc_ac_iface));
 
 	/* A class-specific AC interface header should follow. */
 	ibuf = buf + offs;
-	acdp = (struct usb_audio_control_descriptor *)ibuf;
+	acdp = (const struct usb_audio_control_descriptor *)ibuf;
 	if (acdp->bDescriptorType != UDESC_CS_INTERFACE ||
 	    acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)
 		return (USBD_INVAL);
@@ -1439,127 +2063,266 @@ uaudio_identify_ac(struct uaudio_softc *
 		return (USBD_INVAL);
 
 	sc->sc_audio_rev = UGETW(acdp->bcdADC);
-	DPRINTFN(2,("uaudio_identify: found AC header, vers=%03x, len=%d\n",
+	DPRINTFN(2,("uaudio_identify_ac: found AC header, vers=%03x, len=%d\n",
 		 sc->sc_audio_rev, aclen));
 
 	sc->sc_nullalt = -1;
 
 	/* Scan through all the AC specific descriptors */
 	ibufend = ibuf + aclen;
-	dp = (usb_descriptor_t *)ibuf;
+	dp = (const usb_descriptor_t *)ibuf;
 	ndps = 0;
-	memset(dps, 0, sizeof dps);
+	iot = malloc(sizeof(struct io_terminal) * 256, M_TEMP, M_NOWAIT | M_ZERO);
+	if (iot == NULL) {
+		printf("%s: no memory\n", __func__);
+		return USBD_NOMEM;
+	}
 	for (;;) {
 		ibuf += dp->bLength;
 		if (ibuf >= ibufend)
 			break;
-		dp = (usb_descriptor_t *)ibuf;
+		dp = (const usb_descriptor_t *)ibuf;
 		if (ibuf + dp->bLength > ibufend)
 			return (USBD_INVAL);
 		if (dp->bDescriptorType != UDESC_CS_INTERFACE) {
-			printf("uaudio_identify: skip desc type=0x%02x\n",
+			printf("uaudio_identify_ac: skip desc type=0x%02x\n",
 			       dp->bDescriptorType);
 			continue;
 		}
-		i = ((struct usb_audio_input_terminal *)dp)->bTerminalId;
-		dps[i] = dp;
+		i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId;
+		iot[i].d.desc = dp;
 		if (i > ndps)
 			ndps = i;
 	}
 	ndps++;
 
+	/* construct io_terminal */
 	for (i = 0; i < ndps; i++) {
-		dp = dps[i];
+		dp = iot[i].d.desc;
 		if (dp == NULL)
 			continue;
-		DPRINTF(("uaudio_identify: subtype=%d\n", 
-			 dp->bDescriptorSubtype));
+		if (dp->bDescriptorSubtype != UDESCSUB_AC_OUTPUT)
+			continue;
+		pot = iot[i].d.ot;
+		tml = uaudio_io_terminaltype(UGETW(pot->wTerminalType), iot, i);
+		if (tml != NULL)
+			free(tml, M_TEMP);
+	}
+
+#ifdef USB_DEBUG
+	for (i = 0; i < 256; i++) {
+		struct usb_audio_cluster cluster;
+
+		if (iot[i].d.desc == NULL)
+			continue;
+		logprintf("id %d:\t", i);
+		switch (iot[i].d.desc->bDescriptorSubtype) {
+		case UDESCSUB_AC_INPUT:
+			logprintf("AC_INPUT type=%s\n", uaudio_get_terminal_name
+				  (UGETW(iot[i].d.it->wTerminalType)));
+			logprintf("\t");
+			cluster = uaudio_get_cluster(i, iot);
+			uaudio_dump_cluster(&cluster);
+			logprintf("\n");
+			break;
+		case UDESCSUB_AC_OUTPUT:
+			logprintf("AC_OUTPUT type=%s ", uaudio_get_terminal_name
+				  (UGETW(iot[i].d.ot->wTerminalType)));
+			logprintf("src=%d\n", iot[i].d.ot->bSourceId);
+			break;
+		case UDESCSUB_AC_MIXER:
+			logprintf("AC_MIXER src=");
+			for (j = 0; j < iot[i].d.mu->bNrInPins; j++)
+				logprintf("%d ", iot[i].d.mu->baSourceId[j]);
+			logprintf("\n\t");
+			cluster = uaudio_get_cluster(i, iot);
+			uaudio_dump_cluster(&cluster);
+			logprintf("\n");
+			break;
+		case UDESCSUB_AC_SELECTOR:
+			logprintf("AC_SELECTOR src=");
+			for (j = 0; j < iot[i].d.su->bNrInPins; j++)
+				logprintf("%d ", iot[i].d.su->baSourceId[j]);
+			logprintf("\n");
+			break;
+		case UDESCSUB_AC_FEATURE:
+			logprintf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId);
+			break;
+		case UDESCSUB_AC_PROCESSING:
+			logprintf("AC_PROCESSING src=");
+			for (j = 0; j < iot[i].d.pu->bNrInPins; j++)
+				logprintf("%d ", iot[i].d.pu->baSourceId[j]);
+			logprintf("\n\t");
+			cluster = uaudio_get_cluster(i, iot);
+			uaudio_dump_cluster(&cluster);
+			logprintf("\n");
+			break;
+		case UDESCSUB_AC_EXTENSION:
+			logprintf("AC_EXTENSION src=");
+			for (j = 0; j < iot[i].d.eu->bNrInPins; j++)
+				logprintf("%d ", iot[i].d.eu->baSourceId[j]);
+			logprintf("\n\t");
+			cluster = uaudio_get_cluster(i, iot);
+			uaudio_dump_cluster(&cluster);
+			logprintf("\n");
+			break;
+		default:
+			logprintf("unknown audio control (subtype=%d)\n",
+				  iot[i].d.desc->bDescriptorSubtype);
+		}
+		for (j = 0; j < iot[i].inputs_size; j++) {
+			int k;
+			logprintf("\tinput%d: ", j);
+			tml = iot[i].inputs[j];
+			if (tml == NULL) {
+				logprintf("NULL\n");
+				continue;
+			}
+			for (k = 0; k < tml->size; k++)
+				logprintf("%s ", uaudio_get_terminal_name
+					  (tml->terminals[k]));
+			logprintf("\n");
+		}
+		logprintf("\toutput: ");
+		tml = iot[i].output;
+		for (j = 0; j < tml->size; j++)
+			logprintf("%s ", uaudio_get_terminal_name(tml->terminals[j]));
+		logprintf("\n");
+	}
+#endif
+
+	for (i = 0; i < ndps; i++) {
+		dp = iot[i].d.desc;
+		if (dp == NULL)
+			continue;
+		DPRINTF(("uaudio_identify_ac: id=%d subtype=%d\n",
+			 i, dp->bDescriptorSubtype));
 		switch (dp->bDescriptorSubtype) {
 		case UDESCSUB_AC_HEADER:
-			printf("uaudio_identify: unexpected AC header\n");
+			printf("uaudio_identify_ac: unexpected AC header\n");
 			break;
 		case UDESCSUB_AC_INPUT:
-			uaudio_add_input(sc, dp, dps);
+			uaudio_add_input(sc, iot, i);
 			break;
 		case UDESCSUB_AC_OUTPUT:
-			uaudio_add_output(sc, dp, dps);
+			uaudio_add_output(sc, iot, i);
 			break;
 		case UDESCSUB_AC_MIXER:
-			uaudio_add_mixer(sc, dp, dps);
+			uaudio_add_mixer(sc, iot, i);
 			break;
 		case UDESCSUB_AC_SELECTOR:
-			uaudio_add_selector(sc, dp, dps);
+			uaudio_add_selector(sc, iot, i);
 			break;
 		case UDESCSUB_AC_FEATURE:
-			uaudio_add_feature(sc, dp, dps);
+			uaudio_add_feature(sc, iot, i);
 			break;
 		case UDESCSUB_AC_PROCESSING:
-			uaudio_add_processing(sc, dp, dps);
+			uaudio_add_processing(sc, iot, i);
 			break;
 		case UDESCSUB_AC_EXTENSION:
-			uaudio_add_extension(sc, dp, dps);
+			uaudio_add_extension(sc, iot, i);
 			break;
 		default:
-			printf("uaudio_identify: bad AC desc subtype=0x%02x\n",
+			printf("uaudio_identify_ac: bad AC desc subtype=0x%02x\n",
 			       dp->bDescriptorSubtype);
 			break;
 		}
 	}
+
+	/* delete io_terminal */
+	for (i = 0; i < 256; i++) {
+		if (iot[i].d.desc == NULL)
+			continue;
+		if (iot[i].inputs != NULL) {
+			for (j = 0; j < iot[i].inputs_size; j++) {
+				if (iot[i].inputs[j] != NULL)
+					free(iot[i].inputs[j], M_TEMP);
+			}
+			free(iot[i].inputs, M_TEMP);
+		}
+		if (iot[i].output != NULL)
+			free(iot[i].output, M_TEMP);
+		iot[i].d.desc = NULL;
+	}
+	free(iot, M_TEMP);
+
 	return (USBD_NORMAL_COMPLETION);
 }
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-int
+Static int
 uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi)
 {
 	struct uaudio_softc *sc = addr;
 	struct mixerctl *mc;
-	int n, nctls;
+	int n, nctls, i;
 
 	DPRINTFN(2,("uaudio_query_devinfo: index=%d\n", mi->index));
 	if (sc->sc_dying)
 		return (EIO);
-    
+
 	n = mi->index;
 	nctls = sc->sc_nctls;
 
-	if (n < 0 || n >= nctls) {
-		switch (n - nctls) {
-		case UAC_OUTPUT:
-			mi->type = AUDIO_MIXER_CLASS;
-			mi->mixer_class = nctls + UAC_OUTPUT;
-			mi->next = mi->prev = AUDIO_MIXER_LAST;
-			strcpy(mi->label.name, AudioCoutputs);
-			return (0);
-		case UAC_INPUT:
-			mi->type = AUDIO_MIXER_CLASS;
-			mi->mixer_class = nctls + UAC_INPUT;
-			mi->next = mi->prev = AUDIO_MIXER_LAST;
-			strcpy(mi->label.name, AudioCinputs);
-			return (0);
-		case UAC_EQUAL:
-			mi->type = AUDIO_MIXER_CLASS;
-			mi->mixer_class = nctls + UAC_EQUAL;
-			mi->next = mi->prev = AUDIO_MIXER_LAST;
-			strcpy(mi->label.name, AudioCequalization);
-			return (0);
-		default:
-			return (ENXIO);
-		}
+	switch (n) {
+	case UAC_OUTPUT:
+		mi->type = AUDIO_MIXER_CLASS;
+		mi->mixer_class = UAC_OUTPUT;
+		mi->next = mi->prev = AUDIO_MIXER_LAST;
+		strlcpy(mi->label.name, AudioCoutputs, sizeof(mi->label.name));
+		return (0);
+	case UAC_INPUT:
+		mi->type = AUDIO_MIXER_CLASS;
+		mi->mixer_class = UAC_INPUT;
+		mi->next = mi->prev = AUDIO_MIXER_LAST;
+		strlcpy(mi->label.name, AudioCinputs, sizeof(mi->label.name));
+		return (0);
+	case UAC_EQUAL:
+		mi->type = AUDIO_MIXER_CLASS;
+		mi->mixer_class = UAC_EQUAL;
+		mi->next = mi->prev = AUDIO_MIXER_LAST;
+		strlcpy(mi->label.name, AudioCequalization,
+		    sizeof(mi->label.name));
+		return (0);
+	case UAC_RECORD:
+		mi->type = AUDIO_MIXER_CLASS;
+		mi->mixer_class = UAC_RECORD;
+		mi->next = mi->prev = AUDIO_MIXER_LAST;
+		strlcpy(mi->label.name, AudioCrecord, sizeof(mi->label.name));
+		return 0;
+	default:
+		break;
 	}
+
+	n -= UAC_NCLASSES;
+	if (n < 0 || n >= nctls)
+		return (ENXIO);
+
 	mc = &sc->sc_ctls[n];
-	strncpy(mi->label.name, mc->ctlname, MAX_AUDIO_DEV_LEN);
+	strlcpy(mi->label.name, mc->ctlname, sizeof(mi->label.name));
 	mi->mixer_class = mc->class;
 	mi->next = mi->prev = AUDIO_MIXER_LAST;	/* XXX */
 	switch (mc->type) {
 	case MIX_ON_OFF:
 		mi->type = AUDIO_MIXER_ENUM;
 		mi->un.e.num_mem = 2;
-		strcpy(mi->un.e.member[0].label.name, AudioNoff);
+		strlcpy(mi->un.e.member[0].label.name, AudioNoff,
+		    sizeof(mi->un.e.member[0].label.name));
 		mi->un.e.member[0].ord = 0;
-		strcpy(mi->un.e.member[1].label.name, AudioNon);
+		strlcpy(mi->un.e.member[1].label.name, AudioNon,
+		    sizeof(mi->un.e.member[1].label.name));
 		mi->un.e.member[1].ord = 1;
 		break;
+	case MIX_SELECTOR:
+		mi->type = AUDIO_MIXER_ENUM;
+		mi->un.e.num_mem = mc->maxval - mc->minval + 1;
+		for (i = 0; i <= mc->maxval - mc->minval; i++) {
+			snprintf(mi->un.e.member[i].label.name,
+				 sizeof(mi->un.e.member[i].label.name),
+				 "%d", i + mc->minval);
+			mi->un.e.member[i].ord = i + mc->minval;
+		}
+		break;
 	default:
 		mi->type = AUDIO_MIXER_VALUE;
 		strncpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN);
@@ -1570,60 +2333,42 @@ uaudio_query_devinfo(void *addr, mixer_d
 	return (0);
 }
 
-int
+Static int
 uaudio_open(void *addr, int flags)
 {
 	struct uaudio_softc *sc = addr;
 
-        DPRINTF(("uaudio_open: sc=%p\n", sc));
+	DPRINTF(("uaudio_open: sc=%p\n", sc));
 	if (sc->sc_dying)
 		return (EIO);
 
-	if (sc->sc_chan.terminal < 0)
-		return (ENXIO);
-
-	if ((flags & FREAD) && !(sc->sc_chan.dir & AUMODE_RECORD))
+	if ((flags & FWRITE) && !(sc->sc_mode & AUMODE_PLAY))
 		return (EACCES);
-	if ((flags & FWRITE) && !(sc->sc_chan.dir & AUMODE_PLAY))
+	if ((flags & FREAD) && !(sc->sc_mode & AUMODE_RECORD))
 		return (EACCES);
 
-        sc->sc_chan.intr = 0;
-
-        return (0);
+	return (0);
 }
 
 /*
  * Close function is called at splaudio().
  */
-void
+Static void
 uaudio_close(void *addr)
 {
-	struct uaudio_softc *sc = addr;
-
-	if (sc->sc_dying)
-		return (EIO);
-
-	DPRINTF(("uaudio_close: sc=%p\n", sc));
-	uaudio_halt_in_dma(sc);
-	uaudio_halt_out_dma(sc);
-
-	sc->sc_chan.intr = 0;
 }
 
-int
+Static int
 uaudio_drain(void *addr)
 {
 	struct uaudio_softc *sc = addr;
 
-	if (sc->sc_dying)
-		return (EIO);
-
 	usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES);
 
 	return (0);
 }
 
-int
+Static int
 uaudio_halt_out_dma(void *addr)
 {
 	struct uaudio_softc *sc = addr;
@@ -1632,29 +2377,31 @@ uaudio_halt_out_dma(void *addr)
 		return (EIO);
 
 	DPRINTF(("uaudio_halt_out_dma: enter\n"));
-	if (sc->sc_chan.pipe != NULL) {
-		uaudio_chan_close(sc, &sc->sc_chan);
-		sc->sc_chan.pipe = 0;
-		uaudio_chan_free_buffers(sc, &sc->sc_chan);
+	if (sc->sc_playchan.pipe != NULL) {
+		uaudio_chan_close(sc, &sc->sc_playchan);
+		sc->sc_playchan.pipe = NULL;
+		uaudio_chan_free_buffers(sc, &sc->sc_playchan);
+		sc->sc_playchan.intr = NULL;
 	}
-        return (0);
+	return (0);
 }
 
-int
+Static int
 uaudio_halt_in_dma(void *addr)
 {
 	struct uaudio_softc *sc = addr;
 
 	DPRINTF(("uaudio_halt_in_dma: enter\n"));
-	if (sc->sc_chan.pipe != NULL) {
-		uaudio_chan_close(sc, &sc->sc_chan);
-		sc->sc_chan.pipe = 0;
-		uaudio_chan_free_buffers(sc, &sc->sc_chan);
+	if (sc->sc_recchan.pipe != NULL) {
+		uaudio_chan_close(sc, &sc->sc_recchan);
+		sc->sc_recchan.pipe = NULL;
+		uaudio_chan_free_buffers(sc, &sc->sc_recchan);
+		sc->sc_recchan.intr = NULL;
 	}
-        return (0);
+	return (0);
 }
 
-int
+Static int
 uaudio_getdev(void *addr, struct audio_device *retp)
 {
 	struct uaudio_softc *sc = addr;
@@ -1662,24 +2409,30 @@ uaudio_getdev(void *addr, struct audio_d
 	DPRINTF(("uaudio_mixer_getdev:\n"));
 	if (sc->sc_dying)
 		return (EIO);
-    
+
 	*retp = uaudio_device;
-        return (0);
+	return (0);
 }
 
 /*
  * Make sure the block size is large enough to hold all outstanding transfers.
  */
-int
+Static int
 uaudio_round_blocksize(void *addr, int blk)
 {
 	struct uaudio_softc *sc = addr;
 	int bpf;
 
-	if (sc->sc_dying)
-		return (EIO);
-
-	bpf = sc->sc_chan.bytes_per_frame + sc->sc_chan.sample_size;
+	DPRINTF(("uaudio_round_blocksize: p.bpf=%d r.bpf=%d\n",
+		 sc->sc_playchan.bytes_per_frame,
+		 sc->sc_recchan.bytes_per_frame));
+	if (sc->sc_playchan.bytes_per_frame > sc->sc_recchan.bytes_per_frame) {
+		bpf = sc->sc_playchan.bytes_per_frame
+		    + sc->sc_playchan.sample_size;
+	} else {
+		bpf = sc->sc_recchan.bytes_per_frame
+		    + sc->sc_recchan.sample_size;
+	}
 	/* XXX */
 	bpf *= UAUDIO_NFRAMES * UAUDIO_NCHANBUFS;
 
@@ -1699,17 +2452,15 @@ uaudio_round_blocksize(void *addr, int b
 	return (blk);
 }
 
-int
+Static int
 uaudio_get_props(void *addr)
 {
-	struct uaudio_softc *sc = addr;
+	return (AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT);
 
-	return (sc->sc_props);
 }
 #endif	/* NetBSD or OpenBSD */
 
-
-int
+Static int
 uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue,
 	   int wIndex, int len)
 {
@@ -1718,8 +2469,10 @@ uaudio_get(struct uaudio_softc *sc, int 
 	usbd_status err;
 	int val;
 
+#if defined(__FreeBSD__)
 	if (sc->sc_dying)
 		return (EIO);
+#endif
 
 	if (wValue == -1)
 		return (0);
@@ -1730,9 +2483,9 @@ uaudio_get(struct uaudio_softc *sc, int 
 	USETW(req.wIndex, wIndex);
 	USETW(req.wLength, len);
 	DPRINTFN(2,("uaudio_get: type=0x%02x req=0x%02x wValue=0x%04x "
-		    "wIndex=0x%04x len=%d\n", 
+		    "wIndex=0x%04x len=%d\n",
 		    type, which, wValue, wIndex, len));
-	err = usbd_do_request(sc->sc_udev, &req, &data);
+	err = usbd_do_request(sc->sc_udev, &req, data);
 	if (err) {
 		DPRINTF(("uaudio_get: err=%s\n", usbd_errstr(err)));
 		return (-1);
@@ -1752,7 +2505,7 @@ uaudio_get(struct uaudio_softc *sc, int 
 	return (val);
 }
 
-void
+Static void
 uaudio_set(struct uaudio_softc *sc, int which, int type, int wValue,
 	   int wIndex, int len, int val)
 {
@@ -1760,8 +2513,10 @@ uaudio_set(struct uaudio_softc *sc, int 
 	u_int8_t data[4];
 	usbd_status err;
 
+#if defined(__FreeBSD__)
 	if (sc->sc_dying)
 		return;
+#endif
 
 	if (wValue == -1)
 		return;
@@ -1783,16 +2538,16 @@ uaudio_set(struct uaudio_softc *sc, int 
 		return;
 	}
 	DPRINTFN(2,("uaudio_set: type=0x%02x req=0x%02x wValue=0x%04x "
-		    "wIndex=0x%04x len=%d, val=%d\n", 
+		    "wIndex=0x%04x len=%d, val=%d\n",
 		    type, which, wValue, wIndex, len, val & 0xffff));
-	err = usbd_do_request(sc->sc_udev, &req, &data);
+	err = usbd_do_request(sc->sc_udev, &req, data);
 #ifdef USB_DEBUG
 	if (err)
 		DPRINTF(("uaudio_set: err=%d\n", err));
 #endif
 }
 
-int
+Static int
 uaudio_signext(int type, int val)
 {
 	if (!MIX_UNSIGNED(type)) {
@@ -1805,15 +2560,18 @@ uaudio_signext(int type, int val)
 }
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-int
+Static int
 uaudio_value2bsd(struct mixerctl *mc, int val)
 {
 	DPRINTFN(5, ("uaudio_value2bsd: type=%03x val=%d min=%d max=%d ",
 		     mc->type, val, mc->minval, mc->maxval));
-	if (mc->type == MIX_ON_OFF)
-		val = val != 0;
-	else
-		val = ((uaudio_signext(mc->type, val) - mc->minval) * 256
+	if (mc->type == MIX_ON_OFF) {
+		val = (val != 0);
+	} else if (mc->type == MIX_SELECTOR) {
+		if (val < mc->minval || val > mc->maxval)
+			val = mc->minval;
+	} else
+		val = ((uaudio_signext(mc->type, val) - mc->minval) * 255
 			+ mc->mul/2) / mc->mul;
 	DPRINTFN(5, ("val'=%d\n", val));
 	return (val);
@@ -1825,17 +2583,20 @@ uaudio_bsd2value(struct mixerctl *mc, in
 {
 	DPRINTFN(5,("uaudio_bsd2value: type=%03x val=%d min=%d max=%d ",
 		    mc->type, val, mc->minval, mc->maxval));
-	if (mc->type == MIX_ON_OFF)
-		val = val != 0;
-	else
-		val = (val + mc->delta/2) * mc->mul / 256 + mc->minval;
+	if (mc->type == MIX_ON_OFF) {
+		val = (val != 0);
+	} else if (mc->type == MIX_SELECTOR) {
+		if (val < mc->minval || val > mc->maxval)
+			val = mc->minval;
+	} else
+		val = (val + mc->delta/2) * mc->mul / 255 + mc->minval;
 	DPRINTFN(5, ("val'=%d\n", val));
 	return (val);
 }
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-int
-uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc, 
+Static int
+uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc,
 	       int chan)
 {
 	int val;
@@ -1847,7 +2608,7 @@ uaudio_ctl_get(struct uaudio_softc *sc, 
 }
 #endif
 
-void
+Static void
 uaudio_ctl_set(struct uaudio_softc *sc, int which, struct mixerctl *mc,
 	       int chan, int val)
 {
@@ -1857,7 +2618,7 @@ uaudio_ctl_set(struct uaudio_softc *sc, 
 }
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-int
+Static int
 uaudio_mixer_get_port(void *addr, mixer_ctrl_t *cp)
 {
 	struct uaudio_softc *sc = addr;
@@ -1868,8 +2629,8 @@ uaudio_mixer_get_port(void *addr, mixer_
 
 	if (sc->sc_dying)
 		return (EIO);
-    
-	n = cp->dev;
+
+	n = cp->dev - UAC_NCLASSES;
 	if (n < 0 || n >= sc->sc_nctls)
 		return (ENXIO);
 	mc = &sc->sc_ctls[n];
@@ -1878,6 +2639,10 @@ uaudio_mixer_get_port(void *addr, mixer_
 		if (cp->type != AUDIO_MIXER_ENUM)
 			return (EINVAL);
 		cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0);
+	} else if (mc->type == MIX_SELECTOR) {
+		if (cp->type != AUDIO_MIXER_ENUM)
+			return (EINVAL);
+		cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0);
 	} else {
 		if (cp->type != AUDIO_MIXER_VALUE)
 			return (EINVAL);
@@ -1897,8 +2662,8 @@ uaudio_mixer_get_port(void *addr, mixer_
 
 	return (0);
 }
-    
-int
+
+Static int
 uaudio_mixer_set_port(void *addr, mixer_ctrl_t *cp)
 {
 	struct uaudio_softc *sc = addr;
@@ -1908,8 +2673,8 @@ uaudio_mixer_set_port(void *addr, mixer_
 	DPRINTFN(2,("uaudio_mixer_set_port: index = %d\n", cp->dev));
 	if (sc->sc_dying)
 		return (EIO);
-    
-	n = cp->dev;
+
+	n = cp->dev - UAC_NCLASSES;
 	if (n < 0 || n >= sc->sc_nctls)
 		return (ENXIO);
 	mc = &sc->sc_ctls[n];
@@ -1918,6 +2683,10 @@ uaudio_mixer_set_port(void *addr, mixer_
 		if (cp->type != AUDIO_MIXER_ENUM)
 			return (EINVAL);
 		uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord);
+	} else if (mc->type == MIX_SELECTOR) {
+		if (cp->type != AUDIO_MIXER_ENUM)
+			return (EINVAL);
+		uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord);
 	} else {
 		if (cp->type != AUDIO_MIXER_VALUE)
 			return (EINVAL);
@@ -1935,13 +2704,13 @@ uaudio_mixer_set_port(void *addr, mixer_
 	return (0);
 }
 
-int
+Static int
 uaudio_trigger_input(void *addr, void *start, void *end, int blksize,
 		     void (*intr)(void *), void *arg,
 		     struct audio_params *param)
 {
 	struct uaudio_softc *sc = addr;
-	struct chan *ch = &sc->sc_chan;
+	struct chan *ch = &sc->sc_recchan;
 	usbd_status err;
 	int i, s;
 
@@ -1951,7 +2720,7 @@ uaudio_trigger_input(void *addr, void *s
 	DPRINTFN(3,("uaudio_trigger_input: sc=%p start=%p end=%p "
 		    "blksize=%d\n", sc, start, end, blksize));
 
-	uaudio_chan_set_param(ch, param, start, end, blksize);
+	uaudio_chan_set_param(ch, start, end, blksize);
 	DPRINTFN(3,("uaudio_trigger_input: sample_size=%d bytes/frame=%d "
 		    "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame,
 		    ch->fraction));
@@ -1966,24 +2735,24 @@ uaudio_trigger_input(void *addr, void *s
 		return (EIO);
 	}
 
-	sc->sc_chan.intr = intr;
-	sc->sc_chan.arg = arg;
+	ch->intr = intr;
+	ch->arg = arg;
 
 	s = splusb();
 	for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX -1 shouldn't be needed */
 		uaudio_chan_rtransfer(ch);
 	splx(s);
 
-        return (0);
+	return (0);
 }
-    
-int
+
+Static int
 uaudio_trigger_output(void *addr, void *start, void *end, int blksize,
 		      void (*intr)(void *), void *arg,
 		      struct audio_params *param)
 {
 	struct uaudio_softc *sc = addr;
-	struct chan *ch = &sc->sc_chan;
+	struct chan *ch = &sc->sc_playchan;
 	usbd_status err;
 	int i, s;
 
@@ -1993,7 +2762,7 @@ uaudio_trigger_output(void *addr, void *
 	DPRINTFN(3,("uaudio_trigger_output: sc=%p start=%p end=%p "
 		    "blksize=%d\n", sc, start, end, blksize));
 
-	uaudio_chan_set_param(ch, param, start, end, blksize);
+	uaudio_chan_set_param(ch, start, end, blksize);
 	DPRINTFN(3,("uaudio_trigger_output: sample_size=%d bytes/frame=%d "
 		    "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame,
 		    ch->fraction));
@@ -2008,30 +2777,32 @@ uaudio_trigger_output(void *addr, void *
 		return (EIO);
 	}
 
-	sc->sc_chan.intr = intr;
-	sc->sc_chan.arg = arg;
+	ch->intr = intr;
+	ch->arg = arg;
 
 	s = splusb();
 	for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */
 		uaudio_chan_ptransfer(ch);
 	splx(s);
 
-        return (0);
+	return (0);
 }
 #endif	/* NetBSD or OpenBSD */
 
 /* Set up a pipe for a channel. */
-usbd_status
+Static usbd_status
 uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch)
 {
-	struct as_info *as = &sc->sc_alts[sc->sc_curaltidx];
+	struct as_info *as = &sc->sc_alts[ch->altidx];
 	int endpt = as->edesc->bEndpointAddress;
 	usbd_status err;
 
+#if defined(__FreeBSD__)
 	if (sc->sc_dying)
 		return (EIO);
+#endif
 
-	DPRINTF(("uaudio_open_chan: endpt=0x%02x, speed=%d, alt=%d\n", 
+	DPRINTF(("uaudio_chan_open: endpt=0x%02x, speed=%d, alt=%d\n",
 		 endpt, ch->sample_rate, as->alt));
 
 	/* Set alternate interface corresponding to the mode. */
@@ -2039,39 +2810,59 @@ uaudio_chan_open(struct uaudio_softc *sc
 	if (err)
 		return (err);
 
-	/* Some devices do not support this request, so ignore errors. */
-#ifdef USB_DEBUG
-	err = uaudio_set_speed(sc, endpt, ch->sample_rate);
-	if (err)
-		DPRINTF(("uaudio_chan_open: set_speed failed err=%s\n",
-			 usbd_errstr(err)));
-#else
-	(void)uaudio_set_speed(sc, endpt, ch->sample_rate);
-#endif
+	/*
+	 * If just one sampling rate is supported,
+	 * no need to call uaudio_set_speed().
+	 * Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request.
+	 */
+	if (as->asf1desc->bSamFreqType != 1) {
+		err = uaudio_set_speed(sc, endpt, ch->sample_rate);
+		if (err)
+			DPRINTF(("uaudio_chan_open: set_speed failed err=%s\n",
+				 usbd_errstr(err)));
+	}
 
-	DPRINTF(("uaudio_open_chan: create pipe to 0x%02x\n", endpt));
+	ch->pipe = 0;
+	ch->sync_pipe = 0;
+	DPRINTF(("uaudio_chan_open: create pipe to 0x%02x\n", endpt));
 	err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->pipe);
-	return (err);
+	if (err)
+		return err;
+	if (as->edesc1 != NULL) {
+		endpt = as->edesc1->bEndpointAddress;
+		DPRINTF(("uaudio_chan_open: create sync-pipe to 0x%02x\n", endpt));
+		err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->sync_pipe);
+	}
+	return err;
 }
 
-void
+Static void
 uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch)
 {
-	struct as_info *as = &sc->sc_alts[sc->sc_curaltidx];
+	struct as_info *as = &sc->sc_alts[ch->altidx];
 
+#if defined(__FreeBSD__)
 	if (sc->sc_dying)
 		return ;
+#endif
 
+	as->sc_busy = 0;
 	if (sc->sc_nullalt >= 0) {
-		DPRINTF(("uaudio_close_chan: set null alt=%d\n",
+		DPRINTF(("uaudio_chan_close: set null alt=%d\n",
 			 sc->sc_nullalt));
 		usbd_set_interface(as->ifaceh, sc->sc_nullalt);
 	}
-	usbd_abort_pipe(ch->pipe);
-	usbd_close_pipe(ch->pipe);
+	if (ch->pipe) {
+		usbd_abort_pipe(ch->pipe);
+		usbd_close_pipe(ch->pipe);
+	}
+	if (ch->sync_pipe) {
+		usbd_abort_pipe(ch->sync_pipe);
+		usbd_close_pipe(ch->sync_pipe);
+	}
 }
 
-usbd_status
+Static usbd_status
 uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch)
 {
 	usbd_xfer_handle xfer;
@@ -2102,7 +2893,7 @@ bad:
 	return (USBD_NOMEM);
 }
 
-void
+Static void
 uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch)
 {
 	int i;
@@ -2112,7 +2903,7 @@ uaudio_chan_free_buffers(struct uaudio_s
 }
 
 /* Called at splusb() */
-void
+Static void
 uaudio_chan_ptransfer(struct chan *ch)
 {
 	struct chanbuf *cb;
@@ -2133,7 +2924,7 @@ uaudio_chan_ptransfer(struct chan *ch)
 		size = ch->bytes_per_frame;
 		residue += ch->fraction;
 		if (residue >= USB_FRAMES_PER_SECOND) {
-			if (!ch->nofrac)
+			if ((ch->sc->sc_altflags & UA_NOFRAC) == 0)
 				size += ch->sample_size;
 			residue -= USB_FRAMES_PER_SECOND;
 		}
@@ -2143,7 +2934,7 @@ uaudio_chan_ptransfer(struct chan *ch)
 	ch->residue = residue;
 	cb->size = total;
 
-	/* 
+	/*
 	 * Transfer data from upper layer buffer to channel buffer, taking
 	 * care of wrapping the upper layer buffer.
 	 */
@@ -2170,14 +2961,14 @@ uaudio_chan_ptransfer(struct chan *ch)
 
 	DPRINTFN(5,("uaudio_chan_transfer: ptransfer xfer=%p\n", cb->xfer));
 	/* Fill the request */
-	usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, 
-			     UAUDIO_NFRAMES, USBD_NO_COPY, 
+	usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes,
+			     UAUDIO_NFRAMES, USBD_NO_COPY,
 			     uaudio_chan_pintr);
 
 	(void)usbd_transfer(cb->xfer);
 }
 
-void
+Static void
 uaudio_chan_pintr(usbd_xfer_handle xfer, usbd_private_handle priv,
 		  usbd_status status)
 {
@@ -2211,7 +3002,7 @@ uaudio_chan_pintr(usbd_xfer_handle xfer,
 	/* Call back to upper layer */
 	while (ch->transferred >= ch->blksize) {
 		ch->transferred -= ch->blksize;
-		DPRINTFN(5,("uaudio_chan_pintr: call %p(%p)\n", 
+		DPRINTFN(5,("uaudio_chan_pintr: call %p(%p)\n",
 			    ch->intr, ch->arg));
 		ch->intr(ch->arg);
 	}
@@ -2223,7 +3014,7 @@ uaudio_chan_pintr(usbd_xfer_handle xfer,
 }
 
 /* Called at splusb() */
-void
+Static void
 uaudio_chan_rtransfer(struct chan *ch)
 {
 	struct chanbuf *cb;
@@ -2242,13 +3033,8 @@ uaudio_chan_rtransfer(struct chan *ch)
 	total = 0;
 	for (i = 0; i < UAUDIO_NFRAMES; i++) {
 		size = ch->bytes_per_frame;
-		residue += ch->fraction;
-		if (residue >= USB_FRAMES_PER_SECOND) {
-			if (!ch->nofrac)
-				size += ch->sample_size;
-			residue -= USB_FRAMES_PER_SECOND;
-		}
 		cb->sizes[i] = size;
+		cb->offsets[i] = total;
 		total += size;
 	}
 	ch->residue = residue;
@@ -2266,21 +3052,21 @@ uaudio_chan_rtransfer(struct chan *ch)
 
 	DPRINTFN(5,("uaudio_chan_rtransfer: transfer xfer=%p\n", cb->xfer));
 	/* Fill the request */
-	usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, 
-			     UAUDIO_NFRAMES, USBD_NO_COPY, 
+	usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes,
+			     UAUDIO_NFRAMES, USBD_NO_COPY,
 			     uaudio_chan_rintr);
 
 	(void)usbd_transfer(cb->xfer);
 }
 
-void
+Static void
 uaudio_chan_rintr(usbd_xfer_handle xfer, usbd_private_handle priv,
 		  usbd_status status)
 {
 	struct chanbuf *cb = priv;
 	struct chan *ch = cb->chan;
 	u_int32_t count;
-	int s, n;
+	int s, i, n, frsize;
 
 	/* Return if we are aborting. */
 	if (status == USBD_CANCELLED)
@@ -2290,36 +3076,34 @@ uaudio_chan_rintr(usbd_xfer_handle xfer,
 	DPRINTFN(5,("uaudio_chan_rintr: count=%d, transferred=%d\n",
 		    count, ch->transferred));
 
-	if (count < cb->size) {
-		/* if the device fails to keep up, copy last byte */
-		u_char b = count ? cb->buffer[count-1] : 0;
-		while (count < cb->size)
-			cb->buffer[count++] = b;
-	}
-
+	/* count < cb->size is normal for asynchronous source */
 #ifdef DIAGNOSTIC
-	if (count != cb->size) {
-		printf("uaudio_chan_rintr: count(%d) != size(%d)\n",
+	if (count > cb->size) {
+		printf("uaudio_chan_rintr: count(%d) > size(%d)\n",
 		       count, cb->size);
 	}
 #endif
 
-	/* 
+	/*
 	 * Transfer data from channel buffer to upper layer buffer, taking
 	 * care of wrapping the upper layer buffer.
 	 */
-	n = min(count, ch->end - ch->cur);
-	memcpy(ch->cur, cb->buffer, n);
-	ch->cur += n;
-	if (ch->cur >= ch->end)
-		ch->cur = ch->start;
-	if (count > n) {
-		memcpy(ch->cur, cb->buffer + n, count - n);
-		ch->cur += count - n;
+	for(i = 0; i < UAUDIO_NFRAMES; i++) {
+		frsize = cb->sizes[i];
+		n = min(frsize, ch->end - ch->cur);
+		memcpy(ch->cur, cb->buffer + cb->offsets[i], n);
+		ch->cur += n;
+		if (ch->cur >= ch->end)
+			ch->cur = ch->start;
+		if (frsize > n) {
+			memcpy(ch->cur, cb->buffer + cb->offsets[i] + n,
+			    frsize - n);
+			ch->cur += frsize - n;
+		}
 	}
 
 	/* Call back to upper layer */
-	ch->transferred += cb->size;
+	ch->transferred += count;
 #if defined(__FreeBSD__)
 	s = spltty();
 	chn_intr(ch->pcm_ch);
@@ -2328,7 +3112,7 @@ uaudio_chan_rintr(usbd_xfer_handle xfer,
 	s = splaudio();
 	while (ch->transferred >= ch->blksize) {
 		ch->transferred -= ch->blksize;
-		DPRINTFN(5,("uaudio_chan_rintr: call %p(%p)\n", 
+		DPRINTFN(5,("uaudio_chan_rintr: call %p(%p)\n",
 			    ch->intr, ch->arg));
 		ch->intr(ch->arg);
 	}
@@ -2340,20 +3124,30 @@ uaudio_chan_rintr(usbd_xfer_handle xfer,
 }
 
 #if defined(__NetBSD__) || defined(__OpenBSD__)
-void
-uaudio_chan_set_param(struct chan *ch, struct audio_params *param,
-		      u_char *start, u_char *end, int blksize)
+Static void
+uaudio_chan_init(struct chan *ch, int altidx, const struct audio_params *param,
+    int maxpktsize)
 {
 	int samples_per_frame, sample_size;
 
-	sample_size = param->precision * param->channels / 8;
-	samples_per_frame = param->sample_rate / USB_FRAMES_PER_SECOND;
-	ch->fraction = param->sample_rate % USB_FRAMES_PER_SECOND;
+	ch->altidx = altidx;
+	sample_size = param->precision * param->factor * param->hw_channels / 8;
+	samples_per_frame = param->hw_sample_rate / USB_FRAMES_PER_SECOND;
 	ch->sample_size = sample_size;
-	ch->sample_rate = param->sample_rate;
-	ch->bytes_per_frame = samples_per_frame * sample_size;
+	ch->sample_rate = param->hw_sample_rate;
+	if (maxpktsize == 0) {
+		ch->fraction = param->hw_sample_rate % USB_FRAMES_PER_SECOND;
+		ch->bytes_per_frame = samples_per_frame * sample_size;
+	} else {
+		ch->fraction = 0;
+		ch->bytes_per_frame = maxpktsize;
+	}
 	ch->residue = 0;
+}
 
+Static void
+uaudio_chan_set_param(struct chan *ch, u_char *start, u_char *end, int blksize)
+{
 	ch->start = start;
 	ch->end = end;
 	ch->cur = start;
@@ -2363,14 +3157,164 @@ uaudio_chan_set_param(struct chan *ch, s
 	ch->curchanbuf = 0;
 }
 
-int
+Static void
+uaudio_get_minmax_rates(int nalts, const struct as_info *alts,
+			const struct audio_params *p, int mode,
+			u_long *min, u_long *max)
+{
+	const struct usb_audio_streaming_type1_descriptor *a1d;
+	int i, j;
+
+	*min = ULONG_MAX;
+	*max = 0;
+	for (i = 0; i < nalts; i++) {
+		a1d = alts[i].asf1desc;
+		if (alts[i].sc_busy)
+			continue;
+		if (p->hw_channels != a1d->bNrChannels)
+			continue;
+		if (p->hw_precision != a1d->bBitResolution)
+			continue;
+		if (p->hw_encoding != alts[i].encoding)
+			continue;
+		if (mode != UE_GET_DIR(alts[i].edesc->bEndpointAddress))
+			continue;
+		if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
+			DPRINTFN(2,("uaudio_get_minmax_rates: cont %d-%d\n",
+				    UA_SAMP_LO(a1d), UA_SAMP_HI(a1d)));
+			if (UA_SAMP_LO(a1d) < *min)
+				*min = UA_SAMP_LO(a1d);
+			if (UA_SAMP_HI(a1d) > *max)
+				*max = UA_SAMP_HI(a1d);
+		} else {
+			for (j = 0; j < a1d->bSamFreqType; j++) {
+				DPRINTFN(2,("uaudio_get_minmax_rates: disc #%d: %d\n",
+					    j, UA_GETSAMP(a1d, j)));
+				if (UA_GETSAMP(a1d, j) < *min)
+					*min = UA_GETSAMP(a1d, j);
+				if (UA_GETSAMP(a1d, j) > *max)
+					*max = UA_GETSAMP(a1d, j);
+			}
+		}
+	}
+}
+
+Static int
+uaudio_match_alt_sub(int nalts, const struct as_info *alts,
+		     const struct audio_params *p, int mode, u_long rate)
+{
+	const struct usb_audio_streaming_type1_descriptor *a1d;
+	int i, j;
+
+	DPRINTF(("uaudio_match_alt_sub: search for %luHz %dch\n",
+		 rate, p->hw_channels));
+	for (i = 0; i < nalts; i++) {
+		a1d = alts[i].asf1desc;
+		if (alts[i].sc_busy)
+			continue;
+		if (p->hw_channels != a1d->bNrChannels)
+			continue;
+		if (p->hw_precision != a1d->bBitResolution)
+			continue;
+		if (p->hw_encoding != alts[i].encoding)
+			continue;
+		if (mode != UE_GET_DIR(alts[i].edesc->bEndpointAddress))
+			continue;
+		if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
+			DPRINTFN(3,("uaudio_match_alt_sub: cont %d-%d\n",
+				    UA_SAMP_LO(a1d), UA_SAMP_HI(a1d)));
+			if (UA_SAMP_LO(a1d) <= rate && rate <= UA_SAMP_HI(a1d))
+				return i;
+		} else {
+			for (j = 0; j < a1d->bSamFreqType; j++) {
+				DPRINTFN(3,("uaudio_match_alt_sub: disc #%d: %d\n",
+					    j, UA_GETSAMP(a1d, j)));
+				/* XXX allow for some slack */
+				if (UA_GETSAMP(a1d, j) == rate)
+					return i;
+			}
+		}
+	}
+	return -1;
+}
+
+Static int
+uaudio_match_alt_chan(int nalts, const struct as_info *alts,
+		      struct audio_params *p, int mode)
+{
+	int i, n;
+	u_long min, max;
+	u_long rate;
+
+	/* Exact match */
+	DPRINTF(("uaudio_match_alt_chan: examine %ldHz %dch %dbit.\n",
+		 p->sample_rate, p->hw_channels, p->hw_precision));
+	i = uaudio_match_alt_sub(nalts, alts, p, mode, p->sample_rate);
+	if (i >= 0)
+		return i;
+
+	uaudio_get_minmax_rates(nalts, alts, p, mode, &min, &max);
+	DPRINTF(("uaudio_match_alt_chan: min=%lu max=%lu\n", min, max));
+	if (max <= 0)
+		return -1;
+	/* Search for biggers */
+	n = 2;
+	while ((rate = p->sample_rate * n++) <= max) {
+		i = uaudio_match_alt_sub(nalts, alts, p, mode, rate);
+		if (i >= 0) {
+			p->hw_sample_rate = rate;
+			return i;
+		}
+	}
+	if (p->sample_rate >= min) {
+		i = uaudio_match_alt_sub(nalts, alts, p, mode, max);
+		if (i >= 0) {
+			p->hw_sample_rate = max;
+			return i;
+		}
+	} else {
+		i = uaudio_match_alt_sub(nalts, alts, p, mode, min);
+		if (i >= 0) {
+			p->hw_sample_rate = min;
+			return i;
+		}
+	}
+	return -1;
+}
+
+Static int
+uaudio_match_alt(int nalts, const struct as_info *alts,
+		 struct audio_params *p, int mode)
+{
+	int i, n;
+
+	mode = mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN;
+	i = uaudio_match_alt_chan(nalts, alts, p, mode);
+	if (i >= 0)
+		return i;
+
+	for (n = p->channels + 1; n <= AUDIO_MAX_CHANNELS; n++) {
+		p->hw_channels = n;
+		i = uaudio_match_alt_chan(nalts, alts, p, mode);
+		if (i >= 0)
+			return i;
+	}
+
+	if (p->channels != 2)
+		return -1;
+	p->hw_channels = 1;
+	return uaudio_match_alt_chan(nalts, alts, p, mode);
+}
+
+Static int
 uaudio_set_params(void *addr, int setmode, int usemode,
 		  struct audio_params *play, struct audio_params *rec)
 {
 	struct uaudio_softc *sc = addr;
 	int flags = sc->sc_altflags;
 	int factor;
-	int enc, i, j;
+	int enc, i;
+	int paltidx=-1, raltidx=-1;
 	void (*swcode)(void *, u_char *buf, int cnt);
 	struct audio_params *p;
 	int mode;
@@ -2378,156 +3322,181 @@ uaudio_set_params(void *addr, int setmod
 	if (sc->sc_dying)
 		return (EIO);
 
-	if (sc->sc_chan.pipe != NULL)
+	if (((usemode & AUMODE_PLAY) && sc->sc_playchan.pipe != NULL) ||
+	    ((usemode & AUMODE_RECORD) && sc->sc_recchan.pipe != NULL))
 		return (EBUSY);
 
+	if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1)
+		sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 0;
+	if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1)
+		sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 0;
+
+	/* Some uaudio devices are unidirectional.  Don't try to find a
+	   matching mode for the unsupported direction. */
+	setmode &= sc->sc_mode;
+
 	for (mode = AUMODE_RECORD; mode != -1;
 	     mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
 		if ((setmode & mode) == 0)
 			continue;
-		if ((sc->sc_chan.dir & mode) == 0)
-			continue;
 
-		p = mode == AUMODE_PLAY ? play : rec;
+		p = (mode == AUMODE_PLAY) ? play : rec;
 
 		factor = 1;
 		swcode = 0;
 		enc = p->encoding;
 		switch (enc) {
 		case AUDIO_ENCODING_SLINEAR_BE:
-			if (p->precision == 16) {
+			/* FALLTHROUGH */
+		case AUDIO_ENCODING_SLINEAR_LE:
+			if (enc == AUDIO_ENCODING_SLINEAR_BE
+			    && p->precision == 16 && (flags & HAS_16)) {
 				swcode = swap_bytes;
 				enc = AUDIO_ENCODING_SLINEAR_LE;
-			} else if (p->precision == 8 && !(flags & HAS_8)) {
-				swcode = change_sign8;
-				enc = AUDIO_ENCODING_ULINEAR_LE;
-			}
-			break;
-		case AUDIO_ENCODING_SLINEAR_LE:
-			if (p->precision == 8 && !(flags & HAS_8)) {
-				swcode = change_sign8;
-				enc = AUDIO_ENCODING_ULINEAR_LE;
+			} else if (p->precision == 8) {
+				if (flags & HAS_8) {
+					/* No conversion */
+				} else if (flags & HAS_8U) {
+					swcode = change_sign8;
+					enc = AUDIO_ENCODING_ULINEAR_LE;
+				} else if (flags & HAS_16) {
+					factor = 2;
+					p->hw_precision = 16;
+					if (mode == AUMODE_PLAY)
+						swcode = linear8_to_linear16_le;
+					else
+						swcode = linear16_to_linear8_le;
+				}
 			}
 			break;
 		case AUDIO_ENCODING_ULINEAR_BE:
+			/* FALLTHROUGH */
+		case AUDIO_ENCODING_ULINEAR_LE:
 			if (p->precision == 16) {
-				if (mode == AUMODE_PLAY)
+				if (enc == AUDIO_ENCODING_ULINEAR_LE)
+					swcode = change_sign16_le;
+				else if (mode == AUMODE_PLAY)
 					swcode = swap_bytes_change_sign16_le;
 				else
 					swcode = change_sign16_swap_bytes_le;
 				enc = AUDIO_ENCODING_SLINEAR_LE;
-			} else if (p->precision == 8 && !(flags & HAS_8U)) {
-				swcode = change_sign8;
-				enc = AUDIO_ENCODING_SLINEAR_LE;
-			}
-			break;
-		case AUDIO_ENCODING_ULINEAR_LE:
-			if (p->precision == 16) {
-				swcode = change_sign16_le;
-				enc = AUDIO_ENCODING_SLINEAR_LE;
-			} else if (p->precision == 8 && !(flags & HAS_8U)) {
-				swcode = change_sign8;
-				enc = AUDIO_ENCODING_SLINEAR_LE;
-			}
-			break;
-		case AUDIO_ENCODING_ULAW:
-			if (!(flags & HAS_MULAW)) {
-				if (mode == AUMODE_PLAY &&
-				    (flags & HAS_16)) {
-					swcode = mulaw_to_slinear16_le;
-					factor = 2;
-					enc = AUDIO_ENCODING_SLINEAR_LE;
-				} else if (flags & HAS_8U) {
-					if (mode == AUMODE_PLAY)
-						swcode = mulaw_to_ulinear8;
-					else
-						swcode = ulinear8_to_mulaw;
-					enc = AUDIO_ENCODING_ULINEAR_LE;
+			} else if (p->precision == 8) {
+				if (flags & HAS_8U) {
+					/* No conversion */
 				} else if (flags & HAS_8) {
-					if (mode == AUMODE_PLAY)
-						swcode = mulaw_to_slinear8;
-					else
-						swcode = slinear8_to_mulaw;
+					swcode = change_sign8;
 					enc = AUDIO_ENCODING_SLINEAR_LE;
-				} else
-					return (EINVAL);
-			}
-			break;
-		case AUDIO_ENCODING_ALAW:
-			if (!(flags & HAS_ALAW)) {
-				if (mode == AUMODE_PLAY &&
-				    (flags & HAS_16)) {
-					swcode = alaw_to_slinear16_le;
+				} else if (flags & HAS_16) {
 					factor = 2;
+					p->hw_precision = 16;
 					enc = AUDIO_ENCODING_SLINEAR_LE;
-				} else if (flags & HAS_8U) {
 					if (mode == AUMODE_PLAY)
-						swcode = alaw_to_ulinear8;
-					else
-						swcode = ulinear8_to_alaw;
-					enc = AUDIO_ENCODING_ULINEAR_LE;
-				} else if (flags & HAS_8) {
-					if (mode == AUMODE_PLAY)
-						swcode = alaw_to_slinear8;
+						swcode = ulinear8_to_slinear16_le;
 					else
-						swcode = slinear8_to_alaw;
-					enc = AUDIO_ENCODING_SLINEAR_LE;
-				} else
-					return (EINVAL);
+						swcode = slinear16_to_ulinear8_le;
+				}
 			}
 			break;
+		case AUDIO_ENCODING_ULAW:
+			if (flags & HAS_MULAW)
+				break;
+			if (flags & HAS_16) {
+				if (mode == AUMODE_PLAY)
+					swcode = mulaw_to_slinear16_le;
+				else
+					swcode = slinear16_to_mulaw_le;
+				factor = 2;
+				enc = AUDIO_ENCODING_SLINEAR_LE;
+				p->hw_precision = 16;
+			} else if (flags & HAS_8U) {
+				if (mode == AUMODE_PLAY)
+					swcode = mulaw_to_ulinear8;
+				else
+					swcode = ulinear8_to_mulaw;
+				enc = AUDIO_ENCODING_ULINEAR_LE;
+			} else if (flags & HAS_8) {
+				if (mode == AUMODE_PLAY)
+					swcode = mulaw_to_slinear8;
+				else
+					swcode = slinear8_to_mulaw;
+				enc = AUDIO_ENCODING_SLINEAR_LE;
+			} else
+				return (EINVAL);
+			break;
+		case AUDIO_ENCODING_ALAW:
+			if (flags & HAS_ALAW)
+				break;
+			if (mode == AUMODE_PLAY && (flags & HAS_16)) {
+				swcode = alaw_to_slinear16_le;
+				factor = 2;
+				enc = AUDIO_ENCODING_SLINEAR_LE;
+				p->hw_precision = 16;
+			} else if (flags & HAS_8U) {
+				if (mode == AUMODE_PLAY)
+					swcode = alaw_to_ulinear8;
+				else
+					swcode = ulinear8_to_alaw;
+				enc = AUDIO_ENCODING_ULINEAR_LE;
+			} else if (flags & HAS_8) {
+				if (mode == AUMODE_PLAY)
+					swcode = alaw_to_slinear8;
+				else
+					swcode = slinear8_to_alaw;
+				enc = AUDIO_ENCODING_SLINEAR_LE;
+			} else
+				return (EINVAL);
+			break;
 		default:
 			return (EINVAL);
 		}
 		/* XXX do some other conversions... */
 
 		DPRINTF(("uaudio_set_params: chan=%d prec=%d enc=%d rate=%ld\n",
-			 p->channels, p->precision, enc, p->sample_rate));
+			 p->channels, p->hw_precision, enc, p->sample_rate));
 
-		for (i = 0; i < sc->sc_nalts; i++) {
-			struct usb_audio_streaming_type1_descriptor *a1d =
-				sc->sc_alts[i].asf1desc;
-			if (p->channels == a1d->bNrChannels &&
-			    p->precision == a1d->bBitResolution &&
-			    enc == sc->sc_alts[i].encoding &&
-			    (mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN) ==
-			    UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress)) {
-				if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) {
-					DPRINTFN(2,("uaudio_set_params: cont %d-%d\n",
-					    UA_SAMP_LO(a1d), UA_SAMP_HI(a1d)));
-					if (UA_SAMP_LO(a1d) < p->sample_rate &&
-					    p->sample_rate < UA_SAMP_HI(a1d))
-						goto found;
-				} else {
-					for (j = 0; j < a1d->bSamFreqType; j++) {
-						DPRINTFN(2,("uaudio_set_params: disc #"
-						    "%d: %d\n", j, UA_GETSAMP(a1d, j)));
-						/* XXX allow for some slack */
-						if (UA_GETSAMP(a1d, j) ==
-						    p->sample_rate)
-							goto found;
-					}
-				}
-			}
-		}
-		return (EINVAL);
+		p->hw_encoding = enc;
+		i = uaudio_match_alt(sc->sc_nalts, sc->sc_alts, p, mode);
+		if (i < 0)
+			return (EINVAL);
 
-	found:
 		p->sw_code = swcode;
 		p->factor  = factor;
-		if (usemode == mode)
-			sc->sc_curaltidx = i;
+
+		if (mode == AUMODE_PLAY)
+			paltidx = i;
+		else
+			raltidx = i;
 	}
 
-	DPRINTF(("uaudio_set_params: use altidx=%d, altno=%d\n", 
-		 sc->sc_curaltidx, 
-		 sc->sc_alts[sc->sc_curaltidx].idesc->bAlternateSetting));
-	
+	if ((setmode & AUMODE_PLAY)) {
+		/* XXX abort transfer if currently happening? */
+		uaudio_chan_init(&sc->sc_playchan, paltidx, play, 0);
+	}
+	if ((setmode & AUMODE_RECORD)) {
+		/* XXX abort transfer if currently happening? */
+		uaudio_chan_init(&sc->sc_recchan, raltidx, rec,
+		    UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize));
+	}
+
+	if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1)
+		sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 1;
+	if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1)
+		sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 1;
+
+	DPRINTF(("uaudio_set_params: use altidx=p%d/r%d, altno=p%d/r%d\n",
+		 sc->sc_playchan.altidx, sc->sc_recchan.altidx,
+		 (sc->sc_playchan.altidx >= 0)
+		   ?sc->sc_alts[sc->sc_playchan.altidx].idesc->bAlternateSetting
+		   : -1,
+		 (sc->sc_recchan.altidx >= 0)
+		   ? sc->sc_alts[sc->sc_recchan.altidx].idesc->bAlternateSetting
+		   : -1));
+
 	return (0);
 }
 #endif /* NetBSD or OpenBSD */
 
-usbd_status
+Static usbd_status
 uaudio_set_speed(struct uaudio_softc *sc, int endpt, u_int speed)
 {
 	usb_device_request_t req;
@@ -2543,14 +3512,14 @@ uaudio_set_speed(struct uaudio_softc *sc
 	data[1] = speed >> 8;
 	data[2] = speed >> 16;
 
-	return (usbd_do_request(sc->sc_udev, &req, &data));
+	return (usbd_do_request(sc->sc_udev, &req, data));
 }
 
 
 #if defined(__FreeBSD__)
 /************************************************************/
-void
-uaudio_init_params(struct uaudio_softc *sc, struct chan *ch)
+int
+uaudio_init_params(struct uaudio_softc *sc, struct chan *ch, int mode)
 {
 	int i, j, enc;
 	int samples_per_frame, sample_size;
@@ -2601,12 +3570,13 @@ uaudio_init_params(struct uaudio_softc *
 	}
 
 /*	for (mode =  ......	 */
+/*But this function is used for output only */
 		for (i = 0; i < sc->sc_nalts; i++) {
-			struct usb_audio_streaming_type1_descriptor *a1d =
+			const struct usb_audio_streaming_type1_descriptor *a1d =
 				sc->sc_alts[i].asf1desc;
 			if (ch->channels == a1d->bNrChannels &&
 			    ch->precision == a1d->bBitResolution &&
-#if 1
+#if 0
 			    enc == sc->sc_alts[i].encoding) {
 #else
 			    enc == sc->sc_alts[i].encoding &&
@@ -2618,7 +3588,10 @@ uaudio_init_params(struct uaudio_softc *
 					    UA_SAMP_LO(a1d), UA_SAMP_HI(a1d)));
 					if (UA_SAMP_LO(a1d) < ch->sample_rate &&
 					    ch->sample_rate < UA_SAMP_HI(a1d)) {
-						sc->sc_curaltidx = i;
+						if (mode == AUMODE_PLAY)
+							sc->sc_playchan.altidx = i;
+						else
+							sc->sc_recchan.altidx = i;
 						goto found;
 					}
 				} else {
@@ -2628,7 +3601,10 @@ uaudio_init_params(struct uaudio_softc *
 						/* XXX allow for some slack */
 						if (UA_GETSAMP(a1d, j) ==
 						    ch->sample_rate) {
-							sc->sc_curaltidx = i;
+							if (mode == AUMODE_PLAY)
+								sc->sc_playchan.altidx = i;
+							else
+								sc->sc_recchan.altidx = i;
 							goto found;
 						}
 					}
@@ -2636,6 +3612,8 @@ uaudio_init_params(struct uaudio_softc *
 			}
 		}
 		/* return (EINVAL); */
+		printf("uaudio: This device can't play in rate=%d.\n", ch->sample_rate);
+		return (-1);
 
 	found:
 #if 0 /* XXX */
@@ -2656,6 +3634,7 @@ uaudio_init_params(struct uaudio_softc *
 	ch->cur = ch->start;
 	ch->transferred = 0;
 	ch->curchanbuf = 0;
+	return (0);
 }
 
 void
@@ -2666,7 +3645,7 @@ uaudio_query_formats(device_t dev, u_int
 	u_int32_t fmt;
 	struct uaudio_softc *sc;
 
-	struct usb_audio_streaming_type1_descriptor *a1d;
+	const struct usb_audio_streaming_type1_descriptor *a1d;
 
 	sc = device_get_softc(dev);
 
@@ -2739,13 +3718,20 @@ uaudio_query_formats(device_t dev, u_int
 
 void
 uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start, u_char *end,
-		struct pcm_channel *pc)
+		struct pcm_channel *pc, int dir)
 {
 	struct uaudio_softc *sc;
 	struct chan *ch;
 
 	sc = device_get_softc(dev);
-	ch = &sc->sc_chan;
+#ifndef NO_RECORDING
+	if (dir == PCMDIR_PLAY)
+		ch = &sc->sc_playchan;
+	else
+		ch = &sc->sc_recchan;
+#else
+	ch = &sc->sc_playchan;
+#endif
 
 	ch->start = start;
 	ch->end = end;
@@ -2756,13 +3742,20 @@ uaudio_chan_set_param_pcm_dma_buff(devic
 }
 
 void
-uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize)
+uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir)
 {
 	struct uaudio_softc *sc;
 	struct chan *ch;
 
 	sc = device_get_softc(dev);
-	ch = &sc->sc_chan;
+#ifndef NO_RECORDING
+	if (dir == PCMDIR_PLAY)
+		ch = &sc->sc_playchan;
+	else
+		ch = &sc->sc_recchan;
+#else
+	ch = &sc->sc_playchan;
+#endif
 
 	ch->blksize = blocksize;
 
@@ -2770,13 +3763,20 @@ uaudio_chan_set_param_blocksize(device_t
 }
 
 void
-uaudio_chan_set_param_speed(device_t dev, u_int32_t speed)
+uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int dir)
 {
 	struct uaudio_softc *sc;
 	struct chan *ch;
 
 	sc = device_get_softc(dev);
-	ch = &sc->sc_chan;
+#ifndef NO_RECORDING
+	if (dir == PCMDIR_PLAY)
+		ch = &sc->sc_playchan;
+	else
+		ch = &sc->sc_recchan;
+#else
+	ch = &sc->sc_playchan;
+#endif
 
 	ch->sample_rate = speed;
 
@@ -2784,14 +3784,21 @@ uaudio_chan_set_param_speed(device_t dev
 }
 
 int
-uaudio_chan_getptr(device_t dev)
+uaudio_chan_getptr(device_t dev, int dir)
 {
 	struct uaudio_softc *sc;
 	struct chan *ch;
 	int ptr;
 
 	sc = device_get_softc(dev);
-	ch = &sc->sc_chan;
+#ifndef NO_RECORDING
+	if (dir == PCMDIR_PLAY)
+		ch = &sc->sc_playchan;
+	else
+		ch = &sc->sc_recchan;
+#else
+	ch = &sc->sc_playchan;
+#endif
 
 	ptr = ch->cur - ch->start;
 
@@ -2799,13 +3806,20 @@ uaudio_chan_getptr(device_t dev)
 }
 
 void
-uaudio_chan_set_param_format(device_t dev, u_int32_t format)
+uaudio_chan_set_param_format(device_t dev, u_int32_t format, int dir)
 {
 	struct uaudio_softc *sc;
 	struct chan *ch;
 
 	sc = device_get_softc(dev);
-	ch = &sc->sc_chan;
+#ifndef NO_RECORDING
+	if (dir == PCMDIR_PLAY)
+		ch = &sc->sc_playchan;
+	else
+		ch = &sc->sc_recchan;
+#else
+	ch = &sc->sc_playchan;
+#endif
 
 	ch->format = format;
 
@@ -2820,14 +3834,14 @@ uaudio_halt_out_dma(device_t dev)
 	sc = device_get_softc(dev);
 
 	DPRINTF(("uaudio_halt_out_dma: enter\n"));
-	if (sc->sc_chan.pipe != NULL) {
-		uaudio_chan_close(sc, &sc->sc_chan);
-		sc->sc_chan.pipe = 0;
-		uaudio_chan_free_buffers(sc, &sc->sc_chan);
+	if (sc->sc_playchan.pipe != NULL) {
+		uaudio_chan_close(sc, &sc->sc_playchan);
+		sc->sc_playchan.pipe = 0;
+		uaudio_chan_free_buffers(sc, &sc->sc_playchan);
 	}
         return (0);
 }
-    
+
 int
 uaudio_trigger_output(device_t dev)
 {
@@ -2837,12 +3851,13 @@ uaudio_trigger_output(device_t dev)
 	int i, s;
 
 	sc = device_get_softc(dev);
-	ch = &sc->sc_chan;
+	ch = &sc->sc_playchan;
 
 	if (sc->sc_dying)
 		return (EIO);
 
-	uaudio_init_params(sc, ch);
+	if (uaudio_init_params(sc, ch, AUMODE_PLAY))
+		return (EIO);
 
 	err = uaudio_chan_alloc_buffers(sc, ch);
 	if (err)
--- src/sys/dev/sound/usb/uaudio.h	Mon Dec 20 01:48:43 2004
+++ src/sys/dev/sound/usb/uaudio-91.h	Mon Dec 20 01:56:58 2004
@@ -1,4 +1,4 @@
-/* $FreeBSD: src/sys/dev/sound/usb/uaudio.h,v 1.1 2002/07/21 17:28:50 nsayer Exp $ */
+/* $FreeBSD: src/sys/dev/sound/usb/uaudio-91.h,v $ */
 
 /*
  * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
@@ -30,7 +30,7 @@
 /* Defined in uaudio.c, used in uaudio_pcm,c */
 
 void	uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start,
-		u_char *end, struct pcm_channel *pc);
+		u_char *end, struct pcm_channel *pc, int dir);
 int	uaudio_trigger_output(device_t dev);
 int	uaudio_halt_out_dma(device_t dev);
 #ifndef NO_RECORDING
@@ -38,12 +38,11 @@ int	uaudio_trigger_input(device_t dev);
 int	uaudio_halt_in_dma(device_t dev);
 #endif
 void	uaudio_chan_set_param(device_t, u_char *, u_char *);
-void	uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize);
-void	uaudio_chan_set_param_speed(device_t dev, u_int32_t speed);
-void	uaudio_chan_set_param_format(device_t dev, u_int32_t format);
-int	uaudio_chan_getptr(device_t dev);
+void	uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir);
+void	uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int dir);
+void	uaudio_chan_set_param_format(device_t dev, u_int32_t format,int dir);
+int	uaudio_chan_getptr(device_t dev, int);
 void	uaudio_mixer_set(device_t dev, unsigned type, unsigned left,
 		unsigned right);
 u_int32_t uaudio_query_mix_info(device_t dev);
 void	uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt);
-
--- src/sys/dev/sound/usb/uaudioreg.h	Mon Dec 20 01:48:43 2004
+++ src/sys/dev/sound/usb/uaudioreg-91.h	Mon Dec 20 01:57:15 2004
@@ -1,5 +1,5 @@
-/*	$NetBSD: uaudioreg.h,v 1.7 2000/12/28 00:29:58 augustss Exp $	*/
-/* $FreeBSD: src/sys/dev/sound/usb/uaudioreg.h,v 1.2 2002/11/06 21:37:21 joe Exp $ */
+/*	$NetBSD: uaudioreg.h,v 1.12 2004/11/05 19:08:29 kent Exp $	*/
+/* $FreeBSD: src/sys/dev/sound/usb/uaudioreg-91.h,v $ */
 
 /*
  * Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -40,7 +40,6 @@
 
 #define UAUDIO_VERSION		0x100
 
-#define UDESC_CS_DEVICE		0x21
 #define UDESC_CS_CONFIG		0x22
 #define UDESC_CS_STRING		0x23
 #define UDESC_CS_INTERFACE	0x24
@@ -63,7 +62,7 @@ typedef struct {
 	uByte		bmAttributes;
 	uWord		wMaxPacketSize;
 	uByte		bInterval;
-	/* 
+	/*
 	 * The following two entries are only used by the Audio Class.
 	 * And according to the specs the Audio Class is the only one
 	 * allowed to extend the endpoint descriptor.
@@ -98,6 +97,9 @@ struct usb_audio_streaming_endpoint_desc
 	uByte		bDescriptorType;
 	uByte		bDescriptorSubtype;
 	uByte		bmAttributes;
+#define UA_SED_FREQ_CONTROL	0x01
+#define UA_SED_PITCH_CONTROL	0x02
+#define UA_SED_MAXPACKETSONLY	0x80
 	uByte		bLockDelayUnits;
 	uWord		wLockDelay;
 } UPACKED;
@@ -121,9 +123,29 @@ struct usb_audio_streaming_type1_descrip
 struct usb_audio_cluster {
 	uByte		bNrChannels;
 	uWord		wChannelConfig;
+#define	UA_CHANNEL_LEFT		0x0001
+#define	UA_CHANNEL_RIGHT	0x0002
+#define	UA_CHANNEL_CENTER	0x0004
+#define	UA_CHANNEL_LFE		0x0008
+#define	UA_CHANNEL_L_SURROUND	0x0010
+#define	UA_CHANNEL_R_SURROUND	0x0020
+#define	UA_CHANNEL_L_CENTER	0x0040
+#define	UA_CHANNEL_R_CENTER	0x0080
+#define	UA_CHANNEL_SURROUND	0x0100
+#define	UA_CHANNEL_L_SIDE	0x0200
+#define	UA_CHANNEL_R_SIDE	0x0400
+#define	UA_CHANNEL_TOP		0x0800
 	uByte		iChannelNames;
 } UPACKED;
 
+/* Shared by all units and terminals */
+struct usb_audio_unit {
+	uByte		bLength;
+	uByte		bDescriptorType;
+	uByte		bDescriptorSubtype;
+	uByte		bUnitId;
+};
+
 /* UDESCSUB_AC_INPUT */
 struct usb_audio_input_terminal {
 	uByte		bLength;
@@ -340,8 +362,11 @@ struct usb_audio_extension_unit_1 {
 #define UA_FMT_IEEE_FLOAT 3
 #define UA_FMT_ALAW	4
 #define UA_FMT_MULAW	5
+#define UA_FMT_MPEG	0x1001
+#define UA_FMT_AC3	0x1002
 
-#define SAMPLING_FREQ_CONTROL 0x01
+#define SAMPLING_FREQ_CONTROL	0x01
+#define PITCH_CONTROL		0x02
 
 #define FORMAT_TYPE_UNDEFINED 0
 #define FORMAT_TYPE_I 1
@@ -377,4 +402,3 @@ struct usb_audio_extension_unit_1 {
 #define  DR_THRESHOLD_CONTROL			4
 #define  DR_ATTACK_TIME_CONTROL			5
 #define  DR_RELEASE_TIME_CONTROL		6
-
--- src/sys/dev/sound/usb/uaudio_pcm.c	Mon Dec 20 01:48:43 2004
+++ src/sys/dev/sound/usb/uaudio_pcm-91.c	Mon Dec 20 01:57:07 2004
@@ -1,4 +1,4 @@
-/* $FreeBSD: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.5 2004/07/16 03:58:57 tanimura Exp $ */
+/* $FreeBSD: src/sys/dev/sound/usb/uaudio_pcm-91.c,v $ */
 
 /*
  * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
@@ -89,7 +89,7 @@ ua_chan_init(kobj_t obj, void *devinfo, 
 
 	buf = end = sndbuf_getbuf(b);
 	end += sndbuf_getsize(b);
-	uaudio_chan_set_param_pcm_dma_buff(pa_dev, buf, end, ch->channel);
+	uaudio_chan_set_param_pcm_dma_buff(pa_dev, buf, end, ch->channel, dir);
 
 	ch->dir = dir;
 #ifndef NO_RECORDING
@@ -113,7 +113,7 @@ ua_chan_setformat(kobj_t obj, void *data
 
 	ua = ch->parent;
 	pa_dev = device_get_parent(ua->sc_dev);
-	uaudio_chan_set_param_format(pa_dev, format);
+	uaudio_chan_set_param_format(pa_dev, format, ch->dir);
 
 	ch->fmt = format;
 	return 0;
@@ -130,7 +130,7 @@ ua_chan_setspeed(kobj_t obj, void *data,
 
 	ua = ch->parent;
 	pa_dev = device_get_parent(ua->sc_dev);
-	uaudio_chan_set_param_speed(pa_dev, speed);
+	uaudio_chan_set_param_speed(pa_dev, speed, ch->dir);
 
 	return ch->spd;
 }
@@ -151,7 +151,7 @@ ua_chan_setblocksize(kobj_t obj, void *d
 	/* XXXXX */
 	ua = ch->parent;
 	pa_dev = device_get_parent(ua->sc_dev);
-	uaudio_chan_set_param_blocksize(pa_dev, blocksize);
+	uaudio_chan_set_param_blocksize(pa_dev, blocksize, ch->dir);
 
 	return ch->blksz;
 }
@@ -198,7 +198,7 @@ ua_chan_getptr(kobj_t obj, void *data)
 	ua = ch->parent;
 	pa_dev = device_get_parent(ua->sc_dev);
 
-	return uaudio_chan_getptr(pa_dev);
+	return uaudio_chan_getptr(pa_dev, ch->dir);
 }
 
 static struct pcmchan_caps *
--- F_41-91.diff ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:



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