xref: /dragonfly/sys/bus/u4b/audio/uaudio.c (revision 2b3f93ea)
109b9c6f2SSascha Wildner /*	$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $	*/
2aaad5092SMarkus Pfeiffer /*	$FreeBSD: head/sys/dev/sound/usb/uaudio.c 278503 2015-02-10 12:08:52Z hselasky $ */
309b9c6f2SSascha Wildner 
409b9c6f2SSascha Wildner /*-
509b9c6f2SSascha Wildner  * Copyright (c) 1999 The NetBSD Foundation, Inc.
609b9c6f2SSascha Wildner  * All rights reserved.
709b9c6f2SSascha Wildner  *
809b9c6f2SSascha Wildner  * This code is derived from software contributed to The NetBSD Foundation
909b9c6f2SSascha Wildner  * by Lennart Augustsson (lennart@augustsson.net) at
1009b9c6f2SSascha Wildner  * Carlstedt Research & Technology.
1109b9c6f2SSascha Wildner  *
1209b9c6f2SSascha Wildner  * Redistribution and use in source and binary forms, with or without
1309b9c6f2SSascha Wildner  * modification, are permitted provided that the following conditions
1409b9c6f2SSascha Wildner  * are met:
1509b9c6f2SSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1609b9c6f2SSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1709b9c6f2SSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1809b9c6f2SSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1909b9c6f2SSascha Wildner  *    documentation and/or other materials provided with the distribution.
2009b9c6f2SSascha Wildner  *
2109b9c6f2SSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2209b9c6f2SSascha Wildner  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2309b9c6f2SSascha Wildner  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2409b9c6f2SSascha Wildner  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2509b9c6f2SSascha Wildner  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2609b9c6f2SSascha Wildner  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2709b9c6f2SSascha Wildner  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2809b9c6f2SSascha Wildner  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2909b9c6f2SSascha Wildner  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3009b9c6f2SSascha Wildner  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3109b9c6f2SSascha Wildner  * POSSIBILITY OF SUCH DAMAGE.
3209b9c6f2SSascha Wildner  */
3309b9c6f2SSascha Wildner 
3409b9c6f2SSascha Wildner /*
3509b9c6f2SSascha Wildner  * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
3609b9c6f2SSascha Wildner  *                  http://www.usb.org/developers/devclass_docs/frmts10.pdf
3709b9c6f2SSascha Wildner  *                  http://www.usb.org/developers/devclass_docs/termt10.pdf
3809b9c6f2SSascha Wildner  */
3909b9c6f2SSascha Wildner 
4009b9c6f2SSascha Wildner /*
4109b9c6f2SSascha Wildner  * Also merged:
4209b9c6f2SSascha Wildner  *  $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $
4309b9c6f2SSascha Wildner  *  $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $
4409b9c6f2SSascha Wildner  *  $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $
4509b9c6f2SSascha Wildner  *  $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $
4609b9c6f2SSascha Wildner  */
4709b9c6f2SSascha Wildner 
4809b9c6f2SSascha Wildner #include <sys/stdint.h>
4909b9c6f2SSascha Wildner #include <sys/param.h>
5009b9c6f2SSascha Wildner #include <sys/queue.h>
5109b9c6f2SSascha Wildner #include <sys/types.h>
5209b9c6f2SSascha Wildner #include <sys/systm.h>
5309b9c6f2SSascha Wildner #include <sys/kernel.h>
5409b9c6f2SSascha Wildner #include <sys/bus.h>
5509b9c6f2SSascha Wildner #include <sys/module.h>
5609b9c6f2SSascha Wildner #include <sys/lock.h>
5709b9c6f2SSascha Wildner #include <sys/condvar.h>
5809b9c6f2SSascha Wildner #include <sys/sysctl.h>
5909b9c6f2SSascha Wildner #include <sys/unistd.h>
6009b9c6f2SSascha Wildner #include <sys/callout.h>
6109b9c6f2SSascha Wildner #include <sys/malloc.h>
62*2b3f93eaSMatthew Dillon #include <sys/caps.h>
6309b9c6f2SSascha Wildner 
64ef4aa9ffSSascha Wildner #include "usbdevs.h"
6509b9c6f2SSascha Wildner #include <bus/u4b/usb.h>
6609b9c6f2SSascha Wildner #include <bus/u4b/usbdi.h>
6709b9c6f2SSascha Wildner #include <bus/u4b/usbdi_util.h>
68a963377aSSascha Wildner #include <bus/u4b/usbhid.h>
69a963377aSSascha Wildner #include <bus/u4b/usb_request.h>
70a963377aSSascha Wildner #include <bus/u4b/usb_process.h>
7109b9c6f2SSascha Wildner 
7209b9c6f2SSascha Wildner #define	USB_DEBUG_VAR uaudio_debug
7309b9c6f2SSascha Wildner #include <bus/u4b/usb_debug.h>
7409b9c6f2SSascha Wildner 
7509b9c6f2SSascha Wildner #include <bus/u4b/quirk/usb_quirk.h>
7609b9c6f2SSascha Wildner 
7709b9c6f2SSascha Wildner #include <sys/reboot.h>			/* for bootverbose */
7809b9c6f2SSascha Wildner 
7909b9c6f2SSascha Wildner #include "opt_snd.h"
8009b9c6f2SSascha Wildner 
8109b9c6f2SSascha Wildner #include <dev/sound/pcm/sound.h>
8209b9c6f2SSascha Wildner #include <bus/u4b/audio/uaudioreg.h>
8309b9c6f2SSascha Wildner #include <bus/u4b/audio/uaudio.h>
8409b9c6f2SSascha Wildner #include <dev/sound/chip.h>
8509b9c6f2SSascha Wildner #include "feeder_if.h"
8609b9c6f2SSascha Wildner 
8709b9c6f2SSascha Wildner static int uaudio_default_rate = 0;		/* use rate list */
8809b9c6f2SSascha Wildner static int uaudio_default_bits = 32;
8909b9c6f2SSascha Wildner static int uaudio_default_channels = 0;		/* use default */
9009b9c6f2SSascha Wildner 
9109b9c6f2SSascha Wildner #ifdef USB_DEBUG
9209b9c6f2SSascha Wildner static int uaudio_debug = 0;
9309b9c6f2SSascha Wildner 
9409b9c6f2SSascha Wildner static SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio");
9509b9c6f2SSascha Wildner 
9609b9c6f2SSascha Wildner SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW,
9709b9c6f2SSascha Wildner     &uaudio_debug, 0, "uaudio debug level");
9809b9c6f2SSascha Wildner 
9909b9c6f2SSascha Wildner TUNABLE_INT("hw.usb.uaudio.default_rate", &uaudio_default_rate);
10009b9c6f2SSascha Wildner SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_rate, CTLFLAG_RW,
10109b9c6f2SSascha Wildner     &uaudio_default_rate, 0, "uaudio default sample rate");
10209b9c6f2SSascha Wildner 
10309b9c6f2SSascha Wildner TUNABLE_INT("hw.usb.uaudio.default_bits", &uaudio_default_bits);
10409b9c6f2SSascha Wildner SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_bits, CTLFLAG_RW,
10509b9c6f2SSascha Wildner     &uaudio_default_bits, 0, "uaudio default sample bits");
10609b9c6f2SSascha Wildner 
10709b9c6f2SSascha Wildner TUNABLE_INT("hw.usb.uaudio.default_channels", &uaudio_default_channels);
10809b9c6f2SSascha Wildner SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW,
10909b9c6f2SSascha Wildner     &uaudio_default_channels, 0, "uaudio default sample channels");
11009b9c6f2SSascha Wildner #endif
11109b9c6f2SSascha Wildner 
112aaad5092SMarkus Pfeiffer #define	UAUDIO_IRQS	(8000 / UAUDIO_NFRAMES)	/* interrupts per second */
11309b9c6f2SSascha Wildner #define	UAUDIO_NFRAMES		64	/* must be factor of 8 due HS-USB */
11409b9c6f2SSascha Wildner #define	UAUDIO_NCHANBUFS	2	/* number of outstanding request */
115a963377aSSascha Wildner #define	UAUDIO_RECURSE_LIMIT	255	/* rounds */
1165e4edd23SMatthew Dillon #define	UAUDIO_CHANNELS_MAX	MIN(64, AFMT_CHANNEL_MAX)
1175e4edd23SMatthew Dillon #define	UAUDIO_MATRIX_MAX	8	/* channels */
11809b9c6f2SSascha Wildner 
11909b9c6f2SSascha Wildner #define	MAKE_WORD(h,l) (((h) << 8) | (l))
12009b9c6f2SSascha Wildner #define	BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1)
12109b9c6f2SSascha Wildner #define	UAUDIO_MAX_CHAN(x) (x)
122a963377aSSascha Wildner #define	MIX(sc) ((sc)->sc_mixer_node)
123a963377aSSascha Wildner 
124a963377aSSascha Wildner union uaudio_asid {
125a963377aSSascha Wildner 	const struct usb_audio_streaming_interface_descriptor *v1;
126a963377aSSascha Wildner 	const struct usb_audio20_streaming_interface_descriptor *v2;
127a963377aSSascha Wildner };
128a963377aSSascha Wildner 
129a963377aSSascha Wildner union uaudio_asf1d {
130a963377aSSascha Wildner 	const struct usb_audio_streaming_type1_descriptor *v1;
131a963377aSSascha Wildner 	const struct usb_audio20_streaming_type1_descriptor *v2;
132a963377aSSascha Wildner };
133a963377aSSascha Wildner 
134a963377aSSascha Wildner union uaudio_sed {
135a963377aSSascha Wildner 	const struct usb_audio_streaming_endpoint_descriptor *v1;
136a963377aSSascha Wildner 	const struct usb_audio20_streaming_endpoint_descriptor *v2;
137a963377aSSascha Wildner };
13809b9c6f2SSascha Wildner 
13909b9c6f2SSascha Wildner struct uaudio_mixer_node {
140a963377aSSascha Wildner 	const char *name;
141a963377aSSascha Wildner 
14209b9c6f2SSascha Wildner 	int32_t	minval;
14309b9c6f2SSascha Wildner 	int32_t	maxval;
144a963377aSSascha Wildner #define	MIX_MAX_CHAN 16
14509b9c6f2SSascha Wildner 	int32_t	wValue[MIX_MAX_CHAN];	/* using nchan */
14609b9c6f2SSascha Wildner 	uint32_t mul;
14709b9c6f2SSascha Wildner 	uint32_t ctl;
14809b9c6f2SSascha Wildner 
149a963377aSSascha Wildner 	int wData[MIX_MAX_CHAN];	/* using nchan */
15009b9c6f2SSascha Wildner 	uint16_t wIndex;
15109b9c6f2SSascha Wildner 
15209b9c6f2SSascha Wildner 	uint8_t	update[(MIX_MAX_CHAN + 7) / 8];
15309b9c6f2SSascha Wildner 	uint8_t	nchan;
15409b9c6f2SSascha Wildner 	uint8_t	type;
15509b9c6f2SSascha Wildner #define	MIX_ON_OFF	1
15609b9c6f2SSascha Wildner #define	MIX_SIGNED_16	2
15709b9c6f2SSascha Wildner #define	MIX_UNSIGNED_16	3
15809b9c6f2SSascha Wildner #define	MIX_SIGNED_8	4
15909b9c6f2SSascha Wildner #define	MIX_SELECTOR	5
16009b9c6f2SSascha Wildner #define	MIX_UNKNOWN     6
16109b9c6f2SSascha Wildner #define	MIX_SIZE(n) ((((n) == MIX_SIGNED_16) || \
16209b9c6f2SSascha Wildner 		      ((n) == MIX_UNSIGNED_16)) ? 2 : 1)
16309b9c6f2SSascha Wildner #define	MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16)
16409b9c6f2SSascha Wildner 
16509b9c6f2SSascha Wildner #define	MAX_SELECTOR_INPUT_PIN 256
16609b9c6f2SSascha Wildner 	uint8_t	slctrtype[MAX_SELECTOR_INPUT_PIN];
16709b9c6f2SSascha Wildner 	uint8_t	class;
168a963377aSSascha Wildner 	uint8_t val_default;
169a963377aSSascha Wildner 
170a963377aSSascha Wildner 	uint8_t desc[64];
17109b9c6f2SSascha Wildner 
17209b9c6f2SSascha Wildner 	struct uaudio_mixer_node *next;
17309b9c6f2SSascha Wildner };
17409b9c6f2SSascha Wildner 
175a963377aSSascha Wildner struct uaudio_configure_msg {
176a963377aSSascha Wildner 	struct usb_proc_msg hdr;
177a963377aSSascha Wildner 	struct uaudio_softc *sc;
178a963377aSSascha Wildner };
179a963377aSSascha Wildner 
180a963377aSSascha Wildner #define	CHAN_MAX_ALT 24
181a963377aSSascha Wildner 
182a963377aSSascha Wildner struct uaudio_chan_alt {
183a963377aSSascha Wildner 	union uaudio_asf1d p_asf1d;
184a963377aSSascha Wildner 	union uaudio_sed p_sed;
185a963377aSSascha Wildner 	const usb_endpoint_descriptor_audio_t *p_ed1;
186a963377aSSascha Wildner 	const struct uaudio_format *p_fmt;
187a963377aSSascha Wildner 	const struct usb_config *usb_cfg;
188a963377aSSascha Wildner 	uint32_t sample_rate;	/* in Hz */
189a963377aSSascha Wildner 	uint16_t sample_size;
190a963377aSSascha Wildner 	uint8_t	iface_index;
191a963377aSSascha Wildner 	uint8_t	iface_alt_index;
192a963377aSSascha Wildner 	uint8_t channels;
193a963377aSSascha Wildner };
194a963377aSSascha Wildner 
19509b9c6f2SSascha Wildner struct uaudio_chan {
19609b9c6f2SSascha Wildner 	struct pcmchan_caps pcm_cap;	/* capabilities */
197a963377aSSascha Wildner 	struct uaudio_chan_alt usb_alt[CHAN_MAX_ALT];
19809b9c6f2SSascha Wildner 	struct snd_dbuf *pcm_buf;
19909b9c6f2SSascha Wildner 	struct lock *pcm_lock;		/* lock protecting this structure */
20009b9c6f2SSascha Wildner 	struct uaudio_softc *priv_sc;
20109b9c6f2SSascha Wildner 	struct pcm_channel *pcm_ch;
202a963377aSSascha Wildner 	struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1];
20309b9c6f2SSascha Wildner 
20409b9c6f2SSascha Wildner 	uint8_t *buf;			/* pointer to buffer */
20509b9c6f2SSascha Wildner 	uint8_t *start;			/* upper layer buffer start */
20609b9c6f2SSascha Wildner 	uint8_t *end;			/* upper layer buffer end */
20709b9c6f2SSascha Wildner 	uint8_t *cur;			/* current position in upper layer
20809b9c6f2SSascha Wildner 					 * buffer */
20909b9c6f2SSascha Wildner 
21009b9c6f2SSascha Wildner 	uint32_t intr_frames;		/* in units */
21109b9c6f2SSascha Wildner 	uint32_t frames_per_second;
21209b9c6f2SSascha Wildner 	uint32_t sample_rem;
21309b9c6f2SSascha Wildner 	uint32_t sample_curr;
214a963377aSSascha Wildner 	uint32_t max_buf;
2155e4edd23SMatthew Dillon 	int32_t jitter_rem;
2165e4edd23SMatthew Dillon 	int32_t jitter_curr;
2175e4edd23SMatthew Dillon 
2185e4edd23SMatthew Dillon 	int feedback_rate;
21909b9c6f2SSascha Wildner 
22009b9c6f2SSascha Wildner 	uint32_t pcm_format[2];
22109b9c6f2SSascha Wildner 
22209b9c6f2SSascha Wildner 	uint16_t bytes_per_frame[2];
22309b9c6f2SSascha Wildner 
2245e4edd23SMatthew Dillon 	uint32_t intr_counter;
2255e4edd23SMatthew Dillon 	uint32_t running;
2265e4edd23SMatthew Dillon 	uint32_t num_alt;
2275e4edd23SMatthew Dillon 	uint32_t cur_alt;
2285e4edd23SMatthew Dillon 	uint32_t set_alt;
2295e4edd23SMatthew Dillon 	uint32_t operation;
230a963377aSSascha Wildner #define	CHAN_OP_NONE 0
231a963377aSSascha Wildner #define	CHAN_OP_START 1
232a963377aSSascha Wildner #define	CHAN_OP_STOP 2
233a963377aSSascha Wildner #define	CHAN_OP_DRAIN 3
23409b9c6f2SSascha Wildner };
23509b9c6f2SSascha Wildner 
236a963377aSSascha Wildner #define	UMIDI_EMB_JACK_MAX   16		/* units */
23709b9c6f2SSascha Wildner #define	UMIDI_TX_FRAMES	   256		/* units */
23809b9c6f2SSascha Wildner #define	UMIDI_TX_BUFFER    (UMIDI_TX_FRAMES * 4)	/* bytes */
23909b9c6f2SSascha Wildner 
24009b9c6f2SSascha Wildner enum {
24109b9c6f2SSascha Wildner 	UMIDI_TX_TRANSFER,
24209b9c6f2SSascha Wildner 	UMIDI_RX_TRANSFER,
24309b9c6f2SSascha Wildner 	UMIDI_N_TRANSFER,
24409b9c6f2SSascha Wildner };
24509b9c6f2SSascha Wildner 
24609b9c6f2SSascha Wildner struct umidi_sub_chan {
24709b9c6f2SSascha Wildner 	struct usb_fifo_sc fifo;
24809b9c6f2SSascha Wildner 	uint8_t *temp_cmd;
24909b9c6f2SSascha Wildner 	uint8_t	temp_0[4];
25009b9c6f2SSascha Wildner 	uint8_t	temp_1[4];
25109b9c6f2SSascha Wildner 	uint8_t	state;
25209b9c6f2SSascha Wildner #define	UMIDI_ST_UNKNOWN   0		/* scan for command */
25309b9c6f2SSascha Wildner #define	UMIDI_ST_1PARAM    1
25409b9c6f2SSascha Wildner #define	UMIDI_ST_2PARAM_1  2
25509b9c6f2SSascha Wildner #define	UMIDI_ST_2PARAM_2  3
25609b9c6f2SSascha Wildner #define	UMIDI_ST_SYSEX_0   4
25709b9c6f2SSascha Wildner #define	UMIDI_ST_SYSEX_1   5
25809b9c6f2SSascha Wildner #define	UMIDI_ST_SYSEX_2   6
25909b9c6f2SSascha Wildner 
26009b9c6f2SSascha Wildner 	uint8_t	read_open:1;
26109b9c6f2SSascha Wildner 	uint8_t	write_open:1;
26209b9c6f2SSascha Wildner 	uint8_t	unused:6;
26309b9c6f2SSascha Wildner };
26409b9c6f2SSascha Wildner 
26509b9c6f2SSascha Wildner struct umidi_chan {
26609b9c6f2SSascha Wildner 
267a963377aSSascha Wildner 	struct umidi_sub_chan sub[UMIDI_EMB_JACK_MAX];
26809b9c6f2SSascha Wildner 	struct lock lock;
26909b9c6f2SSascha Wildner 
27009b9c6f2SSascha Wildner 	struct usb_xfer *xfer[UMIDI_N_TRANSFER];
27109b9c6f2SSascha Wildner 
27209b9c6f2SSascha Wildner 	uint8_t	iface_index;
27309b9c6f2SSascha Wildner 	uint8_t	iface_alt_index;
27409b9c6f2SSascha Wildner 
27509b9c6f2SSascha Wildner 	uint8_t	read_open_refcount;
27609b9c6f2SSascha Wildner 	uint8_t	write_open_refcount;
27709b9c6f2SSascha Wildner 
27809b9c6f2SSascha Wildner 	uint8_t	curr_cable;
279a963377aSSascha Wildner 	uint8_t	max_emb_jack;
28009b9c6f2SSascha Wildner 	uint8_t	valid;
28109b9c6f2SSascha Wildner 	uint8_t single_command;
28209b9c6f2SSascha Wildner };
28309b9c6f2SSascha Wildner 
284a963377aSSascha Wildner struct uaudio_search_result {
285a963377aSSascha Wildner 	uint8_t	bit_input[(256 + 7) / 8];
286a963377aSSascha Wildner 	uint8_t	bit_output[(256 + 7) / 8];
287a963377aSSascha Wildner 	uint8_t	recurse_level;
288a963377aSSascha Wildner 	uint8_t	id_max;
289a963377aSSascha Wildner 	uint8_t is_input;
290a963377aSSascha Wildner };
29109b9c6f2SSascha Wildner 
292a963377aSSascha Wildner enum {
293a963377aSSascha Wildner 	UAUDIO_HID_RX_TRANSFER,
294a963377aSSascha Wildner 	UAUDIO_HID_N_TRANSFER,
295a963377aSSascha Wildner };
296a963377aSSascha Wildner 
297a963377aSSascha Wildner struct uaudio_hid {
298a963377aSSascha Wildner 	struct usb_xfer *xfer[UAUDIO_HID_N_TRANSFER];
299a963377aSSascha Wildner 	struct hid_location volume_up_loc;
300a963377aSSascha Wildner 	struct hid_location volume_down_loc;
301a963377aSSascha Wildner 	struct hid_location mute_loc;
302a963377aSSascha Wildner 	uint32_t flags;
303a963377aSSascha Wildner #define	UAUDIO_HID_VALID		0x0001
304a963377aSSascha Wildner #define	UAUDIO_HID_HAS_ID		0x0002
305a963377aSSascha Wildner #define	UAUDIO_HID_HAS_VOLUME_UP	0x0004
306a963377aSSascha Wildner #define	UAUDIO_HID_HAS_VOLUME_DOWN	0x0008
307a963377aSSascha Wildner #define	UAUDIO_HID_HAS_MUTE		0x0010
308a963377aSSascha Wildner 	uint8_t iface_index;
309a963377aSSascha Wildner 	uint8_t volume_up_id;
310a963377aSSascha Wildner 	uint8_t volume_down_id;
311a963377aSSascha Wildner 	uint8_t mute_id;
312a963377aSSascha Wildner };
313a963377aSSascha Wildner 
314520083baSSascha Wildner #define	UAUDIO_SPDIF_OUT	0x01	/* Enable S/PDIF output */
315520083baSSascha Wildner #define	UAUDIO_SPDIF_OUT_48K	0x02	/* Out sample rate = 48K */
316520083baSSascha Wildner #define	UAUDIO_SPDIF_OUT_96K	0x04	/* Out sample rate = 96K */
317520083baSSascha Wildner #define	UAUDIO_SPDIF_IN_MIX	0x10	/* Input mix enable */
318520083baSSascha Wildner 
319a963377aSSascha Wildner struct uaudio_softc {
32009b9c6f2SSascha Wildner 	struct sbuf sc_sndstat;
32109b9c6f2SSascha Wildner 	struct sndcard_func sc_sndcard_func;
32209b9c6f2SSascha Wildner 	struct uaudio_chan sc_rec_chan;
32309b9c6f2SSascha Wildner 	struct uaudio_chan sc_play_chan;
32409b9c6f2SSascha Wildner 	struct umidi_chan sc_midi_chan;
325a963377aSSascha Wildner 	struct uaudio_hid sc_hid;
326a963377aSSascha Wildner 	struct uaudio_search_result sc_mixer_clocks;
327a963377aSSascha Wildner 	struct uaudio_mixer_node sc_mixer_node;
328a963377aSSascha Wildner 	struct uaudio_configure_msg sc_config_msg[2];
32909b9c6f2SSascha Wildner 
330a963377aSSascha Wildner 	struct lock *sc_mixer_lock;
331a963377aSSascha Wildner 	struct snd_mixer *sc_mixer_dev;
33209b9c6f2SSascha Wildner 	struct usb_device *sc_udev;
33309b9c6f2SSascha Wildner 	struct usb_xfer *sc_mixer_xfer[1];
33409b9c6f2SSascha Wildner 	struct uaudio_mixer_node *sc_mixer_root;
33509b9c6f2SSascha Wildner 	struct uaudio_mixer_node *sc_mixer_curr;
336520083baSSascha Wildner 	int     (*sc_set_spdif_fn) (struct uaudio_softc *, int);
33709b9c6f2SSascha Wildner 
33809b9c6f2SSascha Wildner 	uint32_t sc_mix_info;
33909b9c6f2SSascha Wildner 	uint32_t sc_recsrc_info;
34009b9c6f2SSascha Wildner 
34109b9c6f2SSascha Wildner 	uint16_t sc_audio_rev;
34209b9c6f2SSascha Wildner 	uint16_t sc_mixer_count;
34309b9c6f2SSascha Wildner 
34409b9c6f2SSascha Wildner 	uint8_t	sc_sndstat_valid;
34509b9c6f2SSascha Wildner 	uint8_t	sc_mixer_iface_index;
34609b9c6f2SSascha Wildner 	uint8_t	sc_mixer_iface_no;
34709b9c6f2SSascha Wildner 	uint8_t	sc_mixer_chan;
34809b9c6f2SSascha Wildner 	uint8_t	sc_pcm_registered:1;
34909b9c6f2SSascha Wildner 	uint8_t	sc_mixer_init:1;
35009b9c6f2SSascha Wildner 	uint8_t	sc_uq_audio_swap_lr:1;
35109b9c6f2SSascha Wildner 	uint8_t	sc_uq_au_inp_async:1;
35209b9c6f2SSascha Wildner 	uint8_t	sc_uq_au_no_xu:1;
35309b9c6f2SSascha Wildner 	uint8_t	sc_uq_bad_adc:1;
35409b9c6f2SSascha Wildner 	uint8_t	sc_uq_au_vendor_class:1;
3555e4edd23SMatthew Dillon 	uint8_t	sc_pcm_bitperfect:1;
35609b9c6f2SSascha Wildner };
35709b9c6f2SSascha Wildner 
35809b9c6f2SSascha Wildner struct uaudio_terminal_node {
35909b9c6f2SSascha Wildner 	union {
36009b9c6f2SSascha Wildner 		const struct usb_descriptor *desc;
361a963377aSSascha Wildner 		const struct usb_audio_input_terminal *it_v1;
362a963377aSSascha Wildner 		const struct usb_audio_output_terminal *ot_v1;
363a963377aSSascha Wildner 		const struct usb_audio_mixer_unit_0 *mu_v1;
364a963377aSSascha Wildner 		const struct usb_audio_selector_unit *su_v1;
365a963377aSSascha Wildner 		const struct usb_audio_feature_unit *fu_v1;
366a963377aSSascha Wildner 		const struct usb_audio_processing_unit_0 *pu_v1;
367a963377aSSascha Wildner 		const struct usb_audio_extension_unit_0 *eu_v1;
368a963377aSSascha Wildner 		const struct usb_audio20_clock_source_unit *csrc_v2;
369a963377aSSascha Wildner 		const struct usb_audio20_clock_selector_unit_0 *csel_v2;
370a963377aSSascha Wildner 		const struct usb_audio20_clock_multiplier_unit *cmul_v2;
371a963377aSSascha Wildner 		const struct usb_audio20_input_terminal *it_v2;
372a963377aSSascha Wildner 		const struct usb_audio20_output_terminal *ot_v2;
373a963377aSSascha Wildner 		const struct usb_audio20_mixer_unit_0 *mu_v2;
374a963377aSSascha Wildner 		const struct usb_audio20_selector_unit *su_v2;
375a963377aSSascha Wildner 		const struct usb_audio20_feature_unit *fu_v2;
376a963377aSSascha Wildner 		const struct usb_audio20_sample_rate_unit *ru_v2;
377a963377aSSascha Wildner 		const struct usb_audio20_processing_unit_0 *pu_v2;
378a963377aSSascha Wildner 		const struct usb_audio20_extension_unit_0 *eu_v2;
379a963377aSSascha Wildner 		const struct usb_audio20_effect_unit *ef_v2;
38009b9c6f2SSascha Wildner 	}	u;
38109b9c6f2SSascha Wildner 	struct uaudio_search_result usr;
38209b9c6f2SSascha Wildner 	struct uaudio_terminal_node *root;
38309b9c6f2SSascha Wildner };
38409b9c6f2SSascha Wildner 
38509b9c6f2SSascha Wildner struct uaudio_format {
38609b9c6f2SSascha Wildner 	uint16_t wFormat;
38709b9c6f2SSascha Wildner 	uint8_t	bPrecision;
38809b9c6f2SSascha Wildner 	uint32_t freebsd_fmt;
38909b9c6f2SSascha Wildner 	const char *description;
39009b9c6f2SSascha Wildner };
39109b9c6f2SSascha Wildner 
392a963377aSSascha Wildner static const struct uaudio_format uaudio10_formats[] = {
39309b9c6f2SSascha Wildner 
39409b9c6f2SSascha Wildner 	{UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
39509b9c6f2SSascha Wildner 	{UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
39609b9c6f2SSascha Wildner 	{UA_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
39709b9c6f2SSascha Wildner 	{UA_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
39809b9c6f2SSascha Wildner 
39909b9c6f2SSascha Wildner 	{UA_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
40009b9c6f2SSascha Wildner 	{UA_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
40109b9c6f2SSascha Wildner 	{UA_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
40209b9c6f2SSascha Wildner 	{UA_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
40309b9c6f2SSascha Wildner 
40409b9c6f2SSascha Wildner 	{UA_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
40509b9c6f2SSascha Wildner 	{UA_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
40609b9c6f2SSascha Wildner 
40709b9c6f2SSascha Wildner 	{0, 0, 0, NULL}
40809b9c6f2SSascha Wildner };
40909b9c6f2SSascha Wildner 
410a963377aSSascha Wildner static const struct uaudio_format uaudio20_formats[] = {
411a963377aSSascha Wildner 
412a963377aSSascha Wildner 	{UA20_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
413a963377aSSascha Wildner 	{UA20_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
414a963377aSSascha Wildner 	{UA20_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
415a963377aSSascha Wildner 	{UA20_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
416a963377aSSascha Wildner 
417a963377aSSascha Wildner 	{UA20_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
418a963377aSSascha Wildner 	{UA20_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
419a963377aSSascha Wildner 	{UA20_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
420a963377aSSascha Wildner 	{UA20_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
421a963377aSSascha Wildner 
422a963377aSSascha Wildner 	{UA20_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
423a963377aSSascha Wildner 	{UA20_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
424a963377aSSascha Wildner 
425a963377aSSascha Wildner 	{0, 0, 0, NULL}
426a963377aSSascha Wildner };
427a963377aSSascha Wildner 
42809b9c6f2SSascha Wildner #define	UAC_OUTPUT	0
42909b9c6f2SSascha Wildner #define	UAC_INPUT	1
43009b9c6f2SSascha Wildner #define	UAC_EQUAL	2
43109b9c6f2SSascha Wildner #define	UAC_RECORD	3
43209b9c6f2SSascha Wildner #define	UAC_NCLASSES	4
43309b9c6f2SSascha Wildner 
43409b9c6f2SSascha Wildner #ifdef USB_DEBUG
43509b9c6f2SSascha Wildner static const char *uac_names[] = {
43609b9c6f2SSascha Wildner 	"outputs", "inputs", "equalization", "record"
43709b9c6f2SSascha Wildner };
43809b9c6f2SSascha Wildner 
43909b9c6f2SSascha Wildner #endif
44009b9c6f2SSascha Wildner 
44109b9c6f2SSascha Wildner /* prototypes */
44209b9c6f2SSascha Wildner 
44309b9c6f2SSascha Wildner static device_probe_t uaudio_probe;
44409b9c6f2SSascha Wildner static device_attach_t uaudio_attach;
44509b9c6f2SSascha Wildner static device_detach_t uaudio_detach;
44609b9c6f2SSascha Wildner 
44709b9c6f2SSascha Wildner static usb_callback_t uaudio_chan_play_callback;
448a963377aSSascha Wildner static usb_callback_t uaudio_chan_play_sync_callback;
44909b9c6f2SSascha Wildner static usb_callback_t uaudio_chan_record_callback;
450a963377aSSascha Wildner static usb_callback_t uaudio_chan_record_sync_callback;
45109b9c6f2SSascha Wildner static usb_callback_t uaudio_mixer_write_cfg_callback;
45209b9c6f2SSascha Wildner static usb_callback_t umidi_bulk_read_callback;
45309b9c6f2SSascha Wildner static usb_callback_t umidi_bulk_write_callback;
454a963377aSSascha Wildner static usb_callback_t uaudio_hid_rx_callback;
45509b9c6f2SSascha Wildner 
456a963377aSSascha Wildner static usb_proc_callback_t uaudio_configure_msg;
457a963377aSSascha Wildner 
458a963377aSSascha Wildner /* ==== USB mixer ==== */
459a963377aSSascha Wildner 
460a963377aSSascha Wildner static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS);
461a963377aSSascha Wildner static void uaudio_mixer_ctl_free(struct uaudio_softc *);
462a963377aSSascha Wildner static void uaudio_mixer_register_sysctl(struct uaudio_softc *, device_t);
463a963377aSSascha Wildner static void uaudio_mixer_reload_all(struct uaudio_softc *);
464a963377aSSascha Wildner static void uaudio_mixer_controls_create_ftu(struct uaudio_softc *);
465a963377aSSascha Wildner 
466a963377aSSascha Wildner /* ==== USB audio v1.0 ==== */
467a963377aSSascha Wildner 
46809b9c6f2SSascha Wildner static void	uaudio_mixer_add_mixer(struct uaudio_softc *,
46909b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *, int);
47009b9c6f2SSascha Wildner static void	uaudio_mixer_add_selector(struct uaudio_softc *,
47109b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *, int);
47209b9c6f2SSascha Wildner static uint32_t	uaudio_mixer_feature_get_bmaControls(
47309b9c6f2SSascha Wildner 		    const struct usb_audio_feature_unit *, uint8_t);
47409b9c6f2SSascha Wildner static void	uaudio_mixer_add_feature(struct uaudio_softc *,
47509b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *, int);
47609b9c6f2SSascha Wildner static void	uaudio_mixer_add_processing_updown(struct uaudio_softc *,
47709b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *, int);
47809b9c6f2SSascha Wildner static void	uaudio_mixer_add_processing(struct uaudio_softc *,
47909b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *, int);
48009b9c6f2SSascha Wildner static void	uaudio_mixer_add_extension(struct uaudio_softc *,
48109b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *, int);
48209b9c6f2SSascha Wildner static struct	usb_audio_cluster uaudio_mixer_get_cluster(uint8_t,
48309b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *);
48409b9c6f2SSascha Wildner static uint16_t	uaudio_mixer_determine_class(const struct uaudio_terminal_node *,
48509b9c6f2SSascha Wildner 		    struct uaudio_mixer_node *);
48609b9c6f2SSascha Wildner static uint16_t	uaudio_mixer_feature_name(const struct uaudio_terminal_node *,
48709b9c6f2SSascha Wildner 		    struct uaudio_mixer_node *);
488a963377aSSascha Wildner static void	uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *,
489a963377aSSascha Wildner 		    const uint8_t *, uint8_t, struct uaudio_search_result *);
490a963377aSSascha Wildner static const void *uaudio_mixer_verify_desc(const void *, uint32_t);
491a963377aSSascha Wildner static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t);
492a963377aSSascha Wildner static int	uaudio_mixer_get(struct usb_device *, uint16_t, uint8_t,
493a963377aSSascha Wildner 		    struct uaudio_mixer_node *);
494a963377aSSascha Wildner 
495a963377aSSascha Wildner /* ==== USB audio v2.0 ==== */
496a963377aSSascha Wildner 
497a963377aSSascha Wildner static void	uaudio20_mixer_add_mixer(struct uaudio_softc *,
498a963377aSSascha Wildner 		    const struct uaudio_terminal_node *, int);
499a963377aSSascha Wildner static void	uaudio20_mixer_add_selector(struct uaudio_softc *,
500a963377aSSascha Wildner 		    const struct uaudio_terminal_node *, int);
501a963377aSSascha Wildner static void	uaudio20_mixer_add_feature(struct uaudio_softc *,
502a963377aSSascha Wildner 		    const struct uaudio_terminal_node *, int);
503a963377aSSascha Wildner static struct	usb_audio20_cluster uaudio20_mixer_get_cluster(uint8_t,
504a963377aSSascha Wildner 		    const struct uaudio_terminal_node *);
505a963377aSSascha Wildner static uint16_t	uaudio20_mixer_determine_class(const struct uaudio_terminal_node *,
506a963377aSSascha Wildner 		    struct uaudio_mixer_node *);
507a963377aSSascha Wildner static uint16_t	uaudio20_mixer_feature_name(const struct uaudio_terminal_node *,
508a963377aSSascha Wildner 		    struct uaudio_mixer_node *);
509a963377aSSascha Wildner static void	uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *,
510a963377aSSascha Wildner 		    const uint8_t *, uint8_t, struct uaudio_search_result *);
511a963377aSSascha Wildner static const void *uaudio20_mixer_verify_desc(const void *, uint32_t);
512a963377aSSascha Wildner static usb_error_t uaudio20_set_speed(struct usb_device *, uint8_t,
513a963377aSSascha Wildner 		    uint8_t, uint32_t);
514a963377aSSascha Wildner 
515a963377aSSascha Wildner /* USB audio v1.0 and v2.0 */
516a963377aSSascha Wildner 
517a963377aSSascha Wildner static void	uaudio_chan_fill_info_sub(struct uaudio_softc *,
518a963377aSSascha Wildner 		    struct usb_device *, uint32_t, uint8_t, uint8_t);
519a963377aSSascha Wildner static void	uaudio_chan_fill_info(struct uaudio_softc *,
520a963377aSSascha Wildner 		    struct usb_device *);
521a963377aSSascha Wildner static void	uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
522a963377aSSascha Wildner 		    struct uaudio_mixer_node *);
523a963377aSSascha Wildner static void	uaudio_mixer_add_ctl(struct uaudio_softc *,
524a963377aSSascha Wildner 		    struct uaudio_mixer_node *);
525a963377aSSascha Wildner static void	uaudio_mixer_fill_info(struct uaudio_softc *,
526a963377aSSascha Wildner 		    struct usb_device *, void *);
527a963377aSSascha Wildner static void	uaudio_mixer_ctl_set(struct uaudio_softc *,
528a963377aSSascha Wildner 		    struct uaudio_mixer_node *, uint8_t, int32_t val);
529a963377aSSascha Wildner static int	uaudio_mixer_signext(uint8_t, int);
530a963377aSSascha Wildner static int	uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val);
531a963377aSSascha Wildner static void	uaudio_mixer_init(struct uaudio_softc *);
53209b9c6f2SSascha Wildner static const struct uaudio_terminal_node *uaudio_mixer_get_input(
53309b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *, uint8_t);
53409b9c6f2SSascha Wildner static const struct uaudio_terminal_node *uaudio_mixer_get_output(
53509b9c6f2SSascha Wildner 		    const struct uaudio_terminal_node *, uint8_t);
53609b9c6f2SSascha Wildner static void	uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *,
53709b9c6f2SSascha Wildner 		    uint8_t, uint8_t, struct uaudio_search_result *);
53809b9c6f2SSascha Wildner static uint8_t	umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t);
53909b9c6f2SSascha Wildner static struct	umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *);
54009b9c6f2SSascha Wildner static void	umidi_start_read(struct usb_fifo *);
54109b9c6f2SSascha Wildner static void	umidi_stop_read(struct usb_fifo *);
54209b9c6f2SSascha Wildner static void	umidi_start_write(struct usb_fifo *);
54309b9c6f2SSascha Wildner static void	umidi_stop_write(struct usb_fifo *);
54409b9c6f2SSascha Wildner static int	umidi_open(struct usb_fifo *, int);
54509b9c6f2SSascha Wildner static int	umidi_ioctl(struct usb_fifo *, u_long cmd, void *, int);
54609b9c6f2SSascha Wildner static void	umidi_close(struct usb_fifo *, int);
54709b9c6f2SSascha Wildner static void	umidi_init(device_t dev);
54809b9c6f2SSascha Wildner static int	umidi_probe(device_t dev);
54909b9c6f2SSascha Wildner static int	umidi_detach(device_t dev);
550a963377aSSascha Wildner static int	uaudio_hid_probe(struct uaudio_softc *sc,
551a963377aSSascha Wildner 		    struct usb_attach_arg *uaa);
552a963377aSSascha Wildner static void	uaudio_hid_detach(struct uaudio_softc *sc);
55309b9c6f2SSascha Wildner 
55409b9c6f2SSascha Wildner #ifdef USB_DEBUG
55509b9c6f2SSascha Wildner static void	uaudio_chan_dump_ep_desc(
55609b9c6f2SSascha Wildner 		    const usb_endpoint_descriptor_audio_t *);
55709b9c6f2SSascha Wildner #endif
55809b9c6f2SSascha Wildner 
55909b9c6f2SSascha Wildner static const struct usb_config
560a963377aSSascha Wildner 	uaudio_cfg_record[UAUDIO_NCHANBUFS + 1] = {
56109b9c6f2SSascha Wildner 	[0] = {
56209b9c6f2SSascha Wildner 		.type = UE_ISOCHRONOUS,
56309b9c6f2SSascha Wildner 		.endpoint = UE_ADDR_ANY,
56409b9c6f2SSascha Wildner 		.direction = UE_DIR_IN,
56509b9c6f2SSascha Wildner 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
56609b9c6f2SSascha Wildner 		.frames = UAUDIO_NFRAMES,
56709b9c6f2SSascha Wildner 		.flags = {.short_xfer_ok = 1,},
56809b9c6f2SSascha Wildner 		.callback = &uaudio_chan_record_callback,
56909b9c6f2SSascha Wildner 	},
57009b9c6f2SSascha Wildner 
57109b9c6f2SSascha Wildner 	[1] = {
57209b9c6f2SSascha Wildner 		.type = UE_ISOCHRONOUS,
57309b9c6f2SSascha Wildner 		.endpoint = UE_ADDR_ANY,
57409b9c6f2SSascha Wildner 		.direction = UE_DIR_IN,
57509b9c6f2SSascha Wildner 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
57609b9c6f2SSascha Wildner 		.frames = UAUDIO_NFRAMES,
57709b9c6f2SSascha Wildner 		.flags = {.short_xfer_ok = 1,},
57809b9c6f2SSascha Wildner 		.callback = &uaudio_chan_record_callback,
57909b9c6f2SSascha Wildner 	},
580a963377aSSascha Wildner 
581a963377aSSascha Wildner 	[2] = {
582a963377aSSascha Wildner 		.type = UE_ISOCHRONOUS,
583a963377aSSascha Wildner 		.endpoint = UE_ADDR_ANY,
584a963377aSSascha Wildner 		.direction = UE_DIR_OUT,
585a963377aSSascha Wildner 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
586a963377aSSascha Wildner 		.frames = 1,
587a963377aSSascha Wildner 		.flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
588a963377aSSascha Wildner 		.callback = &uaudio_chan_record_sync_callback,
589a963377aSSascha Wildner 	},
59009b9c6f2SSascha Wildner };
59109b9c6f2SSascha Wildner 
59209b9c6f2SSascha Wildner static const struct usb_config
593a963377aSSascha Wildner 	uaudio_cfg_play[UAUDIO_NCHANBUFS + 1] = {
59409b9c6f2SSascha Wildner 	[0] = {
59509b9c6f2SSascha Wildner 		.type = UE_ISOCHRONOUS,
59609b9c6f2SSascha Wildner 		.endpoint = UE_ADDR_ANY,
59709b9c6f2SSascha Wildner 		.direction = UE_DIR_OUT,
59809b9c6f2SSascha Wildner 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
59909b9c6f2SSascha Wildner 		.frames = UAUDIO_NFRAMES,
60009b9c6f2SSascha Wildner 		.flags = {.short_xfer_ok = 1,},
60109b9c6f2SSascha Wildner 		.callback = &uaudio_chan_play_callback,
60209b9c6f2SSascha Wildner 	},
60309b9c6f2SSascha Wildner 
60409b9c6f2SSascha Wildner 	[1] = {
60509b9c6f2SSascha Wildner 		.type = UE_ISOCHRONOUS,
60609b9c6f2SSascha Wildner 		.endpoint = UE_ADDR_ANY,
60709b9c6f2SSascha Wildner 		.direction = UE_DIR_OUT,
60809b9c6f2SSascha Wildner 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
60909b9c6f2SSascha Wildner 		.frames = UAUDIO_NFRAMES,
61009b9c6f2SSascha Wildner 		.flags = {.short_xfer_ok = 1,},
61109b9c6f2SSascha Wildner 		.callback = &uaudio_chan_play_callback,
61209b9c6f2SSascha Wildner 	},
613a963377aSSascha Wildner 
614a963377aSSascha Wildner 	[2] = {
615a963377aSSascha Wildner 		.type = UE_ISOCHRONOUS,
616a963377aSSascha Wildner 		.endpoint = UE_ADDR_ANY,
617a963377aSSascha Wildner 		.direction = UE_DIR_IN,
618a963377aSSascha Wildner 		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
619a963377aSSascha Wildner 		.frames = 1,
620a963377aSSascha Wildner 		.flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
621a963377aSSascha Wildner 		.callback = &uaudio_chan_play_sync_callback,
622a963377aSSascha Wildner 	},
62309b9c6f2SSascha Wildner };
62409b9c6f2SSascha Wildner 
62509b9c6f2SSascha Wildner static const struct usb_config
62609b9c6f2SSascha Wildner 	uaudio_mixer_config[1] = {
62709b9c6f2SSascha Wildner 	[0] = {
62809b9c6f2SSascha Wildner 		.type = UE_CONTROL,
62909b9c6f2SSascha Wildner 		.endpoint = 0x00,	/* Control pipe */
63009b9c6f2SSascha Wildner 		.direction = UE_DIR_ANY,
63109b9c6f2SSascha Wildner 		.bufsize = (sizeof(struct usb_device_request) + 4),
63209b9c6f2SSascha Wildner 		.callback = &uaudio_mixer_write_cfg_callback,
63309b9c6f2SSascha Wildner 		.timeout = 1000,	/* 1 second */
63409b9c6f2SSascha Wildner 	},
63509b9c6f2SSascha Wildner };
63609b9c6f2SSascha Wildner 
63709b9c6f2SSascha Wildner static const
63809b9c6f2SSascha Wildner uint8_t	umidi_cmd_to_len[16] = {
63909b9c6f2SSascha Wildner 	[0x0] = 0,			/* reserved */
64009b9c6f2SSascha Wildner 	[0x1] = 0,			/* reserved */
64109b9c6f2SSascha Wildner 	[0x2] = 2,			/* bytes */
64209b9c6f2SSascha Wildner 	[0x3] = 3,			/* bytes */
64309b9c6f2SSascha Wildner 	[0x4] = 3,			/* bytes */
64409b9c6f2SSascha Wildner 	[0x5] = 1,			/* bytes */
64509b9c6f2SSascha Wildner 	[0x6] = 2,			/* bytes */
64609b9c6f2SSascha Wildner 	[0x7] = 3,			/* bytes */
64709b9c6f2SSascha Wildner 	[0x8] = 3,			/* bytes */
64809b9c6f2SSascha Wildner 	[0x9] = 3,			/* bytes */
64909b9c6f2SSascha Wildner 	[0xA] = 3,			/* bytes */
65009b9c6f2SSascha Wildner 	[0xB] = 3,			/* bytes */
65109b9c6f2SSascha Wildner 	[0xC] = 2,			/* bytes */
65209b9c6f2SSascha Wildner 	[0xD] = 2,			/* bytes */
65309b9c6f2SSascha Wildner 	[0xE] = 3,			/* bytes */
65409b9c6f2SSascha Wildner 	[0xF] = 1,			/* bytes */
65509b9c6f2SSascha Wildner };
65609b9c6f2SSascha Wildner 
65709b9c6f2SSascha Wildner static const struct usb_config
65809b9c6f2SSascha Wildner 	umidi_config[UMIDI_N_TRANSFER] = {
65909b9c6f2SSascha Wildner 	[UMIDI_TX_TRANSFER] = {
66009b9c6f2SSascha Wildner 		.type = UE_BULK,
66109b9c6f2SSascha Wildner 		.endpoint = UE_ADDR_ANY,
66209b9c6f2SSascha Wildner 		.direction = UE_DIR_OUT,
66309b9c6f2SSascha Wildner 		.bufsize = UMIDI_TX_BUFFER,
66409b9c6f2SSascha Wildner 		.callback = &umidi_bulk_write_callback,
66509b9c6f2SSascha Wildner 	},
66609b9c6f2SSascha Wildner 
66709b9c6f2SSascha Wildner 	[UMIDI_RX_TRANSFER] = {
66809b9c6f2SSascha Wildner 		.type = UE_BULK,
66909b9c6f2SSascha Wildner 		.endpoint = UE_ADDR_ANY,
67009b9c6f2SSascha Wildner 		.direction = UE_DIR_IN,
67109b9c6f2SSascha Wildner 		.bufsize = 4,	/* bytes */
67209b9c6f2SSascha Wildner 		.flags = {.short_xfer_ok = 1,.proxy_buffer = 1,},
67309b9c6f2SSascha Wildner 		.callback = &umidi_bulk_read_callback,
67409b9c6f2SSascha Wildner 	},
67509b9c6f2SSascha Wildner };
67609b9c6f2SSascha Wildner 
677a963377aSSascha Wildner static const struct usb_config
678a963377aSSascha Wildner 	uaudio_hid_config[UAUDIO_HID_N_TRANSFER] = {
679a963377aSSascha Wildner 	[UAUDIO_HID_RX_TRANSFER] = {
680a963377aSSascha Wildner 		.type = UE_INTERRUPT,
681a963377aSSascha Wildner 		.endpoint = UE_ADDR_ANY,
682a963377aSSascha Wildner 		.direction = UE_DIR_IN,
683a963377aSSascha Wildner 		.bufsize = 0,	/* use wMaxPacketSize */
684a963377aSSascha Wildner 		.flags = {.short_xfer_ok = 1,},
685a963377aSSascha Wildner 		.callback = &uaudio_hid_rx_callback,
686a963377aSSascha Wildner 	},
687a963377aSSascha Wildner };
688a963377aSSascha Wildner 
68909b9c6f2SSascha Wildner static devclass_t uaudio_devclass;
69009b9c6f2SSascha Wildner 
69109b9c6f2SSascha Wildner static device_method_t uaudio_methods[] = {
69209b9c6f2SSascha Wildner 	DEVMETHOD(device_probe, uaudio_probe),
69309b9c6f2SSascha Wildner 	DEVMETHOD(device_attach, uaudio_attach),
69409b9c6f2SSascha Wildner 	DEVMETHOD(device_detach, uaudio_detach),
69509b9c6f2SSascha Wildner 	DEVMETHOD(device_suspend, bus_generic_suspend),
69609b9c6f2SSascha Wildner 	DEVMETHOD(device_resume, bus_generic_resume),
69709b9c6f2SSascha Wildner 	DEVMETHOD(device_shutdown, bus_generic_shutdown),
69809b9c6f2SSascha Wildner 
699d3c9c58eSSascha Wildner 	DEVMETHOD_END
70009b9c6f2SSascha Wildner };
70109b9c6f2SSascha Wildner 
70209b9c6f2SSascha Wildner static driver_t uaudio_driver = {
70309b9c6f2SSascha Wildner 	.name = "uaudio",
70409b9c6f2SSascha Wildner 	.methods = uaudio_methods,
70509b9c6f2SSascha Wildner 	.size = sizeof(struct uaudio_softc),
70609b9c6f2SSascha Wildner };
70709b9c6f2SSascha Wildner 
708a963377aSSascha Wildner /* The following table is derived from Linux's quirks-table.h */
709a963377aSSascha Wildner static const STRUCT_USB_HOST_ID uaudio_vendor_midi[] = {
710a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1000, 0) }, /* UX256 */
711a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1001, 0) }, /* MU1000 */
712a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1002, 0) }, /* MU2000 */
713a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1003, 0) }, /* MU500 */
714a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1004, 3) }, /* UW500 */
715a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1005, 0) }, /* MOTIF6 */
716a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1006, 0) }, /* MOTIF7 */
717a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1007, 0) }, /* MOTIF8 */
718a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1008, 0) }, /* UX96 */
719a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1009, 0) }, /* UX16 */
720a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100a, 3) }, /* EOS BX */
721a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100c, 0) }, /* UC-MX */
722a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100d, 0) }, /* UC-KX */
723a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100e, 0) }, /* S08 */
724a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100f, 0) }, /* CLP-150 */
725a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1010, 0) }, /* CLP-170 */
726a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1011, 0) }, /* P-250 */
727a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1012, 0) }, /* TYROS */
728a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1013, 0) }, /* PF-500 */
729a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1014, 0) }, /* S90 */
730a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1015, 0) }, /* MOTIF-R */
731a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1016, 0) }, /* MDP-5 */
732a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1017, 0) }, /* CVP-204 */
733a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1018, 0) }, /* CVP-206 */
734a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1019, 0) }, /* CVP-208 */
735a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101a, 0) }, /* CVP-210 */
736a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101b, 0) }, /* PSR-1100 */
737a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101c, 0) }, /* PSR-2100 */
738a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101d, 0) }, /* CLP-175 */
739a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101e, 0) }, /* PSR-K1 */
740a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101f, 0) }, /* EZ-J24 */
741a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1020, 0) }, /* EZ-250i */
742a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1021, 0) }, /* MOTIF ES 6 */
743a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1022, 0) }, /* MOTIF ES 7 */
744a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1023, 0) }, /* MOTIF ES 8 */
745a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1024, 0) }, /* CVP-301 */
746a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1025, 0) }, /* CVP-303 */
747a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1026, 0) }, /* CVP-305 */
748a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1027, 0) }, /* CVP-307 */
749a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1028, 0) }, /* CVP-309 */
750a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1029, 0) }, /* CVP-309GP */
751a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x102a, 0) }, /* PSR-1500 */
752a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x102b, 0) }, /* PSR-3000 */
753a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x102e, 0) }, /* ELS-01/01C */
754a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1030, 0) }, /* PSR-295/293 */
755a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1031, 0) }, /* DGX-205/203 */
756a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1032, 0) }, /* DGX-305 */
757a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1033, 0) }, /* DGX-505 */
758a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1034, 0) }, /* NULL */
759a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1035, 0) }, /* NULL */
760a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1036, 0) }, /* NULL */
761a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1037, 0) }, /* NULL */
762a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1038, 0) }, /* NULL */
763a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1039, 0) }, /* NULL */
764a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103a, 0) }, /* NULL */
765a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103b, 0) }, /* NULL */
766a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103c, 0) }, /* NULL */
767a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103d, 0) }, /* NULL */
768a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103e, 0) }, /* NULL */
769a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103f, 0) }, /* NULL */
770a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1040, 0) }, /* NULL */
771a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1041, 0) }, /* NULL */
772a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1042, 0) }, /* NULL */
773a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1043, 0) }, /* NULL */
774a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1044, 0) }, /* NULL */
775a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1045, 0) }, /* NULL */
776a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x104e, 0) }, /* NULL */
777a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x104f, 0) }, /* NULL */
778a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1050, 0) }, /* NULL */
779a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1051, 0) }, /* NULL */
780a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1052, 0) }, /* NULL */
781a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1053, 0) }, /* NULL */
782a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1054, 0) }, /* NULL */
783a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1055, 0) }, /* NULL */
784a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1056, 0) }, /* NULL */
785a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1057, 0) }, /* NULL */
786a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1058, 0) }, /* NULL */
787a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1059, 0) }, /* NULL */
788a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x105a, 0) }, /* NULL */
789a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x105b, 0) }, /* NULL */
790a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x105c, 0) }, /* NULL */
791a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x105d, 0) }, /* NULL */
792a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1503, 3) }, /* MOX6/MOX8 */
793a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x2000, 0) }, /* DGP-7 */
794a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x2001, 0) }, /* DGP-5 */
795a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x2002, 0) }, /* NULL */
796a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x2003, 0) }, /* NULL */
797a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5000, 0) }, /* CS1D */
798a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5001, 0) }, /* DSP1D */
799a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5002, 0) }, /* DME32 */
800a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5003, 0) }, /* DM2000 */
801a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5004, 0) }, /* 02R96 */
802a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5005, 0) }, /* ACU16-C */
803a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5006, 0) }, /* NHB32-C */
804a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5007, 0) }, /* DM1000 */
805a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5008, 0) }, /* 01V96 */
806a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5009, 0) }, /* SPX2000 */
807a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500a, 0) }, /* PM5D */
808a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500b, 0) }, /* DME64N */
809a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500c, 0) }, /* DME24N */
810a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500d, 0) }, /* NULL */
811a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500e, 0) }, /* NULL */
812a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500f, 0) }, /* NULL */
813a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x7000, 0) }, /* DTX */
814a963377aSSascha Wildner 	{ USB_VPI(USB_VENDOR_YAMAHA, 0x7010, 0) }, /* UB99 */
815a963377aSSascha Wildner };
816a963377aSSascha Wildner 
81709b9c6f2SSascha Wildner static const STRUCT_USB_HOST_ID __used uaudio_devs[] = {
81809b9c6f2SSascha Wildner 	/* Generic USB audio class match */
81909b9c6f2SSascha Wildner 	{USB_IFACE_CLASS(UICLASS_AUDIO),
82009b9c6f2SSascha Wildner 	 USB_IFACE_SUBCLASS(UISUBCLASS_AUDIOCONTROL),},
82109b9c6f2SSascha Wildner 	/* Generic USB MIDI class match */
82209b9c6f2SSascha Wildner 	{USB_IFACE_CLASS(UICLASS_AUDIO),
82309b9c6f2SSascha Wildner 	 USB_IFACE_SUBCLASS(UISUBCLASS_MIDISTREAM),},
82409b9c6f2SSascha Wildner };
82509b9c6f2SSascha Wildner 
82609b9c6f2SSascha Wildner static int
uaudio_probe(device_t dev)82709b9c6f2SSascha Wildner uaudio_probe(device_t dev)
82809b9c6f2SSascha Wildner {
82909b9c6f2SSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
83009b9c6f2SSascha Wildner 
83109b9c6f2SSascha Wildner 	if (uaa->usb_mode != USB_MODE_HOST)
83209b9c6f2SSascha Wildner 		return (ENXIO);
83309b9c6f2SSascha Wildner 
834a963377aSSascha Wildner 	/* lookup non-standard device(s) */
835a963377aSSascha Wildner 
836a963377aSSascha Wildner 	if (usbd_lookup_id_by_uaa(uaudio_vendor_midi,
837a963377aSSascha Wildner 	    sizeof(uaudio_vendor_midi), uaa) == 0) {
838a963377aSSascha Wildner 		return (BUS_PROBE_SPECIFIC);
839a963377aSSascha Wildner 	}
84009b9c6f2SSascha Wildner 
84109b9c6f2SSascha Wildner 	if (uaa->info.bInterfaceClass != UICLASS_AUDIO) {
842a963377aSSascha Wildner 		if (uaa->info.bInterfaceClass != UICLASS_VENDOR ||
843a963377aSSascha Wildner 		    usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0)
84409b9c6f2SSascha Wildner 			return (ENXIO);
84509b9c6f2SSascha Wildner 	}
84609b9c6f2SSascha Wildner 
84709b9c6f2SSascha Wildner 	/* check for AUDIO control interface */
84809b9c6f2SSascha Wildner 
84909b9c6f2SSascha Wildner 	if (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL) {
85009b9c6f2SSascha Wildner 		if (usb_test_quirk(uaa, UQ_BAD_AUDIO))
85109b9c6f2SSascha Wildner 			return (ENXIO);
85209b9c6f2SSascha Wildner 		else
85309b9c6f2SSascha Wildner 			return (BUS_PROBE_GENERIC);
85409b9c6f2SSascha Wildner 	}
85509b9c6f2SSascha Wildner 
85609b9c6f2SSascha Wildner 	/* check for MIDI stream */
85709b9c6f2SSascha Wildner 
85809b9c6f2SSascha Wildner 	if (uaa->info.bInterfaceSubClass == UISUBCLASS_MIDISTREAM) {
85909b9c6f2SSascha Wildner 		if (usb_test_quirk(uaa, UQ_BAD_MIDI))
86009b9c6f2SSascha Wildner 			return (ENXIO);
86109b9c6f2SSascha Wildner 		else
86209b9c6f2SSascha Wildner 			return (BUS_PROBE_GENERIC);
86309b9c6f2SSascha Wildner 	}
86409b9c6f2SSascha Wildner 	return (ENXIO);
86509b9c6f2SSascha Wildner }
86609b9c6f2SSascha Wildner 
867520083baSSascha Wildner /*
868520083baSSascha Wildner  * Set Cmedia CM6206 S/PDIF settings
869520083baSSascha Wildner  * Source: CM6206 Datasheet v2.3.
870520083baSSascha Wildner  */
871520083baSSascha Wildner static int
uaudio_set_spdif_cm6206(struct uaudio_softc * sc,int flags)872520083baSSascha Wildner uaudio_set_spdif_cm6206(struct uaudio_softc *sc, int flags)
873520083baSSascha Wildner {
874520083baSSascha Wildner 	uint8_t cmd[2][4] = {
875520083baSSascha Wildner 		{0x20, 0x20, 0x00, 0},
876520083baSSascha Wildner 		{0x20, 0x30, 0x02, 1}
877520083baSSascha Wildner 	};
878520083baSSascha Wildner 	int i;
879520083baSSascha Wildner 
880520083baSSascha Wildner 	if (flags & UAUDIO_SPDIF_OUT)
881520083baSSascha Wildner 		cmd[1][1] = 0x00;
882520083baSSascha Wildner 	else
883520083baSSascha Wildner 		cmd[1][1] = 0x02;
884520083baSSascha Wildner 
885520083baSSascha Wildner 	if (flags & UAUDIO_SPDIF_OUT_96K)
886520083baSSascha Wildner 		cmd[0][1] = 0x60;	/* 96K: 3'b110 */
887520083baSSascha Wildner 
888520083baSSascha Wildner 	if (flags & UAUDIO_SPDIF_IN_MIX)
889520083baSSascha Wildner 		cmd[1][1] = 0x03;	/* SPDIFMIX */
890520083baSSascha Wildner 
891520083baSSascha Wildner 	for (i = 0; i < 2; i++) {
892520083baSSascha Wildner 		if (usbd_req_set_report(sc->sc_udev, NULL,
893520083baSSascha Wildner 		    cmd[i], sizeof(cmd[0]),
894520083baSSascha Wildner 		    sc->sc_mixer_iface_index, UHID_OUTPUT_REPORT, 0) != 0) {
895520083baSSascha Wildner 			return (ENXIO);
896520083baSSascha Wildner 		}
897520083baSSascha Wildner 	}
898520083baSSascha Wildner 	return (0);
899520083baSSascha Wildner }
900520083baSSascha Wildner 
901520083baSSascha Wildner static int
uaudio_set_spdif_dummy(struct uaudio_softc * sc,int flags)902520083baSSascha Wildner uaudio_set_spdif_dummy(struct uaudio_softc *sc, int flags)
903520083baSSascha Wildner {
904520083baSSascha Wildner 	return (0);
905520083baSSascha Wildner }
906520083baSSascha Wildner 
90709b9c6f2SSascha Wildner static int
uaudio_attach(device_t dev)90809b9c6f2SSascha Wildner uaudio_attach(device_t dev)
90909b9c6f2SSascha Wildner {
91009b9c6f2SSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
91109b9c6f2SSascha Wildner 	struct uaudio_softc *sc = device_get_softc(dev);
91209b9c6f2SSascha Wildner 	struct usb_interface_descriptor *id;
913a963377aSSascha Wildner 	usb_error_t err;
91409b9c6f2SSascha Wildner 	device_t child;
91509b9c6f2SSascha Wildner 
91609b9c6f2SSascha Wildner 	sc->sc_play_chan.priv_sc = sc;
91709b9c6f2SSascha Wildner 	sc->sc_rec_chan.priv_sc = sc;
91809b9c6f2SSascha Wildner 	sc->sc_udev = uaa->device;
91909b9c6f2SSascha Wildner 	sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
92009b9c6f2SSascha Wildner 	sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
921a963377aSSascha Wildner 	sc->sc_config_msg[0].hdr.pm_callback = &uaudio_configure_msg;
922a963377aSSascha Wildner 	sc->sc_config_msg[0].sc = sc;
923a963377aSSascha Wildner 	sc->sc_config_msg[1].hdr.pm_callback = &uaudio_configure_msg;
924a963377aSSascha Wildner 	sc->sc_config_msg[1].sc = sc;
92509b9c6f2SSascha Wildner 
92609b9c6f2SSascha Wildner 	if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR))
92709b9c6f2SSascha Wildner 		sc->sc_uq_audio_swap_lr = 1;
92809b9c6f2SSascha Wildner 
92909b9c6f2SSascha Wildner 	if (usb_test_quirk(uaa, UQ_AU_INP_ASYNC))
93009b9c6f2SSascha Wildner 		sc->sc_uq_au_inp_async = 1;
93109b9c6f2SSascha Wildner 
93209b9c6f2SSascha Wildner 	if (usb_test_quirk(uaa, UQ_AU_NO_XU))
93309b9c6f2SSascha Wildner 		sc->sc_uq_au_no_xu = 1;
93409b9c6f2SSascha Wildner 
93509b9c6f2SSascha Wildner 	if (usb_test_quirk(uaa, UQ_BAD_ADC))
93609b9c6f2SSascha Wildner 		sc->sc_uq_bad_adc = 1;
93709b9c6f2SSascha Wildner 
93809b9c6f2SSascha Wildner 	if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS))
93909b9c6f2SSascha Wildner 		sc->sc_uq_au_vendor_class = 1;
94009b9c6f2SSascha Wildner 
941520083baSSascha Wildner 	/* set S/PDIF function */
942520083baSSascha Wildner 	if (usb_test_quirk(uaa, UQ_AU_SET_SPDIF_CM6206))
943520083baSSascha Wildner 		sc->sc_set_spdif_fn = uaudio_set_spdif_cm6206;
944520083baSSascha Wildner 	else
945520083baSSascha Wildner 		sc->sc_set_spdif_fn = uaudio_set_spdif_dummy;
946520083baSSascha Wildner 
94709b9c6f2SSascha Wildner 	umidi_init(dev);
94809b9c6f2SSascha Wildner 
94909b9c6f2SSascha Wildner 	device_set_usb_desc(dev);
95009b9c6f2SSascha Wildner 
95109b9c6f2SSascha Wildner 	id = usbd_get_interface_descriptor(uaa->iface);
95209b9c6f2SSascha Wildner 
953a963377aSSascha Wildner 	/* must fill mixer info before channel info */
95409b9c6f2SSascha Wildner 	uaudio_mixer_fill_info(sc, uaa->device, id);
95509b9c6f2SSascha Wildner 
956a963377aSSascha Wildner 	/* fill channel info */
957a963377aSSascha Wildner 	uaudio_chan_fill_info(sc, uaa->device);
958a963377aSSascha Wildner 
95909b9c6f2SSascha Wildner 	DPRINTF("audio rev %d.%02x\n",
96009b9c6f2SSascha Wildner 	    sc->sc_audio_rev >> 8,
96109b9c6f2SSascha Wildner 	    sc->sc_audio_rev & 0xff);
96209b9c6f2SSascha Wildner 
963a963377aSSascha Wildner 	if (sc->sc_mixer_count == 0) {
964a963377aSSascha Wildner 		if (uaa->info.idVendor == USB_VENDOR_MAUDIO &&
965a963377aSSascha Wildner 		    (uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA ||
966a963377aSSascha Wildner 		    uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA8R)) {
967a963377aSSascha Wildner 			DPRINTF("Generating mixer descriptors\n");
968a963377aSSascha Wildner 			uaudio_mixer_controls_create_ftu(sc);
969a963377aSSascha Wildner 		}
970a963377aSSascha Wildner 	}
971a963377aSSascha Wildner 
97209b9c6f2SSascha Wildner 	DPRINTF("%d mixer controls\n",
97309b9c6f2SSascha Wildner 	    sc->sc_mixer_count);
97409b9c6f2SSascha Wildner 
975a963377aSSascha Wildner 	if (sc->sc_play_chan.num_alt > 0) {
976a963377aSSascha Wildner 		uint8_t x;
977a963377aSSascha Wildner 
978a963377aSSascha Wildner 		/*
979a963377aSSascha Wildner 		 * Need to set a default alternate interface, else
980a963377aSSascha Wildner 		 * some USB audio devices might go into an infinte
981a963377aSSascha Wildner 		 * re-enumeration loop:
982a963377aSSascha Wildner 		 */
983a963377aSSascha Wildner 		err = usbd_set_alt_interface_index(sc->sc_udev,
984a963377aSSascha Wildner 		    sc->sc_play_chan.usb_alt[0].iface_index,
985a963377aSSascha Wildner 		    sc->sc_play_chan.usb_alt[0].iface_alt_index);
986a963377aSSascha Wildner 		if (err) {
987a963377aSSascha Wildner 			DPRINTF("setting of alternate index failed: %s!\n",
988a963377aSSascha Wildner 			    usbd_errstr(err));
989a963377aSSascha Wildner 		}
990a963377aSSascha Wildner 		for (x = 0; x != sc->sc_play_chan.num_alt; x++) {
991a963377aSSascha Wildner 			device_printf(dev, "Play: %d Hz, %d ch, %s format, "
992a963377aSSascha Wildner 			    "2x8ms buffer.\n",
993a963377aSSascha Wildner 			    sc->sc_play_chan.usb_alt[x].sample_rate,
994a963377aSSascha Wildner 			    sc->sc_play_chan.usb_alt[x].channels,
995a963377aSSascha Wildner 			    sc->sc_play_chan.usb_alt[x].p_fmt->description);
996a963377aSSascha Wildner 		}
99709b9c6f2SSascha Wildner 	} else {
998a963377aSSascha Wildner 		device_printf(dev, "No playback.\n");
99909b9c6f2SSascha Wildner 	}
100009b9c6f2SSascha Wildner 
1001a963377aSSascha Wildner 	if (sc->sc_rec_chan.num_alt > 0) {
1002a963377aSSascha Wildner 		uint8_t x;
1003a963377aSSascha Wildner 
1004a963377aSSascha Wildner 		/*
1005a963377aSSascha Wildner 		 * Need to set a default alternate interface, else
1006a963377aSSascha Wildner 		 * some USB audio devices might go into an infinte
1007a963377aSSascha Wildner 		 * re-enumeration loop:
1008a963377aSSascha Wildner 		 */
1009a963377aSSascha Wildner 		err = usbd_set_alt_interface_index(sc->sc_udev,
1010a963377aSSascha Wildner 		    sc->sc_rec_chan.usb_alt[0].iface_index,
1011a963377aSSascha Wildner 		    sc->sc_rec_chan.usb_alt[0].iface_alt_index);
1012a963377aSSascha Wildner 		if (err) {
1013a963377aSSascha Wildner 			DPRINTF("setting of alternate index failed: %s!\n",
1014a963377aSSascha Wildner 			    usbd_errstr(err));
1015a963377aSSascha Wildner 		}
1016a963377aSSascha Wildner 		for (x = 0; x != sc->sc_rec_chan.num_alt; x++) {
1017a963377aSSascha Wildner 			device_printf(dev, "Record: %d Hz, %d ch, %s format, "
1018a963377aSSascha Wildner 			    "2x8ms buffer.\n",
1019a963377aSSascha Wildner 			    sc->sc_rec_chan.usb_alt[x].sample_rate,
1020a963377aSSascha Wildner 			    sc->sc_rec_chan.usb_alt[x].channels,
1021a963377aSSascha Wildner 			    sc->sc_rec_chan.usb_alt[x].p_fmt->description);
1022a963377aSSascha Wildner 		}
102309b9c6f2SSascha Wildner 	} else {
1024a963377aSSascha Wildner 		device_printf(dev, "No recording.\n");
1025a963377aSSascha Wildner 	}
1026a963377aSSascha Wildner 
1027a963377aSSascha Wildner 	if (sc->sc_midi_chan.valid == 0) {
1028a963377aSSascha Wildner 		if (usbd_lookup_id_by_uaa(uaudio_vendor_midi,
1029a963377aSSascha Wildner 		    sizeof(uaudio_vendor_midi), uaa) == 0) {
1030a963377aSSascha Wildner 			sc->sc_midi_chan.iface_index =
1031a963377aSSascha Wildner 			    (uint8_t)uaa->driver_info;
1032a963377aSSascha Wildner 			sc->sc_midi_chan.iface_alt_index = 0;
1033a963377aSSascha Wildner 			sc->sc_midi_chan.valid = 1;
1034a963377aSSascha Wildner 		}
103509b9c6f2SSascha Wildner 	}
103609b9c6f2SSascha Wildner 
103709b9c6f2SSascha Wildner 	if (sc->sc_midi_chan.valid) {
103809b9c6f2SSascha Wildner 
103909b9c6f2SSascha Wildner 		if (umidi_probe(dev)) {
104009b9c6f2SSascha Wildner 			goto detach;
104109b9c6f2SSascha Wildner 		}
1042a963377aSSascha Wildner 		device_printf(dev, "MIDI sequencer.\n");
104309b9c6f2SSascha Wildner 	} else {
1044a963377aSSascha Wildner 		device_printf(dev, "No MIDI sequencer.\n");
104509b9c6f2SSascha Wildner 	}
104609b9c6f2SSascha Wildner 
104709b9c6f2SSascha Wildner 	DPRINTF("doing child attach\n");
104809b9c6f2SSascha Wildner 
104909b9c6f2SSascha Wildner 	/* attach the children */
105009b9c6f2SSascha Wildner 
105109b9c6f2SSascha Wildner 	sc->sc_sndcard_func.func = SCF_PCM;
105209b9c6f2SSascha Wildner 
1053a963377aSSascha Wildner 	/*
1054a963377aSSascha Wildner 	 * Only attach a PCM device if we have a playback, recording
1055a963377aSSascha Wildner 	 * or mixer device present:
1056a963377aSSascha Wildner 	 */
1057a963377aSSascha Wildner 	if (sc->sc_play_chan.num_alt > 0 ||
1058a963377aSSascha Wildner 	    sc->sc_rec_chan.num_alt > 0 ||
1059a963377aSSascha Wildner 	    sc->sc_mix_info) {
106009b9c6f2SSascha Wildner 		child = device_add_child(dev, "pcm", -1);
106109b9c6f2SSascha Wildner 
106209b9c6f2SSascha Wildner 		if (child == NULL) {
106309b9c6f2SSascha Wildner 			DPRINTF("out of memory\n");
106409b9c6f2SSascha Wildner 			goto detach;
106509b9c6f2SSascha Wildner 		}
106609b9c6f2SSascha Wildner 		device_set_ivars(child, &sc->sc_sndcard_func);
1067a963377aSSascha Wildner 	}
106809b9c6f2SSascha Wildner 
106909b9c6f2SSascha Wildner 	if (bus_generic_attach(dev)) {
107009b9c6f2SSascha Wildner 		DPRINTF("child attach failed\n");
107109b9c6f2SSascha Wildner 		goto detach;
107209b9c6f2SSascha Wildner 	}
1073a963377aSSascha Wildner 
1074a963377aSSascha Wildner 	if (uaudio_hid_probe(sc, uaa) == 0) {
1075a963377aSSascha Wildner 		device_printf(dev, "HID volume keys found.\n");
1076a963377aSSascha Wildner 	} else {
1077a963377aSSascha Wildner 		device_printf(dev, "No HID volume keys found.\n");
1078a963377aSSascha Wildner 	}
1079a963377aSSascha Wildner 
1080a963377aSSascha Wildner 	/* reload all mixer settings */
1081a963377aSSascha Wildner 	uaudio_mixer_reload_all(sc);
1082a963377aSSascha Wildner 
1083520083baSSascha Wildner 	/* enable S/PDIF output, if any */
1084520083baSSascha Wildner 	if (sc->sc_set_spdif_fn(sc,
1085520083baSSascha Wildner 	    UAUDIO_SPDIF_OUT | UAUDIO_SPDIF_OUT_48K) != 0) {
1086520083baSSascha Wildner 		device_printf(dev, "Failed to enable S/PDIF at 48K\n");
1087520083baSSascha Wildner 	}
108809b9c6f2SSascha Wildner 	return (0);			/* success */
108909b9c6f2SSascha Wildner 
109009b9c6f2SSascha Wildner detach:
109109b9c6f2SSascha Wildner 	uaudio_detach(dev);
109209b9c6f2SSascha Wildner 	return (ENXIO);
109309b9c6f2SSascha Wildner }
109409b9c6f2SSascha Wildner 
109509b9c6f2SSascha Wildner static void
uaudio_pcm_setflags(device_t dev,uint32_t flags)109609b9c6f2SSascha Wildner uaudio_pcm_setflags(device_t dev, uint32_t flags)
109709b9c6f2SSascha Wildner {
109809b9c6f2SSascha Wildner 	pcm_setflags(dev, pcm_getflags(dev) | flags);
109909b9c6f2SSascha Wildner }
110009b9c6f2SSascha Wildner 
110109b9c6f2SSascha Wildner int
uaudio_attach_sub(device_t dev,kobj_class_t mixer_class,kobj_class_t chan_class)110209b9c6f2SSascha Wildner uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class)
110309b9c6f2SSascha Wildner {
110409b9c6f2SSascha Wildner 	struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
110509b9c6f2SSascha Wildner 	char status[SND_STATUSLEN];
110609b9c6f2SSascha Wildner 
110709b9c6f2SSascha Wildner 	uaudio_mixer_init(sc);
110809b9c6f2SSascha Wildner 
110909b9c6f2SSascha Wildner 	if (sc->sc_uq_audio_swap_lr) {
111009b9c6f2SSascha Wildner 		DPRINTF("hardware has swapped left and right\n");
111109b9c6f2SSascha Wildner 		/* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */
111209b9c6f2SSascha Wildner 	}
111309b9c6f2SSascha Wildner 	if (!(sc->sc_mix_info & SOUND_MASK_PCM)) {
111409b9c6f2SSascha Wildner 
111509b9c6f2SSascha Wildner 		DPRINTF("emulating master volume\n");
111609b9c6f2SSascha Wildner 
111709b9c6f2SSascha Wildner 		/*
111809b9c6f2SSascha Wildner 		 * Emulate missing pcm mixer controller
111909b9c6f2SSascha Wildner 		 * through FEEDER_VOLUME
112009b9c6f2SSascha Wildner 		 */
112109b9c6f2SSascha Wildner 		uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL);
112209b9c6f2SSascha Wildner 	}
11235e4edd23SMatthew Dillon 	if (sc->sc_pcm_bitperfect) {
11245e4edd23SMatthew Dillon 		DPRINTF("device needs bitperfect by default\n");
11255e4edd23SMatthew Dillon 		uaudio_pcm_setflags(dev, SD_F_BITPERFECT);
11265e4edd23SMatthew Dillon 	}
1127a963377aSSascha Wildner 	if (mixer_init(dev, mixer_class, sc))
112809b9c6f2SSascha Wildner 		goto detach;
112909b9c6f2SSascha Wildner 	sc->sc_mixer_init = 1;
113009b9c6f2SSascha Wildner 
1131a963377aSSascha Wildner 	mixer_hwvol_init(dev);
1132a963377aSSascha Wildner 
113309b9c6f2SSascha Wildner 	ksnprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
113409b9c6f2SSascha Wildner 
113509b9c6f2SSascha Wildner 	if (pcm_register(dev, sc,
1136a963377aSSascha Wildner 	    (sc->sc_play_chan.num_alt > 0) ? 1 : 0,
1137a963377aSSascha Wildner 	    (sc->sc_rec_chan.num_alt > 0) ? 1 : 0)) {
113809b9c6f2SSascha Wildner 		goto detach;
113909b9c6f2SSascha Wildner 	}
114009b9c6f2SSascha Wildner 
114109b9c6f2SSascha Wildner 	uaudio_pcm_setflags(dev, SD_F_MPSAFE);
114209b9c6f2SSascha Wildner 	sc->sc_pcm_registered = 1;
114309b9c6f2SSascha Wildner 
1144a963377aSSascha Wildner 	if (sc->sc_play_chan.num_alt > 0) {
114509b9c6f2SSascha Wildner 		pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc);
114609b9c6f2SSascha Wildner 	}
1147a963377aSSascha Wildner 	if (sc->sc_rec_chan.num_alt > 0) {
114809b9c6f2SSascha Wildner 		pcm_addchan(dev, PCMDIR_REC, chan_class, sc);
114909b9c6f2SSascha Wildner 	}
115009b9c6f2SSascha Wildner 	pcm_setstatus(dev, status);
115109b9c6f2SSascha Wildner 
1152a963377aSSascha Wildner 	uaudio_mixer_register_sysctl(sc, dev);
1153a963377aSSascha Wildner 
11545e4edd23SMatthew Dillon 	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
11555e4edd23SMatthew Dillon 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
11565e4edd23SMatthew Dillon 	    "feedback_rate", CTLFLAG_RD, &sc->sc_play_chan.feedback_rate,
11575e4edd23SMatthew Dillon 	    0, "Feedback sample rate in Hz");
11585e4edd23SMatthew Dillon 
115909b9c6f2SSascha Wildner 	return (0);			/* success */
116009b9c6f2SSascha Wildner 
116109b9c6f2SSascha Wildner detach:
116209b9c6f2SSascha Wildner 	uaudio_detach_sub(dev);
116309b9c6f2SSascha Wildner 	return (ENXIO);
116409b9c6f2SSascha Wildner }
116509b9c6f2SSascha Wildner 
116609b9c6f2SSascha Wildner int
uaudio_detach_sub(device_t dev)116709b9c6f2SSascha Wildner uaudio_detach_sub(device_t dev)
116809b9c6f2SSascha Wildner {
116909b9c6f2SSascha Wildner 	struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
117009b9c6f2SSascha Wildner 	int error = 0;
117109b9c6f2SSascha Wildner 
1172520083baSSascha Wildner 	/* disable S/PDIF output, if any */
1173520083baSSascha Wildner 	(void) sc->sc_set_spdif_fn(sc, 0);
1174520083baSSascha Wildner 
117509b9c6f2SSascha Wildner repeat:
117609b9c6f2SSascha Wildner 	if (sc->sc_pcm_registered) {
117709b9c6f2SSascha Wildner 		error = pcm_unregister(dev);
117809b9c6f2SSascha Wildner 	} else {
117909b9c6f2SSascha Wildner 		if (sc->sc_mixer_init) {
118009b9c6f2SSascha Wildner 			error = mixer_uninit(dev);
118109b9c6f2SSascha Wildner 		}
118209b9c6f2SSascha Wildner 	}
118309b9c6f2SSascha Wildner 
118409b9c6f2SSascha Wildner 	if (error) {
118509b9c6f2SSascha Wildner 		device_printf(dev, "Waiting for sound application to exit!\n");
118609b9c6f2SSascha Wildner 		usb_pause_mtx(NULL, 2 * hz);
118709b9c6f2SSascha Wildner 		goto repeat;		/* try again */
118809b9c6f2SSascha Wildner 	}
118909b9c6f2SSascha Wildner 	return (0);			/* success */
119009b9c6f2SSascha Wildner }
119109b9c6f2SSascha Wildner 
119209b9c6f2SSascha Wildner static int
uaudio_detach(device_t dev)119309b9c6f2SSascha Wildner uaudio_detach(device_t dev)
119409b9c6f2SSascha Wildner {
119509b9c6f2SSascha Wildner 	struct uaudio_softc *sc = device_get_softc(dev);
119609b9c6f2SSascha Wildner 
119709b9c6f2SSascha Wildner 	/*
119809b9c6f2SSascha Wildner 	 * Stop USB transfers early so that any audio applications
119909b9c6f2SSascha Wildner 	 * will time out and close opened /dev/dspX.Y device(s), if
120009b9c6f2SSascha Wildner 	 * any.
120109b9c6f2SSascha Wildner 	 */
1202a963377aSSascha Wildner 	usb_proc_explore_lock(sc->sc_udev);
1203a963377aSSascha Wildner 	sc->sc_play_chan.operation = CHAN_OP_DRAIN;
1204a963377aSSascha Wildner 	sc->sc_rec_chan.operation = CHAN_OP_DRAIN;
1205a963377aSSascha Wildner 	usb_proc_explore_mwait(sc->sc_udev,
1206a963377aSSascha Wildner 	    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
1207a963377aSSascha Wildner 	usb_proc_explore_unlock(sc->sc_udev);
1208a963377aSSascha Wildner 
1209a963377aSSascha Wildner 	usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
1210a963377aSSascha Wildner 	usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);
1211a963377aSSascha Wildner 
1212a963377aSSascha Wildner 	uaudio_hid_detach(sc);
121309b9c6f2SSascha Wildner 
121409b9c6f2SSascha Wildner 	if (bus_generic_detach(dev) != 0) {
121509b9c6f2SSascha Wildner 		DPRINTF("detach failed!\n");
121609b9c6f2SSascha Wildner 	}
121709b9c6f2SSascha Wildner 	sbuf_delete(&sc->sc_sndstat);
121809b9c6f2SSascha Wildner 	sc->sc_sndstat_valid = 0;
121909b9c6f2SSascha Wildner 
122009b9c6f2SSascha Wildner 	umidi_detach(dev);
1221a963377aSSascha Wildner 
1222a963377aSSascha Wildner 	/* free mixer data */
1223a963377aSSascha Wildner 
1224a963377aSSascha Wildner 	uaudio_mixer_ctl_free(sc);
122509b9c6f2SSascha Wildner 
122609b9c6f2SSascha Wildner 	return (0);
122709b9c6f2SSascha Wildner }
122809b9c6f2SSascha Wildner 
1229a963377aSSascha Wildner static uint32_t
uaudio_get_buffer_size(struct uaudio_chan * ch,uint8_t alt)1230a963377aSSascha Wildner uaudio_get_buffer_size(struct uaudio_chan *ch, uint8_t alt)
1231a963377aSSascha Wildner {
1232a963377aSSascha Wildner 	struct uaudio_chan_alt *chan_alt = &ch->usb_alt[alt];
1233a963377aSSascha Wildner 	/* We use 2 times 8ms of buffer */
1234a963377aSSascha Wildner 	uint32_t buf_size = (((chan_alt->sample_rate * (UAUDIO_NFRAMES / 8)) +
1235a963377aSSascha Wildner 	    1000 - 1) / 1000) * chan_alt->sample_size;
1236a963377aSSascha Wildner 	return (buf_size);
1237a963377aSSascha Wildner }
1238a963377aSSascha Wildner 
1239a963377aSSascha Wildner static void
uaudio_configure_msg_sub(struct uaudio_softc * sc,struct uaudio_chan * chan,int dir)1240a963377aSSascha Wildner uaudio_configure_msg_sub(struct uaudio_softc *sc,
1241a963377aSSascha Wildner     struct uaudio_chan *chan, int dir)
1242a963377aSSascha Wildner {
1243a963377aSSascha Wildner 	struct uaudio_chan_alt *chan_alt;
1244a963377aSSascha Wildner 	uint32_t frames;
1245a963377aSSascha Wildner 	uint32_t buf_size;
1246a963377aSSascha Wildner 	uint16_t fps;
1247a963377aSSascha Wildner 	uint8_t set_alt;
1248a963377aSSascha Wildner 	uint8_t fps_shift;
1249a963377aSSascha Wildner 	uint8_t operation;
1250a963377aSSascha Wildner 	usb_error_t err;
1251a963377aSSascha Wildner 
1252a963377aSSascha Wildner 	if (chan->num_alt <= 0)
1253a963377aSSascha Wildner 		return;
1254a963377aSSascha Wildner 
1255a963377aSSascha Wildner 	DPRINTF("\n");
1256a963377aSSascha Wildner 
1257a963377aSSascha Wildner 	usb_proc_explore_lock(sc->sc_udev);
1258a963377aSSascha Wildner 	operation = chan->operation;
1259a963377aSSascha Wildner 	chan->operation = CHAN_OP_NONE;
1260a963377aSSascha Wildner 	usb_proc_explore_unlock(sc->sc_udev);
1261a963377aSSascha Wildner 
1262a963377aSSascha Wildner 	lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
1263a963377aSSascha Wildner 	if (chan->cur_alt != chan->set_alt)
1264a963377aSSascha Wildner 		set_alt = chan->set_alt;
1265a963377aSSascha Wildner 	else
1266a963377aSSascha Wildner 		set_alt = CHAN_MAX_ALT;
1267a963377aSSascha Wildner 	lockmgr(chan->pcm_lock, LK_RELEASE);
1268a963377aSSascha Wildner 
1269a963377aSSascha Wildner 	if (set_alt >= chan->num_alt)
1270a963377aSSascha Wildner 		goto done;
1271a963377aSSascha Wildner 
1272a963377aSSascha Wildner 	chan_alt = chan->usb_alt + set_alt;
1273a963377aSSascha Wildner 
1274a963377aSSascha Wildner 	usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
1275a963377aSSascha Wildner 
1276a963377aSSascha Wildner 	err = usbd_set_alt_interface_index(sc->sc_udev,
1277a963377aSSascha Wildner 	    chan_alt->iface_index, chan_alt->iface_alt_index);
1278a963377aSSascha Wildner 	if (err) {
1279a963377aSSascha Wildner 		DPRINTF("setting of alternate index failed: %s!\n",
1280a963377aSSascha Wildner 		    usbd_errstr(err));
1281a963377aSSascha Wildner 		goto error;
1282a963377aSSascha Wildner 	}
1283a963377aSSascha Wildner 
1284a963377aSSascha Wildner 	/*
1285a963377aSSascha Wildner 	 * Only set the sample rate if the channel reports that it
1286a963377aSSascha Wildner 	 * supports the frequency control.
1287a963377aSSascha Wildner 	 */
1288a963377aSSascha Wildner 
1289a963377aSSascha Wildner 	if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
1290a963377aSSascha Wildner 		/* FALLTHROUGH */
1291a963377aSSascha Wildner 	} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
1292a963377aSSascha Wildner 		unsigned int x;
1293a963377aSSascha Wildner 
1294a963377aSSascha Wildner 		for (x = 0; x != 256; x++) {
1295a963377aSSascha Wildner 			if (dir == PCMDIR_PLAY) {
1296691f0a75SSascha Wildner 				if (isclr(sc->sc_mixer_clocks.bit_output, x)) {
1297a963377aSSascha Wildner 					continue;
1298a963377aSSascha Wildner 				}
1299a963377aSSascha Wildner 			} else {
1300691f0a75SSascha Wildner 				if (isclr(sc->sc_mixer_clocks.bit_input, x)) {
1301a963377aSSascha Wildner 					continue;
1302a963377aSSascha Wildner 				}
1303a963377aSSascha Wildner 			}
1304a963377aSSascha Wildner 
1305a963377aSSascha Wildner 			if (uaudio20_set_speed(sc->sc_udev,
1306a963377aSSascha Wildner 			    sc->sc_mixer_iface_no, x, chan_alt->sample_rate)) {
1307a963377aSSascha Wildner 				/*
1308a963377aSSascha Wildner 				 * If the endpoint is adaptive setting
1309a963377aSSascha Wildner 				 * the speed may fail.
1310a963377aSSascha Wildner 				 */
1311a963377aSSascha Wildner 				DPRINTF("setting of sample rate failed! "
1312a963377aSSascha Wildner 				    "(continuing anyway)\n");
1313a963377aSSascha Wildner 			}
1314a963377aSSascha Wildner 		}
1315a963377aSSascha Wildner 	} else if (chan_alt->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) {
1316a963377aSSascha Wildner 		if (uaudio_set_speed(sc->sc_udev,
1317a963377aSSascha Wildner 		    chan_alt->p_ed1->bEndpointAddress, chan_alt->sample_rate)) {
1318a963377aSSascha Wildner 			/*
1319a963377aSSascha Wildner 			 * If the endpoint is adaptive setting the
1320a963377aSSascha Wildner 			 * speed may fail.
1321a963377aSSascha Wildner 			 */
1322a963377aSSascha Wildner 			DPRINTF("setting of sample rate failed! "
1323a963377aSSascha Wildner 			    "(continuing anyway)\n");
1324a963377aSSascha Wildner 		}
1325a963377aSSascha Wildner 	}
1326a963377aSSascha Wildner 	if (usbd_transfer_setup(sc->sc_udev, &chan_alt->iface_index, chan->xfer,
1327a963377aSSascha Wildner 	    chan_alt->usb_cfg, UAUDIO_NCHANBUFS + 1, chan, chan->pcm_lock)) {
1328a963377aSSascha Wildner 		DPRINTF("could not allocate USB transfers!\n");
1329a963377aSSascha Wildner 		goto error;
1330a963377aSSascha Wildner 	}
1331a963377aSSascha Wildner 
1332a963377aSSascha Wildner 	fps = usbd_get_isoc_fps(sc->sc_udev);
1333a963377aSSascha Wildner 
1334a963377aSSascha Wildner 	if (fps < 8000) {
1335a963377aSSascha Wildner 		/* FULL speed USB */
1336a963377aSSascha Wildner 		frames = 8;
1337a963377aSSascha Wildner 	} else {
1338a963377aSSascha Wildner 		/* HIGH speed USB */
1339a963377aSSascha Wildner 		frames = UAUDIO_NFRAMES;
1340a963377aSSascha Wildner 	}
1341a963377aSSascha Wildner 
1342a963377aSSascha Wildner 	fps_shift = usbd_xfer_get_fps_shift(chan->xfer[0]);
1343a963377aSSascha Wildner 
1344a963377aSSascha Wildner 	/* down shift number of frames per second, if any */
1345a963377aSSascha Wildner 	fps >>= fps_shift;
1346a963377aSSascha Wildner 	frames >>= fps_shift;
1347a963377aSSascha Wildner 
1348a963377aSSascha Wildner 	/* bytes per frame should not be zero */
1349a963377aSSascha Wildner 	chan->bytes_per_frame[0] =
1350a963377aSSascha Wildner 	    ((chan_alt->sample_rate / fps) * chan_alt->sample_size);
1351a963377aSSascha Wildner 	chan->bytes_per_frame[1] =
1352a963377aSSascha Wildner 	    (((chan_alt->sample_rate + fps - 1) / fps) * chan_alt->sample_size);
1353a963377aSSascha Wildner 
1354a963377aSSascha Wildner 	/* setup data rate dithering, if any */
1355a963377aSSascha Wildner 	chan->frames_per_second = fps;
1356a963377aSSascha Wildner 	chan->sample_rem = chan_alt->sample_rate % fps;
1357a963377aSSascha Wildner 	chan->sample_curr = 0;
1358a963377aSSascha Wildner 
1359a963377aSSascha Wildner 	/* compute required buffer size */
1360a963377aSSascha Wildner 	buf_size = (chan->bytes_per_frame[1] * frames);
1361a963377aSSascha Wildner 
1362a963377aSSascha Wildner 	if (buf_size > (chan->end - chan->start)) {
1363a963377aSSascha Wildner 		DPRINTF("buffer size is too big\n");
1364a963377aSSascha Wildner 		goto error;
1365a963377aSSascha Wildner 	}
1366a963377aSSascha Wildner 
1367a963377aSSascha Wildner 	chan->intr_frames = frames;
1368a963377aSSascha Wildner 
1369a963377aSSascha Wildner 	DPRINTF("fps=%d sample_rem=%d\n", (int)fps, (int)chan->sample_rem);
1370a963377aSSascha Wildner 
1371a963377aSSascha Wildner 	if (chan->intr_frames == 0) {
1372a963377aSSascha Wildner 		DPRINTF("frame shift is too high!\n");
1373a963377aSSascha Wildner 		goto error;
1374a963377aSSascha Wildner 	}
1375a963377aSSascha Wildner 
1376a963377aSSascha Wildner 	lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
1377a963377aSSascha Wildner 	chan->cur_alt = set_alt;
1378a963377aSSascha Wildner 	lockmgr(chan->pcm_lock, LK_RELEASE);
1379a963377aSSascha Wildner 
1380a963377aSSascha Wildner done:
1381a963377aSSascha Wildner #if (UAUDIO_NCHANBUFS != 2)
1382a963377aSSascha Wildner #error "please update code"
1383a963377aSSascha Wildner #endif
1384a963377aSSascha Wildner 	switch (operation) {
1385a963377aSSascha Wildner 	case CHAN_OP_START:
1386a963377aSSascha Wildner 		lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
1387a963377aSSascha Wildner 		usbd_transfer_start(chan->xfer[0]);
1388a963377aSSascha Wildner 		usbd_transfer_start(chan->xfer[1]);
1389a963377aSSascha Wildner 		lockmgr(chan->pcm_lock, LK_RELEASE);
1390a963377aSSascha Wildner 		break;
1391a963377aSSascha Wildner 	case CHAN_OP_STOP:
1392a963377aSSascha Wildner 		lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
1393a963377aSSascha Wildner 		usbd_transfer_stop(chan->xfer[0]);
1394a963377aSSascha Wildner 		usbd_transfer_stop(chan->xfer[1]);
1395a963377aSSascha Wildner 		lockmgr(chan->pcm_lock, LK_RELEASE);
1396a963377aSSascha Wildner 		break;
1397a963377aSSascha Wildner 	default:
1398a963377aSSascha Wildner 		break;
1399a963377aSSascha Wildner 	}
1400a963377aSSascha Wildner 	return;
1401a963377aSSascha Wildner 
1402a963377aSSascha Wildner error:
1403a963377aSSascha Wildner 	usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
1404a963377aSSascha Wildner 
1405a963377aSSascha Wildner 	lockmgr(chan->pcm_lock, LK_EXCLUSIVE);
1406a963377aSSascha Wildner 	chan->cur_alt = CHAN_MAX_ALT;
1407a963377aSSascha Wildner 	lockmgr(chan->pcm_lock, LK_RELEASE);
1408a963377aSSascha Wildner }
1409a963377aSSascha Wildner 
1410a963377aSSascha Wildner static void
uaudio_configure_msg(struct usb_proc_msg * pm)1411a963377aSSascha Wildner uaudio_configure_msg(struct usb_proc_msg *pm)
1412a963377aSSascha Wildner {
1413a963377aSSascha Wildner 	struct uaudio_softc *sc = ((struct uaudio_configure_msg *)pm)->sc;
1414a963377aSSascha Wildner 
1415a963377aSSascha Wildner 	usb_proc_explore_unlock(sc->sc_udev);
1416a963377aSSascha Wildner 	uaudio_configure_msg_sub(sc, &sc->sc_play_chan, PCMDIR_PLAY);
1417a963377aSSascha Wildner 	uaudio_configure_msg_sub(sc, &sc->sc_rec_chan, PCMDIR_REC);
1418a963377aSSascha Wildner 	usb_proc_explore_lock(sc->sc_udev);
1419a963377aSSascha Wildner }
1420a963377aSSascha Wildner 
142109b9c6f2SSascha Wildner /*========================================================================*
142209b9c6f2SSascha Wildner  * AS - Audio Stream - routines
142309b9c6f2SSascha Wildner  *========================================================================*/
142409b9c6f2SSascha Wildner 
142509b9c6f2SSascha Wildner #ifdef USB_DEBUG
142609b9c6f2SSascha Wildner static void
uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t * ed)142709b9c6f2SSascha Wildner uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed)
142809b9c6f2SSascha Wildner {
142909b9c6f2SSascha Wildner 	if (ed) {
143009b9c6f2SSascha Wildner 		DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n"
143109b9c6f2SSascha Wildner 		    "bEndpointAddress=%d bmAttributes=0x%x \n"
143209b9c6f2SSascha Wildner 		    "wMaxPacketSize=%d bInterval=%d \n"
143309b9c6f2SSascha Wildner 		    "bRefresh=%d bSynchAddress=%d\n",
143409b9c6f2SSascha Wildner 		    ed, ed->bLength, ed->bDescriptorType,
143509b9c6f2SSascha Wildner 		    ed->bEndpointAddress, ed->bmAttributes,
143609b9c6f2SSascha Wildner 		    UGETW(ed->wMaxPacketSize), ed->bInterval,
143709b9c6f2SSascha Wildner 		    UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0,
143809b9c6f2SSascha Wildner 		    UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0);
143909b9c6f2SSascha Wildner 	}
144009b9c6f2SSascha Wildner }
144109b9c6f2SSascha Wildner 
144209b9c6f2SSascha Wildner #endif
144309b9c6f2SSascha Wildner 
144409b9c6f2SSascha Wildner /*
144509b9c6f2SSascha Wildner  * The following is a workaround for broken no-name USB audio devices
144609b9c6f2SSascha Wildner  * sold by dealextreme called "3D sound". The problem is that the
144709b9c6f2SSascha Wildner  * manufacturer computed wMaxPacketSize is too small to hold the
144809b9c6f2SSascha Wildner  * actual data sent. In other words the device sometimes sends more
144909b9c6f2SSascha Wildner  * data than it actually reports it can send in a single isochronous
145009b9c6f2SSascha Wildner  * packet.
145109b9c6f2SSascha Wildner  */
145209b9c6f2SSascha Wildner static void
uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t * ep,uint32_t xps,uint32_t add)145309b9c6f2SSascha Wildner uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep,
145409b9c6f2SSascha Wildner     uint32_t xps, uint32_t add)
145509b9c6f2SSascha Wildner {
145609b9c6f2SSascha Wildner 	uint32_t mps;
145709b9c6f2SSascha Wildner 
145809b9c6f2SSascha Wildner 	mps = UGETW(ep->wMaxPacketSize);
145909b9c6f2SSascha Wildner 
146009b9c6f2SSascha Wildner 	/*
146109b9c6f2SSascha Wildner 	 * If the device indicates it can send more data than what the
146209b9c6f2SSascha Wildner 	 * sample rate indicates, we apply the workaround.
146309b9c6f2SSascha Wildner 	 */
146409b9c6f2SSascha Wildner 	if (mps > xps) {
146509b9c6f2SSascha Wildner 
146609b9c6f2SSascha Wildner 		/* allow additional data */
146709b9c6f2SSascha Wildner 		xps += add;
146809b9c6f2SSascha Wildner 
146909b9c6f2SSascha Wildner 		/* check against the maximum USB 1.x length */
147009b9c6f2SSascha Wildner 		if (xps > 1023)
147109b9c6f2SSascha Wildner 			xps = 1023;
147209b9c6f2SSascha Wildner 
147309b9c6f2SSascha Wildner 		/* check if we should do an update */
147409b9c6f2SSascha Wildner 		if (mps < xps) {
147509b9c6f2SSascha Wildner 			/* simply update the wMaxPacketSize field */
147609b9c6f2SSascha Wildner 			USETW(ep->wMaxPacketSize, xps);
147709b9c6f2SSascha Wildner 			DPRINTF("Workaround: Updated wMaxPacketSize "
147809b9c6f2SSascha Wildner 			    "from %d to %d bytes.\n",
147909b9c6f2SSascha Wildner 			    (int)mps, (int)xps);
148009b9c6f2SSascha Wildner 		}
148109b9c6f2SSascha Wildner 	}
148209b9c6f2SSascha Wildner }
148309b9c6f2SSascha Wildner 
1484a963377aSSascha Wildner static usb_error_t
uaudio20_check_rate(struct usb_device * udev,uint8_t iface_no,uint8_t clockid,uint32_t rate)1485a963377aSSascha Wildner uaudio20_check_rate(struct usb_device *udev, uint8_t iface_no,
1486a963377aSSascha Wildner     uint8_t clockid, uint32_t rate)
1487a963377aSSascha Wildner {
1488a963377aSSascha Wildner 	struct usb_device_request req;
1489a963377aSSascha Wildner 	usb_error_t error;
1490a963377aSSascha Wildner 	uint8_t data[255];
1491a963377aSSascha Wildner 	uint16_t actlen;
1492a963377aSSascha Wildner 	uint16_t rates;
1493a963377aSSascha Wildner 	uint16_t x;
1494a963377aSSascha Wildner 
1495a963377aSSascha Wildner 	DPRINTFN(6, "ifaceno=%d clockid=%d rate=%u\n",
1496a963377aSSascha Wildner 	    iface_no, clockid, rate);
1497a963377aSSascha Wildner 
1498a963377aSSascha Wildner 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
1499a963377aSSascha Wildner 	req.bRequest = UA20_CS_RANGE;
1500a963377aSSascha Wildner 	USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
1501a963377aSSascha Wildner 	USETW2(req.wIndex, clockid, iface_no);
1502a963377aSSascha Wildner 	USETW(req.wLength, 255);
1503a963377aSSascha Wildner 
1504a963377aSSascha Wildner         error = usbd_do_request_flags(udev, NULL, &req, data,
1505a963377aSSascha Wildner 	    USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT);
1506a963377aSSascha Wildner 
1507a963377aSSascha Wildner 	if (error != 0 || actlen < 2)
1508a963377aSSascha Wildner 		return (USB_ERR_INVAL);
1509a963377aSSascha Wildner 
1510a963377aSSascha Wildner 	rates = data[0] | (data[1] << 8);
1511a963377aSSascha Wildner 	actlen = (actlen - 2) / 12;
1512a963377aSSascha Wildner 
1513a963377aSSascha Wildner 	if (rates > actlen) {
1514a963377aSSascha Wildner 		DPRINTF("Too many rates\n");
1515a963377aSSascha Wildner 		rates = actlen;
1516a963377aSSascha Wildner 	}
1517a963377aSSascha Wildner 
1518a963377aSSascha Wildner 	for (x = 0; x != rates; x++) {
1519a963377aSSascha Wildner 		uint32_t min = UGETDW(data + 2 + (12 * x));
1520a963377aSSascha Wildner 		uint32_t max = UGETDW(data + 6 + (12 * x));
1521a963377aSSascha Wildner 		uint32_t res = UGETDW(data + 10 + (12 * x));
1522a963377aSSascha Wildner 
1523a963377aSSascha Wildner 		if (res == 0) {
1524a963377aSSascha Wildner 			DPRINTF("Zero residue\n");
1525a963377aSSascha Wildner 			res = 1;
1526a963377aSSascha Wildner 		}
1527a963377aSSascha Wildner 
1528a963377aSSascha Wildner 		if (min > max) {
1529a963377aSSascha Wildner 			DPRINTF("Swapped max and min\n");
1530a963377aSSascha Wildner 			uint32_t temp;
1531a963377aSSascha Wildner 			temp = min;
1532a963377aSSascha Wildner 			min = max;
1533a963377aSSascha Wildner 			max = temp;
1534a963377aSSascha Wildner 		}
1535a963377aSSascha Wildner 
1536a963377aSSascha Wildner 		if (rate >= min && rate <= max &&
1537a963377aSSascha Wildner 		    (((rate - min) % res) == 0)) {
1538a963377aSSascha Wildner 			return (0);
1539a963377aSSascha Wildner 		}
1540a963377aSSascha Wildner 	}
1541a963377aSSascha Wildner 	return (USB_ERR_INVAL);
1542a963377aSSascha Wildner }
1543a963377aSSascha Wildner 
154409b9c6f2SSascha Wildner static void
uaudio_chan_fill_info_sub(struct uaudio_softc * sc,struct usb_device * udev,uint32_t rate,uint8_t channels,uint8_t bit_resolution)154509b9c6f2SSascha Wildner uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
154609b9c6f2SSascha Wildner     uint32_t rate, uint8_t channels, uint8_t bit_resolution)
154709b9c6f2SSascha Wildner {
154809b9c6f2SSascha Wildner 	struct usb_descriptor *desc = NULL;
1549a963377aSSascha Wildner 	union uaudio_asid asid = { NULL };
1550a963377aSSascha Wildner 	union uaudio_asf1d asf1d = { NULL };
1551a963377aSSascha Wildner 	union uaudio_sed sed = { NULL };
1552a963377aSSascha Wildner 	struct usb_midi_streaming_endpoint_descriptor *msid = NULL;
155309b9c6f2SSascha Wildner 	usb_endpoint_descriptor_audio_t *ed1 = NULL;
1554a963377aSSascha Wildner 	const struct usb_audio_control_descriptor *acdp = NULL;
155509b9c6f2SSascha Wildner 	struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
155609b9c6f2SSascha Wildner 	struct usb_interface_descriptor *id;
1557a963377aSSascha Wildner 	const struct uaudio_format *p_fmt = NULL;
155809b9c6f2SSascha Wildner 	struct uaudio_chan *chan;
1559a963377aSSascha Wildner 	struct uaudio_chan_alt *chan_alt;
1560a963377aSSascha Wildner 	uint32_t format;
156109b9c6f2SSascha Wildner 	uint16_t curidx = 0xFFFF;
156209b9c6f2SSascha Wildner 	uint16_t lastidx = 0xFFFF;
156309b9c6f2SSascha Wildner 	uint16_t alt_index = 0;
1564a963377aSSascha Wildner 	uint16_t audio_rev = 0;
1565a963377aSSascha Wildner 	uint16_t x;
156609b9c6f2SSascha Wildner 	uint8_t ep_dir;
156709b9c6f2SSascha Wildner 	uint8_t bChannels;
156809b9c6f2SSascha Wildner 	uint8_t bBitResolution;
156909b9c6f2SSascha Wildner 	uint8_t audio_if = 0;
1570a963377aSSascha Wildner 	uint8_t midi_if = 0;
157109b9c6f2SSascha Wildner 	uint8_t uma_if_class;
157209b9c6f2SSascha Wildner 
157309b9c6f2SSascha Wildner 	while ((desc = usb_desc_foreach(cd, desc))) {
157409b9c6f2SSascha Wildner 
157509b9c6f2SSascha Wildner 		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
157609b9c6f2SSascha Wildner 		    (desc->bLength >= sizeof(*id))) {
157709b9c6f2SSascha Wildner 
157809b9c6f2SSascha Wildner 			id = (void *)desc;
157909b9c6f2SSascha Wildner 
158009b9c6f2SSascha Wildner 			if (id->bInterfaceNumber != lastidx) {
158109b9c6f2SSascha Wildner 				lastidx = id->bInterfaceNumber;
158209b9c6f2SSascha Wildner 				curidx++;
158309b9c6f2SSascha Wildner 				alt_index = 0;
158409b9c6f2SSascha Wildner 
158509b9c6f2SSascha Wildner 			} else {
158609b9c6f2SSascha Wildner 				alt_index++;
158709b9c6f2SSascha Wildner 			}
158809b9c6f2SSascha Wildner 
1589a963377aSSascha Wildner 			if ((!(sc->sc_hid.flags & UAUDIO_HID_VALID)) &&
1590a963377aSSascha Wildner 			    (id->bInterfaceClass == UICLASS_HID) &&
1591a963377aSSascha Wildner 			    (id->bInterfaceSubClass == 0) &&
1592a963377aSSascha Wildner 			    (id->bInterfaceProtocol == 0) &&
1593a963377aSSascha Wildner 			    (alt_index == 0) &&
1594a963377aSSascha Wildner 			    usbd_get_iface(udev, curidx) != NULL) {
1595a963377aSSascha Wildner 				DPRINTF("Found HID interface at %d\n",
1596a963377aSSascha Wildner 				    curidx);
1597a963377aSSascha Wildner 				sc->sc_hid.flags |= UAUDIO_HID_VALID;
1598a963377aSSascha Wildner 				sc->sc_hid.iface_index = curidx;
1599a963377aSSascha Wildner 			}
1600a963377aSSascha Wildner 
160109b9c6f2SSascha Wildner 			uma_if_class =
160209b9c6f2SSascha Wildner 			    ((id->bInterfaceClass == UICLASS_AUDIO) ||
160309b9c6f2SSascha Wildner 			    ((id->bInterfaceClass == UICLASS_VENDOR) &&
160409b9c6f2SSascha Wildner 			    (sc->sc_uq_au_vendor_class != 0)));
160509b9c6f2SSascha Wildner 
1606a963377aSSascha Wildner 			if ((uma_if_class != 0) &&
1607a963377aSSascha Wildner 			    (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) {
160809b9c6f2SSascha Wildner 				audio_if = 1;
160909b9c6f2SSascha Wildner 			} else {
161009b9c6f2SSascha Wildner 				audio_if = 0;
161109b9c6f2SSascha Wildner 			}
161209b9c6f2SSascha Wildner 
161309b9c6f2SSascha Wildner 			if ((uma_if_class != 0) &&
161409b9c6f2SSascha Wildner 			    (id->bInterfaceSubClass == UISUBCLASS_MIDISTREAM)) {
161509b9c6f2SSascha Wildner 
161609b9c6f2SSascha Wildner 				/*
161709b9c6f2SSascha Wildner 				 * XXX could allow multiple MIDI interfaces
161809b9c6f2SSascha Wildner 				 */
1619a963377aSSascha Wildner 				midi_if = 1;
162009b9c6f2SSascha Wildner 
162109b9c6f2SSascha Wildner 				if ((sc->sc_midi_chan.valid == 0) &&
1622a963377aSSascha Wildner 				    (usbd_get_iface(udev, curidx) != NULL)) {
162309b9c6f2SSascha Wildner 					sc->sc_midi_chan.iface_index = curidx;
162409b9c6f2SSascha Wildner 					sc->sc_midi_chan.iface_alt_index = alt_index;
162509b9c6f2SSascha Wildner 					sc->sc_midi_chan.valid = 1;
162609b9c6f2SSascha Wildner 				}
1627a963377aSSascha Wildner 			} else {
1628a963377aSSascha Wildner 				midi_if = 0;
162909b9c6f2SSascha Wildner 			}
1630a963377aSSascha Wildner 			asid.v1 = NULL;
1631a963377aSSascha Wildner 			asf1d.v1 = NULL;
163209b9c6f2SSascha Wildner 			ed1 = NULL;
1633a963377aSSascha Wildner 			sed.v1 = NULL;
16345e4edd23SMatthew Dillon 
16355e4edd23SMatthew Dillon 			/*
16365e4edd23SMatthew Dillon 			 * There can only be one USB audio instance
16375e4edd23SMatthew Dillon 			 * per USB device. Grab all USB audio
16385e4edd23SMatthew Dillon 			 * interfaces on this USB device so that we
16395e4edd23SMatthew Dillon 			 * don't attach USB audio twice:
16405e4edd23SMatthew Dillon 			 */
16415e4edd23SMatthew Dillon 			if (alt_index == 0 && curidx != sc->sc_mixer_iface_index &&
16425e4edd23SMatthew Dillon 			    (id->bInterfaceClass == UICLASS_AUDIO || audio_if != 0 ||
16435e4edd23SMatthew Dillon 			    midi_if != 0)) {
16445e4edd23SMatthew Dillon 				usbd_set_parent_iface(sc->sc_udev, curidx,
16455e4edd23SMatthew Dillon 				    sc->sc_mixer_iface_index);
16465e4edd23SMatthew Dillon 			}
164709b9c6f2SSascha Wildner 		}
1648a963377aSSascha Wildner 
1649a963377aSSascha Wildner 		if (audio_if == 0) {
1650a963377aSSascha Wildner 			if (midi_if == 0) {
1651a963377aSSascha Wildner 				if ((acdp == NULL) &&
1652a963377aSSascha Wildner 				    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
1653a963377aSSascha Wildner 				    (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) &&
1654a963377aSSascha Wildner 				    (desc->bLength >= sizeof(*acdp))) {
1655a963377aSSascha Wildner 					acdp = (void *)desc;
1656a963377aSSascha Wildner 					audio_rev = UGETW(acdp->bcdADC);
1657a963377aSSascha Wildner 				}
1658a963377aSSascha Wildner 			} else {
1659a963377aSSascha Wildner 				msid = (void *)desc;
1660a963377aSSascha Wildner 
1661a963377aSSascha Wildner 				/* get the maximum number of embedded jacks in use, if any */
1662a963377aSSascha Wildner 				if (msid->bLength >= sizeof(*msid) &&
1663a963377aSSascha Wildner 				    msid->bDescriptorType == UDESC_CS_ENDPOINT &&
1664a963377aSSascha Wildner 				    msid->bDescriptorSubtype == MS_GENERAL &&
1665a963377aSSascha Wildner 				    msid->bNumEmbMIDIJack > sc->sc_midi_chan.max_emb_jack) {
1666a963377aSSascha Wildner 					sc->sc_midi_chan.max_emb_jack = msid->bNumEmbMIDIJack;
1667a963377aSSascha Wildner 				}
1668a963377aSSascha Wildner 			}
1669a963377aSSascha Wildner 			/*
1670a963377aSSascha Wildner 			 * Don't collect any USB audio descriptors if
1671a963377aSSascha Wildner 			 * this is not an USB audio stream interface.
1672a963377aSSascha Wildner 			 */
1673a963377aSSascha Wildner 			continue;
1674a963377aSSascha Wildner 		}
1675a963377aSSascha Wildner 
1676a963377aSSascha Wildner 		if ((acdp != NULL) &&
1677a963377aSSascha Wildner 		    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
167809b9c6f2SSascha Wildner 		    (desc->bDescriptorSubtype == AS_GENERAL) &&
1679a963377aSSascha Wildner 		    (asid.v1 == NULL)) {
1680a963377aSSascha Wildner 			if (audio_rev >= UAUDIO_VERSION_30) {
1681a963377aSSascha Wildner 				/* FALLTHROUGH */
1682a963377aSSascha Wildner 			} else if (audio_rev >= UAUDIO_VERSION_20) {
1683a963377aSSascha Wildner 				if (desc->bLength >= sizeof(*asid.v2)) {
1684a963377aSSascha Wildner 					asid.v2 = (void *)desc;
1685a963377aSSascha Wildner 				}
1686a963377aSSascha Wildner 			} else {
1687a963377aSSascha Wildner 				if (desc->bLength >= sizeof(*asid.v1)) {
1688a963377aSSascha Wildner 					asid.v1 = (void *)desc;
168909b9c6f2SSascha Wildner 				}
169009b9c6f2SSascha Wildner 			}
1691a963377aSSascha Wildner 		}
1692a963377aSSascha Wildner 		if ((acdp != NULL) &&
1693a963377aSSascha Wildner 		    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
169409b9c6f2SSascha Wildner 		    (desc->bDescriptorSubtype == FORMAT_TYPE) &&
1695a963377aSSascha Wildner 		    (asf1d.v1 == NULL)) {
1696a963377aSSascha Wildner 			if (audio_rev >= UAUDIO_VERSION_30) {
1697a963377aSSascha Wildner 				/* FALLTHROUGH */
1698a963377aSSascha Wildner 			} else if (audio_rev >= UAUDIO_VERSION_20) {
1699a963377aSSascha Wildner 				if (desc->bLength >= sizeof(*asf1d.v2))
1700a963377aSSascha Wildner 					asf1d.v2 = (void *)desc;
1701a963377aSSascha Wildner 			} else {
1702a963377aSSascha Wildner 				if (desc->bLength >= sizeof(*asf1d.v1)) {
1703a963377aSSascha Wildner 					asf1d.v1 = (void *)desc;
1704a963377aSSascha Wildner 
1705a963377aSSascha Wildner 					if (asf1d.v1->bFormatType != FORMAT_TYPE_I) {
170609b9c6f2SSascha Wildner 						DPRINTFN(11, "ignored bFormatType = %d\n",
1707a963377aSSascha Wildner 						    asf1d.v1->bFormatType);
1708a963377aSSascha Wildner 						asf1d.v1 = NULL;
170909b9c6f2SSascha Wildner 						continue;
171009b9c6f2SSascha Wildner 					}
1711a963377aSSascha Wildner 					if (desc->bLength < (sizeof(*asf1d.v1) +
1712a963377aSSascha Wildner 					    ((asf1d.v1->bSamFreqType == 0) ? 6 :
1713a963377aSSascha Wildner 					    (asf1d.v1->bSamFreqType * 3)))) {
1714a963377aSSascha Wildner 						DPRINTFN(11, "invalid descriptor, "
1715a963377aSSascha Wildner 						    "too short\n");
1716a963377aSSascha Wildner 						asf1d.v1 = NULL;
171709b9c6f2SSascha Wildner 						continue;
171809b9c6f2SSascha Wildner 					}
171909b9c6f2SSascha Wildner 				}
172009b9c6f2SSascha Wildner 			}
1721a963377aSSascha Wildner 		}
172209b9c6f2SSascha Wildner 		if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
1723a963377aSSascha Wildner 		    (desc->bLength >= UEP_MINSIZE) &&
1724a963377aSSascha Wildner 		    (ed1 == NULL)) {
172509b9c6f2SSascha Wildner 			ed1 = (void *)desc;
172609b9c6f2SSascha Wildner 			if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) {
172709b9c6f2SSascha Wildner 				ed1 = NULL;
1728a963377aSSascha Wildner 				continue;
172909b9c6f2SSascha Wildner 			}
173009b9c6f2SSascha Wildner 		}
1731a963377aSSascha Wildner 		if ((acdp != NULL) &&
1732a963377aSSascha Wildner 		    (desc->bDescriptorType == UDESC_CS_ENDPOINT) &&
173309b9c6f2SSascha Wildner 		    (desc->bDescriptorSubtype == AS_GENERAL) &&
1734a963377aSSascha Wildner 		    (sed.v1 == NULL)) {
1735a963377aSSascha Wildner 			if (audio_rev >= UAUDIO_VERSION_30) {
1736a963377aSSascha Wildner 				/* FALLTHROUGH */
1737a963377aSSascha Wildner 			} else if (audio_rev >= UAUDIO_VERSION_20) {
1738a963377aSSascha Wildner 				if (desc->bLength >= sizeof(*sed.v2))
1739a963377aSSascha Wildner 					sed.v2 = (void *)desc;
1740a963377aSSascha Wildner 			} else {
1741a963377aSSascha Wildner 				if (desc->bLength >= sizeof(*sed.v1))
1742a963377aSSascha Wildner 					sed.v1 = (void *)desc;
174309b9c6f2SSascha Wildner 			}
174409b9c6f2SSascha Wildner 		}
1745a963377aSSascha Wildner 		if (asid.v1 == NULL || asf1d.v1 == NULL ||
1746a963377aSSascha Wildner 		    ed1 == NULL || sed.v1 == NULL) {
1747a963377aSSascha Wildner 			/* need more descriptors */
1748a963377aSSascha Wildner 			continue;
1749a963377aSSascha Wildner 		}
175009b9c6f2SSascha Wildner 
175109b9c6f2SSascha Wildner 		ep_dir = UE_GET_DIR(ed1->bEndpointAddress);
175209b9c6f2SSascha Wildner 
175309b9c6f2SSascha Wildner 		/* We ignore sync endpoint information until further. */
175409b9c6f2SSascha Wildner 
1755a963377aSSascha Wildner 		if (audio_rev >= UAUDIO_VERSION_30) {
1756a963377aSSascha Wildner 			goto next_ep;
1757a963377aSSascha Wildner 		} else if (audio_rev >= UAUDIO_VERSION_20) {
175809b9c6f2SSascha Wildner 
1759a963377aSSascha Wildner 			uint32_t dwFormat;
176009b9c6f2SSascha Wildner 
1761a963377aSSascha Wildner 			dwFormat = UGETDW(asid.v2->bmFormats);
1762a963377aSSascha Wildner 			bChannels = asid.v2->bNrChannels;
1763a963377aSSascha Wildner 			bBitResolution = asf1d.v2->bSubslotSize * 8;
1764a963377aSSascha Wildner 
1765a963377aSSascha Wildner 			if ((bChannels != channels) ||
1766a963377aSSascha Wildner 			    (bBitResolution != bit_resolution)) {
1767a963377aSSascha Wildner 				DPRINTF("Wrong number of channels\n");
1768a963377aSSascha Wildner 				goto next_ep;
1769a963377aSSascha Wildner 			}
1770a963377aSSascha Wildner 
1771a963377aSSascha Wildner 			for (p_fmt = uaudio20_formats;
1772a963377aSSascha Wildner 			    p_fmt->wFormat != 0; p_fmt++) {
1773a963377aSSascha Wildner 				if ((p_fmt->wFormat & dwFormat) &&
1774a963377aSSascha Wildner 				    (p_fmt->bPrecision == bBitResolution))
1775a963377aSSascha Wildner 					break;
1776a963377aSSascha Wildner 			}
1777a963377aSSascha Wildner 
1778a963377aSSascha Wildner 			if (p_fmt->wFormat == 0) {
1779a963377aSSascha Wildner 				DPRINTF("Unsupported audio format\n");
1780a963377aSSascha Wildner 				goto next_ep;
1781a963377aSSascha Wildner 			}
1782a963377aSSascha Wildner 
1783a963377aSSascha Wildner 			for (x = 0; x != 256; x++) {
1784a963377aSSascha Wildner 				if (ep_dir == UE_DIR_OUT) {
1785691f0a75SSascha Wildner 					if (isclr(sc->sc_mixer_clocks.bit_output, x)) {
1786a963377aSSascha Wildner 						continue;
178709b9c6f2SSascha Wildner 					}
178809b9c6f2SSascha Wildner 				} else {
1789691f0a75SSascha Wildner 					if (isclr(sc->sc_mixer_clocks.bit_input, x)) {
1790a963377aSSascha Wildner 						continue;
1791a963377aSSascha Wildner 					}
1792a963377aSSascha Wildner 				}
179309b9c6f2SSascha Wildner 
1794a963377aSSascha Wildner 				DPRINTF("Checking clock ID=%d\n", x);
1795a963377aSSascha Wildner 
1796a963377aSSascha Wildner 				if (uaudio20_check_rate(udev,
1797a963377aSSascha Wildner 				    sc->sc_mixer_iface_no, x, rate)) {
1798a963377aSSascha Wildner 					DPRINTF("Unsupported sampling "
1799a963377aSSascha Wildner 					    "rate, id=%d\n", x);
1800a963377aSSascha Wildner 					goto next_ep;
1801a963377aSSascha Wildner 				}
1802a963377aSSascha Wildner 			}
1803a963377aSSascha Wildner 		} else {
1804a963377aSSascha Wildner 			uint16_t wFormat;
1805a963377aSSascha Wildner 
1806a963377aSSascha Wildner 			wFormat = UGETW(asid.v1->wFormatTag);
1807a963377aSSascha Wildner 			bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels);
1808a963377aSSascha Wildner 			bBitResolution = asf1d.v1->bSubFrameSize * 8;
1809a963377aSSascha Wildner 
1810a963377aSSascha Wildner 			if (asf1d.v1->bSamFreqType == 0) {
1811a963377aSSascha Wildner 				DPRINTFN(16, "Sample rate: %d-%dHz\n",
1812a963377aSSascha Wildner 				    UA_SAMP_LO(asf1d.v1),
1813a963377aSSascha Wildner 				    UA_SAMP_HI(asf1d.v1));
1814a963377aSSascha Wildner 
1815a963377aSSascha Wildner 				if ((rate >= UA_SAMP_LO(asf1d.v1)) &&
1816a963377aSSascha Wildner 				    (rate <= UA_SAMP_HI(asf1d.v1)))
1817a963377aSSascha Wildner 					goto found_rate;
1818a963377aSSascha Wildner 			} else {
1819a963377aSSascha Wildner 
1820a963377aSSascha Wildner 				for (x = 0; x < asf1d.v1->bSamFreqType; x++) {
182109b9c6f2SSascha Wildner 					DPRINTFN(16, "Sample rate = %dHz\n",
1822a963377aSSascha Wildner 					    UA_GETSAMP(asf1d.v1, x));
182309b9c6f2SSascha Wildner 
1824a963377aSSascha Wildner 					if (rate == UA_GETSAMP(asf1d.v1, x))
182509b9c6f2SSascha Wildner 						goto found_rate;
182609b9c6f2SSascha Wildner 				}
182709b9c6f2SSascha Wildner 			}
1828a963377aSSascha Wildner 			goto next_ep;
182909b9c6f2SSascha Wildner 
183009b9c6f2SSascha Wildner 	found_rate:
1831a963377aSSascha Wildner 			for (p_fmt = uaudio10_formats;
1832a963377aSSascha Wildner 			    p_fmt->wFormat != 0; p_fmt++) {
183309b9c6f2SSascha Wildner 				if ((p_fmt->wFormat == wFormat) &&
1834a963377aSSascha Wildner 				    (p_fmt->bPrecision == bBitResolution))
1835a963377aSSascha Wildner 					break;
183609b9c6f2SSascha Wildner 			}
1837a963377aSSascha Wildner 			if (p_fmt->wFormat == 0) {
1838a963377aSSascha Wildner 				DPRINTF("Unsupported audio format\n");
1839a963377aSSascha Wildner 				goto next_ep;
184009b9c6f2SSascha Wildner 			}
184109b9c6f2SSascha Wildner 
1842a963377aSSascha Wildner 			if ((bChannels != channels) ||
1843a963377aSSascha Wildner 			    (bBitResolution != bit_resolution)) {
1844a963377aSSascha Wildner 				DPRINTF("Wrong number of channels\n");
1845a963377aSSascha Wildner 				goto next_ep;
1846a963377aSSascha Wildner 			}
1847a963377aSSascha Wildner 		}
184809b9c6f2SSascha Wildner 
184909b9c6f2SSascha Wildner 		chan = (ep_dir == UE_DIR_IN) ?
1850a963377aSSascha Wildner 		    &sc->sc_rec_chan : &sc->sc_play_chan;
185109b9c6f2SSascha Wildner 
1852a963377aSSascha Wildner 		if (usbd_get_iface(udev, curidx) == NULL) {
1853a963377aSSascha Wildner 			DPRINTF("Interface is not valid\n");
1854a963377aSSascha Wildner 			goto next_ep;
1855a963377aSSascha Wildner 		}
1856a963377aSSascha Wildner 		if (chan->num_alt == CHAN_MAX_ALT) {
1857a963377aSSascha Wildner 			DPRINTF("Too many alternate settings\n");
1858a963377aSSascha Wildner 			goto next_ep;
1859a963377aSSascha Wildner 		}
1860a963377aSSascha Wildner 		chan->set_alt = 0;
1861a963377aSSascha Wildner 		chan->cur_alt = CHAN_MAX_ALT;
186209b9c6f2SSascha Wildner 
1863a963377aSSascha Wildner 		chan_alt = &chan->usb_alt[chan->num_alt++];
1864a963377aSSascha Wildner 
186509b9c6f2SSascha Wildner #ifdef USB_DEBUG
186609b9c6f2SSascha Wildner 		uaudio_chan_dump_ep_desc(ed1);
186709b9c6f2SSascha Wildner #endif
186809b9c6f2SSascha Wildner 		DPRINTF("Sample rate = %dHz, channels = %d, "
186909b9c6f2SSascha Wildner 		    "bits = %d, format = %s\n", rate, channels,
187009b9c6f2SSascha Wildner 		    bit_resolution, p_fmt->description);
187109b9c6f2SSascha Wildner 
1872a963377aSSascha Wildner 		chan_alt->sample_rate = rate;
1873a963377aSSascha Wildner 		chan_alt->p_asf1d = asf1d;
1874a963377aSSascha Wildner 		chan_alt->p_ed1 = ed1;
1875a963377aSSascha Wildner 		chan_alt->p_fmt = p_fmt;
1876a963377aSSascha Wildner 		chan_alt->p_sed = sed;
1877a963377aSSascha Wildner 		chan_alt->iface_index = curidx;
1878a963377aSSascha Wildner 		chan_alt->iface_alt_index = alt_index;
1879a963377aSSascha Wildner 
188009b9c6f2SSascha Wildner 		if (ep_dir == UE_DIR_IN)
1881a963377aSSascha Wildner 			chan_alt->usb_cfg = uaudio_cfg_record;
188209b9c6f2SSascha Wildner 		else
1883a963377aSSascha Wildner 			chan_alt->usb_cfg = uaudio_cfg_play;
188409b9c6f2SSascha Wildner 
1885a963377aSSascha Wildner 		chan_alt->sample_size = (UAUDIO_MAX_CHAN(channels) *
1886a963377aSSascha Wildner 		    p_fmt->bPrecision) / 8;
1887a963377aSSascha Wildner 		chan_alt->channels = channels;
188809b9c6f2SSascha Wildner 
188909b9c6f2SSascha Wildner 		if (ep_dir == UE_DIR_IN &&
189009b9c6f2SSascha Wildner 		    usbd_get_speed(udev) == USB_SPEED_FULL) {
189109b9c6f2SSascha Wildner 			uaudio_record_fix_fs(ed1,
1892a963377aSSascha Wildner 			    chan_alt->sample_size * (rate / 1000),
1893a963377aSSascha Wildner 			    chan_alt->sample_size * (rate / 4000));
189409b9c6f2SSascha Wildner 		}
189509b9c6f2SSascha Wildner 
1896a963377aSSascha Wildner 		/* setup play/record format */
1897a963377aSSascha Wildner 
1898a963377aSSascha Wildner 		format = chan_alt->p_fmt->freebsd_fmt;
1899a963377aSSascha Wildner 
19005e4edd23SMatthew Dillon 		/* get default SND_FORMAT() */
19015e4edd23SMatthew Dillon 		format = SND_FORMAT(format, chan_alt->channels, 0);
19025e4edd23SMatthew Dillon 
1903a963377aSSascha Wildner 		switch (chan_alt->channels) {
19045e4edd23SMatthew Dillon 		uint32_t temp_fmt;
1905a963377aSSascha Wildner 		case 1:
19065e4edd23SMatthew Dillon 		case 2:
19075e4edd23SMatthew Dillon 			/* mono and stereo */
1908a963377aSSascha Wildner 			break;
1909a963377aSSascha Wildner 		default:
1910a963377aSSascha Wildner 			/* surround and more */
19115e4edd23SMatthew Dillon 			temp_fmt = feeder_matrix_default_format(format);
19125e4edd23SMatthew Dillon 			/* if multichannel, then format can be zero */
19135e4edd23SMatthew Dillon 			if (temp_fmt != 0)
19145e4edd23SMatthew Dillon 				format = temp_fmt;
1915a963377aSSascha Wildner 			break;
1916a963377aSSascha Wildner 		}
1917a963377aSSascha Wildner 
1918a963377aSSascha Wildner 		/* check if format is not supported */
1919a963377aSSascha Wildner 		if (format == 0) {
1920a963377aSSascha Wildner 			DPRINTF("The selected audio format is not supported\n");
1921a963377aSSascha Wildner 			chan->num_alt--;
1922a963377aSSascha Wildner 			goto next_ep;
1923a963377aSSascha Wildner 		}
1924a963377aSSascha Wildner 		if (chan->num_alt > 1) {
1925a963377aSSascha Wildner 			/* we only accumulate one format at different sample rates */
1926a963377aSSascha Wildner 			if (chan->pcm_format[0] != format) {
1927a963377aSSascha Wildner 				DPRINTF("Multiple formats is not supported\n");
1928a963377aSSascha Wildner 				chan->num_alt--;
1929a963377aSSascha Wildner 				goto next_ep;
1930a963377aSSascha Wildner 			}
1931a963377aSSascha Wildner 			/* ignore if duplicate sample rate entry */
1932a963377aSSascha Wildner 			if (rate == chan->usb_alt[chan->num_alt - 2].sample_rate) {
1933a963377aSSascha Wildner 				DPRINTF("Duplicate sample rate detected\n");
1934a963377aSSascha Wildner 				chan->num_alt--;
1935a963377aSSascha Wildner 				goto next_ep;
1936a963377aSSascha Wildner 			}
1937a963377aSSascha Wildner 		}
1938a963377aSSascha Wildner 		chan->pcm_cap.fmtlist = chan->pcm_format;
1939a963377aSSascha Wildner 		chan->pcm_cap.fmtlist[0] = format;
1940a963377aSSascha Wildner 
19415e4edd23SMatthew Dillon 		/* check if device needs bitperfect */
19425e4edd23SMatthew Dillon 		if (chan_alt->channels > UAUDIO_MATRIX_MAX)
19435e4edd23SMatthew Dillon 			sc->sc_pcm_bitperfect = 1;
19445e4edd23SMatthew Dillon 
1945a963377aSSascha Wildner 		if (rate < chan->pcm_cap.minspeed || chan->pcm_cap.minspeed == 0)
1946a963377aSSascha Wildner 			chan->pcm_cap.minspeed = rate;
1947a963377aSSascha Wildner 		if (rate > chan->pcm_cap.maxspeed || chan->pcm_cap.maxspeed == 0)
1948a963377aSSascha Wildner 			chan->pcm_cap.maxspeed = rate;
1949a963377aSSascha Wildner 
1950a963377aSSascha Wildner 		if (sc->sc_sndstat_valid != 0) {
195109b9c6f2SSascha Wildner 			sbuf_printf(&sc->sc_sndstat, "\n\t"
1952a963377aSSascha Wildner 			    "mode %d.%d:(%s) %dch, %dbit, %s, %dHz",
195309b9c6f2SSascha Wildner 			    curidx, alt_index,
195409b9c6f2SSascha Wildner 			    (ep_dir == UE_DIR_IN) ? "input" : "output",
1955a963377aSSascha Wildner 				    channels, p_fmt->bPrecision,
195609b9c6f2SSascha Wildner 				    p_fmt->description, rate);
195709b9c6f2SSascha Wildner 		}
1958a963377aSSascha Wildner 
1959a963377aSSascha Wildner 	next_ep:
1960a963377aSSascha Wildner 		sed.v1 = NULL;
1961a963377aSSascha Wildner 		ed1 = NULL;
196209b9c6f2SSascha Wildner 	}
196309b9c6f2SSascha Wildner }
196409b9c6f2SSascha Wildner 
196509b9c6f2SSascha Wildner /* This structure defines all the supported rates. */
196609b9c6f2SSascha Wildner 
1967a963377aSSascha Wildner static const uint32_t uaudio_rate_list[CHAN_MAX_ALT] = {
1968a963377aSSascha Wildner 	384000,
1969a963377aSSascha Wildner 	352800,
1970a963377aSSascha Wildner 	192000,
1971a963377aSSascha Wildner 	176400,
197209b9c6f2SSascha Wildner 	96000,
1973a963377aSSascha Wildner 	88200,
197409b9c6f2SSascha Wildner 	88000,
197509b9c6f2SSascha Wildner 	80000,
197609b9c6f2SSascha Wildner 	72000,
197709b9c6f2SSascha Wildner 	64000,
197809b9c6f2SSascha Wildner 	56000,
197909b9c6f2SSascha Wildner 	48000,
198009b9c6f2SSascha Wildner 	44100,
198109b9c6f2SSascha Wildner 	40000,
198209b9c6f2SSascha Wildner 	32000,
198309b9c6f2SSascha Wildner 	24000,
198409b9c6f2SSascha Wildner 	22050,
198509b9c6f2SSascha Wildner 	16000,
198609b9c6f2SSascha Wildner 	11025,
198709b9c6f2SSascha Wildner 	8000,
198809b9c6f2SSascha Wildner 	0
198909b9c6f2SSascha Wildner };
199009b9c6f2SSascha Wildner 
199109b9c6f2SSascha Wildner static void
uaudio_chan_fill_info(struct uaudio_softc * sc,struct usb_device * udev)199209b9c6f2SSascha Wildner uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
199309b9c6f2SSascha Wildner {
199409b9c6f2SSascha Wildner 	uint32_t rate = uaudio_default_rate;
199509b9c6f2SSascha Wildner 	uint8_t z;
199609b9c6f2SSascha Wildner 	uint8_t bits = uaudio_default_bits;
199709b9c6f2SSascha Wildner 	uint8_t y;
199809b9c6f2SSascha Wildner 	uint8_t channels = uaudio_default_channels;
199909b9c6f2SSascha Wildner 	uint8_t x;
200009b9c6f2SSascha Wildner 
200109b9c6f2SSascha Wildner 	bits -= (bits % 8);
200209b9c6f2SSascha Wildner 	if ((bits == 0) || (bits > 32)) {
200309b9c6f2SSascha Wildner 		/* set a valid value */
200409b9c6f2SSascha Wildner 		bits = 32;
200509b9c6f2SSascha Wildner 	}
200609b9c6f2SSascha Wildner 	if (channels == 0) {
200709b9c6f2SSascha Wildner 		switch (usbd_get_speed(udev)) {
200809b9c6f2SSascha Wildner 		case USB_SPEED_LOW:
200909b9c6f2SSascha Wildner 		case USB_SPEED_FULL:
201009b9c6f2SSascha Wildner 			/*
201109b9c6f2SSascha Wildner 			 * Due to high bandwidth usage and problems
201209b9c6f2SSascha Wildner 			 * with HIGH-speed split transactions we
201309b9c6f2SSascha Wildner 			 * disable surround setups on FULL-speed USB
201409b9c6f2SSascha Wildner 			 * by default
201509b9c6f2SSascha Wildner 			 */
2016a963377aSSascha Wildner 			channels = 4;
201709b9c6f2SSascha Wildner 			break;
201809b9c6f2SSascha Wildner 		default:
20195e4edd23SMatthew Dillon 			channels = UAUDIO_CHANNELS_MAX;
202009b9c6f2SSascha Wildner 			break;
202109b9c6f2SSascha Wildner 		}
20225e4edd23SMatthew Dillon 	} else if (channels > UAUDIO_CHANNELS_MAX)
20235e4edd23SMatthew Dillon 		channels = UAUDIO_CHANNELS_MAX;
20245e4edd23SMatthew Dillon 
20255e4edd23SMatthew Dillon 	if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND))
202609b9c6f2SSascha Wildner 		sc->sc_sndstat_valid = 1;
20275e4edd23SMatthew Dillon 
202809b9c6f2SSascha Wildner 	/* try to search for a valid config */
202909b9c6f2SSascha Wildner 
203009b9c6f2SSascha Wildner 	for (x = channels; x; x--) {
203109b9c6f2SSascha Wildner 		for (y = bits; y; y -= 8) {
203209b9c6f2SSascha Wildner 
203309b9c6f2SSascha Wildner 			/* try user defined rate, if any */
203409b9c6f2SSascha Wildner 			if (rate != 0)
203509b9c6f2SSascha Wildner 				uaudio_chan_fill_info_sub(sc, udev, rate, x, y);
203609b9c6f2SSascha Wildner 
203709b9c6f2SSascha Wildner 			/* try find a matching rate, if any */
2038a963377aSSascha Wildner 			for (z = 0; uaudio_rate_list[z]; z++)
203909b9c6f2SSascha Wildner 				uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y);
204009b9c6f2SSascha Wildner 		}
204109b9c6f2SSascha Wildner 	}
2042a963377aSSascha Wildner 	if (sc->sc_sndstat_valid)
204309b9c6f2SSascha Wildner 		sbuf_finish(&sc->sc_sndstat);
204409b9c6f2SSascha Wildner }
2045a963377aSSascha Wildner 
2046a963377aSSascha Wildner static void
uaudio_chan_play_sync_callback(struct usb_xfer * xfer,usb_error_t error)2047a963377aSSascha Wildner uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
2048a963377aSSascha Wildner {
2049a963377aSSascha Wildner 	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
2050a963377aSSascha Wildner 	struct usb_page_cache *pc;
20515e4edd23SMatthew Dillon 	uint64_t sample_rate;
2052a963377aSSascha Wildner 	uint8_t buf[4];
2053a963377aSSascha Wildner 	uint64_t temp;
2054a963377aSSascha Wildner 	int len;
2055a963377aSSascha Wildner 	int actlen;
2056a963377aSSascha Wildner 	int nframes;
2057a963377aSSascha Wildner 
2058a963377aSSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
2059a963377aSSascha Wildner 
2060a963377aSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
2061a963377aSSascha Wildner 	case USB_ST_TRANSFERRED:
2062a963377aSSascha Wildner 
2063a963377aSSascha Wildner 		DPRINTFN(6, "transferred %d bytes\n", actlen);
2064a963377aSSascha Wildner 
2065a963377aSSascha Wildner 		if (nframes == 0)
2066a963377aSSascha Wildner 			break;
2067a963377aSSascha Wildner 		len = usbd_xfer_frame_len(xfer, 0);
2068a963377aSSascha Wildner 		if (len == 0)
2069a963377aSSascha Wildner 			break;
2070a963377aSSascha Wildner 		if (len > sizeof(buf))
2071a963377aSSascha Wildner 			len = sizeof(buf);
2072a963377aSSascha Wildner 
2073a963377aSSascha Wildner 		memset(buf, 0, sizeof(buf));
2074a963377aSSascha Wildner 
2075a963377aSSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
2076a963377aSSascha Wildner 		usbd_copy_out(pc, 0, buf, len);
2077a963377aSSascha Wildner 
2078a963377aSSascha Wildner 		temp = UGETDW(buf);
2079a963377aSSascha Wildner 
2080a963377aSSascha Wildner 		DPRINTF("Value = 0x%08x\n", (int)temp);
2081a963377aSSascha Wildner 
2082a963377aSSascha Wildner 		/* auto-detect SYNC format */
2083a963377aSSascha Wildner 
2084a963377aSSascha Wildner 		if (len == 4)
2085a963377aSSascha Wildner 			temp &= 0x0fffffff;
2086a963377aSSascha Wildner 
2087a963377aSSascha Wildner 		/* check for no data */
2088a963377aSSascha Wildner 
2089a963377aSSascha Wildner 		if (temp == 0)
2090a963377aSSascha Wildner 			break;
2091a963377aSSascha Wildner 
2092aaad5092SMarkus Pfeiffer 		temp *= 125ULL;
2093a963377aSSascha Wildner 
20945e4edd23SMatthew Dillon 		sample_rate = ch->usb_alt[ch->cur_alt].sample_rate;
20955e4edd23SMatthew Dillon 
2096a963377aSSascha Wildner 		/* auto adjust */
2097a963377aSSascha Wildner 		while (temp < (sample_rate - (sample_rate / 4)))
2098a963377aSSascha Wildner 			temp *= 2;
2099a963377aSSascha Wildner 
2100a963377aSSascha Wildner 		while (temp > (sample_rate + (sample_rate / 2)))
2101a963377aSSascha Wildner 			temp /= 2;
2102a963377aSSascha Wildner 
21035e4edd23SMatthew Dillon 		DPRINTF("Comparing %d Hz :: %d Hz\n",
21045e4edd23SMatthew Dillon 		    (int)temp, (int)sample_rate);
2105a963377aSSascha Wildner 
2106aaad5092SMarkus Pfeiffer 		/*
21075e4edd23SMatthew Dillon 		 * Use feedback value as fallback when there is no
21085e4edd23SMatthew Dillon 		 * recording channel:
2109aaad5092SMarkus Pfeiffer 		 */
21105e4edd23SMatthew Dillon 		if (ch->priv_sc->sc_rec_chan.num_alt == 0)
21115e4edd23SMatthew Dillon 			ch->jitter_curr = temp - sample_rate;
21125e4edd23SMatthew Dillon 
21135e4edd23SMatthew Dillon 		ch->feedback_rate = temp;
2114a963377aSSascha Wildner 		break;
2115a963377aSSascha Wildner 
2116a963377aSSascha Wildner 	case USB_ST_SETUP:
2117a963377aSSascha Wildner 		usbd_xfer_set_frames(xfer, 1);
2118a963377aSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_framelen(xfer));
2119a963377aSSascha Wildner 		usbd_transfer_submit(xfer);
2120a963377aSSascha Wildner 		break;
2121a963377aSSascha Wildner 
2122a963377aSSascha Wildner 	default:			/* Error */
2123a963377aSSascha Wildner 		break;
2124a963377aSSascha Wildner 	}
212509b9c6f2SSascha Wildner }
212609b9c6f2SSascha Wildner 
21275e4edd23SMatthew Dillon static int
uaudio_chan_is_async(struct uaudio_chan * ch,uint8_t alt)21285e4edd23SMatthew Dillon uaudio_chan_is_async(struct uaudio_chan *ch, uint8_t alt)
21295e4edd23SMatthew Dillon {
21305e4edd23SMatthew Dillon 	uint8_t attr = ch->usb_alt[alt].p_ed1->bmAttributes;
21315e4edd23SMatthew Dillon 	return (UE_GET_ISO_TYPE(attr) == UE_ISO_ASYNC);
21325e4edd23SMatthew Dillon }
21335e4edd23SMatthew Dillon 
213409b9c6f2SSascha Wildner static void
uaudio_chan_play_callback(struct usb_xfer * xfer,usb_error_t error)213509b9c6f2SSascha Wildner uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
213609b9c6f2SSascha Wildner {
213709b9c6f2SSascha Wildner 	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
21385e4edd23SMatthew Dillon 	struct uaudio_chan *ch_rec;
213909b9c6f2SSascha Wildner 	struct usb_page_cache *pc;
2140a963377aSSascha Wildner 	uint32_t mfl;
214109b9c6f2SSascha Wildner 	uint32_t total;
214209b9c6f2SSascha Wildner 	uint32_t blockcount;
214309b9c6f2SSascha Wildner 	uint32_t n;
214409b9c6f2SSascha Wildner 	uint32_t offset;
21455e4edd23SMatthew Dillon 	int sample_size;
214609b9c6f2SSascha Wildner 	int actlen;
214709b9c6f2SSascha Wildner 	int sumlen;
214809b9c6f2SSascha Wildner 
21495e4edd23SMatthew Dillon 	if (ch->running == 0 || ch->start == ch->end) {
21505e4edd23SMatthew Dillon 		DPRINTF("not running or no buffer!\n");
215109b9c6f2SSascha Wildner 		return;
215209b9c6f2SSascha Wildner 	}
215309b9c6f2SSascha Wildner 
21545e4edd23SMatthew Dillon 	/* check if there is a record channel */
21555e4edd23SMatthew Dillon 	if (ch->priv_sc->sc_rec_chan.num_alt > 0)
21565e4edd23SMatthew Dillon 		ch_rec = &ch->priv_sc->sc_rec_chan;
21575e4edd23SMatthew Dillon 	else
21585e4edd23SMatthew Dillon 		ch_rec = NULL;
21595e4edd23SMatthew Dillon 
21605e4edd23SMatthew Dillon 	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
21615e4edd23SMatthew Dillon 
216209b9c6f2SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
21635e4edd23SMatthew Dillon 	case USB_ST_SETUP:
21645e4edd23SMatthew Dillon tr_setup:
21655e4edd23SMatthew Dillon 		if (ch_rec != NULL) {
21665e4edd23SMatthew Dillon 			/* reset receive jitter counters */
21675e4edd23SMatthew Dillon 			lockmgr(ch_rec->pcm_lock, LK_EXCLUSIVE);
21685e4edd23SMatthew Dillon 			ch_rec->jitter_curr = 0;
21695e4edd23SMatthew Dillon 			ch_rec->jitter_rem = 0;
21705e4edd23SMatthew Dillon 			lockmgr(ch_rec->pcm_lock, LK_RELEASE);
21715e4edd23SMatthew Dillon 		}
21725e4edd23SMatthew Dillon 
21735e4edd23SMatthew Dillon 		/* reset transmit jitter counters */
21745e4edd23SMatthew Dillon 		ch->jitter_curr = 0;
21755e4edd23SMatthew Dillon 		ch->jitter_rem = 0;
21765e4edd23SMatthew Dillon 
21775e4edd23SMatthew Dillon 		/* FALLTHROUGH */
217809b9c6f2SSascha Wildner 	case USB_ST_TRANSFERRED:
217909b9c6f2SSascha Wildner 		if (actlen < sumlen) {
218009b9c6f2SSascha Wildner 			DPRINTF("short transfer, "
218109b9c6f2SSascha Wildner 			    "%d of %d bytes\n", actlen, sumlen);
218209b9c6f2SSascha Wildner 		}
218309b9c6f2SSascha Wildner 		chn_intr(ch->pcm_ch);
218409b9c6f2SSascha Wildner 
21855e4edd23SMatthew Dillon 		/*
21865e4edd23SMatthew Dillon 		 * Check for asynchronous playback endpoint and that
21875e4edd23SMatthew Dillon 		 * the playback endpoint is properly configured:
21885e4edd23SMatthew Dillon 		 */
21895e4edd23SMatthew Dillon 		if (ch_rec != NULL &&
21905e4edd23SMatthew Dillon 		    uaudio_chan_is_async(ch, ch->cur_alt) != 0) {
21915e4edd23SMatthew Dillon 			lockmgr(ch_rec->pcm_lock, LK_EXCLUSIVE);
21925e4edd23SMatthew Dillon 			if (ch_rec->cur_alt < ch_rec->num_alt) {
21935e4edd23SMatthew Dillon 				int64_t tx_jitter;
21945e4edd23SMatthew Dillon 				int64_t rx_rate;
21955e4edd23SMatthew Dillon 
21965e4edd23SMatthew Dillon 				/* translate receive jitter into transmit jitter */
21975e4edd23SMatthew Dillon 				tx_jitter = ch->usb_alt[ch->cur_alt].sample_rate;
21985e4edd23SMatthew Dillon 				tx_jitter = (tx_jitter * ch_rec->jitter_curr) +
21995e4edd23SMatthew Dillon 				    ch->jitter_rem;
22005e4edd23SMatthew Dillon 
22015e4edd23SMatthew Dillon 				/* reset receive jitter counters */
22025e4edd23SMatthew Dillon 				ch_rec->jitter_curr = 0;
22035e4edd23SMatthew Dillon 				ch_rec->jitter_rem = 0;
22045e4edd23SMatthew Dillon 
22055e4edd23SMatthew Dillon 				/* compute exact number of transmit jitter samples */
22065e4edd23SMatthew Dillon 				rx_rate = ch_rec->usb_alt[ch_rec->cur_alt].sample_rate;
22075e4edd23SMatthew Dillon 				ch->jitter_curr += tx_jitter / rx_rate;
22085e4edd23SMatthew Dillon 				ch->jitter_rem = tx_jitter % rx_rate;
22095e4edd23SMatthew Dillon 			}
22105e4edd23SMatthew Dillon 			lockmgr(ch_rec->pcm_lock, LK_RELEASE);
22115e4edd23SMatthew Dillon 		}
22125e4edd23SMatthew Dillon 
2213aaad5092SMarkus Pfeiffer 		/* start the SYNC transfer one time per second, if any */
22145e4edd23SMatthew Dillon 		if (++(ch->intr_counter) >= UAUDIO_IRQS) {
22155e4edd23SMatthew Dillon 			ch->intr_counter = 0;
2216a963377aSSascha Wildner 			usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]);
2217a963377aSSascha Wildner 		}
2218a963377aSSascha Wildner 
2219a963377aSSascha Wildner 		mfl = usbd_xfer_max_framelen(xfer);
2220a963377aSSascha Wildner 
2221a963377aSSascha Wildner 		if (ch->bytes_per_frame[1] > mfl) {
222209b9c6f2SSascha Wildner 			DPRINTF("bytes per transfer, %d, "
222309b9c6f2SSascha Wildner 			    "exceeds maximum, %d!\n",
222409b9c6f2SSascha Wildner 			    ch->bytes_per_frame[1],
2225a963377aSSascha Wildner 			    mfl);
222609b9c6f2SSascha Wildner 			break;
222709b9c6f2SSascha Wildner 		}
222809b9c6f2SSascha Wildner 
222909b9c6f2SSascha Wildner 		blockcount = ch->intr_frames;
223009b9c6f2SSascha Wildner 
223109b9c6f2SSascha Wildner 		/* setup number of frames */
223209b9c6f2SSascha Wildner 		usbd_xfer_set_frames(xfer, blockcount);
223309b9c6f2SSascha Wildner 
22345e4edd23SMatthew Dillon 		/* get sample size */
22355e4edd23SMatthew Dillon 		sample_size = ch->usb_alt[ch->cur_alt].sample_size;
22365e4edd23SMatthew Dillon 
223709b9c6f2SSascha Wildner 		/* reset total length */
223809b9c6f2SSascha Wildner 		total = 0;
223909b9c6f2SSascha Wildner 
224009b9c6f2SSascha Wildner 		/* setup frame lengths */
224109b9c6f2SSascha Wildner 		for (n = 0; n != blockcount; n++) {
2242a963377aSSascha Wildner 			uint32_t frame_len;
2243a963377aSSascha Wildner 
224409b9c6f2SSascha Wildner 			ch->sample_curr += ch->sample_rem;
224509b9c6f2SSascha Wildner 			if (ch->sample_curr >= ch->frames_per_second) {
224609b9c6f2SSascha Wildner 				ch->sample_curr -= ch->frames_per_second;
2247a963377aSSascha Wildner 				frame_len = ch->bytes_per_frame[1];
224809b9c6f2SSascha Wildner 			} else {
2249a963377aSSascha Wildner 				frame_len = ch->bytes_per_frame[0];
225009b9c6f2SSascha Wildner 			}
2251a963377aSSascha Wildner 
22525e4edd23SMatthew Dillon 			/* handle free running clock case */
22535e4edd23SMatthew Dillon 			if (ch->jitter_curr > 0 &&
22545e4edd23SMatthew Dillon 			    (frame_len + sample_size) <= mfl) {
2255a963377aSSascha Wildner 				DPRINTFN(6, "sending one sample more\n");
22565e4edd23SMatthew Dillon 				ch->jitter_curr--;
2257a963377aSSascha Wildner 				frame_len += sample_size;
22585e4edd23SMatthew Dillon 			} else if (ch->jitter_curr < 0 &&
22595e4edd23SMatthew Dillon 			    frame_len >= sample_size) {
2260a963377aSSascha Wildner 				DPRINTFN(6, "sending one sample less\n");
22615e4edd23SMatthew Dillon 				ch->jitter_curr++;
2262a963377aSSascha Wildner 				frame_len -= sample_size;
2263a963377aSSascha Wildner 			}
2264a963377aSSascha Wildner 			usbd_xfer_set_frame_len(xfer, n, frame_len);
2265a963377aSSascha Wildner 			total += frame_len;
226609b9c6f2SSascha Wildner 		}
226709b9c6f2SSascha Wildner 
22685e4edd23SMatthew Dillon 		DPRINTFN(6, "transferring %d bytes\n", total);
226909b9c6f2SSascha Wildner 
227009b9c6f2SSascha Wildner 		offset = 0;
227109b9c6f2SSascha Wildner 
227209b9c6f2SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
227309b9c6f2SSascha Wildner 		while (total > 0) {
227409b9c6f2SSascha Wildner 
227509b9c6f2SSascha Wildner 			n = (ch->end - ch->cur);
22765e4edd23SMatthew Dillon 			if (n > total)
227709b9c6f2SSascha Wildner 				n = total;
22785e4edd23SMatthew Dillon 
227909b9c6f2SSascha Wildner 			usbd_copy_in(pc, offset, ch->cur, n);
228009b9c6f2SSascha Wildner 
228109b9c6f2SSascha Wildner 			total -= n;
228209b9c6f2SSascha Wildner 			ch->cur += n;
228309b9c6f2SSascha Wildner 			offset += n;
228409b9c6f2SSascha Wildner 
22855e4edd23SMatthew Dillon 			if (ch->cur >= ch->end)
228609b9c6f2SSascha Wildner 				ch->cur = ch->start;
228709b9c6f2SSascha Wildner 		}
228809b9c6f2SSascha Wildner 		usbd_transfer_submit(xfer);
228909b9c6f2SSascha Wildner 		break;
229009b9c6f2SSascha Wildner 
229109b9c6f2SSascha Wildner 	default:			/* Error */
22925e4edd23SMatthew Dillon 		if (error != USB_ERR_CANCELLED)
22935e4edd23SMatthew Dillon 			goto tr_setup;
229409b9c6f2SSascha Wildner 		break;
229509b9c6f2SSascha Wildner 	}
229609b9c6f2SSascha Wildner }
229709b9c6f2SSascha Wildner 
229809b9c6f2SSascha Wildner static void
uaudio_chan_record_sync_callback(struct usb_xfer * xfer,usb_error_t error)2299a963377aSSascha Wildner uaudio_chan_record_sync_callback(struct usb_xfer *xfer, usb_error_t error)
2300a963377aSSascha Wildner {
2301a963377aSSascha Wildner 	/* TODO */
2302a963377aSSascha Wildner }
2303a963377aSSascha Wildner 
2304a963377aSSascha Wildner static void
uaudio_chan_record_callback(struct usb_xfer * xfer,usb_error_t error)230509b9c6f2SSascha Wildner uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
230609b9c6f2SSascha Wildner {
230709b9c6f2SSascha Wildner 	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
230809b9c6f2SSascha Wildner 	struct usb_page_cache *pc;
230909b9c6f2SSascha Wildner 	uint32_t offset0;
231009b9c6f2SSascha Wildner 	uint32_t mfl;
2311a963377aSSascha Wildner 	int m;
2312a963377aSSascha Wildner 	int n;
231309b9c6f2SSascha Wildner 	int len;
231409b9c6f2SSascha Wildner 	int actlen;
231509b9c6f2SSascha Wildner 	int nframes;
23165e4edd23SMatthew Dillon 	int expected_bytes;
23175e4edd23SMatthew Dillon 	int sample_size;
231809b9c6f2SSascha Wildner 
23195e4edd23SMatthew Dillon 	if (ch->start == ch->end) {
232009b9c6f2SSascha Wildner 		DPRINTF("no buffer!\n");
232109b9c6f2SSascha Wildner 		return;
232209b9c6f2SSascha Wildner 	}
232309b9c6f2SSascha Wildner 
23245e4edd23SMatthew Dillon 	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
23255e4edd23SMatthew Dillon 	mfl = usbd_xfer_max_framelen(xfer);
23265e4edd23SMatthew Dillon 
232709b9c6f2SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
232809b9c6f2SSascha Wildner 	case USB_ST_TRANSFERRED:
232909b9c6f2SSascha Wildner 
233009b9c6f2SSascha Wildner 		offset0 = 0;
233109b9c6f2SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
233209b9c6f2SSascha Wildner 
23335e4edd23SMatthew Dillon 		/* try to compute the number of expected bytes */
23345e4edd23SMatthew Dillon 		ch->sample_curr += (ch->sample_rem * ch->intr_frames);
233509b9c6f2SSascha Wildner 
23365e4edd23SMatthew Dillon 		/* compute number of expected bytes */
23375e4edd23SMatthew Dillon 		expected_bytes = (ch->intr_frames * ch->bytes_per_frame[0]) +
23385e4edd23SMatthew Dillon 		    ((ch->sample_curr / ch->frames_per_second) *
23395e4edd23SMatthew Dillon 		    (ch->bytes_per_frame[1] - ch->bytes_per_frame[0]));
23405e4edd23SMatthew Dillon 
23415e4edd23SMatthew Dillon 		/* keep remainder */
23425e4edd23SMatthew Dillon 		ch->sample_curr %= ch->frames_per_second;
23435e4edd23SMatthew Dillon 
23445e4edd23SMatthew Dillon 		/* get current sample size */
23455e4edd23SMatthew Dillon 		sample_size = ch->usb_alt[ch->cur_alt].sample_size;
23465e4edd23SMatthew Dillon 
23475e4edd23SMatthew Dillon 		for (n = 0; n != nframes; n++) {
23485e4edd23SMatthew Dillon 			uint32_t offset1 = offset0;
23495e4edd23SMatthew Dillon 
235009b9c6f2SSascha Wildner 			len = usbd_xfer_frame_len(xfer, n);
235109b9c6f2SSascha Wildner 
23525e4edd23SMatthew Dillon 			/* make sure we only receive complete samples */
23535e4edd23SMatthew Dillon 			len = len - (len % sample_size);
23545e4edd23SMatthew Dillon 
23555e4edd23SMatthew Dillon 			/* subtract bytes received from expected payload */
23565e4edd23SMatthew Dillon 			expected_bytes -= len;
23575e4edd23SMatthew Dillon 
23585e4edd23SMatthew Dillon 			/* don't receive data when not ready */
23595e4edd23SMatthew Dillon 			if (ch->running == 0 || ch->cur_alt != ch->set_alt)
23605e4edd23SMatthew Dillon 				continue;
23615e4edd23SMatthew Dillon 
23625e4edd23SMatthew Dillon 			/* fill ring buffer with samples, if any */
236309b9c6f2SSascha Wildner 			while (len > 0) {
236409b9c6f2SSascha Wildner 
236509b9c6f2SSascha Wildner 				m = (ch->end - ch->cur);
236609b9c6f2SSascha Wildner 
2367a963377aSSascha Wildner 				if (m > len)
236809b9c6f2SSascha Wildner 					m = len;
2369a963377aSSascha Wildner 
237009b9c6f2SSascha Wildner 				usbd_copy_out(pc, offset1, ch->cur, m);
237109b9c6f2SSascha Wildner 
237209b9c6f2SSascha Wildner 				len -= m;
237309b9c6f2SSascha Wildner 				offset1 += m;
237409b9c6f2SSascha Wildner 				ch->cur += m;
237509b9c6f2SSascha Wildner 
23765e4edd23SMatthew Dillon 				if (ch->cur >= ch->end)
237709b9c6f2SSascha Wildner 					ch->cur = ch->start;
237809b9c6f2SSascha Wildner 			}
237909b9c6f2SSascha Wildner 
238009b9c6f2SSascha Wildner 			offset0 += mfl;
238109b9c6f2SSascha Wildner 		}
238209b9c6f2SSascha Wildner 
23835e4edd23SMatthew Dillon 		/* update current jitter */
23845e4edd23SMatthew Dillon 		ch->jitter_curr -= (expected_bytes / sample_size);
23855e4edd23SMatthew Dillon 
23865e4edd23SMatthew Dillon 		/* don't allow a huge amount of jitter to accumulate */
23875e4edd23SMatthew Dillon 		nframes = 2 * ch->intr_frames;
23885e4edd23SMatthew Dillon 
23895e4edd23SMatthew Dillon 		/* range check current jitter */
23905e4edd23SMatthew Dillon 		if (ch->jitter_curr < -nframes)
23915e4edd23SMatthew Dillon 			ch->jitter_curr = -nframes;
23925e4edd23SMatthew Dillon 		else if (ch->jitter_curr > nframes)
23935e4edd23SMatthew Dillon 			ch->jitter_curr = nframes;
23945e4edd23SMatthew Dillon 
23955e4edd23SMatthew Dillon 		DPRINTFN(6, "transferred %d bytes, jitter %d samples\n",
23965e4edd23SMatthew Dillon 		    actlen, ch->jitter_curr);
23975e4edd23SMatthew Dillon 
23985e4edd23SMatthew Dillon 		if (ch->running != 0)
239909b9c6f2SSascha Wildner 			chn_intr(ch->pcm_ch);
240009b9c6f2SSascha Wildner 
240109b9c6f2SSascha Wildner 	case USB_ST_SETUP:
240209b9c6f2SSascha Wildner tr_setup:
24035e4edd23SMatthew Dillon 		nframes = ch->intr_frames;
240409b9c6f2SSascha Wildner 
24055e4edd23SMatthew Dillon 		usbd_xfer_set_frames(xfer, nframes);
24065e4edd23SMatthew Dillon 		for (n = 0; n != nframes; n++)
240709b9c6f2SSascha Wildner 			usbd_xfer_set_frame_len(xfer, n, mfl);
240809b9c6f2SSascha Wildner 
240909b9c6f2SSascha Wildner 		usbd_transfer_submit(xfer);
241009b9c6f2SSascha Wildner 		break;
241109b9c6f2SSascha Wildner 
241209b9c6f2SSascha Wildner 	default:			/* Error */
24135e4edd23SMatthew Dillon 		if (error != USB_ERR_CANCELLED)
241409b9c6f2SSascha Wildner 			goto tr_setup;
24155e4edd23SMatthew Dillon 		break;
241609b9c6f2SSascha Wildner 	}
241709b9c6f2SSascha Wildner }
241809b9c6f2SSascha Wildner 
241909b9c6f2SSascha Wildner void   *
uaudio_chan_init(struct uaudio_softc * sc,struct snd_dbuf * b,struct pcm_channel * c,int dir)242009b9c6f2SSascha Wildner uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
242109b9c6f2SSascha Wildner     struct pcm_channel *c, int dir)
242209b9c6f2SSascha Wildner {
242309b9c6f2SSascha Wildner 	struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
242409b9c6f2SSascha Wildner 	    &sc->sc_play_chan : &sc->sc_rec_chan);
242509b9c6f2SSascha Wildner 	uint32_t buf_size;
2426a963377aSSascha Wildner 	uint8_t x;
242709b9c6f2SSascha Wildner 
2428a963377aSSascha Wildner 	/* store mutex and PCM channel */
242909b9c6f2SSascha Wildner 
243009b9c6f2SSascha Wildner 	ch->pcm_ch = c;
243109b9c6f2SSascha Wildner 	ch->pcm_lock = c->lock;
243209b9c6f2SSascha Wildner 
2433a963377aSSascha Wildner 	/* compute worst case buffer */
243409b9c6f2SSascha Wildner 
2435a963377aSSascha Wildner 	buf_size = 0;
2436a963377aSSascha Wildner 	for (x = 0; x != ch->num_alt; x++) {
2437a963377aSSascha Wildner 		uint32_t temp = uaudio_get_buffer_size(ch, x);
2438a963377aSSascha Wildner 		if (temp > buf_size)
2439a963377aSSascha Wildner 			buf_size = temp;
244009b9c6f2SSascha Wildner 	}
244109b9c6f2SSascha Wildner 
2442a963377aSSascha Wildner 	/* allow double buffering */
244309b9c6f2SSascha Wildner 	buf_size *= 2;
2444a963377aSSascha Wildner 
2445a963377aSSascha Wildner 	DPRINTF("Worst case buffer is %d bytes\n", (int)buf_size);
244609b9c6f2SSascha Wildner 
244709b9c6f2SSascha Wildner 	ch->buf = kmalloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
2448aaad5092SMarkus Pfeiffer 	if (ch->buf == NULL)
2449aaad5092SMarkus Pfeiffer 		goto error;
245009b9c6f2SSascha Wildner 	if (sndbuf_setup(b, ch->buf, buf_size) != 0)
245109b9c6f2SSascha Wildner 		goto error;
245209b9c6f2SSascha Wildner 
245309b9c6f2SSascha Wildner 	ch->start = ch->buf;
245409b9c6f2SSascha Wildner 	ch->end = ch->buf + buf_size;
245509b9c6f2SSascha Wildner 	ch->cur = ch->buf;
245609b9c6f2SSascha Wildner 	ch->pcm_buf = b;
2457a963377aSSascha Wildner 	ch->max_buf = buf_size;
245809b9c6f2SSascha Wildner 
245909b9c6f2SSascha Wildner 	if (ch->pcm_lock == NULL) {
246009b9c6f2SSascha Wildner 		DPRINTF("ERROR: PCM channels does not have a mutex!\n");
246109b9c6f2SSascha Wildner 		goto error;
246209b9c6f2SSascha Wildner 	}
246309b9c6f2SSascha Wildner 	return (ch);
246409b9c6f2SSascha Wildner 
246509b9c6f2SSascha Wildner error:
246609b9c6f2SSascha Wildner 	uaudio_chan_free(ch);
246709b9c6f2SSascha Wildner 	return (NULL);
246809b9c6f2SSascha Wildner }
246909b9c6f2SSascha Wildner 
247009b9c6f2SSascha Wildner int
uaudio_chan_free(struct uaudio_chan * ch)247109b9c6f2SSascha Wildner uaudio_chan_free(struct uaudio_chan *ch)
247209b9c6f2SSascha Wildner {
247309b9c6f2SSascha Wildner 	if (ch->buf != NULL) {
247409b9c6f2SSascha Wildner 		kfree(ch->buf, M_DEVBUF);
247509b9c6f2SSascha Wildner 		ch->buf = NULL;
247609b9c6f2SSascha Wildner 	}
2477a963377aSSascha Wildner 	usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS + 1);
247809b9c6f2SSascha Wildner 
2479a963377aSSascha Wildner 	ch->num_alt = 0;
248009b9c6f2SSascha Wildner 
248109b9c6f2SSascha Wildner 	return (0);
248209b9c6f2SSascha Wildner }
248309b9c6f2SSascha Wildner 
248409b9c6f2SSascha Wildner int
uaudio_chan_set_param_blocksize(struct uaudio_chan * ch,uint32_t blocksize)248509b9c6f2SSascha Wildner uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
248609b9c6f2SSascha Wildner {
2487a963377aSSascha Wildner 	uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
2488a963377aSSascha Wildner 	sndbuf_setup(ch->pcm_buf, ch->buf, temp);
2489a963377aSSascha Wildner 	return (temp / 2);
249009b9c6f2SSascha Wildner }
249109b9c6f2SSascha Wildner 
249209b9c6f2SSascha Wildner int
uaudio_chan_set_param_fragments(struct uaudio_chan * ch,uint32_t blocksize,uint32_t blockcount)249309b9c6f2SSascha Wildner uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
249409b9c6f2SSascha Wildner     uint32_t blockcount)
249509b9c6f2SSascha Wildner {
249609b9c6f2SSascha Wildner 	return (1);
249709b9c6f2SSascha Wildner }
249809b9c6f2SSascha Wildner 
249909b9c6f2SSascha Wildner int
uaudio_chan_set_param_speed(struct uaudio_chan * ch,uint32_t speed)250009b9c6f2SSascha Wildner uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
250109b9c6f2SSascha Wildner {
25025e4edd23SMatthew Dillon 	struct uaudio_softc *sc;
2503a963377aSSascha Wildner 	uint8_t x;
2504a963377aSSascha Wildner 
25055e4edd23SMatthew Dillon 	sc = ch->priv_sc;
25065e4edd23SMatthew Dillon 
2507a963377aSSascha Wildner 	for (x = 0; x < ch->num_alt; x++) {
2508a963377aSSascha Wildner 		if (ch->usb_alt[x].sample_rate < speed) {
2509a963377aSSascha Wildner 			/* sample rate is too low */
2510a963377aSSascha Wildner 			break;
251109b9c6f2SSascha Wildner 		}
2512a963377aSSascha Wildner 	}
2513a963377aSSascha Wildner 
2514a963377aSSascha Wildner 	if (x != 0)
2515a963377aSSascha Wildner 		x--;
2516a963377aSSascha Wildner 
25175e4edd23SMatthew Dillon 	usb_proc_explore_lock(sc->sc_udev);
2518a963377aSSascha Wildner 	ch->set_alt = x;
25195e4edd23SMatthew Dillon 	usb_proc_explore_unlock(sc->sc_udev);
2520a963377aSSascha Wildner 
2521a963377aSSascha Wildner 	DPRINTF("Selecting alt %d\n", (int)x);
2522a963377aSSascha Wildner 
2523a963377aSSascha Wildner 	return (ch->usb_alt[x].sample_rate);
252409b9c6f2SSascha Wildner }
252509b9c6f2SSascha Wildner 
252609b9c6f2SSascha Wildner int
uaudio_chan_getptr(struct uaudio_chan * ch)252709b9c6f2SSascha Wildner uaudio_chan_getptr(struct uaudio_chan *ch)
252809b9c6f2SSascha Wildner {
252909b9c6f2SSascha Wildner 	return (ch->cur - ch->start);
253009b9c6f2SSascha Wildner }
253109b9c6f2SSascha Wildner 
253209b9c6f2SSascha Wildner struct pcmchan_caps *
uaudio_chan_getcaps(struct uaudio_chan * ch)253309b9c6f2SSascha Wildner uaudio_chan_getcaps(struct uaudio_chan *ch)
253409b9c6f2SSascha Wildner {
253509b9c6f2SSascha Wildner 	return (&ch->pcm_cap);
253609b9c6f2SSascha Wildner }
253709b9c6f2SSascha Wildner 
253809b9c6f2SSascha Wildner static struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = {
253909b9c6f2SSascha Wildner 	.id = SND_CHN_MATRIX_DRV,
254009b9c6f2SSascha Wildner 	.channels = 2,
254109b9c6f2SSascha Wildner 	.ext = 0,
254209b9c6f2SSascha Wildner 	.map = {
254309b9c6f2SSascha Wildner 		/* Right */
254409b9c6f2SSascha Wildner 		[0] = {
254509b9c6f2SSascha Wildner 			.type = SND_CHN_T_FR,
254609b9c6f2SSascha Wildner 			.members =
254709b9c6f2SSascha Wildner 			    SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC |
254809b9c6f2SSascha Wildner 			    SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR |
254909b9c6f2SSascha Wildner 			    SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR
255009b9c6f2SSascha Wildner 		},
255109b9c6f2SSascha Wildner 		/* Left */
255209b9c6f2SSascha Wildner 		[1] = {
255309b9c6f2SSascha Wildner 			.type = SND_CHN_T_FL,
255409b9c6f2SSascha Wildner 			.members =
255509b9c6f2SSascha Wildner 			    SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC |
255609b9c6f2SSascha Wildner 			    SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL |
255709b9c6f2SSascha Wildner 			    SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL
255809b9c6f2SSascha Wildner 		},
255909b9c6f2SSascha Wildner 		[2] = {
256009b9c6f2SSascha Wildner 			.type = SND_CHN_T_MAX,
256109b9c6f2SSascha Wildner 			.members = 0
256209b9c6f2SSascha Wildner 		}
256309b9c6f2SSascha Wildner 	},
256409b9c6f2SSascha Wildner 	.mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL,
256509b9c6f2SSascha Wildner 	.offset = {  1,  0, -1, -1, -1, -1, -1, -1, -1,
256609b9c6f2SSascha Wildner 		    -1, -1, -1, -1, -1, -1, -1, -1, -1  }
256709b9c6f2SSascha Wildner };
256809b9c6f2SSascha Wildner 
256909b9c6f2SSascha Wildner struct pcmchan_matrix *
uaudio_chan_getmatrix(struct uaudio_chan * ch,uint32_t format)257009b9c6f2SSascha Wildner uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format)
257109b9c6f2SSascha Wildner {
257209b9c6f2SSascha Wildner 	struct uaudio_softc *sc;
257309b9c6f2SSascha Wildner 
257409b9c6f2SSascha Wildner 	sc = ch->priv_sc;
257509b9c6f2SSascha Wildner 
257609b9c6f2SSascha Wildner 	if (sc != NULL && sc->sc_uq_audio_swap_lr != 0 &&
257709b9c6f2SSascha Wildner 	    AFMT_CHANNEL(format) == 2)
257809b9c6f2SSascha Wildner 		return (&uaudio_chan_matrix_swap_2_0);
257909b9c6f2SSascha Wildner 
258009b9c6f2SSascha Wildner 	return (feeder_matrix_format_map(format));
258109b9c6f2SSascha Wildner }
2582a963377aSSascha Wildner 
258309b9c6f2SSascha Wildner int
uaudio_chan_set_param_format(struct uaudio_chan * ch,uint32_t format)258409b9c6f2SSascha Wildner uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
258509b9c6f2SSascha Wildner {
2586a963377aSSascha Wildner 	DPRINTF("Selecting format 0x%08x\n", (unsigned int)format);
258709b9c6f2SSascha Wildner 	return (0);
258809b9c6f2SSascha Wildner }
258909b9c6f2SSascha Wildner 
25905e4edd23SMatthew Dillon static void
uaudio_chan_start_sub(struct uaudio_chan * ch)25915e4edd23SMatthew Dillon uaudio_chan_start_sub(struct uaudio_chan *ch)
259209b9c6f2SSascha Wildner {
2593a963377aSSascha Wildner 	struct uaudio_softc *sc = ch->priv_sc;
2594a963377aSSascha Wildner 	int do_start = 0;
259509b9c6f2SSascha Wildner 
2596a963377aSSascha Wildner 	if (ch->operation != CHAN_OP_DRAIN) {
2597a963377aSSascha Wildner 		if (ch->cur_alt == ch->set_alt &&
25985e4edd23SMatthew Dillon 		    ch->operation == CHAN_OP_NONE &&
25995e4edd23SMatthew Dillon 		    lockowned(ch->pcm_lock) != 0) {
2600a963377aSSascha Wildner 			/* save doing the explore task */
2601a963377aSSascha Wildner 			do_start = 1;
2602a963377aSSascha Wildner 		} else {
2603a963377aSSascha Wildner 			ch->operation = CHAN_OP_START;
2604a963377aSSascha Wildner 			(void)usb_proc_explore_msignal(sc->sc_udev,
2605a963377aSSascha Wildner 			    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
260609b9c6f2SSascha Wildner 		}
2607a963377aSSascha Wildner 	}
2608a963377aSSascha Wildner 	if (do_start) {
2609a963377aSSascha Wildner 		usbd_transfer_start(ch->xfer[0]);
261009b9c6f2SSascha Wildner 		usbd_transfer_start(ch->xfer[1]);
261109b9c6f2SSascha Wildner 	}
261209b9c6f2SSascha Wildner }
261309b9c6f2SSascha Wildner 
26145e4edd23SMatthew Dillon static int
uaudio_chan_need_both(struct uaudio_softc * sc)26155e4edd23SMatthew Dillon uaudio_chan_need_both(struct uaudio_softc *sc)
26165e4edd23SMatthew Dillon {
26175e4edd23SMatthew Dillon 	return (sc->sc_play_chan.num_alt > 0 &&
26185e4edd23SMatthew Dillon 	    sc->sc_play_chan.running != 0 &&
26195e4edd23SMatthew Dillon 	    uaudio_chan_is_async(&sc->sc_play_chan,
26205e4edd23SMatthew Dillon 	    sc->sc_play_chan.set_alt) != 0 &&
26215e4edd23SMatthew Dillon 	    sc->sc_rec_chan.num_alt > 0 &&
26225e4edd23SMatthew Dillon 	    sc->sc_rec_chan.running == 0);
26235e4edd23SMatthew Dillon }
26245e4edd23SMatthew Dillon 
26255e4edd23SMatthew Dillon static int
uaudio_chan_need_none(struct uaudio_softc * sc)26265e4edd23SMatthew Dillon uaudio_chan_need_none(struct uaudio_softc *sc)
26275e4edd23SMatthew Dillon {
26285e4edd23SMatthew Dillon 	return (sc->sc_play_chan.num_alt > 0 &&
26295e4edd23SMatthew Dillon 	    sc->sc_play_chan.running == 0 &&
26305e4edd23SMatthew Dillon 	    sc->sc_rec_chan.num_alt > 0 &&
26315e4edd23SMatthew Dillon 	    sc->sc_rec_chan.running == 0);
26325e4edd23SMatthew Dillon }
26335e4edd23SMatthew Dillon 
26345e4edd23SMatthew Dillon void
uaudio_chan_start(struct uaudio_chan * ch)26355e4edd23SMatthew Dillon uaudio_chan_start(struct uaudio_chan *ch)
26365e4edd23SMatthew Dillon {
26375e4edd23SMatthew Dillon 	struct uaudio_softc *sc = ch->priv_sc;
26385e4edd23SMatthew Dillon 
26395e4edd23SMatthew Dillon 	/* make operation atomic */
26405e4edd23SMatthew Dillon 	usb_proc_explore_lock(sc->sc_udev);
26415e4edd23SMatthew Dillon 
26425e4edd23SMatthew Dillon 	/* check if not running */
26435e4edd23SMatthew Dillon 	if (ch->running == 0) {
26445e4edd23SMatthew Dillon 		uint32_t temp;
26455e4edd23SMatthew Dillon 
26465e4edd23SMatthew Dillon 		/* get current buffer size */
26475e4edd23SMatthew Dillon 		temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
26485e4edd23SMatthew Dillon 
26495e4edd23SMatthew Dillon 		/* set running flag */
26505e4edd23SMatthew Dillon 		ch->running = 1;
26515e4edd23SMatthew Dillon 
26525e4edd23SMatthew Dillon 		/* ensure the hardware buffer is reset */
26535e4edd23SMatthew Dillon 		ch->start = ch->buf;
26545e4edd23SMatthew Dillon 		ch->end = ch->buf + temp;
26555e4edd23SMatthew Dillon 		ch->cur = ch->buf;
26565e4edd23SMatthew Dillon 
26575e4edd23SMatthew Dillon 		if (uaudio_chan_need_both(sc)) {
26585e4edd23SMatthew Dillon 			/*
26595e4edd23SMatthew Dillon 			 * Start both endpoints because of need for
26605e4edd23SMatthew Dillon 			 * jitter information:
26615e4edd23SMatthew Dillon 			 */
26625e4edd23SMatthew Dillon 			uaudio_chan_start_sub(&sc->sc_rec_chan);
26635e4edd23SMatthew Dillon 			uaudio_chan_start_sub(&sc->sc_play_chan);
26645e4edd23SMatthew Dillon 		} else {
26655e4edd23SMatthew Dillon 			uaudio_chan_start_sub(ch);
26665e4edd23SMatthew Dillon 		}
26675e4edd23SMatthew Dillon 	}
26685e4edd23SMatthew Dillon 
26695e4edd23SMatthew Dillon 	/* exit atomic operation */
26705e4edd23SMatthew Dillon 	usb_proc_explore_unlock(sc->sc_udev);
26715e4edd23SMatthew Dillon }
26725e4edd23SMatthew Dillon 
26735e4edd23SMatthew Dillon static void
uaudio_chan_stop_sub(struct uaudio_chan * ch)26745e4edd23SMatthew Dillon uaudio_chan_stop_sub(struct uaudio_chan *ch)
267509b9c6f2SSascha Wildner {
2676a963377aSSascha Wildner 	struct uaudio_softc *sc = ch->priv_sc;
2677a963377aSSascha Wildner 	int do_stop = 0;
2678a963377aSSascha Wildner 
2679a963377aSSascha Wildner 	if (ch->operation != CHAN_OP_DRAIN) {
2680a963377aSSascha Wildner 		if (ch->cur_alt == ch->set_alt &&
26815e4edd23SMatthew Dillon 		    ch->operation == CHAN_OP_NONE &&
26825e4edd23SMatthew Dillon 		    lockowned(ch->pcm_lock) != 0) {
2683a963377aSSascha Wildner 			/* save doing the explore task */
2684a963377aSSascha Wildner 			do_stop = 1;
2685a963377aSSascha Wildner 		} else {
2686a963377aSSascha Wildner 			ch->operation = CHAN_OP_STOP;
2687a963377aSSascha Wildner 			(void)usb_proc_explore_msignal(sc->sc_udev,
2688a963377aSSascha Wildner 			    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
2689a963377aSSascha Wildner 		}
2690a963377aSSascha Wildner 	}
2691a963377aSSascha Wildner 	if (do_stop) {
269209b9c6f2SSascha Wildner 		usbd_transfer_stop(ch->xfer[0]);
269309b9c6f2SSascha Wildner 		usbd_transfer_stop(ch->xfer[1]);
2694a963377aSSascha Wildner 	}
26955e4edd23SMatthew Dillon }
26965e4edd23SMatthew Dillon 
26975e4edd23SMatthew Dillon void
uaudio_chan_stop(struct uaudio_chan * ch)26985e4edd23SMatthew Dillon uaudio_chan_stop(struct uaudio_chan *ch)
26995e4edd23SMatthew Dillon {
27005e4edd23SMatthew Dillon 	struct uaudio_softc *sc = ch->priv_sc;
27015e4edd23SMatthew Dillon 
27025e4edd23SMatthew Dillon 	/* make operation atomic */
27035e4edd23SMatthew Dillon 	usb_proc_explore_lock(sc->sc_udev);
27045e4edd23SMatthew Dillon 
27055e4edd23SMatthew Dillon 	/* check if running */
27065e4edd23SMatthew Dillon 	if (ch->running != 0) {
27075e4edd23SMatthew Dillon 		/* clear running flag */
27085e4edd23SMatthew Dillon 		ch->running = 0;
27095e4edd23SMatthew Dillon 
27105e4edd23SMatthew Dillon 		if (uaudio_chan_need_both(sc)) {
27115e4edd23SMatthew Dillon 			/*
27125e4edd23SMatthew Dillon 			 * Leave the endpoints running because we need
27135e4edd23SMatthew Dillon 			 * information about jitter!
27145e4edd23SMatthew Dillon 			 */
27155e4edd23SMatthew Dillon 		} else if (uaudio_chan_need_none(sc)) {
27165e4edd23SMatthew Dillon 			/*
27175e4edd23SMatthew Dillon 			 * Stop both endpoints in case the one was used for
27185e4edd23SMatthew Dillon 			 * jitter information:
27195e4edd23SMatthew Dillon 			 */
27205e4edd23SMatthew Dillon 			uaudio_chan_stop_sub(&sc->sc_rec_chan);
27215e4edd23SMatthew Dillon 			uaudio_chan_stop_sub(&sc->sc_play_chan);
27225e4edd23SMatthew Dillon 		} else {
27235e4edd23SMatthew Dillon 			uaudio_chan_stop_sub(ch);
27245e4edd23SMatthew Dillon 		}
27255e4edd23SMatthew Dillon 	}
27265e4edd23SMatthew Dillon 
27275e4edd23SMatthew Dillon 	/* exit atomic operation */
27285e4edd23SMatthew Dillon 	usb_proc_explore_unlock(sc->sc_udev);
272909b9c6f2SSascha Wildner }
273009b9c6f2SSascha Wildner 
273109b9c6f2SSascha Wildner /*========================================================================*
273209b9c6f2SSascha Wildner  * AC - Audio Controller - routines
273309b9c6f2SSascha Wildner  *========================================================================*/
273409b9c6f2SSascha Wildner 
2735a963377aSSascha Wildner static int
uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS)2736a963377aSSascha Wildner uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS)
2737a963377aSSascha Wildner {
2738a963377aSSascha Wildner 	struct uaudio_softc *sc;
2739a963377aSSascha Wildner 	struct uaudio_mixer_node *pmc;
2740a963377aSSascha Wildner 	int hint;
2741a963377aSSascha Wildner 	int error;
2742a963377aSSascha Wildner 	int temp = 0;
2743a963377aSSascha Wildner 	int chan = 0;
2744a963377aSSascha Wildner 
2745a963377aSSascha Wildner 	sc = (struct uaudio_softc *)oidp->oid_arg1;
2746a963377aSSascha Wildner 	hint = oidp->oid_arg2;
2747a963377aSSascha Wildner 
2748a963377aSSascha Wildner 	if (sc->sc_mixer_lock == NULL)
2749a963377aSSascha Wildner 		return (ENXIO);
2750a963377aSSascha Wildner 
2751a963377aSSascha Wildner 	/* lookup mixer node */
2752a963377aSSascha Wildner 
2753a963377aSSascha Wildner 	lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE);
2754a963377aSSascha Wildner 	for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) {
2755a963377aSSascha Wildner 		for (chan = 0; chan != (int)pmc->nchan; chan++) {
2756a963377aSSascha Wildner 			if (pmc->wValue[chan] != -1 &&
2757a963377aSSascha Wildner 			    pmc->wValue[chan] == hint) {
2758a963377aSSascha Wildner 				temp = pmc->wData[chan];
2759a963377aSSascha Wildner 				goto found;
2760a963377aSSascha Wildner 			}
2761a963377aSSascha Wildner 		}
2762a963377aSSascha Wildner 	}
2763a963377aSSascha Wildner found:
2764a963377aSSascha Wildner 	lockmgr(sc->sc_mixer_lock, LK_RELEASE);
2765a963377aSSascha Wildner 
2766a963377aSSascha Wildner 	error = sysctl_handle_int(oidp, &temp, 0, req);
2767a963377aSSascha Wildner 	if (error != 0 || req->newptr == NULL)
2768a963377aSSascha Wildner 		return (error);
2769a963377aSSascha Wildner 
2770a963377aSSascha Wildner 	/* update mixer value */
2771a963377aSSascha Wildner 
2772a963377aSSascha Wildner 	lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE);
2773a963377aSSascha Wildner 	if (pmc != NULL &&
2774a963377aSSascha Wildner 	    temp >= pmc->minval &&
2775a963377aSSascha Wildner 	    temp <= pmc->maxval) {
2776a963377aSSascha Wildner 
2777a963377aSSascha Wildner 		pmc->wData[chan] = temp;
2778a963377aSSascha Wildner 		pmc->update[(chan / 8)] |= (1 << (chan % 8));
2779a963377aSSascha Wildner 
2780a963377aSSascha Wildner 		/* start the transfer, if not already started */
2781a963377aSSascha Wildner 		usbd_transfer_start(sc->sc_mixer_xfer[0]);
2782a963377aSSascha Wildner 	}
2783a963377aSSascha Wildner 	lockmgr(sc->sc_mixer_lock, LK_RELEASE);
2784a963377aSSascha Wildner 
2785a963377aSSascha Wildner 	return (0);
2786a963377aSSascha Wildner }
2787a963377aSSascha Wildner 
2788a963377aSSascha Wildner static void
uaudio_mixer_ctl_free(struct uaudio_softc * sc)2789a963377aSSascha Wildner uaudio_mixer_ctl_free(struct uaudio_softc *sc)
2790a963377aSSascha Wildner {
2791a963377aSSascha Wildner 	struct uaudio_mixer_node *p_mc;
2792a963377aSSascha Wildner 
2793a963377aSSascha Wildner 	while ((p_mc = sc->sc_mixer_root) != NULL) {
2794a963377aSSascha Wildner 		sc->sc_mixer_root = p_mc->next;
2795a963377aSSascha Wildner 		kfree(p_mc, M_USBDEV);
2796a963377aSSascha Wildner 	}
2797a963377aSSascha Wildner }
2798a963377aSSascha Wildner 
2799a963377aSSascha Wildner static void
uaudio_mixer_register_sysctl(struct uaudio_softc * sc,device_t dev)2800a963377aSSascha Wildner uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev)
2801a963377aSSascha Wildner {
2802a963377aSSascha Wildner 	struct uaudio_mixer_node *pmc;
2803a963377aSSascha Wildner 	struct sysctl_oid *mixer_tree;
2804a963377aSSascha Wildner 	struct sysctl_oid *control_tree;
2805a963377aSSascha Wildner 	char buf[32];
2806a963377aSSascha Wildner 	int chan;
2807a963377aSSascha Wildner 	int n;
2808a963377aSSascha Wildner 
2809a963377aSSascha Wildner 	mixer_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
2810a963377aSSascha Wildner 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "mixer",
2811a963377aSSascha Wildner 	    CTLFLAG_RD, NULL, "");
2812a963377aSSascha Wildner 
2813a963377aSSascha Wildner 	if (mixer_tree == NULL)
2814a963377aSSascha Wildner 		return;
2815a963377aSSascha Wildner 
2816a963377aSSascha Wildner 	for (n = 0, pmc = sc->sc_mixer_root; pmc != NULL;
2817a963377aSSascha Wildner 	    pmc = pmc->next, n++) {
2818a963377aSSascha Wildner 
2819a963377aSSascha Wildner 		for (chan = 0; chan < pmc->nchan; chan++) {
2820a963377aSSascha Wildner 
2821a963377aSSascha Wildner 			if (pmc->nchan > 1) {
2822a963377aSSascha Wildner 				ksnprintf(buf, sizeof(buf), "%s_%d_%d",
2823a963377aSSascha Wildner 				    pmc->name, n, chan);
2824a963377aSSascha Wildner 			} else {
2825a963377aSSascha Wildner 				ksnprintf(buf, sizeof(buf), "%s_%d",
2826a963377aSSascha Wildner 				    pmc->name, n);
2827a963377aSSascha Wildner 			}
2828a963377aSSascha Wildner 
2829a963377aSSascha Wildner 			control_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
2830a963377aSSascha Wildner 			    SYSCTL_CHILDREN(mixer_tree), OID_AUTO, buf,
2831a963377aSSascha Wildner 			    CTLFLAG_RD, NULL, "Mixer control nodes");
2832a963377aSSascha Wildner 
2833a963377aSSascha Wildner 			if (control_tree == NULL)
2834a963377aSSascha Wildner 				continue;
2835a963377aSSascha Wildner 
2836a963377aSSascha Wildner 			SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
2837a963377aSSascha Wildner 			    SYSCTL_CHILDREN(control_tree),
2838a963377aSSascha Wildner 			    OID_AUTO, "val", CTLTYPE_INT | CTLFLAG_RW, sc,
2839a963377aSSascha Wildner 			    pmc->wValue[chan],
2840a963377aSSascha Wildner 			    uaudio_mixer_sysctl_handler, "I", "Current value");
2841a963377aSSascha Wildner 
2842a963377aSSascha Wildner 			SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
2843a963377aSSascha Wildner 			    SYSCTL_CHILDREN(control_tree),
2844a963377aSSascha Wildner 			    OID_AUTO, "min", CTLFLAG_RD, 0, pmc->minval,
2845a963377aSSascha Wildner 			    "Minimum value");
2846a963377aSSascha Wildner 
2847a963377aSSascha Wildner 			SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
2848a963377aSSascha Wildner 			    SYSCTL_CHILDREN(control_tree),
2849a963377aSSascha Wildner 			    OID_AUTO, "max", CTLFLAG_RD, 0, pmc->maxval,
2850a963377aSSascha Wildner 			    "Maximum value");
2851a963377aSSascha Wildner 
2852a963377aSSascha Wildner 			SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev),
2853a963377aSSascha Wildner 			    SYSCTL_CHILDREN(control_tree),
2854a963377aSSascha Wildner 			    OID_AUTO, "desc", CTLFLAG_RD, pmc->desc, 0,
2855a963377aSSascha Wildner 			    "Description");
2856a963377aSSascha Wildner 		}
2857a963377aSSascha Wildner 	}
2858a963377aSSascha Wildner }
2859a963377aSSascha Wildner 
2860a963377aSSascha Wildner /* M-Audio FastTrack Ultra Mixer Description */
2861a963377aSSascha Wildner /* Origin: Linux USB Audio driver */
2862a963377aSSascha Wildner static void
uaudio_mixer_controls_create_ftu(struct uaudio_softc * sc)2863a963377aSSascha Wildner uaudio_mixer_controls_create_ftu(struct uaudio_softc *sc)
2864a963377aSSascha Wildner {
2865a963377aSSascha Wildner 	int chx;
2866a963377aSSascha Wildner 	int chy;
2867a963377aSSascha Wildner 
2868a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2869a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
2870a963377aSSascha Wildner 	MIX(sc).wValue[0] = MAKE_WORD(8, 0);
2871a963377aSSascha Wildner 	MIX(sc).class = UAC_OUTPUT;
2872a963377aSSascha Wildner 	MIX(sc).type = MIX_UNSIGNED_16;
2873a963377aSSascha Wildner 	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2874a963377aSSascha Wildner 	MIX(sc).name = "effect";
2875a963377aSSascha Wildner 	MIX(sc).minval = 0;
2876a963377aSSascha Wildner 	MIX(sc).maxval = 7;
2877a963377aSSascha Wildner 	MIX(sc).mul = 7;
2878a963377aSSascha Wildner 	MIX(sc).nchan = 1;
2879a963377aSSascha Wildner 	MIX(sc).update[0] = 1;
2880a963377aSSascha Wildner 	strlcpy(MIX(sc).desc, "Room1,2,3,Hall1,2,Plate,Delay,Echo", sizeof(MIX(sc).desc));
2881a963377aSSascha Wildner 	uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
2882a963377aSSascha Wildner 
2883a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2884a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no);
2885a963377aSSascha Wildner 
2886a963377aSSascha Wildner 	for (chx = 0; chx != 8; chx++) {
2887a963377aSSascha Wildner 		for (chy = 0; chy != 8; chy++) {
2888a963377aSSascha Wildner 
2889a963377aSSascha Wildner 			MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1);
2890a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_16;
2891a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2892a963377aSSascha Wildner 			MIX(sc).name = "mix_rec";
2893a963377aSSascha Wildner 			MIX(sc).nchan = 1;
2894a963377aSSascha Wildner 			MIX(sc).update[0] = 1;
2895a963377aSSascha Wildner 			MIX(sc).val_default = 0;
2896a963377aSSascha Wildner 			ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2897a963377aSSascha Wildner 			    "AIn%d - Out%d Record Volume", chy + 1, chx + 1);
2898a963377aSSascha Wildner 
2899a963377aSSascha Wildner 			uaudio_mixer_add_ctl(sc, &MIX(sc));
2900a963377aSSascha Wildner 
2901a963377aSSascha Wildner 			MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1 + 8);
2902a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_16;
2903a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2904a963377aSSascha Wildner 			MIX(sc).name = "mix_play";
2905a963377aSSascha Wildner 			MIX(sc).nchan = 1;
2906a963377aSSascha Wildner 			MIX(sc).update[0] = 1;
2907a963377aSSascha Wildner 			MIX(sc).val_default = (chx == chy) ? 2 : 0;
2908a963377aSSascha Wildner 			ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2909a963377aSSascha Wildner 			    "DIn%d - Out%d Playback Volume", chy + 1, chx + 1);
2910a963377aSSascha Wildner 
2911a963377aSSascha Wildner 			uaudio_mixer_add_ctl(sc, &MIX(sc));
2912a963377aSSascha Wildner 		}
2913a963377aSSascha Wildner 	}
2914a963377aSSascha Wildner 
2915a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2916a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
2917a963377aSSascha Wildner 	MIX(sc).wValue[0] = MAKE_WORD(2, 0);
2918a963377aSSascha Wildner 	MIX(sc).class = UAC_OUTPUT;
2919a963377aSSascha Wildner 	MIX(sc).type = MIX_SIGNED_8;
2920a963377aSSascha Wildner 	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2921a963377aSSascha Wildner 	MIX(sc).name = "effect_vol";
2922a963377aSSascha Wildner 	MIX(sc).nchan = 1;
2923a963377aSSascha Wildner 	MIX(sc).update[0] = 1;
2924a963377aSSascha Wildner 	MIX(sc).minval = 0;
2925a963377aSSascha Wildner 	MIX(sc).maxval = 0x7f;
2926a963377aSSascha Wildner 	MIX(sc).mul = 0x7f;
2927a963377aSSascha Wildner 	MIX(sc).nchan = 1;
2928a963377aSSascha Wildner 	MIX(sc).update[0] = 1;
2929a963377aSSascha Wildner 	strlcpy(MIX(sc).desc, "Effect Volume", sizeof(MIX(sc).desc));
2930a963377aSSascha Wildner 	uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
2931a963377aSSascha Wildner 
2932a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2933a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
2934a963377aSSascha Wildner 	MIX(sc).wValue[0] = MAKE_WORD(3, 0);
2935a963377aSSascha Wildner 	MIX(sc).class = UAC_OUTPUT;
2936a963377aSSascha Wildner 	MIX(sc).type = MIX_SIGNED_16;
2937a963377aSSascha Wildner 	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2938a963377aSSascha Wildner 	MIX(sc).name = "effect_dur";
2939a963377aSSascha Wildner 	MIX(sc).nchan = 1;
2940a963377aSSascha Wildner 	MIX(sc).update[0] = 1;
2941a963377aSSascha Wildner 	MIX(sc).minval = 0;
2942a963377aSSascha Wildner 	MIX(sc).maxval = 0x7f00;
2943a963377aSSascha Wildner 	MIX(sc).mul = 0x7f00;
2944a963377aSSascha Wildner 	MIX(sc).nchan = 1;
2945a963377aSSascha Wildner 	MIX(sc).update[0] = 1;
2946a963377aSSascha Wildner 	strlcpy(MIX(sc).desc, "Effect Duration", sizeof(MIX(sc).desc));
2947a963377aSSascha Wildner 	uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
2948a963377aSSascha Wildner 
2949a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2950a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
2951a963377aSSascha Wildner 	MIX(sc).wValue[0] = MAKE_WORD(4, 0);
2952a963377aSSascha Wildner 	MIX(sc).class = UAC_OUTPUT;
2953a963377aSSascha Wildner 	MIX(sc).type = MIX_SIGNED_8;
2954a963377aSSascha Wildner 	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2955a963377aSSascha Wildner 	MIX(sc).name = "effect_fb";
2956a963377aSSascha Wildner 	MIX(sc).nchan = 1;
2957a963377aSSascha Wildner 	MIX(sc).update[0] = 1;
2958a963377aSSascha Wildner 	MIX(sc).minval = 0;
2959a963377aSSascha Wildner 	MIX(sc).maxval = 0x7f;
2960a963377aSSascha Wildner 	MIX(sc).mul = 0x7f;
2961a963377aSSascha Wildner 	MIX(sc).nchan = 1;
2962a963377aSSascha Wildner 	MIX(sc).update[0] = 1;
2963a963377aSSascha Wildner 	strlcpy(MIX(sc).desc, "Effect Feedback Volume", sizeof(MIX(sc).desc));
2964a963377aSSascha Wildner 	uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
2965a963377aSSascha Wildner 
2966a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2967a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(7, sc->sc_mixer_iface_no);
2968a963377aSSascha Wildner 	for (chy = 0; chy != 4; chy++) {
2969a963377aSSascha Wildner 
2970a963377aSSascha Wildner 		MIX(sc).wValue[0] = MAKE_WORD(7, chy + 1);
2971a963377aSSascha Wildner 		MIX(sc).type = MIX_SIGNED_16;
2972a963377aSSascha Wildner 		MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2973a963377aSSascha Wildner 		MIX(sc).name = "effect_ret";
2974a963377aSSascha Wildner 		MIX(sc).nchan = 1;
2975a963377aSSascha Wildner 		MIX(sc).update[0] = 1;
2976a963377aSSascha Wildner 		ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2977a963377aSSascha Wildner 		    "Effect Return %d Volume", chy + 1);
2978a963377aSSascha Wildner 
2979a963377aSSascha Wildner 		uaudio_mixer_add_ctl(sc, &MIX(sc));
2980a963377aSSascha Wildner 	}
2981a963377aSSascha Wildner 
2982a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2983a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no);
2984a963377aSSascha Wildner 
2985a963377aSSascha Wildner 	for (chy = 0; chy != 8; chy++) {
2986a963377aSSascha Wildner 		MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1);
2987a963377aSSascha Wildner 		MIX(sc).type = MIX_SIGNED_16;
2988a963377aSSascha Wildner 		MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2989a963377aSSascha Wildner 		MIX(sc).name = "effect_send";
2990a963377aSSascha Wildner 		MIX(sc).nchan = 1;
2991a963377aSSascha Wildner 		MIX(sc).update[0] = 1;
2992a963377aSSascha Wildner 		ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2993a963377aSSascha Wildner 		    "Effect Send AIn%d Volume", chy + 1);
2994a963377aSSascha Wildner 
2995a963377aSSascha Wildner 		uaudio_mixer_add_ctl(sc, &MIX(sc));
2996a963377aSSascha Wildner 
2997a963377aSSascha Wildner 		MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1 + 8);
2998a963377aSSascha Wildner 		MIX(sc).type = MIX_SIGNED_16;
2999a963377aSSascha Wildner 		MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3000a963377aSSascha Wildner 		MIX(sc).name = "effect_send";
3001a963377aSSascha Wildner 		MIX(sc).nchan = 1;
3002a963377aSSascha Wildner 		MIX(sc).update[0] = 1;
3003a963377aSSascha Wildner 		ksnprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
3004a963377aSSascha Wildner 		    "Effect Send DIn%d Volume", chy + 1);
3005a963377aSSascha Wildner 
3006a963377aSSascha Wildner 		uaudio_mixer_add_ctl(sc, &MIX(sc));
3007a963377aSSascha Wildner 	}
3008a963377aSSascha Wildner }
3009a963377aSSascha Wildner 
3010a963377aSSascha Wildner static void
uaudio_mixer_reload_all(struct uaudio_softc * sc)3011a963377aSSascha Wildner uaudio_mixer_reload_all(struct uaudio_softc *sc)
3012a963377aSSascha Wildner {
3013a963377aSSascha Wildner 	struct uaudio_mixer_node *pmc;
3014a963377aSSascha Wildner 	int chan;
3015a963377aSSascha Wildner 
3016a963377aSSascha Wildner 	if (sc->sc_mixer_lock == NULL)
3017a963377aSSascha Wildner 		return;
3018a963377aSSascha Wildner 
3019a963377aSSascha Wildner 	lockmgr(sc->sc_mixer_lock, LK_EXCLUSIVE);
3020a963377aSSascha Wildner 	for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) {
3021a963377aSSascha Wildner 		/* use reset defaults for non-oss controlled settings */
3022a963377aSSascha Wildner 		if (pmc->ctl == SOUND_MIXER_NRDEVICES)
3023a963377aSSascha Wildner 			continue;
3024a963377aSSascha Wildner 		for (chan = 0; chan < pmc->nchan; chan++)
3025a963377aSSascha Wildner 			pmc->update[chan / 8] |= (1 << (chan % 8));
3026a963377aSSascha Wildner 	}
3027a963377aSSascha Wildner 	usbd_transfer_start(sc->sc_mixer_xfer[0]);
3028a963377aSSascha Wildner 
3029a963377aSSascha Wildner 	/* start HID volume keys, if any */
3030a963377aSSascha Wildner 	usbd_transfer_start(sc->sc_hid.xfer[0]);
3031a963377aSSascha Wildner 	lockmgr(sc->sc_mixer_lock, LK_RELEASE);
3032a963377aSSascha Wildner }
3033a963377aSSascha Wildner 
303409b9c6f2SSascha Wildner static void
uaudio_mixer_add_ctl_sub(struct uaudio_softc * sc,struct uaudio_mixer_node * mc)303509b9c6f2SSascha Wildner uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
303609b9c6f2SSascha Wildner {
303709b9c6f2SSascha Wildner 	struct uaudio_mixer_node *p_mc_new =
303809b9c6f2SSascha Wildner 	    kmalloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK);
3039a963377aSSascha Wildner 	int ch;
304009b9c6f2SSascha Wildner 
3041aaad5092SMarkus Pfeiffer 	if (p_mc_new != NULL) {
304209b9c6f2SSascha Wildner 		memcpy(p_mc_new, mc, sizeof(*p_mc_new));
304309b9c6f2SSascha Wildner 		p_mc_new->next = sc->sc_mixer_root;
304409b9c6f2SSascha Wildner 		sc->sc_mixer_root = p_mc_new;
304509b9c6f2SSascha Wildner 		sc->sc_mixer_count++;
3046a963377aSSascha Wildner 
3047a963377aSSascha Wildner 		/* set default value for all channels */
3048a963377aSSascha Wildner 		for (ch = 0; ch < p_mc_new->nchan; ch++) {
3049a963377aSSascha Wildner 			switch (p_mc_new->val_default) {
3050a963377aSSascha Wildner 			case 1:
3051a963377aSSascha Wildner 				/* 50% */
3052a963377aSSascha Wildner 				p_mc_new->wData[ch] = (p_mc_new->maxval + p_mc_new->minval) / 2;
3053a963377aSSascha Wildner 				break;
3054a963377aSSascha Wildner 			case 2:
3055a963377aSSascha Wildner 				/* 100% */
3056a963377aSSascha Wildner 				p_mc_new->wData[ch] = p_mc_new->maxval;
3057a963377aSSascha Wildner 				break;
3058a963377aSSascha Wildner 			default:
3059a963377aSSascha Wildner 				/* 0% */
3060a963377aSSascha Wildner 				p_mc_new->wData[ch] = p_mc_new->minval;
3061a963377aSSascha Wildner 				break;
3062a963377aSSascha Wildner 			}
306309b9c6f2SSascha Wildner 		}
3064aaad5092SMarkus Pfeiffer 	} else {
3065aaad5092SMarkus Pfeiffer 		DPRINTF("out of memory\n");
3066aaad5092SMarkus Pfeiffer 	}
306709b9c6f2SSascha Wildner }
306809b9c6f2SSascha Wildner 
306909b9c6f2SSascha Wildner static void
uaudio_mixer_add_ctl(struct uaudio_softc * sc,struct uaudio_mixer_node * mc)307009b9c6f2SSascha Wildner uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
307109b9c6f2SSascha Wildner {
307209b9c6f2SSascha Wildner 	int32_t res;
307309b9c6f2SSascha Wildner 
307409b9c6f2SSascha Wildner 	if (mc->class < UAC_NCLASSES) {
307509b9c6f2SSascha Wildner 		DPRINTF("adding %s.%d\n",
307609b9c6f2SSascha Wildner 		    uac_names[mc->class], mc->ctl);
307709b9c6f2SSascha Wildner 	} else {
307809b9c6f2SSascha Wildner 		DPRINTF("adding %d\n", mc->ctl);
307909b9c6f2SSascha Wildner 	}
308009b9c6f2SSascha Wildner 
308109b9c6f2SSascha Wildner 	if (mc->type == MIX_ON_OFF) {
308209b9c6f2SSascha Wildner 		mc->minval = 0;
308309b9c6f2SSascha Wildner 		mc->maxval = 1;
308409b9c6f2SSascha Wildner 	} else if (mc->type == MIX_SELECTOR) {
308509b9c6f2SSascha Wildner 	} else {
308609b9c6f2SSascha Wildner 
308709b9c6f2SSascha Wildner 		/* determine min and max values */
308809b9c6f2SSascha Wildner 
3089a963377aSSascha Wildner 		mc->minval = uaudio_mixer_get(sc->sc_udev,
3090a963377aSSascha Wildner 		    sc->sc_audio_rev, GET_MIN, mc);
3091a963377aSSascha Wildner 		mc->maxval = uaudio_mixer_get(sc->sc_udev,
3092a963377aSSascha Wildner 		    sc->sc_audio_rev, GET_MAX, mc);
309309b9c6f2SSascha Wildner 
309409b9c6f2SSascha Wildner 		/* check if max and min was swapped */
309509b9c6f2SSascha Wildner 
309609b9c6f2SSascha Wildner 		if (mc->maxval < mc->minval) {
309709b9c6f2SSascha Wildner 			res = mc->maxval;
309809b9c6f2SSascha Wildner 			mc->maxval = mc->minval;
309909b9c6f2SSascha Wildner 			mc->minval = res;
310009b9c6f2SSascha Wildner 		}
310109b9c6f2SSascha Wildner 
310209b9c6f2SSascha Wildner 		/* compute value range */
310309b9c6f2SSascha Wildner 		mc->mul = mc->maxval - mc->minval;
310409b9c6f2SSascha Wildner 		if (mc->mul == 0)
310509b9c6f2SSascha Wildner 			mc->mul = 1;
310609b9c6f2SSascha Wildner 
310709b9c6f2SSascha Wildner 		/* compute value alignment */
3108a963377aSSascha Wildner 		res = uaudio_mixer_get(sc->sc_udev,
3109a963377aSSascha Wildner 		    sc->sc_audio_rev, GET_RES, mc);
311009b9c6f2SSascha Wildner 
311109b9c6f2SSascha Wildner 		DPRINTF("Resolution = %d\n", (int)res);
311209b9c6f2SSascha Wildner 	}
311309b9c6f2SSascha Wildner 
311409b9c6f2SSascha Wildner 	uaudio_mixer_add_ctl_sub(sc, mc);
311509b9c6f2SSascha Wildner 
311609b9c6f2SSascha Wildner #ifdef USB_DEBUG
311709b9c6f2SSascha Wildner 	if (uaudio_debug > 2) {
311809b9c6f2SSascha Wildner 		uint8_t i;
311909b9c6f2SSascha Wildner 
312009b9c6f2SSascha Wildner 		for (i = 0; i < mc->nchan; i++) {
312109b9c6f2SSascha Wildner 			DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]);
312209b9c6f2SSascha Wildner 		}
312309b9c6f2SSascha Wildner 		DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' "
312409b9c6f2SSascha Wildner 		    "min=%d max=%d\n",
312509b9c6f2SSascha Wildner 		    mc->wIndex, mc->type, mc->ctl,
312609b9c6f2SSascha Wildner 		    mc->minval, mc->maxval);
312709b9c6f2SSascha Wildner 	}
312809b9c6f2SSascha Wildner #endif
312909b9c6f2SSascha Wildner }
313009b9c6f2SSascha Wildner 
313109b9c6f2SSascha Wildner static void
uaudio_mixer_add_mixer(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)313209b9c6f2SSascha Wildner uaudio_mixer_add_mixer(struct uaudio_softc *sc,
313309b9c6f2SSascha Wildner     const struct uaudio_terminal_node *iot, int id)
313409b9c6f2SSascha Wildner {
3135a963377aSSascha Wildner 	const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu_v1;
313609b9c6f2SSascha Wildner 	const struct usb_audio_mixer_unit_1 *d1;
313709b9c6f2SSascha Wildner 
313809b9c6f2SSascha Wildner 	uint32_t bno;			/* bit number */
313909b9c6f2SSascha Wildner 	uint32_t p;			/* bit number accumulator */
314009b9c6f2SSascha Wildner 	uint32_t mo;			/* matching outputs */
314109b9c6f2SSascha Wildner 	uint32_t mc;			/* matching channels */
314209b9c6f2SSascha Wildner 	uint32_t ichs;			/* input channels */
314309b9c6f2SSascha Wildner 	uint32_t ochs;			/* output channels */
314409b9c6f2SSascha Wildner 	uint32_t c;
314509b9c6f2SSascha Wildner 	uint32_t chs;			/* channels */
314609b9c6f2SSascha Wildner 	uint32_t i;
314709b9c6f2SSascha Wildner 	uint32_t o;
314809b9c6f2SSascha Wildner 
314909b9c6f2SSascha Wildner 	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
315009b9c6f2SSascha Wildner 	    d0->bUnitId, d0->bNrInPins);
315109b9c6f2SSascha Wildner 
315209b9c6f2SSascha Wildner 	/* compute the number of input channels */
315309b9c6f2SSascha Wildner 
315409b9c6f2SSascha Wildner 	ichs = 0;
315509b9c6f2SSascha Wildner 	for (i = 0; i < d0->bNrInPins; i++) {
3156a963377aSSascha Wildner 		ichs += uaudio_mixer_get_cluster(
3157a963377aSSascha Wildner 		    d0->baSourceId[i], iot).bNrChannels;
315809b9c6f2SSascha Wildner 	}
315909b9c6f2SSascha Wildner 
316009b9c6f2SSascha Wildner 	d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
316109b9c6f2SSascha Wildner 
316209b9c6f2SSascha Wildner 	/* and the number of output channels */
316309b9c6f2SSascha Wildner 
316409b9c6f2SSascha Wildner 	ochs = d1->bNrChannels;
316509b9c6f2SSascha Wildner 
316609b9c6f2SSascha Wildner 	DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
316709b9c6f2SSascha Wildner 
3168a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
316909b9c6f2SSascha Wildner 
3170a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3171a963377aSSascha Wildner 	uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3172a963377aSSascha Wildner 	MIX(sc).type = MIX_SIGNED_16;
317309b9c6f2SSascha Wildner 
3174a963377aSSascha Wildner 	if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL)
317509b9c6f2SSascha Wildner 		return;
3176a963377aSSascha Wildner 
317709b9c6f2SSascha Wildner 	for (p = i = 0; i < d0->bNrInPins; i++) {
3178a963377aSSascha Wildner 		chs = uaudio_mixer_get_cluster(
3179a963377aSSascha Wildner 		    d0->baSourceId[i], iot).bNrChannels;
318009b9c6f2SSascha Wildner 		mc = 0;
318109b9c6f2SSascha Wildner 		for (c = 0; c < chs; c++) {
318209b9c6f2SSascha Wildner 			mo = 0;
318309b9c6f2SSascha Wildner 			for (o = 0; o < ochs; o++) {
318409b9c6f2SSascha Wildner 				bno = ((p + c) * ochs) + o;
3185a963377aSSascha Wildner 				if (BIT_TEST(d1->bmControls, bno))
318609b9c6f2SSascha Wildner 					mo++;
318709b9c6f2SSascha Wildner 			}
3188a963377aSSascha Wildner 			if (mo == 1)
318909b9c6f2SSascha Wildner 				mc++;
319009b9c6f2SSascha Wildner 		}
319109b9c6f2SSascha Wildner 		if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
319209b9c6f2SSascha Wildner 
319309b9c6f2SSascha Wildner 			/* repeat bit-scan */
319409b9c6f2SSascha Wildner 
319509b9c6f2SSascha Wildner 			mc = 0;
319609b9c6f2SSascha Wildner 			for (c = 0; c < chs; c++) {
319709b9c6f2SSascha Wildner 				for (o = 0; o < ochs; o++) {
319809b9c6f2SSascha Wildner 					bno = ((p + c) * ochs) + o;
3199a963377aSSascha Wildner 					if (BIT_TEST(d1->bmControls, bno))
3200a963377aSSascha Wildner 						MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
320109b9c6f2SSascha Wildner 				}
320209b9c6f2SSascha Wildner 			}
3203a963377aSSascha Wildner 			MIX(sc).nchan = chs;
3204a963377aSSascha Wildner 			uaudio_mixer_add_ctl(sc, &MIX(sc));
320509b9c6f2SSascha Wildner 		}
3206a963377aSSascha Wildner 		p += chs;
3207a963377aSSascha Wildner 	}
3208a963377aSSascha Wildner }
3209a963377aSSascha Wildner 
3210a963377aSSascha Wildner static void
uaudio20_mixer_add_mixer(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)3211a963377aSSascha Wildner uaudio20_mixer_add_mixer(struct uaudio_softc *sc,
3212a963377aSSascha Wildner     const struct uaudio_terminal_node *iot, int id)
3213a963377aSSascha Wildner {
3214a963377aSSascha Wildner 	const struct usb_audio20_mixer_unit_0 *d0 = iot[id].u.mu_v2;
3215a963377aSSascha Wildner 	const struct usb_audio20_mixer_unit_1 *d1;
3216a963377aSSascha Wildner 
3217a963377aSSascha Wildner 	uint32_t bno;			/* bit number */
3218a963377aSSascha Wildner 	uint32_t p;			/* bit number accumulator */
3219a963377aSSascha Wildner 	uint32_t mo;			/* matching outputs */
3220a963377aSSascha Wildner 	uint32_t mc;			/* matching channels */
3221a963377aSSascha Wildner 	uint32_t ichs;			/* input channels */
3222a963377aSSascha Wildner 	uint32_t ochs;			/* output channels */
3223a963377aSSascha Wildner 	uint32_t c;
3224a963377aSSascha Wildner 	uint32_t chs;			/* channels */
3225a963377aSSascha Wildner 	uint32_t i;
3226a963377aSSascha Wildner 	uint32_t o;
3227a963377aSSascha Wildner 
3228a963377aSSascha Wildner 	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
3229a963377aSSascha Wildner 	    d0->bUnitId, d0->bNrInPins);
3230a963377aSSascha Wildner 
3231a963377aSSascha Wildner 	/* compute the number of input channels */
3232a963377aSSascha Wildner 
3233a963377aSSascha Wildner 	ichs = 0;
3234a963377aSSascha Wildner 	for (i = 0; i < d0->bNrInPins; i++) {
3235a963377aSSascha Wildner 		ichs += uaudio20_mixer_get_cluster(
3236a963377aSSascha Wildner 		    d0->baSourceId[i], iot).bNrChannels;
3237a963377aSSascha Wildner 	}
3238a963377aSSascha Wildner 
3239a963377aSSascha Wildner 	d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
3240a963377aSSascha Wildner 
3241a963377aSSascha Wildner 	/* and the number of output channels */
3242a963377aSSascha Wildner 
3243a963377aSSascha Wildner 	ochs = d1->bNrChannels;
3244a963377aSSascha Wildner 
3245a963377aSSascha Wildner 	DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
3246a963377aSSascha Wildner 
3247a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3248a963377aSSascha Wildner 
3249a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3250a963377aSSascha Wildner 	uaudio20_mixer_determine_class(&iot[id], &MIX(sc));
3251a963377aSSascha Wildner 	MIX(sc).type = MIX_SIGNED_16;
3252a963377aSSascha Wildner 
3253a963377aSSascha Wildner 	if (uaudio20_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL)
3254a963377aSSascha Wildner 		return;
3255a963377aSSascha Wildner 
3256a963377aSSascha Wildner 	for (p = i = 0; i < d0->bNrInPins; i++) {
3257a963377aSSascha Wildner 		chs = uaudio20_mixer_get_cluster(
3258a963377aSSascha Wildner 		    d0->baSourceId[i], iot).bNrChannels;
3259a963377aSSascha Wildner 		mc = 0;
3260a963377aSSascha Wildner 		for (c = 0; c < chs; c++) {
3261a963377aSSascha Wildner 			mo = 0;
3262a963377aSSascha Wildner 			for (o = 0; o < ochs; o++) {
3263a963377aSSascha Wildner 				bno = ((p + c) * ochs) + o;
3264a963377aSSascha Wildner 				if (BIT_TEST(d1->bmControls, bno))
3265a963377aSSascha Wildner 					mo++;
3266a963377aSSascha Wildner 			}
3267a963377aSSascha Wildner 			if (mo == 1)
3268a963377aSSascha Wildner 				mc++;
3269a963377aSSascha Wildner 		}
3270a963377aSSascha Wildner 		if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
3271a963377aSSascha Wildner 
3272a963377aSSascha Wildner 			/* repeat bit-scan */
3273a963377aSSascha Wildner 
3274a963377aSSascha Wildner 			mc = 0;
3275a963377aSSascha Wildner 			for (c = 0; c < chs; c++) {
3276a963377aSSascha Wildner 				for (o = 0; o < ochs; o++) {
3277a963377aSSascha Wildner 					bno = ((p + c) * ochs) + o;
3278a963377aSSascha Wildner 					if (BIT_TEST(d1->bmControls, bno))
3279a963377aSSascha Wildner 						MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
3280a963377aSSascha Wildner 				}
3281a963377aSSascha Wildner 			}
3282a963377aSSascha Wildner 			MIX(sc).nchan = chs;
3283a963377aSSascha Wildner 			uaudio_mixer_add_ctl(sc, &MIX(sc));
328409b9c6f2SSascha Wildner 		}
328509b9c6f2SSascha Wildner 		p += chs;
328609b9c6f2SSascha Wildner 	}
328709b9c6f2SSascha Wildner }
328809b9c6f2SSascha Wildner 
328909b9c6f2SSascha Wildner static void
uaudio_mixer_add_selector(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)329009b9c6f2SSascha Wildner uaudio_mixer_add_selector(struct uaudio_softc *sc,
329109b9c6f2SSascha Wildner     const struct uaudio_terminal_node *iot, int id)
329209b9c6f2SSascha Wildner {
3293a963377aSSascha Wildner 	const struct usb_audio_selector_unit *d = iot[id].u.su_v1;
329409b9c6f2SSascha Wildner 	uint16_t i;
329509b9c6f2SSascha Wildner 
329609b9c6f2SSascha Wildner 	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
329709b9c6f2SSascha Wildner 	    d->bUnitId, d->bNrInPins);
329809b9c6f2SSascha Wildner 
3299a963377aSSascha Wildner 	if (d->bNrInPins == 0)
330009b9c6f2SSascha Wildner 		return;
3301a963377aSSascha Wildner 
3302a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3303a963377aSSascha Wildner 
3304a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
3305a963377aSSascha Wildner 	MIX(sc).wValue[0] = MAKE_WORD(0, 0);
3306a963377aSSascha Wildner 	uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3307a963377aSSascha Wildner 	MIX(sc).nchan = 1;
3308a963377aSSascha Wildner 	MIX(sc).type = MIX_SELECTOR;
3309a963377aSSascha Wildner 	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3310a963377aSSascha Wildner 	MIX(sc).minval = 1;
3311a963377aSSascha Wildner 	MIX(sc).maxval = d->bNrInPins;
3312a963377aSSascha Wildner 	MIX(sc).name = "selector";
3313a963377aSSascha Wildner 
3314a963377aSSascha Wildner 	i = d->baSourceId[d->bNrInPins];
3315a963377aSSascha Wildner 	if (i == 0 ||
3316a963377aSSascha Wildner 	    usbd_req_get_string_any(sc->sc_udev, NULL,
3317a963377aSSascha Wildner 	    MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
3318a963377aSSascha Wildner 		MIX(sc).desc[0] = 0;
331909b9c6f2SSascha Wildner 	}
332009b9c6f2SSascha Wildner 
3321a963377aSSascha Wildner 	if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN) {
3322a963377aSSascha Wildner 		MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN;
332309b9c6f2SSascha Wildner 	}
3324a963377aSSascha Wildner 	MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval);
332509b9c6f2SSascha Wildner 	for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) {
3326a963377aSSascha Wildner 		MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES;
332709b9c6f2SSascha Wildner 	}
332809b9c6f2SSascha Wildner 
3329a963377aSSascha Wildner 	for (i = 0; i < MIX(sc).maxval; i++) {
3330a963377aSSascha Wildner 		MIX(sc).slctrtype[i] = uaudio_mixer_feature_name(
3331a963377aSSascha Wildner 		    &iot[d->baSourceId[i]], &MIX(sc));
333209b9c6f2SSascha Wildner 	}
333309b9c6f2SSascha Wildner 
3334a963377aSSascha Wildner 	MIX(sc).class = 0;			/* not used */
333509b9c6f2SSascha Wildner 
3336a963377aSSascha Wildner 	uaudio_mixer_add_ctl(sc, &MIX(sc));
3337a963377aSSascha Wildner }
3338a963377aSSascha Wildner 
3339a963377aSSascha Wildner static void
uaudio20_mixer_add_selector(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)3340a963377aSSascha Wildner uaudio20_mixer_add_selector(struct uaudio_softc *sc,
3341a963377aSSascha Wildner     const struct uaudio_terminal_node *iot, int id)
3342a963377aSSascha Wildner {
3343a963377aSSascha Wildner 	const struct usb_audio20_selector_unit *d = iot[id].u.su_v2;
3344a963377aSSascha Wildner 	uint16_t i;
3345a963377aSSascha Wildner 
3346a963377aSSascha Wildner 	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
3347a963377aSSascha Wildner 	    d->bUnitId, d->bNrInPins);
3348a963377aSSascha Wildner 
3349a963377aSSascha Wildner 	if (d->bNrInPins == 0)
3350a963377aSSascha Wildner 		return;
3351a963377aSSascha Wildner 
3352a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3353a963377aSSascha Wildner 
3354a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
3355a963377aSSascha Wildner 	MIX(sc).wValue[0] = MAKE_WORD(0, 0);
3356a963377aSSascha Wildner 	uaudio20_mixer_determine_class(&iot[id], &MIX(sc));
3357a963377aSSascha Wildner 	MIX(sc).nchan = 1;
3358a963377aSSascha Wildner 	MIX(sc).type = MIX_SELECTOR;
3359a963377aSSascha Wildner 	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3360a963377aSSascha Wildner 	MIX(sc).minval = 1;
3361a963377aSSascha Wildner 	MIX(sc).maxval = d->bNrInPins;
3362a963377aSSascha Wildner 	MIX(sc).name = "selector";
3363a963377aSSascha Wildner 
3364a963377aSSascha Wildner 	i = d->baSourceId[d->bNrInPins];
3365a963377aSSascha Wildner 	if (i == 0 ||
3366a963377aSSascha Wildner 	    usbd_req_get_string_any(sc->sc_udev, NULL,
3367a963377aSSascha Wildner 	    MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
3368a963377aSSascha Wildner 		MIX(sc).desc[0] = 0;
3369a963377aSSascha Wildner 	}
3370a963377aSSascha Wildner 
3371a963377aSSascha Wildner 	if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN)
3372a963377aSSascha Wildner 		MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN;
3373a963377aSSascha Wildner 
3374a963377aSSascha Wildner 	MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval);
3375a963377aSSascha Wildner 	for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++)
3376a963377aSSascha Wildner 		MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES;
3377a963377aSSascha Wildner 
3378a963377aSSascha Wildner 	for (i = 0; i < MIX(sc).maxval; i++) {
3379a963377aSSascha Wildner 		MIX(sc).slctrtype[i] = uaudio20_mixer_feature_name(
3380a963377aSSascha Wildner 		    &iot[d->baSourceId[i]], &MIX(sc));
3381a963377aSSascha Wildner 	}
3382a963377aSSascha Wildner 
3383a963377aSSascha Wildner 	MIX(sc).class = 0;			/* not used */
3384a963377aSSascha Wildner 
3385a963377aSSascha Wildner 	uaudio_mixer_add_ctl(sc, &MIX(sc));
338609b9c6f2SSascha Wildner }
338709b9c6f2SSascha Wildner 
338809b9c6f2SSascha Wildner static uint32_t
uaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit * d,uint8_t i)338909b9c6f2SSascha Wildner uaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit *d,
3390a963377aSSascha Wildner     uint8_t i)
339109b9c6f2SSascha Wildner {
339209b9c6f2SSascha Wildner 	uint32_t temp = 0;
3393a963377aSSascha Wildner 	uint32_t offset = (i * d->bControlSize);
339409b9c6f2SSascha Wildner 
339509b9c6f2SSascha Wildner 	if (d->bControlSize > 0) {
339609b9c6f2SSascha Wildner 		temp |= d->bmaControls[offset];
339709b9c6f2SSascha Wildner 		if (d->bControlSize > 1) {
339809b9c6f2SSascha Wildner 			temp |= d->bmaControls[offset + 1] << 8;
339909b9c6f2SSascha Wildner 			if (d->bControlSize > 2) {
340009b9c6f2SSascha Wildner 				temp |= d->bmaControls[offset + 2] << 16;
340109b9c6f2SSascha Wildner 				if (d->bControlSize > 3) {
340209b9c6f2SSascha Wildner 					temp |= d->bmaControls[offset + 3] << 24;
340309b9c6f2SSascha Wildner 				}
340409b9c6f2SSascha Wildner 			}
340509b9c6f2SSascha Wildner 		}
340609b9c6f2SSascha Wildner 	}
340709b9c6f2SSascha Wildner 	return (temp);
340809b9c6f2SSascha Wildner }
340909b9c6f2SSascha Wildner 
341009b9c6f2SSascha Wildner static void
uaudio_mixer_add_feature(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)341109b9c6f2SSascha Wildner uaudio_mixer_add_feature(struct uaudio_softc *sc,
341209b9c6f2SSascha Wildner     const struct uaudio_terminal_node *iot, int id)
341309b9c6f2SSascha Wildner {
3414a963377aSSascha Wildner 	const struct usb_audio_feature_unit *d = iot[id].u.fu_v1;
341509b9c6f2SSascha Wildner 	uint32_t fumask;
341609b9c6f2SSascha Wildner 	uint32_t mmask;
341709b9c6f2SSascha Wildner 	uint32_t cmask;
341809b9c6f2SSascha Wildner 	uint16_t mixernumber;
341909b9c6f2SSascha Wildner 	uint8_t nchan;
342009b9c6f2SSascha Wildner 	uint8_t chan;
342109b9c6f2SSascha Wildner 	uint8_t ctl;
342209b9c6f2SSascha Wildner 	uint8_t i;
342309b9c6f2SSascha Wildner 
3424a963377aSSascha Wildner 	if (d->bControlSize == 0)
342509b9c6f2SSascha Wildner 		return;
3426a963377aSSascha Wildner 
3427a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
342809b9c6f2SSascha Wildner 
342909b9c6f2SSascha Wildner 	nchan = (d->bLength - 7) / d->bControlSize;
343009b9c6f2SSascha Wildner 	mmask = uaudio_mixer_feature_get_bmaControls(d, 0);
343109b9c6f2SSascha Wildner 	cmask = 0;
343209b9c6f2SSascha Wildner 
3433a963377aSSascha Wildner 	if (nchan == 0)
343409b9c6f2SSascha Wildner 		return;
3435a963377aSSascha Wildner 
343609b9c6f2SSascha Wildner 	/* figure out what we can control */
343709b9c6f2SSascha Wildner 
343809b9c6f2SSascha Wildner 	for (chan = 1; chan < nchan; chan++) {
343909b9c6f2SSascha Wildner 		DPRINTFN(10, "chan=%d mask=%x\n",
344009b9c6f2SSascha Wildner 		    chan, uaudio_mixer_feature_get_bmaControls(d, chan));
344109b9c6f2SSascha Wildner 
344209b9c6f2SSascha Wildner 		cmask |= uaudio_mixer_feature_get_bmaControls(d, chan);
344309b9c6f2SSascha Wildner 	}
344409b9c6f2SSascha Wildner 
344509b9c6f2SSascha Wildner 	if (nchan > MIX_MAX_CHAN) {
344609b9c6f2SSascha Wildner 		nchan = MIX_MAX_CHAN;
344709b9c6f2SSascha Wildner 	}
3448a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
3449a963377aSSascha Wildner 
3450a963377aSSascha Wildner 	i = d->bmaControls[d->bControlSize];
3451a963377aSSascha Wildner 	if (i == 0 ||
3452a963377aSSascha Wildner 	    usbd_req_get_string_any(sc->sc_udev, NULL,
3453a963377aSSascha Wildner 	    MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
3454a963377aSSascha Wildner 		MIX(sc).desc[0] = 0;
3455a963377aSSascha Wildner 	}
345609b9c6f2SSascha Wildner 
345709b9c6f2SSascha Wildner 	for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) {
345809b9c6f2SSascha Wildner 
345909b9c6f2SSascha Wildner 		fumask = FU_MASK(ctl);
346009b9c6f2SSascha Wildner 
346109b9c6f2SSascha Wildner 		DPRINTFN(5, "ctl=%d fumask=0x%04x\n",
346209b9c6f2SSascha Wildner 		    ctl, fumask);
346309b9c6f2SSascha Wildner 
346409b9c6f2SSascha Wildner 		if (mmask & fumask) {
3465a963377aSSascha Wildner 			MIX(sc).nchan = 1;
3466a963377aSSascha Wildner 			MIX(sc).wValue[0] = MAKE_WORD(ctl, 0);
346709b9c6f2SSascha Wildner 		} else if (cmask & fumask) {
3468a963377aSSascha Wildner 			MIX(sc).nchan = nchan - 1;
346909b9c6f2SSascha Wildner 			for (i = 1; i < nchan; i++) {
347009b9c6f2SSascha Wildner 				if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask)
3471a963377aSSascha Wildner 					MIX(sc).wValue[i - 1] = MAKE_WORD(ctl, i);
347209b9c6f2SSascha Wildner 				else
3473a963377aSSascha Wildner 					MIX(sc).wValue[i - 1] = -1;
347409b9c6f2SSascha Wildner 			}
347509b9c6f2SSascha Wildner 		} else {
347609b9c6f2SSascha Wildner 			continue;
347709b9c6f2SSascha Wildner 		}
347809b9c6f2SSascha Wildner 
3479a963377aSSascha Wildner 		mixernumber = uaudio_mixer_feature_name(&iot[id], &MIX(sc));
348009b9c6f2SSascha Wildner 
348109b9c6f2SSascha Wildner 		switch (ctl) {
348209b9c6f2SSascha Wildner 		case MUTE_CONTROL:
3483a963377aSSascha Wildner 			MIX(sc).type = MIX_ON_OFF;
3484a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3485a963377aSSascha Wildner 			MIX(sc).name = "mute";
348609b9c6f2SSascha Wildner 			break;
348709b9c6f2SSascha Wildner 
348809b9c6f2SSascha Wildner 		case VOLUME_CONTROL:
3489a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_16;
3490a963377aSSascha Wildner 			MIX(sc).ctl = mixernumber;
3491a963377aSSascha Wildner 			MIX(sc).name = "vol";
349209b9c6f2SSascha Wildner 			break;
349309b9c6f2SSascha Wildner 
349409b9c6f2SSascha Wildner 		case BASS_CONTROL:
3495a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_8;
3496a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_BASS;
3497a963377aSSascha Wildner 			MIX(sc).name = "bass";
349809b9c6f2SSascha Wildner 			break;
349909b9c6f2SSascha Wildner 
350009b9c6f2SSascha Wildner 		case MID_CONTROL:
3501a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_8;
3502a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3503a963377aSSascha Wildner 			MIX(sc).name = "mid";
350409b9c6f2SSascha Wildner 			break;
350509b9c6f2SSascha Wildner 
350609b9c6f2SSascha Wildner 		case TREBLE_CONTROL:
3507a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_8;
3508a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_TREBLE;
3509a963377aSSascha Wildner 			MIX(sc).name = "treble";
351009b9c6f2SSascha Wildner 			break;
351109b9c6f2SSascha Wildner 
351209b9c6f2SSascha Wildner 		case GRAPHIC_EQUALIZER_CONTROL:
351309b9c6f2SSascha Wildner 			continue;	/* XXX don't add anything */
351409b9c6f2SSascha Wildner 
351509b9c6f2SSascha Wildner 		case AGC_CONTROL:
3516a963377aSSascha Wildner 			MIX(sc).type = MIX_ON_OFF;
3517a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3518a963377aSSascha Wildner 			MIX(sc).name = "agc";
351909b9c6f2SSascha Wildner 			break;
352009b9c6f2SSascha Wildner 
352109b9c6f2SSascha Wildner 		case DELAY_CONTROL:
3522a963377aSSascha Wildner 			MIX(sc).type = MIX_UNSIGNED_16;
3523a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3524a963377aSSascha Wildner 			MIX(sc).name = "delay";
352509b9c6f2SSascha Wildner 			break;
352609b9c6f2SSascha Wildner 
352709b9c6f2SSascha Wildner 		case BASS_BOOST_CONTROL:
3528a963377aSSascha Wildner 			MIX(sc).type = MIX_ON_OFF;
3529a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3530a963377aSSascha Wildner 			MIX(sc).name = "boost";
353109b9c6f2SSascha Wildner 			break;
353209b9c6f2SSascha Wildner 
353309b9c6f2SSascha Wildner 		case LOUDNESS_CONTROL:
3534a963377aSSascha Wildner 			MIX(sc).type = MIX_ON_OFF;
3535a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_LOUD;	/* Is this correct ? */
3536a963377aSSascha Wildner 			MIX(sc).name = "loudness";
353709b9c6f2SSascha Wildner 			break;
353809b9c6f2SSascha Wildner 
353909b9c6f2SSascha Wildner 		default:
3540a963377aSSascha Wildner 			MIX(sc).type = MIX_UNKNOWN;
354109b9c6f2SSascha Wildner 			break;
354209b9c6f2SSascha Wildner 		}
354309b9c6f2SSascha Wildner 
3544a963377aSSascha Wildner 		if (MIX(sc).type != MIX_UNKNOWN)
3545a963377aSSascha Wildner 			uaudio_mixer_add_ctl(sc, &MIX(sc));
354609b9c6f2SSascha Wildner 	}
354709b9c6f2SSascha Wildner }
3548a963377aSSascha Wildner 
3549a963377aSSascha Wildner static void
uaudio20_mixer_add_feature(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)3550a963377aSSascha Wildner uaudio20_mixer_add_feature(struct uaudio_softc *sc,
3551a963377aSSascha Wildner     const struct uaudio_terminal_node *iot, int id)
3552a963377aSSascha Wildner {
3553a963377aSSascha Wildner 	const struct usb_audio20_feature_unit *d = iot[id].u.fu_v2;
3554a963377aSSascha Wildner 	uint32_t ctl;
3555a963377aSSascha Wildner 	uint32_t mmask;
3556a963377aSSascha Wildner 	uint32_t cmask;
3557a963377aSSascha Wildner 	uint16_t mixernumber;
3558a963377aSSascha Wildner 	uint8_t nchan;
3559a963377aSSascha Wildner 	uint8_t chan;
3560a963377aSSascha Wildner 	uint8_t i;
3561a963377aSSascha Wildner 	uint8_t what;
3562a963377aSSascha Wildner 
3563a963377aSSascha Wildner 	if (UGETDW(d->bmaControls[0]) == 0)
3564a963377aSSascha Wildner 		return;
3565a963377aSSascha Wildner 
3566a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3567a963377aSSascha Wildner 
3568a963377aSSascha Wildner 	nchan = (d->bLength - 6) / 4;
3569a963377aSSascha Wildner 	mmask = UGETDW(d->bmaControls[0]);
3570a963377aSSascha Wildner 	cmask = 0;
3571a963377aSSascha Wildner 
3572a963377aSSascha Wildner 	if (nchan == 0)
3573a963377aSSascha Wildner 		return;
3574a963377aSSascha Wildner 
3575a963377aSSascha Wildner 	/* figure out what we can control */
3576a963377aSSascha Wildner 
3577a963377aSSascha Wildner 	for (chan = 1; chan < nchan; chan++)
3578a963377aSSascha Wildner 		cmask |= UGETDW(d->bmaControls[chan]);
3579a963377aSSascha Wildner 
3580a963377aSSascha Wildner 	if (nchan > MIX_MAX_CHAN)
3581a963377aSSascha Wildner 		nchan = MIX_MAX_CHAN;
3582a963377aSSascha Wildner 
3583a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
3584a963377aSSascha Wildner 
3585a963377aSSascha Wildner 	i = d->bmaControls[nchan][0];
3586a963377aSSascha Wildner 	if (i == 0 ||
3587a963377aSSascha Wildner 	    usbd_req_get_string_any(sc->sc_udev, NULL,
3588a963377aSSascha Wildner 	    MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
3589a963377aSSascha Wildner 		MIX(sc).desc[0] = 0;
3590a963377aSSascha Wildner 	}
3591a963377aSSascha Wildner 
3592a963377aSSascha Wildner 	for (ctl = 3; ctl != 0; ctl <<= 2) {
3593a963377aSSascha Wildner 
3594a963377aSSascha Wildner 		mixernumber = uaudio20_mixer_feature_name(&iot[id], &MIX(sc));
3595a963377aSSascha Wildner 
3596a963377aSSascha Wildner 		switch (ctl) {
3597a963377aSSascha Wildner 		case (3 << 0):
3598a963377aSSascha Wildner 			MIX(sc).type = MIX_ON_OFF;
3599a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3600a963377aSSascha Wildner 			MIX(sc).name = "mute";
3601a963377aSSascha Wildner 			what = MUTE_CONTROL;
3602a963377aSSascha Wildner 			break;
3603a963377aSSascha Wildner 		case (3 << 2):
3604a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_16;
3605a963377aSSascha Wildner 			MIX(sc).ctl = mixernumber;
3606a963377aSSascha Wildner 			MIX(sc).name = "vol";
3607a963377aSSascha Wildner 			what = VOLUME_CONTROL;
3608a963377aSSascha Wildner 			break;
3609a963377aSSascha Wildner 		case (3 << 4):
3610a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_8;
3611a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_BASS;
3612a963377aSSascha Wildner 			MIX(sc).name = "bass";
3613a963377aSSascha Wildner 			what = BASS_CONTROL;
3614a963377aSSascha Wildner 			break;
3615a963377aSSascha Wildner 		case (3 << 6):
3616a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_8;
3617a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3618a963377aSSascha Wildner 			MIX(sc).name = "mid";
3619a963377aSSascha Wildner 			what = MID_CONTROL;
3620a963377aSSascha Wildner 			break;
3621a963377aSSascha Wildner 		case (3 << 8):
3622a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_8;
3623a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_TREBLE;
3624a963377aSSascha Wildner 			MIX(sc).name = "treble";
3625a963377aSSascha Wildner 			what = TREBLE_CONTROL;
3626a963377aSSascha Wildner 			break;
3627a963377aSSascha Wildner 		case (3 << 12):
3628a963377aSSascha Wildner 			MIX(sc).type = MIX_ON_OFF;
3629a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3630a963377aSSascha Wildner 			MIX(sc).name = "agc";
3631a963377aSSascha Wildner 			what = AGC_CONTROL;
3632a963377aSSascha Wildner 			break;
3633a963377aSSascha Wildner 		case (3 << 14):
3634a963377aSSascha Wildner 			MIX(sc).type = MIX_UNSIGNED_16;
3635a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3636a963377aSSascha Wildner 			MIX(sc).name = "delay";
3637a963377aSSascha Wildner 			what = DELAY_CONTROL;
3638a963377aSSascha Wildner 			break;
3639a963377aSSascha Wildner 		case (3 << 16):
3640a963377aSSascha Wildner 			MIX(sc).type = MIX_ON_OFF;
3641a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3642a963377aSSascha Wildner 			MIX(sc).name = "boost";
3643a963377aSSascha Wildner 			what = BASS_BOOST_CONTROL;
3644a963377aSSascha Wildner 			break;
3645a963377aSSascha Wildner 		case (3 << 18):
3646a963377aSSascha Wildner 			MIX(sc).type = MIX_ON_OFF;
3647a963377aSSascha Wildner 			MIX(sc).ctl = SOUND_MIXER_LOUD;	/* Is this correct ? */
3648a963377aSSascha Wildner 			MIX(sc).name = "loudness";
3649a963377aSSascha Wildner 			what = LOUDNESS_CONTROL;
3650a963377aSSascha Wildner 			break;
3651a963377aSSascha Wildner 		case (3 << 20):
3652a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_16;
3653a963377aSSascha Wildner 			MIX(sc).ctl = mixernumber;
3654a963377aSSascha Wildner 			MIX(sc).name = "igain";
3655a963377aSSascha Wildner 			what = INPUT_GAIN_CONTROL;
3656a963377aSSascha Wildner 			break;
3657a963377aSSascha Wildner 		case (3 << 22):
3658a963377aSSascha Wildner 			MIX(sc).type = MIX_SIGNED_16;
3659a963377aSSascha Wildner 			MIX(sc).ctl = mixernumber;
3660a963377aSSascha Wildner 			MIX(sc).name = "igainpad";
3661a963377aSSascha Wildner 			what = INPUT_GAIN_PAD_CONTROL;
3662a963377aSSascha Wildner 			break;
3663a963377aSSascha Wildner 		default:
3664a963377aSSascha Wildner 			continue;
3665a963377aSSascha Wildner 		}
3666a963377aSSascha Wildner 
3667a963377aSSascha Wildner 		if ((mmask & ctl) == ctl) {
3668a963377aSSascha Wildner 			MIX(sc).nchan = 1;
3669a963377aSSascha Wildner 			MIX(sc).wValue[0] = MAKE_WORD(what, 0);
3670a963377aSSascha Wildner 		} else if ((cmask & ctl) == ctl) {
3671a963377aSSascha Wildner 			MIX(sc).nchan = nchan - 1;
3672a963377aSSascha Wildner 			for (i = 1; i < nchan; i++) {
3673a963377aSSascha Wildner 				if ((UGETDW(d->bmaControls[i]) & ctl) == ctl)
3674a963377aSSascha Wildner 					MIX(sc).wValue[i - 1] = MAKE_WORD(what, i);
3675a963377aSSascha Wildner 				else
3676a963377aSSascha Wildner 					MIX(sc).wValue[i - 1] = -1;
3677a963377aSSascha Wildner 			}
3678a963377aSSascha Wildner 		} else {
3679a963377aSSascha Wildner 			continue;
3680a963377aSSascha Wildner 		}
3681a963377aSSascha Wildner 
3682a963377aSSascha Wildner 		if (MIX(sc).type != MIX_UNKNOWN)
3683a963377aSSascha Wildner 			uaudio_mixer_add_ctl(sc, &MIX(sc));
3684a963377aSSascha Wildner 	}
368509b9c6f2SSascha Wildner }
368609b9c6f2SSascha Wildner 
368709b9c6f2SSascha Wildner static void
uaudio_mixer_add_processing_updown(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)368809b9c6f2SSascha Wildner uaudio_mixer_add_processing_updown(struct uaudio_softc *sc,
368909b9c6f2SSascha Wildner     const struct uaudio_terminal_node *iot, int id)
369009b9c6f2SSascha Wildner {
3691a963377aSSascha Wildner 	const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1;
369209b9c6f2SSascha Wildner 	const struct usb_audio_processing_unit_1 *d1 =
369309b9c6f2SSascha Wildner 	    (const void *)(d0->baSourceId + d0->bNrInPins);
369409b9c6f2SSascha Wildner 	const struct usb_audio_processing_unit_updown *ud =
369509b9c6f2SSascha Wildner 	    (const void *)(d1->bmControls + d1->bControlSize);
369609b9c6f2SSascha Wildner 	uint8_t i;
369709b9c6f2SSascha Wildner 
369809b9c6f2SSascha Wildner 	if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) {
369909b9c6f2SSascha Wildner 		return;
370009b9c6f2SSascha Wildner 	}
370109b9c6f2SSascha Wildner 	if (uaudio_mixer_verify_desc(d0, sizeof(*ud) + (2 * ud->bNrModes))
370209b9c6f2SSascha Wildner 	    == NULL) {
370309b9c6f2SSascha Wildner 		return;
370409b9c6f2SSascha Wildner 	}
370509b9c6f2SSascha Wildner 	DPRINTFN(3, "bUnitId=%d bNrModes=%d\n",
370609b9c6f2SSascha Wildner 	    d0->bUnitId, ud->bNrModes);
370709b9c6f2SSascha Wildner 
370809b9c6f2SSascha Wildner 	if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) {
370909b9c6f2SSascha Wildner 		DPRINTF("no mode select\n");
371009b9c6f2SSascha Wildner 		return;
371109b9c6f2SSascha Wildner 	}
3712a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
371309b9c6f2SSascha Wildner 
3714a963377aSSascha Wildner 	MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3715a963377aSSascha Wildner 	MIX(sc).nchan = 1;
3716a963377aSSascha Wildner 	MIX(sc).wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0);
3717a963377aSSascha Wildner 	uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3718a963377aSSascha Wildner 	MIX(sc).type = MIX_ON_OFF;		/* XXX */
371909b9c6f2SSascha Wildner 
372009b9c6f2SSascha Wildner 	for (i = 0; i < ud->bNrModes; i++) {
372109b9c6f2SSascha Wildner 		DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i]));
372209b9c6f2SSascha Wildner 		/* XXX */
372309b9c6f2SSascha Wildner 	}
372409b9c6f2SSascha Wildner 
3725a963377aSSascha Wildner 	uaudio_mixer_add_ctl(sc, &MIX(sc));
372609b9c6f2SSascha Wildner }
372709b9c6f2SSascha Wildner 
372809b9c6f2SSascha Wildner static void
uaudio_mixer_add_processing(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)372909b9c6f2SSascha Wildner uaudio_mixer_add_processing(struct uaudio_softc *sc,
373009b9c6f2SSascha Wildner     const struct uaudio_terminal_node *iot, int id)
373109b9c6f2SSascha Wildner {
3732a963377aSSascha Wildner 	const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1;
373309b9c6f2SSascha Wildner 	const struct usb_audio_processing_unit_1 *d1 =
373409b9c6f2SSascha Wildner 	    (const void *)(d0->baSourceId + d0->bNrInPins);
373509b9c6f2SSascha Wildner 	uint16_t ptype;
373609b9c6f2SSascha Wildner 
3737a963377aSSascha Wildner 	memset(&MIX(sc), 0, sizeof(MIX(sc)));
373809b9c6f2SSascha Wildner 
373909b9c6f2SSascha Wildner 	ptype = UGETW(d0->wProcessType);
374009b9c6f2SSascha Wildner 
374109b9c6f2SSascha Wildner 	DPRINTFN(3, "wProcessType=%d bUnitId=%d "
374209b9c6f2SSascha Wildner 	    "bNrInPins=%d\n", ptype, d0->bUnitId, d0->bNrInPins);
374309b9c6f2SSascha Wildner 
374409b9c6f2SSascha Wildner 	if (d1->bControlSize == 0) {
374509b9c6f2SSascha Wildner 		return;
374609b9c6f2SSascha Wildner 	}
374709b9c6f2SSascha Wildner 	if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) {
3748a963377aSSascha Wildner 		MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3749a963377aSSascha Wildner 		MIX(sc).nchan = 1;
3750a963377aSSascha Wildner 		MIX(sc).wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0);
3751a963377aSSascha Wildner 		uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3752a963377aSSascha Wildner 		MIX(sc).type = MIX_ON_OFF;
3753a963377aSSascha Wildner 		uaudio_mixer_add_ctl(sc, &MIX(sc));
375409b9c6f2SSascha Wildner 	}
375509b9c6f2SSascha Wildner 	switch (ptype) {
375609b9c6f2SSascha Wildner 	case UPDOWNMIX_PROCESS:
375709b9c6f2SSascha Wildner 		uaudio_mixer_add_processing_updown(sc, iot, id);
375809b9c6f2SSascha Wildner 		break;
375909b9c6f2SSascha Wildner 
376009b9c6f2SSascha Wildner 	case DOLBY_PROLOGIC_PROCESS:
376109b9c6f2SSascha Wildner 	case P3D_STEREO_EXTENDER_PROCESS:
376209b9c6f2SSascha Wildner 	case REVERBATION_PROCESS:
376309b9c6f2SSascha Wildner 	case CHORUS_PROCESS:
376409b9c6f2SSascha Wildner 	case DYN_RANGE_COMP_PROCESS:
376509b9c6f2SSascha Wildner 	default:
376609b9c6f2SSascha Wildner 		DPRINTF("unit %d, type=%d is not implemented\n",
376709b9c6f2SSascha Wildner 		    d0->bUnitId, ptype);
376809b9c6f2SSascha Wildner 		break;
376909b9c6f2SSascha Wildner 	}
377009b9c6f2SSascha Wildner }
377109b9c6f2SSascha Wildner 
377209b9c6f2SSascha Wildner static void
uaudio_mixer_add_extension(struct uaudio_softc * sc,const struct uaudio_terminal_node * iot,int id)377309b9c6f2SSascha Wildner uaudio_mixer_add_extension(struct uaudio_softc *sc,
377409b9c6f2SSascha Wildner     const struct uaudio_terminal_node *iot, int id)
377509b9c6f2SSascha Wildner {
3776a963377aSSascha Wildner 	const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu_v1;
377709b9c6f2SSascha Wildner 	const struct usb_audio_extension_unit_1 *d1 =
377809b9c6f2SSascha Wildner 	    (const void *)(d0->baSourceId + d0->bNrInPins);
377909b9c6f2SSascha Wildner 
378009b9c6f2SSascha Wildner 	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
378109b9c6f2SSascha Wildner 	    d0->bUnitId, d0->bNrInPins);
378209b9c6f2SSascha Wildner 
378309b9c6f2SSascha Wildner 	if (sc->sc_uq_au_no_xu) {
378409b9c6f2SSascha Wildner 		return;
378509b9c6f2SSascha Wildner 	}
378609b9c6f2SSascha Wildner 	if (d1->bControlSize == 0) {
378709b9c6f2SSascha Wildner 		return;
378809b9c6f2SSascha Wildner 	}
378909b9c6f2SSascha Wildner 	if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) {
379009b9c6f2SSascha Wildner 
3791a963377aSSascha Wildner 		memset(&MIX(sc), 0, sizeof(MIX(sc)));
379209b9c6f2SSascha Wildner 
3793a963377aSSascha Wildner 		MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3794a963377aSSascha Wildner 		MIX(sc).nchan = 1;
3795a963377aSSascha Wildner 		MIX(sc).wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0);
3796a963377aSSascha Wildner 		uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3797a963377aSSascha Wildner 		MIX(sc).type = MIX_ON_OFF;
379809b9c6f2SSascha Wildner 
3799a963377aSSascha Wildner 		uaudio_mixer_add_ctl(sc, &MIX(sc));
380009b9c6f2SSascha Wildner 	}
380109b9c6f2SSascha Wildner }
380209b9c6f2SSascha Wildner 
380309b9c6f2SSascha Wildner static const void *
uaudio_mixer_verify_desc(const void * arg,uint32_t len)380409b9c6f2SSascha Wildner uaudio_mixer_verify_desc(const void *arg, uint32_t len)
380509b9c6f2SSascha Wildner {
380609b9c6f2SSascha Wildner 	const struct usb_audio_mixer_unit_1 *d1;
380709b9c6f2SSascha Wildner 	const struct usb_audio_extension_unit_1 *e1;
380809b9c6f2SSascha Wildner 	const struct usb_audio_processing_unit_1 *u1;
380909b9c6f2SSascha Wildner 
381009b9c6f2SSascha Wildner 	union {
381109b9c6f2SSascha Wildner 		const struct usb_descriptor *desc;
381209b9c6f2SSascha Wildner 		const struct usb_audio_input_terminal *it;
381309b9c6f2SSascha Wildner 		const struct usb_audio_output_terminal *ot;
381409b9c6f2SSascha Wildner 		const struct usb_audio_mixer_unit_0 *mu;
381509b9c6f2SSascha Wildner 		const struct usb_audio_selector_unit *su;
381609b9c6f2SSascha Wildner 		const struct usb_audio_feature_unit *fu;
381709b9c6f2SSascha Wildner 		const struct usb_audio_processing_unit_0 *pu;
381809b9c6f2SSascha Wildner 		const struct usb_audio_extension_unit_0 *eu;
381909b9c6f2SSascha Wildner 	}     u;
382009b9c6f2SSascha Wildner 
382109b9c6f2SSascha Wildner 	u.desc = arg;
382209b9c6f2SSascha Wildner 
382309b9c6f2SSascha Wildner 	if (u.desc == NULL) {
382409b9c6f2SSascha Wildner 		goto error;
382509b9c6f2SSascha Wildner 	}
382609b9c6f2SSascha Wildner 	if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) {
382709b9c6f2SSascha Wildner 		goto error;
382809b9c6f2SSascha Wildner 	}
382909b9c6f2SSascha Wildner 	switch (u.desc->bDescriptorSubtype) {
383009b9c6f2SSascha Wildner 	case UDESCSUB_AC_INPUT:
383109b9c6f2SSascha Wildner 		len += sizeof(*u.it);
383209b9c6f2SSascha Wildner 		break;
383309b9c6f2SSascha Wildner 
383409b9c6f2SSascha Wildner 	case UDESCSUB_AC_OUTPUT:
383509b9c6f2SSascha Wildner 		len += sizeof(*u.ot);
383609b9c6f2SSascha Wildner 		break;
383709b9c6f2SSascha Wildner 
383809b9c6f2SSascha Wildner 	case UDESCSUB_AC_MIXER:
383909b9c6f2SSascha Wildner 		len += sizeof(*u.mu);
384009b9c6f2SSascha Wildner 
384109b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
384209b9c6f2SSascha Wildner 			goto error;
384309b9c6f2SSascha Wildner 		}
384409b9c6f2SSascha Wildner 		len += u.mu->bNrInPins;
384509b9c6f2SSascha Wildner 
384609b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
384709b9c6f2SSascha Wildner 			goto error;
384809b9c6f2SSascha Wildner 		}
384909b9c6f2SSascha Wildner 		d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins);
385009b9c6f2SSascha Wildner 
385109b9c6f2SSascha Wildner 		len += sizeof(*d1);
385209b9c6f2SSascha Wildner 		break;
385309b9c6f2SSascha Wildner 
385409b9c6f2SSascha Wildner 	case UDESCSUB_AC_SELECTOR:
385509b9c6f2SSascha Wildner 		len += sizeof(*u.su);
385609b9c6f2SSascha Wildner 
385709b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
385809b9c6f2SSascha Wildner 			goto error;
385909b9c6f2SSascha Wildner 		}
3860a963377aSSascha Wildner 		len += u.su->bNrInPins + 1;
386109b9c6f2SSascha Wildner 		break;
386209b9c6f2SSascha Wildner 
386309b9c6f2SSascha Wildner 	case UDESCSUB_AC_FEATURE:
3864a963377aSSascha Wildner 		len += sizeof(*u.fu) + 1;
3865a963377aSSascha Wildner 
3866a963377aSSascha Wildner 		if (u.desc->bLength < len)
3867a963377aSSascha Wildner 			goto error;
3868a963377aSSascha Wildner 
3869a963377aSSascha Wildner 		len += u.fu->bControlSize;
387009b9c6f2SSascha Wildner 		break;
387109b9c6f2SSascha Wildner 
387209b9c6f2SSascha Wildner 	case UDESCSUB_AC_PROCESSING:
387309b9c6f2SSascha Wildner 		len += sizeof(*u.pu);
387409b9c6f2SSascha Wildner 
387509b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
387609b9c6f2SSascha Wildner 			goto error;
387709b9c6f2SSascha Wildner 		}
387809b9c6f2SSascha Wildner 		len += u.pu->bNrInPins;
387909b9c6f2SSascha Wildner 
388009b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
388109b9c6f2SSascha Wildner 			goto error;
388209b9c6f2SSascha Wildner 		}
388309b9c6f2SSascha Wildner 		u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins);
388409b9c6f2SSascha Wildner 
388509b9c6f2SSascha Wildner 		len += sizeof(*u1);
388609b9c6f2SSascha Wildner 
388709b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
388809b9c6f2SSascha Wildner 			goto error;
388909b9c6f2SSascha Wildner 		}
389009b9c6f2SSascha Wildner 		len += u1->bControlSize;
389109b9c6f2SSascha Wildner 
389209b9c6f2SSascha Wildner 		break;
389309b9c6f2SSascha Wildner 
389409b9c6f2SSascha Wildner 	case UDESCSUB_AC_EXTENSION:
389509b9c6f2SSascha Wildner 		len += sizeof(*u.eu);
389609b9c6f2SSascha Wildner 
389709b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
389809b9c6f2SSascha Wildner 			goto error;
389909b9c6f2SSascha Wildner 		}
390009b9c6f2SSascha Wildner 		len += u.eu->bNrInPins;
390109b9c6f2SSascha Wildner 
390209b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
390309b9c6f2SSascha Wildner 			goto error;
390409b9c6f2SSascha Wildner 		}
390509b9c6f2SSascha Wildner 		e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
390609b9c6f2SSascha Wildner 
390709b9c6f2SSascha Wildner 		len += sizeof(*e1);
390809b9c6f2SSascha Wildner 
390909b9c6f2SSascha Wildner 		if (u.desc->bLength < len) {
391009b9c6f2SSascha Wildner 			goto error;
391109b9c6f2SSascha Wildner 		}
391209b9c6f2SSascha Wildner 		len += e1->bControlSize;
391309b9c6f2SSascha Wildner 		break;
391409b9c6f2SSascha Wildner 
391509b9c6f2SSascha Wildner 	default:
391609b9c6f2SSascha Wildner 		goto error;
391709b9c6f2SSascha Wildner 	}
391809b9c6f2SSascha Wildner 
391909b9c6f2SSascha Wildner 	if (u.desc->bLength < len) {
392009b9c6f2SSascha Wildner 		goto error;
392109b9c6f2SSascha Wildner 	}
392209b9c6f2SSascha Wildner 	return (u.desc);
392309b9c6f2SSascha Wildner 
392409b9c6f2SSascha Wildner error:
392509b9c6f2SSascha Wildner 	if (u.desc) {
392609b9c6f2SSascha Wildner 		DPRINTF("invalid descriptor, type=%d, "
392709b9c6f2SSascha Wildner 		    "sub_type=%d, len=%d of %d bytes\n",
392809b9c6f2SSascha Wildner 		    u.desc->bDescriptorType,
392909b9c6f2SSascha Wildner 		    u.desc->bDescriptorSubtype,
393009b9c6f2SSascha Wildner 		    u.desc->bLength, len);
393109b9c6f2SSascha Wildner 	}
393209b9c6f2SSascha Wildner 	return (NULL);
393309b9c6f2SSascha Wildner }
393409b9c6f2SSascha Wildner 
3935a963377aSSascha Wildner static const void *
uaudio20_mixer_verify_desc(const void * arg,uint32_t len)3936a963377aSSascha Wildner uaudio20_mixer_verify_desc(const void *arg, uint32_t len)
393709b9c6f2SSascha Wildner {
3938a963377aSSascha Wildner 	const struct usb_audio20_mixer_unit_1 *d1;
3939a963377aSSascha Wildner 	const struct usb_audio20_extension_unit_1 *e1;
3940a963377aSSascha Wildner 	const struct usb_audio20_processing_unit_1 *u1;
3941a963377aSSascha Wildner 	const struct usb_audio20_clock_selector_unit_1 *c1;
394209b9c6f2SSascha Wildner 
3943a963377aSSascha Wildner 	union {
3944a963377aSSascha Wildner 		const struct usb_descriptor *desc;
3945a963377aSSascha Wildner 		const struct usb_audio20_clock_source_unit *csrc;
3946a963377aSSascha Wildner 		const struct usb_audio20_clock_selector_unit_0 *csel;
3947a963377aSSascha Wildner 		const struct usb_audio20_clock_multiplier_unit *cmul;
3948a963377aSSascha Wildner 		const struct usb_audio20_input_terminal *it;
3949a963377aSSascha Wildner 		const struct usb_audio20_output_terminal *ot;
3950a963377aSSascha Wildner 		const struct usb_audio20_mixer_unit_0 *mu;
3951a963377aSSascha Wildner 		const struct usb_audio20_selector_unit *su;
3952a963377aSSascha Wildner 		const struct usb_audio20_feature_unit *fu;
3953a963377aSSascha Wildner 		const struct usb_audio20_sample_rate_unit *ru;
3954a963377aSSascha Wildner 		const struct usb_audio20_processing_unit_0 *pu;
3955a963377aSSascha Wildner 		const struct usb_audio20_extension_unit_0 *eu;
3956a963377aSSascha Wildner 		const struct usb_audio20_effect_unit *ef;
3957a963377aSSascha Wildner 	}     u;
395809b9c6f2SSascha Wildner 
3959a963377aSSascha Wildner 	u.desc = arg;
396009b9c6f2SSascha Wildner 
3961a963377aSSascha Wildner 	if (u.desc == NULL)
3962a963377aSSascha Wildner 		goto error;
3963a963377aSSascha Wildner 
3964a963377aSSascha Wildner 	if (u.desc->bDescriptorType != UDESC_CS_INTERFACE)
3965a963377aSSascha Wildner 		goto error;
3966a963377aSSascha Wildner 
3967a963377aSSascha Wildner 	switch (u.desc->bDescriptorSubtype) {
3968a963377aSSascha Wildner 	case UDESCSUB_AC_INPUT:
3969a963377aSSascha Wildner 		len += sizeof(*u.it);
3970a963377aSSascha Wildner 		break;
3971a963377aSSascha Wildner 
3972a963377aSSascha Wildner 	case UDESCSUB_AC_OUTPUT:
3973a963377aSSascha Wildner 		len += sizeof(*u.ot);
3974a963377aSSascha Wildner 		break;
3975a963377aSSascha Wildner 
3976a963377aSSascha Wildner 	case UDESCSUB_AC_MIXER:
3977a963377aSSascha Wildner 		len += sizeof(*u.mu);
3978a963377aSSascha Wildner 
3979a963377aSSascha Wildner 		if (u.desc->bLength < len)
3980a963377aSSascha Wildner 			goto error;
3981a963377aSSascha Wildner 		len += u.mu->bNrInPins;
3982a963377aSSascha Wildner 
3983a963377aSSascha Wildner 		if (u.desc->bLength < len)
3984a963377aSSascha Wildner 			goto error;
3985a963377aSSascha Wildner 
3986a963377aSSascha Wildner 		d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins);
3987a963377aSSascha Wildner 
3988a963377aSSascha Wildner 		len += sizeof(*d1) + d1->bNrChannels;
3989a963377aSSascha Wildner 		break;
3990a963377aSSascha Wildner 
3991a963377aSSascha Wildner 	case UDESCSUB_AC_SELECTOR:
3992a963377aSSascha Wildner 		len += sizeof(*u.su);
3993a963377aSSascha Wildner 
3994a963377aSSascha Wildner 		if (u.desc->bLength < len)
3995a963377aSSascha Wildner 			goto error;
3996a963377aSSascha Wildner 
3997a963377aSSascha Wildner 		len += u.su->bNrInPins + 1;
3998a963377aSSascha Wildner 		break;
3999a963377aSSascha Wildner 
4000a963377aSSascha Wildner 	case UDESCSUB_AC_FEATURE:
4001a963377aSSascha Wildner 		len += sizeof(*u.fu) + 1;
4002a963377aSSascha Wildner 		break;
4003a963377aSSascha Wildner 
4004a963377aSSascha Wildner 	case UDESCSUB_AC_EFFECT:
4005a963377aSSascha Wildner 		len += sizeof(*u.ef) + 4;
4006a963377aSSascha Wildner 		break;
4007a963377aSSascha Wildner 
4008a963377aSSascha Wildner 	case UDESCSUB_AC_PROCESSING_V2:
4009a963377aSSascha Wildner 		len += sizeof(*u.pu);
4010a963377aSSascha Wildner 
4011a963377aSSascha Wildner 		if (u.desc->bLength < len)
4012a963377aSSascha Wildner 			goto error;
4013a963377aSSascha Wildner 
4014a963377aSSascha Wildner 		len += u.pu->bNrInPins;
4015a963377aSSascha Wildner 
4016a963377aSSascha Wildner 		if (u.desc->bLength < len)
4017a963377aSSascha Wildner 			goto error;
4018a963377aSSascha Wildner 
4019a963377aSSascha Wildner 		u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins);
4020a963377aSSascha Wildner 
4021a963377aSSascha Wildner 		len += sizeof(*u1);
4022a963377aSSascha Wildner 		break;
4023a963377aSSascha Wildner 
4024a963377aSSascha Wildner 	case UDESCSUB_AC_EXTENSION_V2:
4025a963377aSSascha Wildner 		len += sizeof(*u.eu);
4026a963377aSSascha Wildner 
4027a963377aSSascha Wildner 		if (u.desc->bLength < len)
4028a963377aSSascha Wildner 			goto error;
4029a963377aSSascha Wildner 
4030a963377aSSascha Wildner 		len += u.eu->bNrInPins;
4031a963377aSSascha Wildner 
4032a963377aSSascha Wildner 		if (u.desc->bLength < len)
4033a963377aSSascha Wildner 			goto error;
4034a963377aSSascha Wildner 
4035a963377aSSascha Wildner 		e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
4036a963377aSSascha Wildner 
4037a963377aSSascha Wildner 		len += sizeof(*e1);
4038a963377aSSascha Wildner 		break;
4039a963377aSSascha Wildner 
4040a963377aSSascha Wildner 	case UDESCSUB_AC_CLOCK_SRC:
4041a963377aSSascha Wildner 		len += sizeof(*u.csrc);
4042a963377aSSascha Wildner 		break;
4043a963377aSSascha Wildner 
4044a963377aSSascha Wildner 	case UDESCSUB_AC_CLOCK_SEL:
4045a963377aSSascha Wildner 		len += sizeof(*u.csel);
4046a963377aSSascha Wildner 
4047a963377aSSascha Wildner 		if (u.desc->bLength < len)
4048a963377aSSascha Wildner 			goto error;
4049a963377aSSascha Wildner 
4050a963377aSSascha Wildner 		len += u.csel->bNrInPins;
4051a963377aSSascha Wildner 
4052a963377aSSascha Wildner 		if (u.desc->bLength < len)
4053a963377aSSascha Wildner 			goto error;
4054a963377aSSascha Wildner 
4055a963377aSSascha Wildner 		c1 = (const void *)(u.csel->baCSourceId + u.csel->bNrInPins);
4056a963377aSSascha Wildner 
4057a963377aSSascha Wildner 		len += sizeof(*c1);
4058a963377aSSascha Wildner 		break;
4059a963377aSSascha Wildner 
4060a963377aSSascha Wildner 	case UDESCSUB_AC_CLOCK_MUL:
4061a963377aSSascha Wildner 		len += sizeof(*u.cmul);
4062a963377aSSascha Wildner 		break;
4063a963377aSSascha Wildner 
4064a963377aSSascha Wildner 	case UDESCSUB_AC_SAMPLE_RT:
4065a963377aSSascha Wildner 		len += sizeof(*u.ru);
4066a963377aSSascha Wildner 		break;
4067a963377aSSascha Wildner 
4068a963377aSSascha Wildner 	default:
4069a963377aSSascha Wildner 		goto error;
407009b9c6f2SSascha Wildner 	}
407109b9c6f2SSascha Wildner 
4072a963377aSSascha Wildner 	if (u.desc->bLength < len)
4073a963377aSSascha Wildner 		goto error;
4074a963377aSSascha Wildner 
4075a963377aSSascha Wildner 	return (u.desc);
4076a963377aSSascha Wildner 
4077a963377aSSascha Wildner error:
4078a963377aSSascha Wildner 	if (u.desc) {
4079a963377aSSascha Wildner 		DPRINTF("invalid descriptor, type=%d, "
4080a963377aSSascha Wildner 		    "sub_type=%d, len=%d of %d bytes\n",
4081a963377aSSascha Wildner 		    u.desc->bDescriptorType,
4082a963377aSSascha Wildner 		    u.desc->bDescriptorSubtype,
4083a963377aSSascha Wildner 		    u.desc->bLength, len);
4084a963377aSSascha Wildner 	}
4085a963377aSSascha Wildner 	return (NULL);
4086a963377aSSascha Wildner }
408709b9c6f2SSascha Wildner 
408809b9c6f2SSascha Wildner static struct usb_audio_cluster
uaudio_mixer_get_cluster(uint8_t id,const struct uaudio_terminal_node * iot)408909b9c6f2SSascha Wildner uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
409009b9c6f2SSascha Wildner {
409109b9c6f2SSascha Wildner 	struct usb_audio_cluster r;
409209b9c6f2SSascha Wildner 	const struct usb_descriptor *dp;
409309b9c6f2SSascha Wildner 	uint8_t i;
409409b9c6f2SSascha Wildner 
409509b9c6f2SSascha Wildner 	for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) {	/* avoid infinite loops */
409609b9c6f2SSascha Wildner 		dp = iot[id].u.desc;
409709b9c6f2SSascha Wildner 		if (dp == NULL) {
409809b9c6f2SSascha Wildner 			goto error;
409909b9c6f2SSascha Wildner 		}
410009b9c6f2SSascha Wildner 		switch (dp->bDescriptorSubtype) {
410109b9c6f2SSascha Wildner 		case UDESCSUB_AC_INPUT:
4102a963377aSSascha Wildner 			r.bNrChannels = iot[id].u.it_v1->bNrChannels;
4103a963377aSSascha Wildner 			r.wChannelConfig[0] = iot[id].u.it_v1->wChannelConfig[0];
4104a963377aSSascha Wildner 			r.wChannelConfig[1] = iot[id].u.it_v1->wChannelConfig[1];
4105a963377aSSascha Wildner 			r.iChannelNames = iot[id].u.it_v1->iChannelNames;
410609b9c6f2SSascha Wildner 			goto done;
410709b9c6f2SSascha Wildner 
410809b9c6f2SSascha Wildner 		case UDESCSUB_AC_OUTPUT:
4109a963377aSSascha Wildner 			id = iot[id].u.ot_v1->bSourceId;
411009b9c6f2SSascha Wildner 			break;
411109b9c6f2SSascha Wildner 
411209b9c6f2SSascha Wildner 		case UDESCSUB_AC_MIXER:
411309b9c6f2SSascha Wildner 			r = *(const struct usb_audio_cluster *)
4114a963377aSSascha Wildner 			    &iot[id].u.mu_v1->baSourceId[
4115a963377aSSascha Wildner 			    iot[id].u.mu_v1->bNrInPins];
411609b9c6f2SSascha Wildner 			goto done;
411709b9c6f2SSascha Wildner 
411809b9c6f2SSascha Wildner 		case UDESCSUB_AC_SELECTOR:
4119a963377aSSascha Wildner 			if (iot[id].u.su_v1->bNrInPins > 0) {
412009b9c6f2SSascha Wildner 				/* XXX This is not really right */
4121a963377aSSascha Wildner 				id = iot[id].u.su_v1->baSourceId[0];
412209b9c6f2SSascha Wildner 			}
412309b9c6f2SSascha Wildner 			break;
412409b9c6f2SSascha Wildner 
412509b9c6f2SSascha Wildner 		case UDESCSUB_AC_FEATURE:
4126a963377aSSascha Wildner 			id = iot[id].u.fu_v1->bSourceId;
412709b9c6f2SSascha Wildner 			break;
412809b9c6f2SSascha Wildner 
412909b9c6f2SSascha Wildner 		case UDESCSUB_AC_PROCESSING:
413009b9c6f2SSascha Wildner 			r = *((const struct usb_audio_cluster *)
4131a963377aSSascha Wildner 			    &iot[id].u.pu_v1->baSourceId[
4132a963377aSSascha Wildner 			    iot[id].u.pu_v1->bNrInPins]);
413309b9c6f2SSascha Wildner 			goto done;
413409b9c6f2SSascha Wildner 
413509b9c6f2SSascha Wildner 		case UDESCSUB_AC_EXTENSION:
413609b9c6f2SSascha Wildner 			r = *((const struct usb_audio_cluster *)
4137a963377aSSascha Wildner 			    &iot[id].u.eu_v1->baSourceId[
4138a963377aSSascha Wildner 			    iot[id].u.eu_v1->bNrInPins]);
413909b9c6f2SSascha Wildner 			goto done;
414009b9c6f2SSascha Wildner 
414109b9c6f2SSascha Wildner 		default:
414209b9c6f2SSascha Wildner 			goto error;
414309b9c6f2SSascha Wildner 		}
414409b9c6f2SSascha Wildner 	}
414509b9c6f2SSascha Wildner error:
414609b9c6f2SSascha Wildner 	DPRINTF("bad data\n");
414709b9c6f2SSascha Wildner 	memset(&r, 0, sizeof(r));
414809b9c6f2SSascha Wildner done:
414909b9c6f2SSascha Wildner 	return (r);
415009b9c6f2SSascha Wildner }
415109b9c6f2SSascha Wildner 
4152a963377aSSascha Wildner static struct usb_audio20_cluster
uaudio20_mixer_get_cluster(uint8_t id,const struct uaudio_terminal_node * iot)4153a963377aSSascha Wildner uaudio20_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
415409b9c6f2SSascha Wildner {
4155a963377aSSascha Wildner 	struct usb_audio20_cluster r;
4156a963377aSSascha Wildner 	const struct usb_descriptor *dp;
4157a963377aSSascha Wildner 	uint8_t i;
415809b9c6f2SSascha Wildner 
4159a963377aSSascha Wildner 	for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) {	/* avoid infinite loops */
4160a963377aSSascha Wildner 		dp = iot[id].u.desc;
4161a963377aSSascha Wildner 		if (dp == NULL)
4162a963377aSSascha Wildner 			goto error;
4163a963377aSSascha Wildner 
4164a963377aSSascha Wildner 		switch (dp->bDescriptorSubtype) {
4165a963377aSSascha Wildner 		case UDESCSUB_AC_INPUT:
4166a963377aSSascha Wildner 			r.bNrChannels = iot[id].u.it_v2->bNrChannels;
4167a963377aSSascha Wildner 			r.bmChannelConfig[0] = iot[id].u.it_v2->bmChannelConfig[0];
4168a963377aSSascha Wildner 			r.bmChannelConfig[1] = iot[id].u.it_v2->bmChannelConfig[1];
4169a963377aSSascha Wildner 			r.bmChannelConfig[2] = iot[id].u.it_v2->bmChannelConfig[2];
4170a963377aSSascha Wildner 			r.bmChannelConfig[3] = iot[id].u.it_v2->bmChannelConfig[3];
4171a963377aSSascha Wildner 			r.iChannelNames = iot[id].u.it_v2->iTerminal;
4172a963377aSSascha Wildner 			goto done;
4173a963377aSSascha Wildner 
4174a963377aSSascha Wildner 		case UDESCSUB_AC_OUTPUT:
4175a963377aSSascha Wildner 			id = iot[id].u.ot_v2->bSourceId;
417609b9c6f2SSascha Wildner 			break;
417709b9c6f2SSascha Wildner 
4178a963377aSSascha Wildner 		case UDESCSUB_AC_MIXER:
4179a963377aSSascha Wildner 			r = *(const struct usb_audio20_cluster *)
4180a963377aSSascha Wildner 			    &iot[id].u.mu_v2->baSourceId[
4181a963377aSSascha Wildner 			    iot[id].u.mu_v2->bNrInPins];
4182a963377aSSascha Wildner 			goto done;
4183a963377aSSascha Wildner 
4184a963377aSSascha Wildner 		case UDESCSUB_AC_SELECTOR:
4185a963377aSSascha Wildner 			if (iot[id].u.su_v2->bNrInPins > 0) {
4186a963377aSSascha Wildner 				/* XXX This is not really right */
4187a963377aSSascha Wildner 				id = iot[id].u.su_v2->baSourceId[0];
4188a963377aSSascha Wildner 			}
4189a963377aSSascha Wildner 			break;
4190a963377aSSascha Wildner 
4191a963377aSSascha Wildner 		case UDESCSUB_AC_SAMPLE_RT:
4192a963377aSSascha Wildner 			id = iot[id].u.ru_v2->bSourceId;
4193a963377aSSascha Wildner 			break;
4194a963377aSSascha Wildner 
4195a963377aSSascha Wildner 		case UDESCSUB_AC_EFFECT:
4196a963377aSSascha Wildner 			id = iot[id].u.ef_v2->bSourceId;
4197a963377aSSascha Wildner 			break;
4198a963377aSSascha Wildner 
4199a963377aSSascha Wildner 		case UDESCSUB_AC_FEATURE:
4200a963377aSSascha Wildner 			id = iot[id].u.fu_v2->bSourceId;
4201a963377aSSascha Wildner 			break;
4202a963377aSSascha Wildner 
4203a963377aSSascha Wildner 		case UDESCSUB_AC_PROCESSING_V2:
4204a963377aSSascha Wildner 			r = *((const struct usb_audio20_cluster *)
4205a963377aSSascha Wildner 			    &iot[id].u.pu_v2->baSourceId[
4206a963377aSSascha Wildner 			    iot[id].u.pu_v2->bNrInPins]);
4207a963377aSSascha Wildner 			goto done;
4208a963377aSSascha Wildner 
4209a963377aSSascha Wildner 		case UDESCSUB_AC_EXTENSION_V2:
4210a963377aSSascha Wildner 			r = *((const struct usb_audio20_cluster *)
4211a963377aSSascha Wildner 			    &iot[id].u.eu_v2->baSourceId[
4212a963377aSSascha Wildner 			    iot[id].u.eu_v2->bNrInPins]);
4213a963377aSSascha Wildner 			goto done;
4214a963377aSSascha Wildner 
4215a963377aSSascha Wildner 		default:
4216a963377aSSascha Wildner 			goto error;
4217a963377aSSascha Wildner 		}
4218a963377aSSascha Wildner 	}
4219a963377aSSascha Wildner error:
4220a963377aSSascha Wildner 	DPRINTF("Bad data!\n");
4221a963377aSSascha Wildner 	memset(&r, 0, sizeof(r));
4222a963377aSSascha Wildner done:
4223a963377aSSascha Wildner 	return (r);
4224a963377aSSascha Wildner }
422509b9c6f2SSascha Wildner 
422609b9c6f2SSascha Wildner static uint16_t
uaudio_mixer_determine_class(const struct uaudio_terminal_node * iot,struct uaudio_mixer_node * mix)422709b9c6f2SSascha Wildner uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot,
422809b9c6f2SSascha Wildner     struct uaudio_mixer_node *mix)
422909b9c6f2SSascha Wildner {
423009b9c6f2SSascha Wildner 	uint16_t terminal_type = 0x0000;
423109b9c6f2SSascha Wildner 	const struct uaudio_terminal_node *input[2];
423209b9c6f2SSascha Wildner 	const struct uaudio_terminal_node *output[2];
423309b9c6f2SSascha Wildner 
423409b9c6f2SSascha Wildner 	input[0] = uaudio_mixer_get_input(iot, 0);
423509b9c6f2SSascha Wildner 	input[1] = uaudio_mixer_get_input(iot, 1);
423609b9c6f2SSascha Wildner 
423709b9c6f2SSascha Wildner 	output[0] = uaudio_mixer_get_output(iot, 0);
423809b9c6f2SSascha Wildner 	output[1] = uaudio_mixer_get_output(iot, 1);
423909b9c6f2SSascha Wildner 
424009b9c6f2SSascha Wildner 	/*
424109b9c6f2SSascha Wildner 	 * check if there is only
424209b9c6f2SSascha Wildner 	 * one output terminal:
424309b9c6f2SSascha Wildner 	 */
424409b9c6f2SSascha Wildner 	if (output[0] && (!output[1])) {
4245a963377aSSascha Wildner 		terminal_type =
4246a963377aSSascha Wildner 		    UGETW(output[0]->u.ot_v1->wTerminalType);
424709b9c6f2SSascha Wildner 	}
424809b9c6f2SSascha Wildner 	/*
424909b9c6f2SSascha Wildner 	 * If the only output terminal is USB,
425009b9c6f2SSascha Wildner 	 * the class is UAC_RECORD.
425109b9c6f2SSascha Wildner 	 */
425209b9c6f2SSascha Wildner 	if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
425309b9c6f2SSascha Wildner 
425409b9c6f2SSascha Wildner 		mix->class = UAC_RECORD;
425509b9c6f2SSascha Wildner 		if (input[0] && (!input[1])) {
4256a963377aSSascha Wildner 			terminal_type =
4257a963377aSSascha Wildner 			    UGETW(input[0]->u.it_v1->wTerminalType);
425809b9c6f2SSascha Wildner 		} else {
425909b9c6f2SSascha Wildner 			terminal_type = 0;
426009b9c6f2SSascha Wildner 		}
426109b9c6f2SSascha Wildner 		goto done;
426209b9c6f2SSascha Wildner 	}
426309b9c6f2SSascha Wildner 	/*
426409b9c6f2SSascha Wildner 	 * if the unit is connected to just
426509b9c6f2SSascha Wildner 	 * one input terminal, the
426609b9c6f2SSascha Wildner 	 * class is UAC_INPUT:
426709b9c6f2SSascha Wildner 	 */
426809b9c6f2SSascha Wildner 	if (input[0] && (!input[1])) {
426909b9c6f2SSascha Wildner 		mix->class = UAC_INPUT;
4270a963377aSSascha Wildner 		terminal_type =
4271a963377aSSascha Wildner 		    UGETW(input[0]->u.it_v1->wTerminalType);
4272a963377aSSascha Wildner 		goto done;
4273a963377aSSascha Wildner 	}
4274a963377aSSascha Wildner 	/*
4275a963377aSSascha Wildner 	 * Otherwise, the class is UAC_OUTPUT.
4276a963377aSSascha Wildner 	 */
4277a963377aSSascha Wildner 	mix->class = UAC_OUTPUT;
4278a963377aSSascha Wildner done:
4279a963377aSSascha Wildner 	return (terminal_type);
4280a963377aSSascha Wildner }
4281a963377aSSascha Wildner 
4282a963377aSSascha Wildner static uint16_t
uaudio20_mixer_determine_class(const struct uaudio_terminal_node * iot,struct uaudio_mixer_node * mix)4283a963377aSSascha Wildner uaudio20_mixer_determine_class(const struct uaudio_terminal_node *iot,
4284a963377aSSascha Wildner     struct uaudio_mixer_node *mix)
4285a963377aSSascha Wildner {
4286a963377aSSascha Wildner 	uint16_t terminal_type = 0x0000;
4287a963377aSSascha Wildner 	const struct uaudio_terminal_node *input[2];
4288a963377aSSascha Wildner 	const struct uaudio_terminal_node *output[2];
4289a963377aSSascha Wildner 
4290a963377aSSascha Wildner 	input[0] = uaudio_mixer_get_input(iot, 0);
4291a963377aSSascha Wildner 	input[1] = uaudio_mixer_get_input(iot, 1);
4292a963377aSSascha Wildner 
4293a963377aSSascha Wildner 	output[0] = uaudio_mixer_get_output(iot, 0);
4294a963377aSSascha Wildner 	output[1] = uaudio_mixer_get_output(iot, 1);
4295a963377aSSascha Wildner 
4296a963377aSSascha Wildner 	/*
4297a963377aSSascha Wildner 	 * check if there is only
4298a963377aSSascha Wildner 	 * one output terminal:
4299a963377aSSascha Wildner 	 */
4300a963377aSSascha Wildner 	if (output[0] && (!output[1]))
4301a963377aSSascha Wildner 		terminal_type = UGETW(output[0]->u.ot_v2->wTerminalType);
4302a963377aSSascha Wildner 	/*
4303a963377aSSascha Wildner 	 * If the only output terminal is USB,
4304a963377aSSascha Wildner 	 * the class is UAC_RECORD.
4305a963377aSSascha Wildner 	 */
4306a963377aSSascha Wildner 	if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
4307a963377aSSascha Wildner 
4308a963377aSSascha Wildner 		mix->class = UAC_RECORD;
4309a963377aSSascha Wildner 		if (input[0] && (!input[1])) {
4310a963377aSSascha Wildner 			terminal_type =
4311a963377aSSascha Wildner 			    UGETW(input[0]->u.it_v2->wTerminalType);
4312a963377aSSascha Wildner 		} else {
4313a963377aSSascha Wildner 			terminal_type = 0;
4314a963377aSSascha Wildner 		}
4315a963377aSSascha Wildner 		goto done;
4316a963377aSSascha Wildner 	}
4317a963377aSSascha Wildner 	/*
4318a963377aSSascha Wildner 	 * if the unit is connected to just
4319a963377aSSascha Wildner 	 * one input terminal, the
4320a963377aSSascha Wildner 	 * class is UAC_INPUT:
4321a963377aSSascha Wildner 	 */
4322a963377aSSascha Wildner 	if (input[0] && (!input[1])) {
4323a963377aSSascha Wildner 		mix->class = UAC_INPUT;
4324a963377aSSascha Wildner 		terminal_type =
4325a963377aSSascha Wildner 		    UGETW(input[0]->u.it_v2->wTerminalType);
432609b9c6f2SSascha Wildner 		goto done;
432709b9c6f2SSascha Wildner 	}
432809b9c6f2SSascha Wildner 	/*
432909b9c6f2SSascha Wildner 	 * Otherwise, the class is UAC_OUTPUT.
433009b9c6f2SSascha Wildner 	 */
433109b9c6f2SSascha Wildner 	mix->class = UAC_OUTPUT;
433209b9c6f2SSascha Wildner done:
433309b9c6f2SSascha Wildner 	return (terminal_type);
433409b9c6f2SSascha Wildner }
433509b9c6f2SSascha Wildner 
433609b9c6f2SSascha Wildner struct uaudio_tt_to_feature {
433709b9c6f2SSascha Wildner 	uint16_t terminal_type;
433809b9c6f2SSascha Wildner 	uint16_t feature;
433909b9c6f2SSascha Wildner };
434009b9c6f2SSascha Wildner 
434109b9c6f2SSascha Wildner static const struct uaudio_tt_to_feature uaudio_tt_to_feature[] = {
434209b9c6f2SSascha Wildner 
434309b9c6f2SSascha Wildner 	{UAT_STREAM, SOUND_MIXER_PCM},
434409b9c6f2SSascha Wildner 
434509b9c6f2SSascha Wildner 	{UATI_MICROPHONE, SOUND_MIXER_MIC},
434609b9c6f2SSascha Wildner 	{UATI_DESKMICROPHONE, SOUND_MIXER_MIC},
434709b9c6f2SSascha Wildner 	{UATI_PERSONALMICROPHONE, SOUND_MIXER_MIC},
434809b9c6f2SSascha Wildner 	{UATI_OMNIMICROPHONE, SOUND_MIXER_MIC},
434909b9c6f2SSascha Wildner 	{UATI_MICROPHONEARRAY, SOUND_MIXER_MIC},
435009b9c6f2SSascha Wildner 	{UATI_PROCMICROPHONEARR, SOUND_MIXER_MIC},
435109b9c6f2SSascha Wildner 
435209b9c6f2SSascha Wildner 	{UATO_SPEAKER, SOUND_MIXER_SPEAKER},
435309b9c6f2SSascha Wildner 	{UATO_DESKTOPSPEAKER, SOUND_MIXER_SPEAKER},
435409b9c6f2SSascha Wildner 	{UATO_ROOMSPEAKER, SOUND_MIXER_SPEAKER},
435509b9c6f2SSascha Wildner 	{UATO_COMMSPEAKER, SOUND_MIXER_SPEAKER},
435609b9c6f2SSascha Wildner 
435709b9c6f2SSascha Wildner 	{UATE_ANALOGCONN, SOUND_MIXER_LINE},
435809b9c6f2SSascha Wildner 	{UATE_LINECONN, SOUND_MIXER_LINE},
435909b9c6f2SSascha Wildner 	{UATE_LEGACYCONN, SOUND_MIXER_LINE},
436009b9c6f2SSascha Wildner 
436109b9c6f2SSascha Wildner 	{UATE_DIGITALAUIFC, SOUND_MIXER_ALTPCM},
436209b9c6f2SSascha Wildner 	{UATE_SPDIF, SOUND_MIXER_ALTPCM},
436309b9c6f2SSascha Wildner 	{UATE_1394DA, SOUND_MIXER_ALTPCM},
436409b9c6f2SSascha Wildner 	{UATE_1394DV, SOUND_MIXER_ALTPCM},
436509b9c6f2SSascha Wildner 
436609b9c6f2SSascha Wildner 	{UATF_CDPLAYER, SOUND_MIXER_CD},
436709b9c6f2SSascha Wildner 
436809b9c6f2SSascha Wildner 	{UATF_SYNTHESIZER, SOUND_MIXER_SYNTH},
436909b9c6f2SSascha Wildner 
437009b9c6f2SSascha Wildner 	{UATF_VIDEODISCAUDIO, SOUND_MIXER_VIDEO},
437109b9c6f2SSascha Wildner 	{UATF_DVDAUDIO, SOUND_MIXER_VIDEO},
437209b9c6f2SSascha Wildner 	{UATF_TVTUNERAUDIO, SOUND_MIXER_VIDEO},
437309b9c6f2SSascha Wildner 
437409b9c6f2SSascha Wildner 	/* telephony terminal types */
437509b9c6f2SSascha Wildner 	{UATT_UNDEFINED, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
437609b9c6f2SSascha Wildner 	{UATT_PHONELINE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
437709b9c6f2SSascha Wildner 	{UATT_TELEPHONE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
437809b9c6f2SSascha Wildner 	{UATT_DOWNLINEPHONE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
437909b9c6f2SSascha Wildner 
438009b9c6f2SSascha Wildner 	{UATF_RADIORECV, SOUND_MIXER_RADIO},
438109b9c6f2SSascha Wildner 	{UATF_RADIOXMIT, SOUND_MIXER_RADIO},
438209b9c6f2SSascha Wildner 
438309b9c6f2SSascha Wildner 	{UAT_UNDEFINED, SOUND_MIXER_VOLUME},
438409b9c6f2SSascha Wildner 	{UAT_VENDOR, SOUND_MIXER_VOLUME},
438509b9c6f2SSascha Wildner 	{UATI_UNDEFINED, SOUND_MIXER_VOLUME},
438609b9c6f2SSascha Wildner 
438709b9c6f2SSascha Wildner 	/* output terminal types */
438809b9c6f2SSascha Wildner 	{UATO_UNDEFINED, SOUND_MIXER_VOLUME},
438909b9c6f2SSascha Wildner 	{UATO_DISPLAYAUDIO, SOUND_MIXER_VOLUME},
439009b9c6f2SSascha Wildner 	{UATO_SUBWOOFER, SOUND_MIXER_VOLUME},
439109b9c6f2SSascha Wildner 	{UATO_HEADPHONES, SOUND_MIXER_VOLUME},
439209b9c6f2SSascha Wildner 
439309b9c6f2SSascha Wildner 	/* bidir terminal types */
439409b9c6f2SSascha Wildner 	{UATB_UNDEFINED, SOUND_MIXER_VOLUME},
439509b9c6f2SSascha Wildner 	{UATB_HANDSET, SOUND_MIXER_VOLUME},
439609b9c6f2SSascha Wildner 	{UATB_HEADSET, SOUND_MIXER_VOLUME},
439709b9c6f2SSascha Wildner 	{UATB_SPEAKERPHONE, SOUND_MIXER_VOLUME},
439809b9c6f2SSascha Wildner 	{UATB_SPEAKERPHONEESUP, SOUND_MIXER_VOLUME},
439909b9c6f2SSascha Wildner 	{UATB_SPEAKERPHONEECANC, SOUND_MIXER_VOLUME},
440009b9c6f2SSascha Wildner 
440109b9c6f2SSascha Wildner 	/* external terminal types */
440209b9c6f2SSascha Wildner 	{UATE_UNDEFINED, SOUND_MIXER_VOLUME},
440309b9c6f2SSascha Wildner 
440409b9c6f2SSascha Wildner 	/* embedded function terminal types */
440509b9c6f2SSascha Wildner 	{UATF_UNDEFINED, SOUND_MIXER_VOLUME},
440609b9c6f2SSascha Wildner 	{UATF_CALIBNOISE, SOUND_MIXER_VOLUME},
440709b9c6f2SSascha Wildner 	{UATF_EQUNOISE, SOUND_MIXER_VOLUME},
440809b9c6f2SSascha Wildner 	{UATF_DAT, SOUND_MIXER_VOLUME},
440909b9c6f2SSascha Wildner 	{UATF_DCC, SOUND_MIXER_VOLUME},
441009b9c6f2SSascha Wildner 	{UATF_MINIDISK, SOUND_MIXER_VOLUME},
441109b9c6f2SSascha Wildner 	{UATF_ANALOGTAPE, SOUND_MIXER_VOLUME},
441209b9c6f2SSascha Wildner 	{UATF_PHONOGRAPH, SOUND_MIXER_VOLUME},
441309b9c6f2SSascha Wildner 	{UATF_VCRAUDIO, SOUND_MIXER_VOLUME},
441409b9c6f2SSascha Wildner 	{UATF_SATELLITE, SOUND_MIXER_VOLUME},
441509b9c6f2SSascha Wildner 	{UATF_CABLETUNER, SOUND_MIXER_VOLUME},
441609b9c6f2SSascha Wildner 	{UATF_DSS, SOUND_MIXER_VOLUME},
441709b9c6f2SSascha Wildner 	{UATF_MULTITRACK, SOUND_MIXER_VOLUME},
441809b9c6f2SSascha Wildner 	{0xffff, SOUND_MIXER_VOLUME},
441909b9c6f2SSascha Wildner 
442009b9c6f2SSascha Wildner 	/* default */
442109b9c6f2SSascha Wildner 	{0x0000, SOUND_MIXER_VOLUME},
442209b9c6f2SSascha Wildner };
442309b9c6f2SSascha Wildner 
442409b9c6f2SSascha Wildner static uint16_t
uaudio_mixer_feature_name(const struct uaudio_terminal_node * iot,struct uaudio_mixer_node * mix)442509b9c6f2SSascha Wildner uaudio_mixer_feature_name(const struct uaudio_terminal_node *iot,
442609b9c6f2SSascha Wildner     struct uaudio_mixer_node *mix)
442709b9c6f2SSascha Wildner {
442809b9c6f2SSascha Wildner 	const struct uaudio_tt_to_feature *uat = uaudio_tt_to_feature;
442909b9c6f2SSascha Wildner 	uint16_t terminal_type = uaudio_mixer_determine_class(iot, mix);
443009b9c6f2SSascha Wildner 
443109b9c6f2SSascha Wildner 	if ((mix->class == UAC_RECORD) && (terminal_type == 0)) {
443209b9c6f2SSascha Wildner 		return (SOUND_MIXER_IMIX);
443309b9c6f2SSascha Wildner 	}
443409b9c6f2SSascha Wildner 	while (uat->terminal_type) {
443509b9c6f2SSascha Wildner 		if (uat->terminal_type == terminal_type) {
443609b9c6f2SSascha Wildner 			break;
443709b9c6f2SSascha Wildner 		}
443809b9c6f2SSascha Wildner 		uat++;
443909b9c6f2SSascha Wildner 	}
444009b9c6f2SSascha Wildner 
4441a963377aSSascha Wildner 	DPRINTF("terminal_type=0x%04x -> %d\n",
4442a963377aSSascha Wildner 	    terminal_type, uat->feature);
4443a963377aSSascha Wildner 
4444a963377aSSascha Wildner 	return (uat->feature);
4445a963377aSSascha Wildner }
4446a963377aSSascha Wildner 
4447a963377aSSascha Wildner static uint16_t
uaudio20_mixer_feature_name(const struct uaudio_terminal_node * iot,struct uaudio_mixer_node * mix)4448a963377aSSascha Wildner uaudio20_mixer_feature_name(const struct uaudio_terminal_node *iot,
4449a963377aSSascha Wildner     struct uaudio_mixer_node *mix)
4450a963377aSSascha Wildner {
4451a963377aSSascha Wildner 	const struct uaudio_tt_to_feature *uat;
4452a963377aSSascha Wildner 	uint16_t terminal_type = uaudio20_mixer_determine_class(iot, mix);
4453a963377aSSascha Wildner 
4454a963377aSSascha Wildner 	if ((mix->class == UAC_RECORD) && (terminal_type == 0))
4455a963377aSSascha Wildner 		return (SOUND_MIXER_IMIX);
4456a963377aSSascha Wildner 
4457a963377aSSascha Wildner 	for (uat = uaudio_tt_to_feature; uat->terminal_type != 0; uat++) {
4458a963377aSSascha Wildner 		if (uat->terminal_type == terminal_type)
4459a963377aSSascha Wildner 			break;
4460a963377aSSascha Wildner 	}
4461a963377aSSascha Wildner 
4462a963377aSSascha Wildner 	DPRINTF("terminal_type=0x%04x -> %d\n",
446309b9c6f2SSascha Wildner 	    terminal_type, uat->feature);
446409b9c6f2SSascha Wildner 
446509b9c6f2SSascha Wildner 	return (uat->feature);
446609b9c6f2SSascha Wildner }
446709b9c6f2SSascha Wildner 
44687ea90cc9SSascha Wildner static const struct uaudio_terminal_node *
uaudio_mixer_get_input(const struct uaudio_terminal_node * iot,uint8_t i)4469a963377aSSascha Wildner uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t i)
447009b9c6f2SSascha Wildner {
447109b9c6f2SSascha Wildner 	struct uaudio_terminal_node *root = iot->root;
447209b9c6f2SSascha Wildner 	uint8_t n;
447309b9c6f2SSascha Wildner 
447409b9c6f2SSascha Wildner 	n = iot->usr.id_max;
447509b9c6f2SSascha Wildner 	do {
4476691f0a75SSascha Wildner 		if (isset(iot->usr.bit_input, n)) {
4477a963377aSSascha Wildner 			if (!i--)
447809b9c6f2SSascha Wildner 				return (root + n);
447909b9c6f2SSascha Wildner 		}
448009b9c6f2SSascha Wildner 	} while (n--);
448109b9c6f2SSascha Wildner 
448209b9c6f2SSascha Wildner 	return (NULL);
448309b9c6f2SSascha Wildner }
448409b9c6f2SSascha Wildner 
44857ea90cc9SSascha Wildner static const struct uaudio_terminal_node *
uaudio_mixer_get_output(const struct uaudio_terminal_node * iot,uint8_t i)4486a963377aSSascha Wildner uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t i)
448709b9c6f2SSascha Wildner {
448809b9c6f2SSascha Wildner 	struct uaudio_terminal_node *root = iot->root;
448909b9c6f2SSascha Wildner 	uint8_t n;
449009b9c6f2SSascha Wildner 
449109b9c6f2SSascha Wildner 	n = iot->usr.id_max;
449209b9c6f2SSascha Wildner 	do {
4493691f0a75SSascha Wildner 		if (isset(iot->usr.bit_output, n)) {
4494a963377aSSascha Wildner 			if (!i--)
449509b9c6f2SSascha Wildner 				return (root + n);
449609b9c6f2SSascha Wildner 		}
449709b9c6f2SSascha Wildner 	} while (n--);
449809b9c6f2SSascha Wildner 
449909b9c6f2SSascha Wildner 	return (NULL);
450009b9c6f2SSascha Wildner }
450109b9c6f2SSascha Wildner 
450209b9c6f2SSascha Wildner static void
uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node * root,const uint8_t * p_id,uint8_t n_id,struct uaudio_search_result * info)450309b9c6f2SSascha Wildner uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
450409b9c6f2SSascha Wildner     const uint8_t *p_id, uint8_t n_id,
450509b9c6f2SSascha Wildner     struct uaudio_search_result *info)
450609b9c6f2SSascha Wildner {
450709b9c6f2SSascha Wildner 	struct uaudio_terminal_node *iot;
450809b9c6f2SSascha Wildner 	uint8_t n;
450909b9c6f2SSascha Wildner 	uint8_t i;
4510a963377aSSascha Wildner 	uint8_t is_last;
451109b9c6f2SSascha Wildner 
4512a963377aSSascha Wildner top:
451309b9c6f2SSascha Wildner 	for (n = 0; n < n_id; n++) {
451409b9c6f2SSascha Wildner 
451509b9c6f2SSascha Wildner 		i = p_id[n];
451609b9c6f2SSascha Wildner 
4517a963377aSSascha Wildner 		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
451809b9c6f2SSascha Wildner 			DPRINTF("avoided going into a circle at id=%d!\n", i);
4519a963377aSSascha Wildner 			return;
452009b9c6f2SSascha Wildner 		}
452109b9c6f2SSascha Wildner 
4522a963377aSSascha Wildner 		info->recurse_level++;
4523a963377aSSascha Wildner 
452409b9c6f2SSascha Wildner 		iot = (root + i);
452509b9c6f2SSascha Wildner 
4526a963377aSSascha Wildner 		if (iot->u.desc == NULL)
452709b9c6f2SSascha Wildner 			continue;
4528a963377aSSascha Wildner 
4529a963377aSSascha Wildner 		is_last = ((n + 1) == n_id);
4530a963377aSSascha Wildner 
453109b9c6f2SSascha Wildner 		switch (iot->u.desc->bDescriptorSubtype) {
453209b9c6f2SSascha Wildner 		case UDESCSUB_AC_INPUT:
453309b9c6f2SSascha Wildner 			info->bit_input[i / 8] |= (1 << (i % 8));
453409b9c6f2SSascha Wildner 			break;
453509b9c6f2SSascha Wildner 
453609b9c6f2SSascha Wildner 		case UDESCSUB_AC_FEATURE:
4537a963377aSSascha Wildner 			if (is_last) {
4538a963377aSSascha Wildner 				p_id = &iot->u.fu_v1->bSourceId;
4539a963377aSSascha Wildner 				n_id = 1;
4540a963377aSSascha Wildner 				goto top;
4541a963377aSSascha Wildner 			}
4542a963377aSSascha Wildner 			uaudio_mixer_find_inputs_sub(
4543a963377aSSascha Wildner 			    root, &iot->u.fu_v1->bSourceId, 1, info);
454409b9c6f2SSascha Wildner 			break;
454509b9c6f2SSascha Wildner 
454609b9c6f2SSascha Wildner 		case UDESCSUB_AC_OUTPUT:
4547a963377aSSascha Wildner 			if (is_last) {
4548a963377aSSascha Wildner 				p_id = &iot->u.ot_v1->bSourceId;
4549a963377aSSascha Wildner 				n_id = 1;
4550a963377aSSascha Wildner 				goto top;
4551a963377aSSascha Wildner 			}
4552a963377aSSascha Wildner 			uaudio_mixer_find_inputs_sub(
4553a963377aSSascha Wildner 			    root, &iot->u.ot_v1->bSourceId, 1, info);
455409b9c6f2SSascha Wildner 			break;
455509b9c6f2SSascha Wildner 
455609b9c6f2SSascha Wildner 		case UDESCSUB_AC_MIXER:
4557a963377aSSascha Wildner 			if (is_last) {
4558a963377aSSascha Wildner 				p_id = iot->u.mu_v1->baSourceId;
4559a963377aSSascha Wildner 				n_id = iot->u.mu_v1->bNrInPins;
4560a963377aSSascha Wildner 				goto top;
4561a963377aSSascha Wildner 			}
4562a963377aSSascha Wildner 			uaudio_mixer_find_inputs_sub(
4563a963377aSSascha Wildner 			    root, iot->u.mu_v1->baSourceId,
4564a963377aSSascha Wildner 			    iot->u.mu_v1->bNrInPins, info);
456509b9c6f2SSascha Wildner 			break;
456609b9c6f2SSascha Wildner 
456709b9c6f2SSascha Wildner 		case UDESCSUB_AC_SELECTOR:
4568a963377aSSascha Wildner 			if (is_last) {
4569a963377aSSascha Wildner 				p_id = iot->u.su_v1->baSourceId;
4570a963377aSSascha Wildner 				n_id = iot->u.su_v1->bNrInPins;
4571a963377aSSascha Wildner 				goto top;
4572a963377aSSascha Wildner 			}
4573a963377aSSascha Wildner 			uaudio_mixer_find_inputs_sub(
4574a963377aSSascha Wildner 			    root, iot->u.su_v1->baSourceId,
4575a963377aSSascha Wildner 			    iot->u.su_v1->bNrInPins, info);
457609b9c6f2SSascha Wildner 			break;
457709b9c6f2SSascha Wildner 
457809b9c6f2SSascha Wildner 		case UDESCSUB_AC_PROCESSING:
4579a963377aSSascha Wildner 			if (is_last) {
4580a963377aSSascha Wildner 				p_id = iot->u.pu_v1->baSourceId;
4581a963377aSSascha Wildner 				n_id = iot->u.pu_v1->bNrInPins;
4582a963377aSSascha Wildner 				goto top;
4583a963377aSSascha Wildner 			}
4584a963377aSSascha Wildner 			uaudio_mixer_find_inputs_sub(
4585a963377aSSascha Wildner 			    root, iot->u.pu_v1->baSourceId,
4586a963377aSSascha Wildner 			    iot->u.pu_v1->bNrInPins, info);
458709b9c6f2SSascha Wildner 			break;
458809b9c6f2SSascha Wildner 
458909b9c6f2SSascha Wildner 		case UDESCSUB_AC_EXTENSION:
4590a963377aSSascha Wildner 			if (is_last) {
4591a963377aSSascha Wildner 				p_id = iot->u.eu_v1->baSourceId;
4592a963377aSSascha Wildner 				n_id = iot->u.eu_v1->bNrInPins;
4593a963377aSSascha Wildner 				goto top;
4594a963377aSSascha Wildner 			}
4595a963377aSSascha Wildner 			uaudio_mixer_find_inputs_sub(
4596a963377aSSascha Wildner 			    root, iot->u.eu_v1->baSourceId,
4597a963377aSSascha Wildner 			    iot->u.eu_v1->bNrInPins, info);
459809b9c6f2SSascha Wildner 			break;
459909b9c6f2SSascha Wildner 
460009b9c6f2SSascha Wildner 		default:
460109b9c6f2SSascha Wildner 			break;
460209b9c6f2SSascha Wildner 		}
460309b9c6f2SSascha Wildner 	}
4604a963377aSSascha Wildner }
4605a963377aSSascha Wildner 
4606a963377aSSascha Wildner static void
uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node * root,const uint8_t * p_id,uint8_t n_id,struct uaudio_search_result * info)4607a963377aSSascha Wildner uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
4608a963377aSSascha Wildner     const uint8_t *p_id, uint8_t n_id,
4609a963377aSSascha Wildner     struct uaudio_search_result *info)
4610a963377aSSascha Wildner {
4611a963377aSSascha Wildner 	struct uaudio_terminal_node *iot;
4612a963377aSSascha Wildner 	uint8_t n;
4613a963377aSSascha Wildner 	uint8_t i;
4614a963377aSSascha Wildner 	uint8_t is_last;
4615a963377aSSascha Wildner 
4616a963377aSSascha Wildner top:
4617a963377aSSascha Wildner 	for (n = 0; n < n_id; n++) {
4618a963377aSSascha Wildner 
4619a963377aSSascha Wildner 		i = p_id[n];
4620a963377aSSascha Wildner 
4621a963377aSSascha Wildner 		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
4622a963377aSSascha Wildner 			DPRINTF("avoided going into a circle at id=%d!\n", i);
4623a963377aSSascha Wildner 			return;
4624a963377aSSascha Wildner 		}
4625a963377aSSascha Wildner 
4626a963377aSSascha Wildner 		info->recurse_level++;
4627a963377aSSascha Wildner 
4628a963377aSSascha Wildner 		iot = (root + i);
4629a963377aSSascha Wildner 
4630a963377aSSascha Wildner 		if (iot->u.desc == NULL)
4631a963377aSSascha Wildner 			continue;
4632a963377aSSascha Wildner 
4633a963377aSSascha Wildner 		is_last = ((n + 1) == n_id);
4634a963377aSSascha Wildner 
4635a963377aSSascha Wildner 		switch (iot->u.desc->bDescriptorSubtype) {
4636a963377aSSascha Wildner 		case UDESCSUB_AC_INPUT:
4637a963377aSSascha Wildner 			info->bit_input[i / 8] |= (1 << (i % 8));
4638a963377aSSascha Wildner 			break;
4639a963377aSSascha Wildner 
4640a963377aSSascha Wildner 		case UDESCSUB_AC_OUTPUT:
4641a963377aSSascha Wildner 			if (is_last) {
4642a963377aSSascha Wildner 				p_id = &iot->u.ot_v2->bSourceId;
4643a963377aSSascha Wildner 				n_id = 1;
4644a963377aSSascha Wildner 				goto top;
4645a963377aSSascha Wildner 			}
4646a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(
4647a963377aSSascha Wildner 			    root, &iot->u.ot_v2->bSourceId, 1, info);
4648a963377aSSascha Wildner 			break;
4649a963377aSSascha Wildner 
4650a963377aSSascha Wildner 		case UDESCSUB_AC_MIXER:
4651a963377aSSascha Wildner 			if (is_last) {
4652a963377aSSascha Wildner 				p_id = iot->u.mu_v2->baSourceId;
4653a963377aSSascha Wildner 				n_id = iot->u.mu_v2->bNrInPins;
4654a963377aSSascha Wildner 				goto top;
4655a963377aSSascha Wildner 			}
4656a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(
4657a963377aSSascha Wildner 			    root, iot->u.mu_v2->baSourceId,
4658a963377aSSascha Wildner 			    iot->u.mu_v2->bNrInPins, info);
4659a963377aSSascha Wildner 			break;
4660a963377aSSascha Wildner 
4661a963377aSSascha Wildner 		case UDESCSUB_AC_SELECTOR:
4662a963377aSSascha Wildner 			if (is_last) {
4663a963377aSSascha Wildner 				p_id = iot->u.su_v2->baSourceId;
4664a963377aSSascha Wildner 				n_id = iot->u.su_v2->bNrInPins;
4665a963377aSSascha Wildner 				goto top;
4666a963377aSSascha Wildner 			}
4667a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(
4668a963377aSSascha Wildner 			    root, iot->u.su_v2->baSourceId,
4669a963377aSSascha Wildner 			    iot->u.su_v2->bNrInPins, info);
4670a963377aSSascha Wildner 			break;
4671a963377aSSascha Wildner 
4672a963377aSSascha Wildner 		case UDESCSUB_AC_SAMPLE_RT:
4673a963377aSSascha Wildner 			if (is_last) {
4674a963377aSSascha Wildner 				p_id = &iot->u.ru_v2->bSourceId;
4675a963377aSSascha Wildner 				n_id = 1;
4676a963377aSSascha Wildner 				goto top;
4677a963377aSSascha Wildner 			}
4678a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(
4679a963377aSSascha Wildner 			    root, &iot->u.ru_v2->bSourceId,
4680a963377aSSascha Wildner 			    1, info);
4681a963377aSSascha Wildner 			break;
4682a963377aSSascha Wildner 
4683a963377aSSascha Wildner 		case UDESCSUB_AC_EFFECT:
4684a963377aSSascha Wildner 			if (is_last) {
4685a963377aSSascha Wildner 				p_id = &iot->u.ef_v2->bSourceId;
4686a963377aSSascha Wildner 				n_id = 1;
4687a963377aSSascha Wildner 				goto top;
4688a963377aSSascha Wildner 			}
4689a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(
4690a963377aSSascha Wildner 			    root, &iot->u.ef_v2->bSourceId,
4691a963377aSSascha Wildner 			    1, info);
4692a963377aSSascha Wildner 			break;
4693a963377aSSascha Wildner 
4694a963377aSSascha Wildner 		case UDESCSUB_AC_FEATURE:
4695a963377aSSascha Wildner 			if (is_last) {
4696a963377aSSascha Wildner 				p_id = &iot->u.fu_v2->bSourceId;
4697a963377aSSascha Wildner 				n_id = 1;
4698a963377aSSascha Wildner 				goto top;
4699a963377aSSascha Wildner 			}
4700a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(
4701a963377aSSascha Wildner 			    root, &iot->u.fu_v2->bSourceId, 1, info);
4702a963377aSSascha Wildner 			break;
4703a963377aSSascha Wildner 
4704a963377aSSascha Wildner 		case UDESCSUB_AC_PROCESSING_V2:
4705a963377aSSascha Wildner 			if (is_last) {
4706a963377aSSascha Wildner 				p_id = iot->u.pu_v2->baSourceId;
4707a963377aSSascha Wildner 				n_id = iot->u.pu_v2->bNrInPins;
4708a963377aSSascha Wildner 				goto top;
4709a963377aSSascha Wildner 			}
4710a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(
4711a963377aSSascha Wildner 			    root, iot->u.pu_v2->baSourceId,
4712a963377aSSascha Wildner 			    iot->u.pu_v2->bNrInPins, info);
4713a963377aSSascha Wildner 			break;
4714a963377aSSascha Wildner 
4715a963377aSSascha Wildner 		case UDESCSUB_AC_EXTENSION_V2:
4716a963377aSSascha Wildner 			if (is_last) {
4717a963377aSSascha Wildner 				p_id = iot->u.eu_v2->baSourceId;
4718a963377aSSascha Wildner 				n_id = iot->u.eu_v2->bNrInPins;
4719a963377aSSascha Wildner 				goto top;
4720a963377aSSascha Wildner 			}
4721a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(
4722a963377aSSascha Wildner 			    root, iot->u.eu_v2->baSourceId,
4723a963377aSSascha Wildner 			    iot->u.eu_v2->bNrInPins, info);
4724a963377aSSascha Wildner 			break;
4725a963377aSSascha Wildner 		default:
4726a963377aSSascha Wildner 			break;
4727a963377aSSascha Wildner 		}
4728a963377aSSascha Wildner 	}
4729a963377aSSascha Wildner }
4730a963377aSSascha Wildner 
4731a963377aSSascha Wildner static void
uaudio20_mixer_find_clocks_sub(struct uaudio_terminal_node * root,const uint8_t * p_id,uint8_t n_id,struct uaudio_search_result * info)4732a963377aSSascha Wildner uaudio20_mixer_find_clocks_sub(struct uaudio_terminal_node *root,
4733a963377aSSascha Wildner     const uint8_t *p_id, uint8_t n_id,
4734a963377aSSascha Wildner     struct uaudio_search_result *info)
4735a963377aSSascha Wildner {
4736a963377aSSascha Wildner 	struct uaudio_terminal_node *iot;
4737a963377aSSascha Wildner 	uint8_t n;
4738a963377aSSascha Wildner 	uint8_t i;
4739a963377aSSascha Wildner 	uint8_t is_last;
4740a963377aSSascha Wildner 	uint8_t id;
4741a963377aSSascha Wildner 
4742a963377aSSascha Wildner top:
4743a963377aSSascha Wildner 	for (n = 0; n < n_id; n++) {
4744a963377aSSascha Wildner 
4745a963377aSSascha Wildner 		i = p_id[n];
4746a963377aSSascha Wildner 
4747a963377aSSascha Wildner 		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
4748a963377aSSascha Wildner 			DPRINTF("avoided going into a circle at id=%d!\n", i);
4749a963377aSSascha Wildner 			return;
4750a963377aSSascha Wildner 		}
4751a963377aSSascha Wildner 
4752a963377aSSascha Wildner 		info->recurse_level++;
4753a963377aSSascha Wildner 
4754a963377aSSascha Wildner 		iot = (root + i);
4755a963377aSSascha Wildner 
4756a963377aSSascha Wildner 		if (iot->u.desc == NULL)
4757a963377aSSascha Wildner 			continue;
4758a963377aSSascha Wildner 
4759a963377aSSascha Wildner 		is_last = ((n + 1) == n_id);
4760a963377aSSascha Wildner 
4761a963377aSSascha Wildner 		switch (iot->u.desc->bDescriptorSubtype) {
4762a963377aSSascha Wildner 		case UDESCSUB_AC_INPUT:
4763a963377aSSascha Wildner 			info->is_input = 1;
4764a963377aSSascha Wildner 			if (is_last) {
4765a963377aSSascha Wildner 				p_id = &iot->u.it_v2->bCSourceId;
4766a963377aSSascha Wildner 				n_id = 1;
4767a963377aSSascha Wildner 				goto top;
4768a963377aSSascha Wildner 			}
4769a963377aSSascha Wildner 			uaudio20_mixer_find_clocks_sub(root,
4770a963377aSSascha Wildner 			    &iot->u.it_v2->bCSourceId, 1, info);
4771a963377aSSascha Wildner 			break;
4772a963377aSSascha Wildner 
4773a963377aSSascha Wildner 		case UDESCSUB_AC_OUTPUT:
4774a963377aSSascha Wildner 			info->is_input = 0;
4775a963377aSSascha Wildner 			if (is_last) {
4776a963377aSSascha Wildner 				p_id = &iot->u.ot_v2->bCSourceId;
4777a963377aSSascha Wildner 				n_id = 1;
4778a963377aSSascha Wildner 				goto top;
4779a963377aSSascha Wildner 			}
4780a963377aSSascha Wildner 			uaudio20_mixer_find_clocks_sub(root,
4781a963377aSSascha Wildner 			    &iot->u.ot_v2->bCSourceId, 1, info);
4782a963377aSSascha Wildner 			break;
4783a963377aSSascha Wildner 
4784a963377aSSascha Wildner 		case UDESCSUB_AC_CLOCK_SEL:
4785a963377aSSascha Wildner 			if (is_last) {
4786a963377aSSascha Wildner 				p_id = iot->u.csel_v2->baCSourceId;
4787a963377aSSascha Wildner 				n_id = iot->u.csel_v2->bNrInPins;
4788a963377aSSascha Wildner 				goto top;
4789a963377aSSascha Wildner 			}
4790a963377aSSascha Wildner 			uaudio20_mixer_find_clocks_sub(root,
4791a963377aSSascha Wildner 			    iot->u.csel_v2->baCSourceId,
4792a963377aSSascha Wildner 			    iot->u.csel_v2->bNrInPins, info);
4793a963377aSSascha Wildner 			break;
4794a963377aSSascha Wildner 
4795a963377aSSascha Wildner 		case UDESCSUB_AC_CLOCK_MUL:
4796a963377aSSascha Wildner 			if (is_last) {
4797a963377aSSascha Wildner 				p_id = &iot->u.cmul_v2->bCSourceId;
4798a963377aSSascha Wildner 				n_id = 1;
4799a963377aSSascha Wildner 				goto top;
4800a963377aSSascha Wildner 			}
4801a963377aSSascha Wildner 			uaudio20_mixer_find_clocks_sub(root,
4802a963377aSSascha Wildner 			    &iot->u.cmul_v2->bCSourceId,
4803a963377aSSascha Wildner 			    1, info);
4804a963377aSSascha Wildner 			break;
4805a963377aSSascha Wildner 
4806a963377aSSascha Wildner 		case UDESCSUB_AC_CLOCK_SRC:
4807a963377aSSascha Wildner 
4808a963377aSSascha Wildner 			id = iot->u.csrc_v2->bClockId;
4809a963377aSSascha Wildner 
4810a963377aSSascha Wildner 			switch (info->is_input) {
4811a963377aSSascha Wildner 			case 0:
4812a963377aSSascha Wildner 				info->bit_output[id / 8] |= (1 << (id % 8));
4813a963377aSSascha Wildner 				break;
4814a963377aSSascha Wildner 			case 1:
4815a963377aSSascha Wildner 				info->bit_input[id / 8] |= (1 << (id % 8));
4816a963377aSSascha Wildner 				break;
4817a963377aSSascha Wildner 			default:
4818a963377aSSascha Wildner 				break;
4819a963377aSSascha Wildner 			}
4820a963377aSSascha Wildner 			break;
4821a963377aSSascha Wildner 
4822a963377aSSascha Wildner 		default:
4823a963377aSSascha Wildner 			break;
4824a963377aSSascha Wildner 		}
4825a963377aSSascha Wildner 	}
482609b9c6f2SSascha Wildner }
482709b9c6f2SSascha Wildner 
482809b9c6f2SSascha Wildner static void
uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node * root,uint8_t id,uint8_t n_id,struct uaudio_search_result * info)482909b9c6f2SSascha Wildner uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id,
483009b9c6f2SSascha Wildner     uint8_t n_id, struct uaudio_search_result *info)
483109b9c6f2SSascha Wildner {
483209b9c6f2SSascha Wildner 	struct uaudio_terminal_node *iot = (root + id);
483309b9c6f2SSascha Wildner 	uint8_t j;
483409b9c6f2SSascha Wildner 
483509b9c6f2SSascha Wildner 	j = n_id;
483609b9c6f2SSascha Wildner 	do {
483709b9c6f2SSascha Wildner 		if ((j != id) && ((root + j)->u.desc) &&
483809b9c6f2SSascha Wildner 		    ((root + j)->u.desc->bDescriptorSubtype == UDESCSUB_AC_OUTPUT)) {
483909b9c6f2SSascha Wildner 
484009b9c6f2SSascha Wildner 			/*
484109b9c6f2SSascha Wildner 			 * "j" (output) <--- virtual wire <--- "id" (input)
484209b9c6f2SSascha Wildner 			 *
484309b9c6f2SSascha Wildner 			 * if "j" has "id" on the input, then "id" have "j" on
484409b9c6f2SSascha Wildner 			 * the output, because they are connected:
484509b9c6f2SSascha Wildner 			 */
4846691f0a75SSascha Wildner 			if (isset((root + j)->usr.bit_input, id)) {
484709b9c6f2SSascha Wildner 				iot->usr.bit_output[j / 8] |= (1 << (j % 8));
484809b9c6f2SSascha Wildner 			}
484909b9c6f2SSascha Wildner 		}
485009b9c6f2SSascha Wildner 	} while (j--);
485109b9c6f2SSascha Wildner }
485209b9c6f2SSascha Wildner 
485309b9c6f2SSascha Wildner static void
uaudio_mixer_fill_info(struct uaudio_softc * sc,struct usb_device * udev,void * desc)4854a963377aSSascha Wildner uaudio_mixer_fill_info(struct uaudio_softc *sc,
4855a963377aSSascha Wildner     struct usb_device *udev, void *desc)
485609b9c6f2SSascha Wildner {
485709b9c6f2SSascha Wildner 	const struct usb_audio_control_descriptor *acdp;
485809b9c6f2SSascha Wildner 	struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
485909b9c6f2SSascha Wildner 	const struct usb_descriptor *dp;
486009b9c6f2SSascha Wildner 	const struct usb_audio_unit *au;
486109b9c6f2SSascha Wildner 	struct uaudio_terminal_node *iot = NULL;
486209b9c6f2SSascha Wildner 	uint16_t wTotalLen;
486309b9c6f2SSascha Wildner 	uint8_t ID_max = 0;		/* inclusive */
486409b9c6f2SSascha Wildner 	uint8_t i;
486509b9c6f2SSascha Wildner 
486609b9c6f2SSascha Wildner 	desc = usb_desc_foreach(cd, desc);
486709b9c6f2SSascha Wildner 
486809b9c6f2SSascha Wildner 	if (desc == NULL) {
486909b9c6f2SSascha Wildner 		DPRINTF("no Audio Control header\n");
487009b9c6f2SSascha Wildner 		goto done;
487109b9c6f2SSascha Wildner 	}
487209b9c6f2SSascha Wildner 	acdp = desc;
487309b9c6f2SSascha Wildner 
487409b9c6f2SSascha Wildner 	if ((acdp->bLength < sizeof(*acdp)) ||
487509b9c6f2SSascha Wildner 	    (acdp->bDescriptorType != UDESC_CS_INTERFACE) ||
487609b9c6f2SSascha Wildner 	    (acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)) {
487709b9c6f2SSascha Wildner 		DPRINTF("invalid Audio Control header\n");
487809b9c6f2SSascha Wildner 		goto done;
487909b9c6f2SSascha Wildner 	}
488009b9c6f2SSascha Wildner 	/* "wTotalLen" is allowed to be corrupt */
488109b9c6f2SSascha Wildner 	wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength;
488209b9c6f2SSascha Wildner 
488309b9c6f2SSascha Wildner 	/* get USB audio revision */
488409b9c6f2SSascha Wildner 	sc->sc_audio_rev = UGETW(acdp->bcdADC);
488509b9c6f2SSascha Wildner 
488609b9c6f2SSascha Wildner 	DPRINTFN(3, "found AC header, vers=%03x, len=%d\n",
488709b9c6f2SSascha Wildner 	    sc->sc_audio_rev, wTotalLen);
488809b9c6f2SSascha Wildner 
488909b9c6f2SSascha Wildner 	iot = kmalloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP,
489009b9c6f2SSascha Wildner 	    M_WAITOK | M_ZERO);
489109b9c6f2SSascha Wildner 
4892aaad5092SMarkus Pfeiffer 	if (iot == NULL) {
4893aaad5092SMarkus Pfeiffer 		DPRINTF("no memory!\n");
4894aaad5092SMarkus Pfeiffer 		goto done;
4895aaad5092SMarkus Pfeiffer 	}
489609b9c6f2SSascha Wildner 	while ((desc = usb_desc_foreach(cd, desc))) {
489709b9c6f2SSascha Wildner 
489809b9c6f2SSascha Wildner 		dp = desc;
489909b9c6f2SSascha Wildner 
490009b9c6f2SSascha Wildner 		if (dp->bLength > wTotalLen) {
490109b9c6f2SSascha Wildner 			break;
490209b9c6f2SSascha Wildner 		} else {
490309b9c6f2SSascha Wildner 			wTotalLen -= dp->bLength;
490409b9c6f2SSascha Wildner 		}
490509b9c6f2SSascha Wildner 
4906a963377aSSascha Wildner 		if (sc->sc_audio_rev >= UAUDIO_VERSION_30)
4907a963377aSSascha Wildner 			au = NULL;
4908a963377aSSascha Wildner 		else if (sc->sc_audio_rev >= UAUDIO_VERSION_20)
4909a963377aSSascha Wildner 			au = uaudio20_mixer_verify_desc(dp, 0);
4910a963377aSSascha Wildner 		else
491109b9c6f2SSascha Wildner 			au = uaudio_mixer_verify_desc(dp, 0);
491209b9c6f2SSascha Wildner 
491309b9c6f2SSascha Wildner 		if (au) {
491409b9c6f2SSascha Wildner 			iot[au->bUnitId].u.desc = (const void *)au;
4915a963377aSSascha Wildner 			if (au->bUnitId > ID_max)
491609b9c6f2SSascha Wildner 				ID_max = au->bUnitId;
491709b9c6f2SSascha Wildner 		}
491809b9c6f2SSascha Wildner 	}
491909b9c6f2SSascha Wildner 
492009b9c6f2SSascha Wildner 	DPRINTF("Maximum ID=%d\n", ID_max);
492109b9c6f2SSascha Wildner 
492209b9c6f2SSascha Wildner 	/*
492309b9c6f2SSascha Wildner 	 * determine sourcing inputs for
492409b9c6f2SSascha Wildner 	 * all nodes in the tree:
492509b9c6f2SSascha Wildner 	 */
492609b9c6f2SSascha Wildner 	i = ID_max;
492709b9c6f2SSascha Wildner 	do {
4928a963377aSSascha Wildner 		if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
4929a963377aSSascha Wildner 			/* FALLTHROUGH */
4930a963377aSSascha Wildner 		} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
4931a963377aSSascha Wildner 			uaudio20_mixer_find_inputs_sub(iot,
4932a963377aSSascha Wildner 			    &i, 1, &((iot + i)->usr));
4933a963377aSSascha Wildner 
4934a963377aSSascha Wildner 			sc->sc_mixer_clocks.is_input = 255;
4935a963377aSSascha Wildner 			sc->sc_mixer_clocks.recurse_level = 0;
4936a963377aSSascha Wildner 
4937a963377aSSascha Wildner 			uaudio20_mixer_find_clocks_sub(iot,
4938a963377aSSascha Wildner 			    &i, 1, &sc->sc_mixer_clocks);
4939a963377aSSascha Wildner 		} else {
4940a963377aSSascha Wildner 			uaudio_mixer_find_inputs_sub(iot,
4941a963377aSSascha Wildner 			    &i, 1, &((iot + i)->usr));
4942a963377aSSascha Wildner 		}
494309b9c6f2SSascha Wildner 	} while (i--);
494409b9c6f2SSascha Wildner 
494509b9c6f2SSascha Wildner 	/*
494609b9c6f2SSascha Wildner 	 * determine outputs for
494709b9c6f2SSascha Wildner 	 * all nodes in the tree:
494809b9c6f2SSascha Wildner 	 */
494909b9c6f2SSascha Wildner 	i = ID_max;
495009b9c6f2SSascha Wildner 	do {
4951a963377aSSascha Wildner 		uaudio_mixer_find_outputs_sub(iot,
4952a963377aSSascha Wildner 		    i, ID_max, &((iot + i)->usr));
495309b9c6f2SSascha Wildner 	} while (i--);
495409b9c6f2SSascha Wildner 
495509b9c6f2SSascha Wildner 	/* set "id_max" and "root" */
495609b9c6f2SSascha Wildner 
495709b9c6f2SSascha Wildner 	i = ID_max;
495809b9c6f2SSascha Wildner 	do {
495909b9c6f2SSascha Wildner 		(iot + i)->usr.id_max = ID_max;
496009b9c6f2SSascha Wildner 		(iot + i)->root = iot;
496109b9c6f2SSascha Wildner 	} while (i--);
496209b9c6f2SSascha Wildner 
496309b9c6f2SSascha Wildner 	/*
4964a963377aSSascha Wildner 	 * Scan the config to create a linked list of "mixer" nodes:
496509b9c6f2SSascha Wildner 	 */
496609b9c6f2SSascha Wildner 
496709b9c6f2SSascha Wildner 	i = ID_max;
496809b9c6f2SSascha Wildner 	do {
496909b9c6f2SSascha Wildner 		dp = iot[i].u.desc;
497009b9c6f2SSascha Wildner 
4971a963377aSSascha Wildner 		if (dp == NULL)
497209b9c6f2SSascha Wildner 			continue;
4973a963377aSSascha Wildner 
497409b9c6f2SSascha Wildner 		DPRINTFN(11, "id=%d subtype=%d\n",
497509b9c6f2SSascha Wildner 		    i, dp->bDescriptorSubtype);
497609b9c6f2SSascha Wildner 
4977a963377aSSascha Wildner 		if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
4978a963377aSSascha Wildner 			continue;
4979a963377aSSascha Wildner 		} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
4980a963377aSSascha Wildner 
498109b9c6f2SSascha Wildner 			switch (dp->bDescriptorSubtype) {
498209b9c6f2SSascha Wildner 			case UDESCSUB_AC_HEADER:
498309b9c6f2SSascha Wildner 				DPRINTF("unexpected AC header\n");
498409b9c6f2SSascha Wildner 				break;
498509b9c6f2SSascha Wildner 
498609b9c6f2SSascha Wildner 			case UDESCSUB_AC_INPUT:
4987a963377aSSascha Wildner 			case UDESCSUB_AC_OUTPUT:
4988a963377aSSascha Wildner 			case UDESCSUB_AC_PROCESSING_V2:
4989a963377aSSascha Wildner 			case UDESCSUB_AC_EXTENSION_V2:
4990a963377aSSascha Wildner 			case UDESCSUB_AC_EFFECT:
4991a963377aSSascha Wildner 			case UDESCSUB_AC_CLOCK_SRC:
4992a963377aSSascha Wildner 			case UDESCSUB_AC_CLOCK_SEL:
4993a963377aSSascha Wildner 			case UDESCSUB_AC_CLOCK_MUL:
4994a963377aSSascha Wildner 			case UDESCSUB_AC_SAMPLE_RT:
499509b9c6f2SSascha Wildner 				break;
499609b9c6f2SSascha Wildner 
4997a963377aSSascha Wildner 			case UDESCSUB_AC_MIXER:
4998a963377aSSascha Wildner 				uaudio20_mixer_add_mixer(sc, iot, i);
4999a963377aSSascha Wildner 				break;
5000a963377aSSascha Wildner 
5001a963377aSSascha Wildner 			case UDESCSUB_AC_SELECTOR:
5002a963377aSSascha Wildner 				uaudio20_mixer_add_selector(sc, iot, i);
5003a963377aSSascha Wildner 				break;
5004a963377aSSascha Wildner 
5005a963377aSSascha Wildner 			case UDESCSUB_AC_FEATURE:
5006a963377aSSascha Wildner 				uaudio20_mixer_add_feature(sc, iot, i);
5007a963377aSSascha Wildner 				break;
5008a963377aSSascha Wildner 
5009a963377aSSascha Wildner 			default:
5010a963377aSSascha Wildner 				DPRINTF("bad AC desc subtype=0x%02x\n",
5011a963377aSSascha Wildner 				    dp->bDescriptorSubtype);
5012a963377aSSascha Wildner 				break;
5013a963377aSSascha Wildner 			}
5014a963377aSSascha Wildner 			continue;
5015a963377aSSascha Wildner 		}
5016a963377aSSascha Wildner 
5017a963377aSSascha Wildner 		switch (dp->bDescriptorSubtype) {
5018a963377aSSascha Wildner 		case UDESCSUB_AC_HEADER:
5019a963377aSSascha Wildner 			DPRINTF("unexpected AC header\n");
5020a963377aSSascha Wildner 			break;
5021a963377aSSascha Wildner 
5022a963377aSSascha Wildner 		case UDESCSUB_AC_INPUT:
502309b9c6f2SSascha Wildner 		case UDESCSUB_AC_OUTPUT:
502409b9c6f2SSascha Wildner 			break;
502509b9c6f2SSascha Wildner 
502609b9c6f2SSascha Wildner 		case UDESCSUB_AC_MIXER:
502709b9c6f2SSascha Wildner 			uaudio_mixer_add_mixer(sc, iot, i);
502809b9c6f2SSascha Wildner 			break;
502909b9c6f2SSascha Wildner 
503009b9c6f2SSascha Wildner 		case UDESCSUB_AC_SELECTOR:
503109b9c6f2SSascha Wildner 			uaudio_mixer_add_selector(sc, iot, i);
503209b9c6f2SSascha Wildner 			break;
503309b9c6f2SSascha Wildner 
503409b9c6f2SSascha Wildner 		case UDESCSUB_AC_FEATURE:
503509b9c6f2SSascha Wildner 			uaudio_mixer_add_feature(sc, iot, i);
503609b9c6f2SSascha Wildner 			break;
503709b9c6f2SSascha Wildner 
503809b9c6f2SSascha Wildner 		case UDESCSUB_AC_PROCESSING:
503909b9c6f2SSascha Wildner 			uaudio_mixer_add_processing(sc, iot, i);
504009b9c6f2SSascha Wildner 			break;
504109b9c6f2SSascha Wildner 
504209b9c6f2SSascha Wildner 		case UDESCSUB_AC_EXTENSION:
504309b9c6f2SSascha Wildner 			uaudio_mixer_add_extension(sc, iot, i);
504409b9c6f2SSascha Wildner 			break;
504509b9c6f2SSascha Wildner 
504609b9c6f2SSascha Wildner 		default:
504709b9c6f2SSascha Wildner 			DPRINTF("bad AC desc subtype=0x%02x\n",
504809b9c6f2SSascha Wildner 			    dp->bDescriptorSubtype);
504909b9c6f2SSascha Wildner 			break;
505009b9c6f2SSascha Wildner 		}
505109b9c6f2SSascha Wildner 
505209b9c6f2SSascha Wildner 	} while (i--);
505309b9c6f2SSascha Wildner 
505409b9c6f2SSascha Wildner done:
5055b3f5eba6SAaron LI 	if (iot != NULL)
505609b9c6f2SSascha Wildner 		kfree(iot, M_TEMP);
505709b9c6f2SSascha Wildner }
505809b9c6f2SSascha Wildner 
5059a963377aSSascha Wildner static int
uaudio_mixer_get(struct usb_device * udev,uint16_t audio_rev,uint8_t what,struct uaudio_mixer_node * mc)5060a963377aSSascha Wildner uaudio_mixer_get(struct usb_device *udev, uint16_t audio_rev,
5061a963377aSSascha Wildner     uint8_t what, struct uaudio_mixer_node *mc)
506209b9c6f2SSascha Wildner {
506309b9c6f2SSascha Wildner 	struct usb_device_request req;
5064a963377aSSascha Wildner 	int val;
5065a963377aSSascha Wildner 	uint8_t data[2 + (2 * 3)];
506609b9c6f2SSascha Wildner 	usb_error_t err;
506709b9c6f2SSascha Wildner 
5068a963377aSSascha Wildner 	if (mc->wValue[0] == -1)
506909b9c6f2SSascha Wildner 		return (0);
5070a963377aSSascha Wildner 
5071a963377aSSascha Wildner 	if (audio_rev >= UAUDIO_VERSION_30)
5072a963377aSSascha Wildner 		return (0);
5073a963377aSSascha Wildner 	else if (audio_rev >= UAUDIO_VERSION_20) {
5074a963377aSSascha Wildner 		if (what == GET_CUR) {
5075a963377aSSascha Wildner 			req.bRequest = UA20_CS_CUR;
5076a963377aSSascha Wildner 			USETW(req.wLength, 2);
5077a963377aSSascha Wildner 		} else {
5078a963377aSSascha Wildner 			req.bRequest = UA20_CS_RANGE;
5079a963377aSSascha Wildner 			USETW(req.wLength, 8);
508009b9c6f2SSascha Wildner 		}
5081a963377aSSascha Wildner 	} else {
5082a963377aSSascha Wildner 		uint16_t len = MIX_SIZE(mc->type);
5083a963377aSSascha Wildner 
508409b9c6f2SSascha Wildner 		req.bRequest = what;
5085a963377aSSascha Wildner 		USETW(req.wLength, len);
5086a963377aSSascha Wildner 	}
5087a963377aSSascha Wildner 
5088a963377aSSascha Wildner 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
508909b9c6f2SSascha Wildner 	USETW(req.wValue, mc->wValue[0]);
509009b9c6f2SSascha Wildner 	USETW(req.wIndex, mc->wIndex);
5091a963377aSSascha Wildner 
5092a963377aSSascha Wildner 	memset(data, 0, sizeof(data));
509309b9c6f2SSascha Wildner 
509409b9c6f2SSascha Wildner 	err = usbd_do_request(udev, NULL, &req, data);
509509b9c6f2SSascha Wildner 	if (err) {
509609b9c6f2SSascha Wildner 		DPRINTF("err=%s\n", usbd_errstr(err));
509709b9c6f2SSascha Wildner 		return (0);
509809b9c6f2SSascha Wildner 	}
5099a963377aSSascha Wildner 
5100a963377aSSascha Wildner 	if (audio_rev >= UAUDIO_VERSION_30) {
5101a963377aSSascha Wildner 		val = 0;
5102a963377aSSascha Wildner 	} else if (audio_rev >= UAUDIO_VERSION_20) {
5103a963377aSSascha Wildner 		switch (what) {
5104a963377aSSascha Wildner 		case GET_CUR:
510509b9c6f2SSascha Wildner 			val = (data[0] | (data[1] << 8));
5106a963377aSSascha Wildner 			break;
5107a963377aSSascha Wildner 		case GET_MIN:
5108a963377aSSascha Wildner 			val = (data[2] | (data[3] << 8));
5109a963377aSSascha Wildner 			break;
5110a963377aSSascha Wildner 		case GET_MAX:
5111a963377aSSascha Wildner 			val = (data[4] | (data[5] << 8));
5112a963377aSSascha Wildner 			break;
5113a963377aSSascha Wildner 		case GET_RES:
5114a963377aSSascha Wildner 			val = (data[6] | (data[7] << 8));
5115a963377aSSascha Wildner 			break;
5116a963377aSSascha Wildner 		default:
5117a963377aSSascha Wildner 			val = 0;
5118a963377aSSascha Wildner 			break;
5119a963377aSSascha Wildner 		}
5120a963377aSSascha Wildner 	} else {
5121a963377aSSascha Wildner 		val = (data[0] | (data[1] << 8));
5122a963377aSSascha Wildner 	}
5123a963377aSSascha Wildner 
5124a963377aSSascha Wildner 	if (what == GET_CUR || what == GET_MIN || what == GET_MAX)
5125a963377aSSascha Wildner 		val = uaudio_mixer_signext(mc->type, val);
512609b9c6f2SSascha Wildner 
512709b9c6f2SSascha Wildner 	DPRINTFN(3, "val=%d\n", val);
512809b9c6f2SSascha Wildner 
512909b9c6f2SSascha Wildner 	return (val);
513009b9c6f2SSascha Wildner }
513109b9c6f2SSascha Wildner 
513209b9c6f2SSascha Wildner static void
uaudio_mixer_write_cfg_callback(struct usb_xfer * xfer,usb_error_t error)513309b9c6f2SSascha Wildner uaudio_mixer_write_cfg_callback(struct usb_xfer *xfer, usb_error_t error)
513409b9c6f2SSascha Wildner {
513509b9c6f2SSascha Wildner 	struct usb_device_request req;
513609b9c6f2SSascha Wildner 	struct uaudio_softc *sc = usbd_xfer_softc(xfer);
513709b9c6f2SSascha Wildner 	struct uaudio_mixer_node *mc = sc->sc_mixer_curr;
513809b9c6f2SSascha Wildner 	struct usb_page_cache *pc;
513909b9c6f2SSascha Wildner 	uint16_t len;
514009b9c6f2SSascha Wildner 	uint8_t repeat = 1;
514109b9c6f2SSascha Wildner 	uint8_t update;
514209b9c6f2SSascha Wildner 	uint8_t chan;
514309b9c6f2SSascha Wildner 	uint8_t buf[2];
514409b9c6f2SSascha Wildner 
514509b9c6f2SSascha Wildner 	DPRINTF("\n");
514609b9c6f2SSascha Wildner 
514709b9c6f2SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
514809b9c6f2SSascha Wildner 	case USB_ST_TRANSFERRED:
514909b9c6f2SSascha Wildner tr_transferred:
515009b9c6f2SSascha Wildner 	case USB_ST_SETUP:
515109b9c6f2SSascha Wildner tr_setup:
515209b9c6f2SSascha Wildner 
515309b9c6f2SSascha Wildner 		if (mc == NULL) {
515409b9c6f2SSascha Wildner 			mc = sc->sc_mixer_root;
515509b9c6f2SSascha Wildner 			sc->sc_mixer_curr = mc;
515609b9c6f2SSascha Wildner 			sc->sc_mixer_chan = 0;
515709b9c6f2SSascha Wildner 			repeat = 0;
515809b9c6f2SSascha Wildner 		}
515909b9c6f2SSascha Wildner 		while (mc) {
516009b9c6f2SSascha Wildner 			while (sc->sc_mixer_chan < mc->nchan) {
516109b9c6f2SSascha Wildner 
516209b9c6f2SSascha Wildner 				chan = sc->sc_mixer_chan;
516309b9c6f2SSascha Wildner 
516409b9c6f2SSascha Wildner 				sc->sc_mixer_chan++;
516509b9c6f2SSascha Wildner 
5166691f0a75SSascha Wildner 				update = isset(mc->update, chan) &&
5167691f0a75SSascha Wildner 					  (mc->wValue[chan] != -1);
516809b9c6f2SSascha Wildner 
5169691f0a75SSascha Wildner 				clrbit(mc->update, chan);
517009b9c6f2SSascha Wildner 
517109b9c6f2SSascha Wildner 				if (update) {
517209b9c6f2SSascha Wildner 
517309b9c6f2SSascha Wildner 					req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
517409b9c6f2SSascha Wildner 					USETW(req.wValue, mc->wValue[chan]);
517509b9c6f2SSascha Wildner 					USETW(req.wIndex, mc->wIndex);
517609b9c6f2SSascha Wildner 
5177a963377aSSascha Wildner 					if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
5178a963377aSSascha Wildner 						return;
5179a963377aSSascha Wildner 					} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
5180a963377aSSascha Wildner 						len = 2;
5181a963377aSSascha Wildner 						req.bRequest = UA20_CS_CUR;
5182a963377aSSascha Wildner 						USETW(req.wLength, len);
5183a963377aSSascha Wildner 					} else {
5184a963377aSSascha Wildner 						len = MIX_SIZE(mc->type);
5185a963377aSSascha Wildner 						req.bRequest = SET_CUR;
5186a963377aSSascha Wildner 						USETW(req.wLength, len);
5187a963377aSSascha Wildner 					}
5188a963377aSSascha Wildner 
518909b9c6f2SSascha Wildner 					buf[0] = (mc->wData[chan] & 0xFF);
519009b9c6f2SSascha Wildner 					buf[1] = (mc->wData[chan] >> 8) & 0xFF;
5191a963377aSSascha Wildner 
519209b9c6f2SSascha Wildner 					pc = usbd_xfer_get_frame(xfer, 0);
519309b9c6f2SSascha Wildner 					usbd_copy_in(pc, 0, &req, sizeof(req));
519409b9c6f2SSascha Wildner 					pc = usbd_xfer_get_frame(xfer, 1);
519509b9c6f2SSascha Wildner 					usbd_copy_in(pc, 0, buf, len);
519609b9c6f2SSascha Wildner 
519709b9c6f2SSascha Wildner 					usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
519809b9c6f2SSascha Wildner 					usbd_xfer_set_frame_len(xfer, 1, len);
519909b9c6f2SSascha Wildner 					usbd_xfer_set_frames(xfer, len ? 2 : 1);
520009b9c6f2SSascha Wildner 					usbd_transfer_submit(xfer);
520109b9c6f2SSascha Wildner 					return;
520209b9c6f2SSascha Wildner 				}
520309b9c6f2SSascha Wildner 			}
520409b9c6f2SSascha Wildner 
520509b9c6f2SSascha Wildner 			mc = mc->next;
520609b9c6f2SSascha Wildner 			sc->sc_mixer_curr = mc;
520709b9c6f2SSascha Wildner 			sc->sc_mixer_chan = 0;
520809b9c6f2SSascha Wildner 		}
520909b9c6f2SSascha Wildner 
521009b9c6f2SSascha Wildner 		if (repeat) {
521109b9c6f2SSascha Wildner 			goto tr_setup;
521209b9c6f2SSascha Wildner 		}
521309b9c6f2SSascha Wildner 		break;
521409b9c6f2SSascha Wildner 
521509b9c6f2SSascha Wildner 	default:			/* Error */
521609b9c6f2SSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
521709b9c6f2SSascha Wildner 		if (error == USB_ERR_CANCELLED) {
521809b9c6f2SSascha Wildner 			/* do nothing - we are detaching */
521909b9c6f2SSascha Wildner 			break;
522009b9c6f2SSascha Wildner 		}
522109b9c6f2SSascha Wildner 		goto tr_transferred;
522209b9c6f2SSascha Wildner 	}
522309b9c6f2SSascha Wildner }
522409b9c6f2SSascha Wildner 
522509b9c6f2SSascha Wildner static usb_error_t
uaudio_set_speed(struct usb_device * udev,uint8_t endpt,uint32_t speed)522609b9c6f2SSascha Wildner uaudio_set_speed(struct usb_device *udev, uint8_t endpt, uint32_t speed)
522709b9c6f2SSascha Wildner {
522809b9c6f2SSascha Wildner 	struct usb_device_request req;
522909b9c6f2SSascha Wildner 	uint8_t data[3];
523009b9c6f2SSascha Wildner 
523109b9c6f2SSascha Wildner 	DPRINTFN(6, "endpt=%d speed=%u\n", endpt, speed);
523209b9c6f2SSascha Wildner 
523309b9c6f2SSascha Wildner 	req.bmRequestType = UT_WRITE_CLASS_ENDPOINT;
523409b9c6f2SSascha Wildner 	req.bRequest = SET_CUR;
523509b9c6f2SSascha Wildner 	USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0);
523609b9c6f2SSascha Wildner 	USETW(req.wIndex, endpt);
523709b9c6f2SSascha Wildner 	USETW(req.wLength, 3);
523809b9c6f2SSascha Wildner 	data[0] = speed;
523909b9c6f2SSascha Wildner 	data[1] = speed >> 8;
524009b9c6f2SSascha Wildner 	data[2] = speed >> 16;
524109b9c6f2SSascha Wildner 
524209b9c6f2SSascha Wildner 	return (usbd_do_request(udev, NULL, &req, data));
524309b9c6f2SSascha Wildner }
524409b9c6f2SSascha Wildner 
5245a963377aSSascha Wildner static usb_error_t
uaudio20_set_speed(struct usb_device * udev,uint8_t iface_no,uint8_t clockid,uint32_t speed)5246a963377aSSascha Wildner uaudio20_set_speed(struct usb_device *udev, uint8_t iface_no,
5247a963377aSSascha Wildner     uint8_t clockid, uint32_t speed)
5248a963377aSSascha Wildner {
5249a963377aSSascha Wildner 	struct usb_device_request req;
5250a963377aSSascha Wildner 	uint8_t data[4];
5251a963377aSSascha Wildner 
5252a963377aSSascha Wildner 	DPRINTFN(6, "ifaceno=%d clockid=%d speed=%u\n",
5253a963377aSSascha Wildner 	    iface_no, clockid, speed);
5254a963377aSSascha Wildner 
5255a963377aSSascha Wildner 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
5256a963377aSSascha Wildner 	req.bRequest = UA20_CS_CUR;
5257a963377aSSascha Wildner 	USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
5258a963377aSSascha Wildner 	USETW2(req.wIndex, clockid, iface_no);
5259a963377aSSascha Wildner 	USETW(req.wLength, 4);
5260a963377aSSascha Wildner 	data[0] = speed;
5261a963377aSSascha Wildner 	data[1] = speed >> 8;
5262a963377aSSascha Wildner 	data[2] = speed >> 16;
5263a963377aSSascha Wildner 	data[3] = speed >> 24;
5264a963377aSSascha Wildner 
5265a963377aSSascha Wildner 	return (usbd_do_request(udev, NULL, &req, data));
5266a963377aSSascha Wildner }
5267a963377aSSascha Wildner 
526809b9c6f2SSascha Wildner static int
uaudio_mixer_signext(uint8_t type,int val)526909b9c6f2SSascha Wildner uaudio_mixer_signext(uint8_t type, int val)
527009b9c6f2SSascha Wildner {
527109b9c6f2SSascha Wildner 	if (!MIX_UNSIGNED(type)) {
527209b9c6f2SSascha Wildner 		if (MIX_SIZE(type) == 2) {
527309b9c6f2SSascha Wildner 			val = (int16_t)val;
527409b9c6f2SSascha Wildner 		} else {
527509b9c6f2SSascha Wildner 			val = (int8_t)val;
527609b9c6f2SSascha Wildner 		}
527709b9c6f2SSascha Wildner 	}
527809b9c6f2SSascha Wildner 	return (val);
527909b9c6f2SSascha Wildner }
528009b9c6f2SSascha Wildner 
528109b9c6f2SSascha Wildner static int
uaudio_mixer_bsd2value(struct uaudio_mixer_node * mc,int32_t val)528209b9c6f2SSascha Wildner uaudio_mixer_bsd2value(struct uaudio_mixer_node *mc, int32_t val)
528309b9c6f2SSascha Wildner {
528409b9c6f2SSascha Wildner 	if (mc->type == MIX_ON_OFF) {
528509b9c6f2SSascha Wildner 		val = (val != 0);
528609b9c6f2SSascha Wildner 	} else if (mc->type == MIX_SELECTOR) {
528709b9c6f2SSascha Wildner 		if ((val < mc->minval) ||
528809b9c6f2SSascha Wildner 		    (val > mc->maxval)) {
528909b9c6f2SSascha Wildner 			val = mc->minval;
529009b9c6f2SSascha Wildner 		}
529109b9c6f2SSascha Wildner 	} else {
529209b9c6f2SSascha Wildner 
529309b9c6f2SSascha Wildner 		/* compute actual volume */
529409b9c6f2SSascha Wildner 		val = (val * mc->mul) / 255;
529509b9c6f2SSascha Wildner 
529609b9c6f2SSascha Wildner 		/* add lower offset */
529709b9c6f2SSascha Wildner 		val = val + mc->minval;
529809b9c6f2SSascha Wildner 
529909b9c6f2SSascha Wildner 		/* make sure we don't write a value out of range */
530009b9c6f2SSascha Wildner 		if (val > mc->maxval)
530109b9c6f2SSascha Wildner 			val = mc->maxval;
530209b9c6f2SSascha Wildner 		else if (val < mc->minval)
530309b9c6f2SSascha Wildner 			val = mc->minval;
530409b9c6f2SSascha Wildner 	}
530509b9c6f2SSascha Wildner 
530609b9c6f2SSascha Wildner 	DPRINTFN(6, "type=0x%03x val=%d min=%d max=%d val=%d\n",
530709b9c6f2SSascha Wildner 	    mc->type, val, mc->minval, mc->maxval, val);
530809b9c6f2SSascha Wildner 	return (val);
530909b9c6f2SSascha Wildner }
531009b9c6f2SSascha Wildner 
531109b9c6f2SSascha Wildner static void
uaudio_mixer_ctl_set(struct uaudio_softc * sc,struct uaudio_mixer_node * mc,uint8_t chan,int32_t val)531209b9c6f2SSascha Wildner uaudio_mixer_ctl_set(struct uaudio_softc *sc, struct uaudio_mixer_node *mc,
531309b9c6f2SSascha Wildner     uint8_t chan, int32_t val)
531409b9c6f2SSascha Wildner {
531509b9c6f2SSascha Wildner 	val = uaudio_mixer_bsd2value(mc, val);
531609b9c6f2SSascha Wildner 
531709b9c6f2SSascha Wildner 	mc->update[chan / 8] |= (1 << (chan % 8));
531809b9c6f2SSascha Wildner 	mc->wData[chan] = val;
531909b9c6f2SSascha Wildner 
532009b9c6f2SSascha Wildner 	/* start the transfer, if not already started */
532109b9c6f2SSascha Wildner 
532209b9c6f2SSascha Wildner 	usbd_transfer_start(sc->sc_mixer_xfer[0]);
532309b9c6f2SSascha Wildner }
532409b9c6f2SSascha Wildner 
532509b9c6f2SSascha Wildner static void
uaudio_mixer_init(struct uaudio_softc * sc)532609b9c6f2SSascha Wildner uaudio_mixer_init(struct uaudio_softc *sc)
532709b9c6f2SSascha Wildner {
532809b9c6f2SSascha Wildner 	struct uaudio_mixer_node *mc;
532909b9c6f2SSascha Wildner 	int32_t i;
533009b9c6f2SSascha Wildner 
533109b9c6f2SSascha Wildner 	for (mc = sc->sc_mixer_root; mc;
533209b9c6f2SSascha Wildner 	    mc = mc->next) {
533309b9c6f2SSascha Wildner 
533409b9c6f2SSascha Wildner 		if (mc->ctl != SOUND_MIXER_NRDEVICES) {
533509b9c6f2SSascha Wildner 			/*
533609b9c6f2SSascha Wildner 			 * Set device mask bits. See
533709b9c6f2SSascha Wildner 			 * /usr/include/machine/soundcard.h
533809b9c6f2SSascha Wildner 			 */
533909b9c6f2SSascha Wildner 			sc->sc_mix_info |= (1 << mc->ctl);
534009b9c6f2SSascha Wildner 		}
534109b9c6f2SSascha Wildner 		if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
534209b9c6f2SSascha Wildner 		    (mc->type == MIX_SELECTOR)) {
534309b9c6f2SSascha Wildner 
534409b9c6f2SSascha Wildner 			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
534509b9c6f2SSascha Wildner 				if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) {
534609b9c6f2SSascha Wildner 					continue;
534709b9c6f2SSascha Wildner 				}
534809b9c6f2SSascha Wildner 				sc->sc_recsrc_info |= 1 << mc->slctrtype[i - 1];
534909b9c6f2SSascha Wildner 			}
535009b9c6f2SSascha Wildner 		}
535109b9c6f2SSascha Wildner 	}
535209b9c6f2SSascha Wildner }
535309b9c6f2SSascha Wildner 
535409b9c6f2SSascha Wildner int
uaudio_mixer_init_sub(struct uaudio_softc * sc,struct snd_mixer * m)535509b9c6f2SSascha Wildner uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m)
535609b9c6f2SSascha Wildner {
535709b9c6f2SSascha Wildner 	DPRINTF("\n");
535809b9c6f2SSascha Wildner 
5359a963377aSSascha Wildner 	sc->sc_mixer_lock = mixer_get_lock(m);
5360a963377aSSascha Wildner 	sc->sc_mixer_dev = m;
5361a963377aSSascha Wildner 
536209b9c6f2SSascha Wildner 	if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index,
536309b9c6f2SSascha Wildner 	    sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc,
5364a963377aSSascha Wildner 	    sc->sc_mixer_lock)) {
536509b9c6f2SSascha Wildner 		DPRINTFN(0, "could not allocate USB "
536609b9c6f2SSascha Wildner 		    "transfer for audio mixer!\n");
536709b9c6f2SSascha Wildner 		return (ENOMEM);
536809b9c6f2SSascha Wildner 	}
536909b9c6f2SSascha Wildner 	if (!(sc->sc_mix_info & SOUND_MASK_VOLUME)) {
537009b9c6f2SSascha Wildner 		mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM);
537109b9c6f2SSascha Wildner 		mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
537209b9c6f2SSascha Wildner 	}
537309b9c6f2SSascha Wildner 	mix_setdevs(m, sc->sc_mix_info);
537409b9c6f2SSascha Wildner 	mix_setrecdevs(m, sc->sc_recsrc_info);
537509b9c6f2SSascha Wildner 	return (0);
537609b9c6f2SSascha Wildner }
537709b9c6f2SSascha Wildner 
537809b9c6f2SSascha Wildner int
uaudio_mixer_uninit_sub(struct uaudio_softc * sc)537909b9c6f2SSascha Wildner uaudio_mixer_uninit_sub(struct uaudio_softc *sc)
538009b9c6f2SSascha Wildner {
538109b9c6f2SSascha Wildner 	DPRINTF("\n");
538209b9c6f2SSascha Wildner 
538309b9c6f2SSascha Wildner 	usbd_transfer_unsetup(sc->sc_mixer_xfer, 1);
538409b9c6f2SSascha Wildner 
5385a963377aSSascha Wildner 	sc->sc_mixer_lock = NULL;
5386a963377aSSascha Wildner 
538709b9c6f2SSascha Wildner 	return (0);
538809b9c6f2SSascha Wildner }
538909b9c6f2SSascha Wildner 
539009b9c6f2SSascha Wildner void
uaudio_mixer_set(struct uaudio_softc * sc,unsigned type,unsigned left,unsigned right)539109b9c6f2SSascha Wildner uaudio_mixer_set(struct uaudio_softc *sc, unsigned type,
539209b9c6f2SSascha Wildner     unsigned left, unsigned right)
539309b9c6f2SSascha Wildner {
539409b9c6f2SSascha Wildner 	struct uaudio_mixer_node *mc;
5395b760b3ceSMarkus Pfeiffer 	int chan;
539609b9c6f2SSascha Wildner 
5397a963377aSSascha Wildner 	for (mc = sc->sc_mixer_root; mc != NULL; mc = mc->next) {
539809b9c6f2SSascha Wildner 
539909b9c6f2SSascha Wildner 		if (mc->ctl == type) {
5400b760b3ceSMarkus Pfeiffer 			for (chan = 0; chan < mc->nchan; chan++) {
5401b760b3ceSMarkus Pfeiffer 				uaudio_mixer_ctl_set(sc, mc, chan,
5402a963377aSSascha Wildner 				    (int)((chan == 0 ? left : right) *
5403a963377aSSascha Wildner 				    255) / 100);
540409b9c6f2SSascha Wildner 			}
540509b9c6f2SSascha Wildner 		}
540609b9c6f2SSascha Wildner 	}
540709b9c6f2SSascha Wildner }
540809b9c6f2SSascha Wildner 
540909b9c6f2SSascha Wildner uint32_t
uaudio_mixer_setrecsrc(struct uaudio_softc * sc,uint32_t src)541009b9c6f2SSascha Wildner uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src)
541109b9c6f2SSascha Wildner {
541209b9c6f2SSascha Wildner 	struct uaudio_mixer_node *mc;
541309b9c6f2SSascha Wildner 	uint32_t mask;
541409b9c6f2SSascha Wildner 	uint32_t temp;
541509b9c6f2SSascha Wildner 	int32_t i;
541609b9c6f2SSascha Wildner 
541709b9c6f2SSascha Wildner 	for (mc = sc->sc_mixer_root; mc;
541809b9c6f2SSascha Wildner 	    mc = mc->next) {
541909b9c6f2SSascha Wildner 
542009b9c6f2SSascha Wildner 		if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
542109b9c6f2SSascha Wildner 		    (mc->type == MIX_SELECTOR)) {
542209b9c6f2SSascha Wildner 
542309b9c6f2SSascha Wildner 			/* compute selector mask */
542409b9c6f2SSascha Wildner 
542509b9c6f2SSascha Wildner 			mask = 0;
542609b9c6f2SSascha Wildner 			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
542709b9c6f2SSascha Wildner 				mask |= (1 << mc->slctrtype[i - 1]);
542809b9c6f2SSascha Wildner 			}
542909b9c6f2SSascha Wildner 
543009b9c6f2SSascha Wildner 			temp = mask & src;
543109b9c6f2SSascha Wildner 			if (temp == 0) {
543209b9c6f2SSascha Wildner 				continue;
543309b9c6f2SSascha Wildner 			}
543409b9c6f2SSascha Wildner 			/* find the first set bit */
543509b9c6f2SSascha Wildner 			temp = (-temp) & temp;
543609b9c6f2SSascha Wildner 
543709b9c6f2SSascha Wildner 			/* update "src" */
543809b9c6f2SSascha Wildner 			src &= ~mask;
543909b9c6f2SSascha Wildner 			src |= temp;
544009b9c6f2SSascha Wildner 
544109b9c6f2SSascha Wildner 			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
544209b9c6f2SSascha Wildner 				if (temp != (1 << mc->slctrtype[i - 1])) {
544309b9c6f2SSascha Wildner 					continue;
544409b9c6f2SSascha Wildner 				}
544509b9c6f2SSascha Wildner 				uaudio_mixer_ctl_set(sc, mc, 0, i);
544609b9c6f2SSascha Wildner 				break;
544709b9c6f2SSascha Wildner 			}
544809b9c6f2SSascha Wildner 		}
544909b9c6f2SSascha Wildner 	}
545009b9c6f2SSascha Wildner 	return (src);
545109b9c6f2SSascha Wildner }
545209b9c6f2SSascha Wildner 
545309b9c6f2SSascha Wildner /*========================================================================*
545409b9c6f2SSascha Wildner  * MIDI support routines
545509b9c6f2SSascha Wildner  *========================================================================*/
545609b9c6f2SSascha Wildner 
545709b9c6f2SSascha Wildner static void
umidi_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)545809b9c6f2SSascha Wildner umidi_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
545909b9c6f2SSascha Wildner {
546009b9c6f2SSascha Wildner 	struct umidi_chan *chan = usbd_xfer_softc(xfer);
546109b9c6f2SSascha Wildner 	struct umidi_sub_chan *sub;
546209b9c6f2SSascha Wildner 	struct usb_page_cache *pc;
546309b9c6f2SSascha Wildner 	uint8_t buf[4];
546409b9c6f2SSascha Wildner 	uint8_t cmd_len;
546509b9c6f2SSascha Wildner 	uint8_t cn;
546609b9c6f2SSascha Wildner 	uint16_t pos;
546709b9c6f2SSascha Wildner 	int actlen;
546809b9c6f2SSascha Wildner 
546909b9c6f2SSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
547009b9c6f2SSascha Wildner 
547109b9c6f2SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
547209b9c6f2SSascha Wildner 	case USB_ST_TRANSFERRED:
547309b9c6f2SSascha Wildner 
547409b9c6f2SSascha Wildner 		DPRINTF("actlen=%d bytes\n", actlen);
547509b9c6f2SSascha Wildner 
547609b9c6f2SSascha Wildner 		pos = 0;
547709b9c6f2SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
547809b9c6f2SSascha Wildner 
547909b9c6f2SSascha Wildner 		while (actlen >= 4) {
548009b9c6f2SSascha Wildner 
548109b9c6f2SSascha Wildner 			/* copy out the MIDI data */
548209b9c6f2SSascha Wildner 			usbd_copy_out(pc, pos, buf, 4);
548309b9c6f2SSascha Wildner 			/* command length */
548409b9c6f2SSascha Wildner 			cmd_len = umidi_cmd_to_len[buf[0] & 0xF];
548509b9c6f2SSascha Wildner 			/* cable number */
548609b9c6f2SSascha Wildner 			cn = buf[0] >> 4;
548709b9c6f2SSascha Wildner 			/*
548809b9c6f2SSascha Wildner 			 * Lookup sub-channel. The index is range
548909b9c6f2SSascha Wildner 			 * checked below.
549009b9c6f2SSascha Wildner 			 */
549109b9c6f2SSascha Wildner 			sub = &chan->sub[cn];
549209b9c6f2SSascha Wildner 
5493a963377aSSascha Wildner 			if ((cmd_len != 0) && (cn < chan->max_emb_jack) &&
549409b9c6f2SSascha Wildner 			    (sub->read_open != 0)) {
549509b9c6f2SSascha Wildner 
549609b9c6f2SSascha Wildner 				/* Send data to the application */
549709b9c6f2SSascha Wildner 				usb_fifo_put_data_linear(
549809b9c6f2SSascha Wildner 				    sub->fifo.fp[USB_FIFO_RX],
549909b9c6f2SSascha Wildner 				    buf + 1, cmd_len, 1);
550009b9c6f2SSascha Wildner 			}
550109b9c6f2SSascha Wildner 			actlen -= 4;
550209b9c6f2SSascha Wildner 			pos += 4;
550309b9c6f2SSascha Wildner 		}
550409b9c6f2SSascha Wildner 
550509b9c6f2SSascha Wildner 	case USB_ST_SETUP:
550609b9c6f2SSascha Wildner 		DPRINTF("start\n");
550709b9c6f2SSascha Wildner tr_setup:
550809b9c6f2SSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
550909b9c6f2SSascha Wildner 		usbd_transfer_submit(xfer);
551009b9c6f2SSascha Wildner 		break;
551109b9c6f2SSascha Wildner 
551209b9c6f2SSascha Wildner 	default:
551309b9c6f2SSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
551409b9c6f2SSascha Wildner 
551509b9c6f2SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
551609b9c6f2SSascha Wildner 			/* try to clear stall first */
551709b9c6f2SSascha Wildner 			usbd_xfer_set_stall(xfer);
551809b9c6f2SSascha Wildner 			goto tr_setup;
551909b9c6f2SSascha Wildner 		}
552009b9c6f2SSascha Wildner 		break;
552109b9c6f2SSascha Wildner 	}
552209b9c6f2SSascha Wildner }
552309b9c6f2SSascha Wildner 
552409b9c6f2SSascha Wildner /*
552509b9c6f2SSascha Wildner  * The following statemachine, that converts MIDI commands to
552609b9c6f2SSascha Wildner  * USB MIDI packets, derives from Linux's usbmidi.c, which
552709b9c6f2SSascha Wildner  * was written by "Clemens Ladisch":
552809b9c6f2SSascha Wildner  *
552909b9c6f2SSascha Wildner  * Returns:
553009b9c6f2SSascha Wildner  *    0: No command
553109b9c6f2SSascha Wildner  * Else: Command is complete
553209b9c6f2SSascha Wildner  */
553309b9c6f2SSascha Wildner static uint8_t
umidi_convert_to_usb(struct umidi_sub_chan * sub,uint8_t cn,uint8_t b)553409b9c6f2SSascha Wildner umidi_convert_to_usb(struct umidi_sub_chan *sub, uint8_t cn, uint8_t b)
553509b9c6f2SSascha Wildner {
553609b9c6f2SSascha Wildner 	uint8_t p0 = (cn << 4);
553709b9c6f2SSascha Wildner 
553809b9c6f2SSascha Wildner 	if (b >= 0xf8) {
553909b9c6f2SSascha Wildner 		sub->temp_0[0] = p0 | 0x0f;
554009b9c6f2SSascha Wildner 		sub->temp_0[1] = b;
554109b9c6f2SSascha Wildner 		sub->temp_0[2] = 0;
554209b9c6f2SSascha Wildner 		sub->temp_0[3] = 0;
554309b9c6f2SSascha Wildner 		sub->temp_cmd = sub->temp_0;
554409b9c6f2SSascha Wildner 		return (1);
554509b9c6f2SSascha Wildner 
554609b9c6f2SSascha Wildner 	} else if (b >= 0xf0) {
554709b9c6f2SSascha Wildner 		switch (b) {
554809b9c6f2SSascha Wildner 		case 0xf0:		/* system exclusive begin */
554909b9c6f2SSascha Wildner 			sub->temp_1[1] = b;
555009b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_SYSEX_1;
555109b9c6f2SSascha Wildner 			break;
555209b9c6f2SSascha Wildner 		case 0xf1:		/* MIDI time code */
555309b9c6f2SSascha Wildner 		case 0xf3:		/* song select */
555409b9c6f2SSascha Wildner 			sub->temp_1[1] = b;
555509b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_1PARAM;
555609b9c6f2SSascha Wildner 			break;
555709b9c6f2SSascha Wildner 		case 0xf2:		/* song position pointer */
555809b9c6f2SSascha Wildner 			sub->temp_1[1] = b;
555909b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_2PARAM_1;
556009b9c6f2SSascha Wildner 			break;
556109b9c6f2SSascha Wildner 		case 0xf4:		/* unknown */
556209b9c6f2SSascha Wildner 		case 0xf5:		/* unknown */
556309b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_UNKNOWN;
556409b9c6f2SSascha Wildner 			break;
556509b9c6f2SSascha Wildner 		case 0xf6:		/* tune request */
556609b9c6f2SSascha Wildner 			sub->temp_1[0] = p0 | 0x05;
556709b9c6f2SSascha Wildner 			sub->temp_1[1] = 0xf6;
556809b9c6f2SSascha Wildner 			sub->temp_1[2] = 0;
556909b9c6f2SSascha Wildner 			sub->temp_1[3] = 0;
557009b9c6f2SSascha Wildner 			sub->temp_cmd = sub->temp_1;
557109b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_UNKNOWN;
557209b9c6f2SSascha Wildner 			return (1);
557309b9c6f2SSascha Wildner 
557409b9c6f2SSascha Wildner 		case 0xf7:		/* system exclusive end */
557509b9c6f2SSascha Wildner 			switch (sub->state) {
557609b9c6f2SSascha Wildner 			case UMIDI_ST_SYSEX_0:
557709b9c6f2SSascha Wildner 				sub->temp_1[0] = p0 | 0x05;
557809b9c6f2SSascha Wildner 				sub->temp_1[1] = 0xf7;
557909b9c6f2SSascha Wildner 				sub->temp_1[2] = 0;
558009b9c6f2SSascha Wildner 				sub->temp_1[3] = 0;
558109b9c6f2SSascha Wildner 				sub->temp_cmd = sub->temp_1;
558209b9c6f2SSascha Wildner 				sub->state = UMIDI_ST_UNKNOWN;
558309b9c6f2SSascha Wildner 				return (1);
558409b9c6f2SSascha Wildner 			case UMIDI_ST_SYSEX_1:
558509b9c6f2SSascha Wildner 				sub->temp_1[0] = p0 | 0x06;
558609b9c6f2SSascha Wildner 				sub->temp_1[2] = 0xf7;
558709b9c6f2SSascha Wildner 				sub->temp_1[3] = 0;
558809b9c6f2SSascha Wildner 				sub->temp_cmd = sub->temp_1;
558909b9c6f2SSascha Wildner 				sub->state = UMIDI_ST_UNKNOWN;
559009b9c6f2SSascha Wildner 				return (1);
559109b9c6f2SSascha Wildner 			case UMIDI_ST_SYSEX_2:
559209b9c6f2SSascha Wildner 				sub->temp_1[0] = p0 | 0x07;
559309b9c6f2SSascha Wildner 				sub->temp_1[3] = 0xf7;
559409b9c6f2SSascha Wildner 				sub->temp_cmd = sub->temp_1;
559509b9c6f2SSascha Wildner 				sub->state = UMIDI_ST_UNKNOWN;
559609b9c6f2SSascha Wildner 				return (1);
559709b9c6f2SSascha Wildner 			}
559809b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_UNKNOWN;
559909b9c6f2SSascha Wildner 			break;
560009b9c6f2SSascha Wildner 		}
560109b9c6f2SSascha Wildner 	} else if (b >= 0x80) {
560209b9c6f2SSascha Wildner 		sub->temp_1[1] = b;
560309b9c6f2SSascha Wildner 		if ((b >= 0xc0) && (b <= 0xdf)) {
560409b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_1PARAM;
560509b9c6f2SSascha Wildner 		} else {
560609b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_2PARAM_1;
560709b9c6f2SSascha Wildner 		}
560809b9c6f2SSascha Wildner 	} else {			/* b < 0x80 */
560909b9c6f2SSascha Wildner 		switch (sub->state) {
561009b9c6f2SSascha Wildner 		case UMIDI_ST_1PARAM:
561109b9c6f2SSascha Wildner 			if (sub->temp_1[1] < 0xf0) {
561209b9c6f2SSascha Wildner 				p0 |= sub->temp_1[1] >> 4;
561309b9c6f2SSascha Wildner 			} else {
561409b9c6f2SSascha Wildner 				p0 |= 0x02;
561509b9c6f2SSascha Wildner 				sub->state = UMIDI_ST_UNKNOWN;
561609b9c6f2SSascha Wildner 			}
561709b9c6f2SSascha Wildner 			sub->temp_1[0] = p0;
561809b9c6f2SSascha Wildner 			sub->temp_1[2] = b;
561909b9c6f2SSascha Wildner 			sub->temp_1[3] = 0;
562009b9c6f2SSascha Wildner 			sub->temp_cmd = sub->temp_1;
562109b9c6f2SSascha Wildner 			return (1);
562209b9c6f2SSascha Wildner 		case UMIDI_ST_2PARAM_1:
562309b9c6f2SSascha Wildner 			sub->temp_1[2] = b;
562409b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_2PARAM_2;
562509b9c6f2SSascha Wildner 			break;
562609b9c6f2SSascha Wildner 		case UMIDI_ST_2PARAM_2:
562709b9c6f2SSascha Wildner 			if (sub->temp_1[1] < 0xf0) {
562809b9c6f2SSascha Wildner 				p0 |= sub->temp_1[1] >> 4;
562909b9c6f2SSascha Wildner 				sub->state = UMIDI_ST_2PARAM_1;
563009b9c6f2SSascha Wildner 			} else {
563109b9c6f2SSascha Wildner 				p0 |= 0x03;
563209b9c6f2SSascha Wildner 				sub->state = UMIDI_ST_UNKNOWN;
563309b9c6f2SSascha Wildner 			}
563409b9c6f2SSascha Wildner 			sub->temp_1[0] = p0;
563509b9c6f2SSascha Wildner 			sub->temp_1[3] = b;
563609b9c6f2SSascha Wildner 			sub->temp_cmd = sub->temp_1;
563709b9c6f2SSascha Wildner 			return (1);
563809b9c6f2SSascha Wildner 		case UMIDI_ST_SYSEX_0:
563909b9c6f2SSascha Wildner 			sub->temp_1[1] = b;
564009b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_SYSEX_1;
564109b9c6f2SSascha Wildner 			break;
564209b9c6f2SSascha Wildner 		case UMIDI_ST_SYSEX_1:
564309b9c6f2SSascha Wildner 			sub->temp_1[2] = b;
564409b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_SYSEX_2;
564509b9c6f2SSascha Wildner 			break;
564609b9c6f2SSascha Wildner 		case UMIDI_ST_SYSEX_2:
564709b9c6f2SSascha Wildner 			sub->temp_1[0] = p0 | 0x04;
564809b9c6f2SSascha Wildner 			sub->temp_1[3] = b;
564909b9c6f2SSascha Wildner 			sub->temp_cmd = sub->temp_1;
565009b9c6f2SSascha Wildner 			sub->state = UMIDI_ST_SYSEX_0;
565109b9c6f2SSascha Wildner 			return (1);
565209b9c6f2SSascha Wildner 		default:
565309b9c6f2SSascha Wildner 			break;
565409b9c6f2SSascha Wildner 		}
565509b9c6f2SSascha Wildner 	}
565609b9c6f2SSascha Wildner 	return (0);
565709b9c6f2SSascha Wildner }
565809b9c6f2SSascha Wildner 
565909b9c6f2SSascha Wildner static void
umidi_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)566009b9c6f2SSascha Wildner umidi_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
566109b9c6f2SSascha Wildner {
566209b9c6f2SSascha Wildner 	struct umidi_chan *chan = usbd_xfer_softc(xfer);
566309b9c6f2SSascha Wildner 	struct umidi_sub_chan *sub;
566409b9c6f2SSascha Wildner 	struct usb_page_cache *pc;
566509b9c6f2SSascha Wildner 	uint32_t actlen;
566609b9c6f2SSascha Wildner 	uint16_t nframes;
566709b9c6f2SSascha Wildner 	uint8_t buf;
566809b9c6f2SSascha Wildner 	uint8_t start_cable;
566909b9c6f2SSascha Wildner 	uint8_t tr_any;
567009b9c6f2SSascha Wildner 	int len;
567109b9c6f2SSascha Wildner 
567209b9c6f2SSascha Wildner 	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
567309b9c6f2SSascha Wildner 
567409b9c6f2SSascha Wildner 	/*
567509b9c6f2SSascha Wildner 	 * NOTE: Some MIDI devices only accept 4 bytes of data per
567609b9c6f2SSascha Wildner 	 * short terminated USB transfer.
567709b9c6f2SSascha Wildner 	 */
567809b9c6f2SSascha Wildner 	switch (USB_GET_STATE(xfer)) {
567909b9c6f2SSascha Wildner 	case USB_ST_TRANSFERRED:
568009b9c6f2SSascha Wildner 		DPRINTF("actlen=%d bytes\n", len);
568109b9c6f2SSascha Wildner 
568209b9c6f2SSascha Wildner 	case USB_ST_SETUP:
568309b9c6f2SSascha Wildner tr_setup:
568409b9c6f2SSascha Wildner 		DPRINTF("start\n");
568509b9c6f2SSascha Wildner 
568609b9c6f2SSascha Wildner 		nframes = 0;	/* reset */
568709b9c6f2SSascha Wildner 		start_cable = chan->curr_cable;
568809b9c6f2SSascha Wildner 		tr_any = 0;
568909b9c6f2SSascha Wildner 		pc = usbd_xfer_get_frame(xfer, 0);
569009b9c6f2SSascha Wildner 
569109b9c6f2SSascha Wildner 		while (1) {
569209b9c6f2SSascha Wildner 
569309b9c6f2SSascha Wildner 			/* round robin de-queueing */
569409b9c6f2SSascha Wildner 
569509b9c6f2SSascha Wildner 			sub = &chan->sub[chan->curr_cable];
569609b9c6f2SSascha Wildner 
569709b9c6f2SSascha Wildner 			if (sub->write_open) {
569809b9c6f2SSascha Wildner 				usb_fifo_get_data_linear(sub->fifo.fp[USB_FIFO_TX],
569909b9c6f2SSascha Wildner 				    &buf, 1, &actlen, 0);
570009b9c6f2SSascha Wildner 			} else {
570109b9c6f2SSascha Wildner 				actlen = 0;
570209b9c6f2SSascha Wildner 			}
570309b9c6f2SSascha Wildner 
570409b9c6f2SSascha Wildner 			if (actlen) {
570509b9c6f2SSascha Wildner 
570609b9c6f2SSascha Wildner 				tr_any = 1;
570709b9c6f2SSascha Wildner 
570809b9c6f2SSascha Wildner 				DPRINTF("byte=0x%02x from FIFO %u\n", buf,
570909b9c6f2SSascha Wildner 				    (unsigned int)chan->curr_cable);
571009b9c6f2SSascha Wildner 
571109b9c6f2SSascha Wildner 				if (umidi_convert_to_usb(sub, chan->curr_cable, buf)) {
571209b9c6f2SSascha Wildner 
571309b9c6f2SSascha Wildner 					DPRINTF("sub=0x%02x 0x%02x 0x%02x 0x%02x\n",
571409b9c6f2SSascha Wildner 					    sub->temp_cmd[0], sub->temp_cmd[1],
571509b9c6f2SSascha Wildner 					    sub->temp_cmd[2], sub->temp_cmd[3]);
571609b9c6f2SSascha Wildner 
571709b9c6f2SSascha Wildner 					usbd_copy_in(pc, nframes * 4, sub->temp_cmd, 4);
571809b9c6f2SSascha Wildner 
571909b9c6f2SSascha Wildner 					nframes++;
572009b9c6f2SSascha Wildner 
572109b9c6f2SSascha Wildner 					if ((nframes >= UMIDI_TX_FRAMES) || (chan->single_command != 0))
572209b9c6f2SSascha Wildner 						break;
572309b9c6f2SSascha Wildner 				} else {
572409b9c6f2SSascha Wildner 					continue;
572509b9c6f2SSascha Wildner 				}
572609b9c6f2SSascha Wildner 			}
572709b9c6f2SSascha Wildner 
572809b9c6f2SSascha Wildner 			chan->curr_cable++;
5729a963377aSSascha Wildner 			if (chan->curr_cable >= chan->max_emb_jack)
573009b9c6f2SSascha Wildner 				chan->curr_cable = 0;
573109b9c6f2SSascha Wildner 
573209b9c6f2SSascha Wildner 			if (chan->curr_cable == start_cable) {
573309b9c6f2SSascha Wildner 				if (tr_any == 0)
573409b9c6f2SSascha Wildner 					break;
573509b9c6f2SSascha Wildner 				tr_any = 0;
573609b9c6f2SSascha Wildner 			}
573709b9c6f2SSascha Wildner 		}
573809b9c6f2SSascha Wildner 
573909b9c6f2SSascha Wildner 		if (nframes != 0) {
574009b9c6f2SSascha Wildner 			DPRINTF("Transferring %d frames\n", (int)nframes);
574109b9c6f2SSascha Wildner 			usbd_xfer_set_frame_len(xfer, 0, 4 * nframes);
574209b9c6f2SSascha Wildner 			usbd_transfer_submit(xfer);
574309b9c6f2SSascha Wildner 		}
574409b9c6f2SSascha Wildner 		break;
574509b9c6f2SSascha Wildner 
574609b9c6f2SSascha Wildner 	default:			/* Error */
574709b9c6f2SSascha Wildner 
574809b9c6f2SSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
574909b9c6f2SSascha Wildner 
575009b9c6f2SSascha Wildner 		if (error != USB_ERR_CANCELLED) {
575109b9c6f2SSascha Wildner 			/* try to clear stall first */
575209b9c6f2SSascha Wildner 			usbd_xfer_set_stall(xfer);
575309b9c6f2SSascha Wildner 			goto tr_setup;
575409b9c6f2SSascha Wildner 		}
575509b9c6f2SSascha Wildner 		break;
575609b9c6f2SSascha Wildner 	}
575709b9c6f2SSascha Wildner }
575809b9c6f2SSascha Wildner 
575909b9c6f2SSascha Wildner static struct umidi_sub_chan *
umidi_sub_by_fifo(struct usb_fifo * fifo)576009b9c6f2SSascha Wildner umidi_sub_by_fifo(struct usb_fifo *fifo)
576109b9c6f2SSascha Wildner {
576209b9c6f2SSascha Wildner 	struct umidi_chan *chan = usb_fifo_softc(fifo);
576309b9c6f2SSascha Wildner 	struct umidi_sub_chan *sub;
576409b9c6f2SSascha Wildner 	uint32_t n;
576509b9c6f2SSascha Wildner 
5766a963377aSSascha Wildner 	for (n = 0; n < UMIDI_EMB_JACK_MAX; n++) {
576709b9c6f2SSascha Wildner 		sub = &chan->sub[n];
576809b9c6f2SSascha Wildner 		if ((sub->fifo.fp[USB_FIFO_RX] == fifo) ||
576909b9c6f2SSascha Wildner 		    (sub->fifo.fp[USB_FIFO_TX] == fifo)) {
577009b9c6f2SSascha Wildner 			return (sub);
577109b9c6f2SSascha Wildner 		}
577209b9c6f2SSascha Wildner 	}
577309b9c6f2SSascha Wildner 
577409b9c6f2SSascha Wildner 	panic("%s:%d cannot find usb_fifo!\n",
577509b9c6f2SSascha Wildner 	    __FILE__, __LINE__);
577609b9c6f2SSascha Wildner 
577709b9c6f2SSascha Wildner 	return (NULL);
577809b9c6f2SSascha Wildner }
577909b9c6f2SSascha Wildner 
578009b9c6f2SSascha Wildner static void
umidi_start_read(struct usb_fifo * fifo)578109b9c6f2SSascha Wildner umidi_start_read(struct usb_fifo *fifo)
578209b9c6f2SSascha Wildner {
578309b9c6f2SSascha Wildner 	struct umidi_chan *chan = usb_fifo_softc(fifo);
578409b9c6f2SSascha Wildner 
578509b9c6f2SSascha Wildner 	usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]);
578609b9c6f2SSascha Wildner }
578709b9c6f2SSascha Wildner 
578809b9c6f2SSascha Wildner static void
umidi_stop_read(struct usb_fifo * fifo)578909b9c6f2SSascha Wildner umidi_stop_read(struct usb_fifo *fifo)
579009b9c6f2SSascha Wildner {
579109b9c6f2SSascha Wildner 	struct umidi_chan *chan = usb_fifo_softc(fifo);
579209b9c6f2SSascha Wildner 	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
579309b9c6f2SSascha Wildner 
579409b9c6f2SSascha Wildner 	DPRINTF("\n");
579509b9c6f2SSascha Wildner 
579609b9c6f2SSascha Wildner 	sub->read_open = 0;
579709b9c6f2SSascha Wildner 
579809b9c6f2SSascha Wildner 	if (--(chan->read_open_refcount) == 0) {
579909b9c6f2SSascha Wildner 		/*
580009b9c6f2SSascha Wildner 		 * XXX don't stop the read transfer here, hence that causes
580109b9c6f2SSascha Wildner 		 * problems with some MIDI adapters
580209b9c6f2SSascha Wildner 		 */
580309b9c6f2SSascha Wildner 		DPRINTF("(stopping read transfer)\n");
580409b9c6f2SSascha Wildner 	}
580509b9c6f2SSascha Wildner }
580609b9c6f2SSascha Wildner 
580709b9c6f2SSascha Wildner static void
umidi_start_write(struct usb_fifo * fifo)580809b9c6f2SSascha Wildner umidi_start_write(struct usb_fifo *fifo)
580909b9c6f2SSascha Wildner {
581009b9c6f2SSascha Wildner 	struct umidi_chan *chan = usb_fifo_softc(fifo);
581109b9c6f2SSascha Wildner 
581209b9c6f2SSascha Wildner 	usbd_transfer_start(chan->xfer[UMIDI_TX_TRANSFER]);
581309b9c6f2SSascha Wildner }
581409b9c6f2SSascha Wildner 
581509b9c6f2SSascha Wildner static void
umidi_stop_write(struct usb_fifo * fifo)581609b9c6f2SSascha Wildner umidi_stop_write(struct usb_fifo *fifo)
581709b9c6f2SSascha Wildner {
581809b9c6f2SSascha Wildner 	struct umidi_chan *chan = usb_fifo_softc(fifo);
581909b9c6f2SSascha Wildner 	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
582009b9c6f2SSascha Wildner 
582109b9c6f2SSascha Wildner 	DPRINTF("\n");
582209b9c6f2SSascha Wildner 
582309b9c6f2SSascha Wildner 	sub->write_open = 0;
582409b9c6f2SSascha Wildner 
582509b9c6f2SSascha Wildner 	if (--(chan->write_open_refcount) == 0) {
582609b9c6f2SSascha Wildner 		DPRINTF("(stopping write transfer)\n");
582709b9c6f2SSascha Wildner 		usbd_transfer_stop(chan->xfer[UMIDI_TX_TRANSFER]);
582809b9c6f2SSascha Wildner 	}
582909b9c6f2SSascha Wildner }
583009b9c6f2SSascha Wildner 
583109b9c6f2SSascha Wildner static int
umidi_open(struct usb_fifo * fifo,int fflags)583209b9c6f2SSascha Wildner umidi_open(struct usb_fifo *fifo, int fflags)
583309b9c6f2SSascha Wildner {
583409b9c6f2SSascha Wildner 	struct umidi_chan *chan = usb_fifo_softc(fifo);
583509b9c6f2SSascha Wildner 	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
583609b9c6f2SSascha Wildner 
583709b9c6f2SSascha Wildner 	if (fflags & FREAD) {
583809b9c6f2SSascha Wildner 		if (usb_fifo_alloc_buffer(fifo, 4, (1024 / 4))) {
583909b9c6f2SSascha Wildner 			return (ENOMEM);
584009b9c6f2SSascha Wildner 		}
584109b9c6f2SSascha Wildner 		lockmgr(&chan->lock, LK_EXCLUSIVE);
584209b9c6f2SSascha Wildner 		chan->read_open_refcount++;
584309b9c6f2SSascha Wildner 		sub->read_open = 1;
584409b9c6f2SSascha Wildner 		lockmgr(&chan->lock, LK_RELEASE);
584509b9c6f2SSascha Wildner 	}
584609b9c6f2SSascha Wildner 	if (fflags & FWRITE) {
584709b9c6f2SSascha Wildner 		if (usb_fifo_alloc_buffer(fifo, 32, (1024 / 32))) {
584809b9c6f2SSascha Wildner 			return (ENOMEM);
584909b9c6f2SSascha Wildner 		}
585009b9c6f2SSascha Wildner 		/* clear stall first */
585109b9c6f2SSascha Wildner 		lockmgr(&chan->lock, LK_EXCLUSIVE);
585209b9c6f2SSascha Wildner 		chan->write_open_refcount++;
585309b9c6f2SSascha Wildner 		sub->write_open = 1;
585409b9c6f2SSascha Wildner 
585509b9c6f2SSascha Wildner 		/* reset */
585609b9c6f2SSascha Wildner 		sub->state = UMIDI_ST_UNKNOWN;
585709b9c6f2SSascha Wildner 		lockmgr(&chan->lock, LK_RELEASE);
585809b9c6f2SSascha Wildner 	}
585909b9c6f2SSascha Wildner 	return (0);			/* success */
586009b9c6f2SSascha Wildner }
586109b9c6f2SSascha Wildner 
586209b9c6f2SSascha Wildner static void
umidi_close(struct usb_fifo * fifo,int fflags)586309b9c6f2SSascha Wildner umidi_close(struct usb_fifo *fifo, int fflags)
586409b9c6f2SSascha Wildner {
586509b9c6f2SSascha Wildner 	if (fflags & FREAD) {
586609b9c6f2SSascha Wildner 		usb_fifo_free_buffer(fifo);
586709b9c6f2SSascha Wildner 	}
586809b9c6f2SSascha Wildner 	if (fflags & FWRITE) {
586909b9c6f2SSascha Wildner 		usb_fifo_free_buffer(fifo);
587009b9c6f2SSascha Wildner 	}
587109b9c6f2SSascha Wildner }
587209b9c6f2SSascha Wildner 
587309b9c6f2SSascha Wildner 
587409b9c6f2SSascha Wildner static int
umidi_ioctl(struct usb_fifo * fifo,u_long cmd,void * data,int fflags)587509b9c6f2SSascha Wildner umidi_ioctl(struct usb_fifo *fifo, u_long cmd, void *data,
587609b9c6f2SSascha Wildner     int fflags)
587709b9c6f2SSascha Wildner {
587809b9c6f2SSascha Wildner 	return (ENODEV);
587909b9c6f2SSascha Wildner }
588009b9c6f2SSascha Wildner 
588109b9c6f2SSascha Wildner static void
umidi_init(device_t dev)588209b9c6f2SSascha Wildner umidi_init(device_t dev)
588309b9c6f2SSascha Wildner {
588409b9c6f2SSascha Wildner 	struct uaudio_softc *sc = device_get_softc(dev);
588509b9c6f2SSascha Wildner 	struct umidi_chan *chan = &sc->sc_midi_chan;
588609b9c6f2SSascha Wildner 
588709b9c6f2SSascha Wildner 	lockinit(&chan->lock, "umidi lock", 0, LK_CANRECURSE);
588809b9c6f2SSascha Wildner }
588909b9c6f2SSascha Wildner 
589009b9c6f2SSascha Wildner static struct usb_fifo_methods umidi_fifo_methods = {
589109b9c6f2SSascha Wildner 	.f_start_read = &umidi_start_read,
589209b9c6f2SSascha Wildner 	.f_start_write = &umidi_start_write,
589309b9c6f2SSascha Wildner 	.f_stop_read = &umidi_stop_read,
589409b9c6f2SSascha Wildner 	.f_stop_write = &umidi_stop_write,
589509b9c6f2SSascha Wildner 	.f_open = &umidi_open,
589609b9c6f2SSascha Wildner 	.f_close = &umidi_close,
589709b9c6f2SSascha Wildner 	.f_ioctl = &umidi_ioctl,
589809b9c6f2SSascha Wildner 	.basename[0] = "umidi",
589909b9c6f2SSascha Wildner };
590009b9c6f2SSascha Wildner 
590109b9c6f2SSascha Wildner static int
umidi_probe(device_t dev)590209b9c6f2SSascha Wildner umidi_probe(device_t dev)
590309b9c6f2SSascha Wildner {
590409b9c6f2SSascha Wildner 	struct uaudio_softc *sc = device_get_softc(dev);
590509b9c6f2SSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
590609b9c6f2SSascha Wildner 	struct umidi_chan *chan = &sc->sc_midi_chan;
590709b9c6f2SSascha Wildner 	struct umidi_sub_chan *sub;
590809b9c6f2SSascha Wildner 	int unit = device_get_unit(dev);
590909b9c6f2SSascha Wildner 	int error;
591009b9c6f2SSascha Wildner 	uint32_t n;
591109b9c6f2SSascha Wildner 
591209b9c6f2SSascha Wildner 	if (usb_test_quirk(uaa, UQ_SINGLE_CMD_MIDI))
591309b9c6f2SSascha Wildner 		chan->single_command = 1;
591409b9c6f2SSascha Wildner 
591509b9c6f2SSascha Wildner 	if (usbd_set_alt_interface_index(sc->sc_udev, chan->iface_index,
591609b9c6f2SSascha Wildner 	    chan->iface_alt_index)) {
591709b9c6f2SSascha Wildner 		DPRINTF("setting of alternate index failed!\n");
591809b9c6f2SSascha Wildner 		goto detach;
591909b9c6f2SSascha Wildner 	}
592009b9c6f2SSascha Wildner 	usbd_set_parent_iface(sc->sc_udev, chan->iface_index,
592109b9c6f2SSascha Wildner 	    sc->sc_mixer_iface_index);
592209b9c6f2SSascha Wildner 
592309b9c6f2SSascha Wildner 	error = usbd_transfer_setup(uaa->device, &chan->iface_index,
592409b9c6f2SSascha Wildner 	    chan->xfer, umidi_config, UMIDI_N_TRANSFER,
592509b9c6f2SSascha Wildner 	    chan, &chan->lock);
592609b9c6f2SSascha Wildner 	if (error) {
592709b9c6f2SSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
592809b9c6f2SSascha Wildner 		goto detach;
592909b9c6f2SSascha Wildner 	}
5930a963377aSSascha Wildner 
5931a963377aSSascha Wildner 	/*
5932a963377aSSascha Wildner 	 * Some USB MIDI device makers couldn't resist using
5933a963377aSSascha Wildner 	 * wMaxPacketSize = 4 for RX and TX BULK endpoints, although
5934a963377aSSascha Wildner 	 * that size is an unsupported value for FULL speed BULK
5935a963377aSSascha Wildner 	 * endpoints. The same applies to some HIGH speed MIDI devices
5936a963377aSSascha Wildner 	 * which are using a wMaxPacketSize different from 512 bytes.
5937a963377aSSascha Wildner 	 *
5938a963377aSSascha Wildner 	 * Refer to section 5.8.3 in USB 2.0 PDF: Cite: "All Host
5939a963377aSSascha Wildner 	 * Controllers are required to have support for 8-, 16-, 32-,
5940a963377aSSascha Wildner 	 * and 64-byte maximum packet sizes for full-speed bulk
5941a963377aSSascha Wildner 	 * endpoints and 512 bytes for high-speed bulk endpoints."
5942a963377aSSascha Wildner 	 */
5943a963377aSSascha Wildner 	if (usbd_xfer_maxp_was_clamped(chan->xfer[UMIDI_TX_TRANSFER]))
5944a963377aSSascha Wildner 		chan->single_command = 1;
5945a963377aSSascha Wildner 
5946a963377aSSascha Wildner 	if (chan->single_command != 0)
5947a963377aSSascha Wildner 		device_printf(dev, "Single command MIDI quirk enabled\n");
5948a963377aSSascha Wildner 
5949a963377aSSascha Wildner 	if ((chan->max_emb_jack == 0) ||
5950a963377aSSascha Wildner 	    (chan->max_emb_jack > UMIDI_EMB_JACK_MAX)) {
5951a963377aSSascha Wildner 		chan->max_emb_jack = UMIDI_EMB_JACK_MAX;
595209b9c6f2SSascha Wildner 	}
595309b9c6f2SSascha Wildner 
5954a963377aSSascha Wildner 	for (n = 0; n < chan->max_emb_jack; n++) {
595509b9c6f2SSascha Wildner 
595609b9c6f2SSascha Wildner 		sub = &chan->sub[n];
595709b9c6f2SSascha Wildner 
595809b9c6f2SSascha Wildner 		error = usb_fifo_attach(sc->sc_udev, chan, &chan->lock,
595909b9c6f2SSascha Wildner 		    &umidi_fifo_methods, &sub->fifo, unit, n,
596009b9c6f2SSascha Wildner 		    chan->iface_index,
596109b9c6f2SSascha Wildner 		    UID_ROOT, GID_OPERATOR, 0644);
596209b9c6f2SSascha Wildner 		if (error) {
596309b9c6f2SSascha Wildner 			goto detach;
596409b9c6f2SSascha Wildner 		}
596509b9c6f2SSascha Wildner 	}
596609b9c6f2SSascha Wildner 
596709b9c6f2SSascha Wildner 	lockmgr(&chan->lock, LK_EXCLUSIVE);
596809b9c6f2SSascha Wildner 
596909b9c6f2SSascha Wildner 	/*
597009b9c6f2SSascha Wildner 	 * NOTE: At least one device will not work properly unless the
597109b9c6f2SSascha Wildner 	 * BULK IN pipe is open all the time. This might have to do
597209b9c6f2SSascha Wildner 	 * about that the internal queues of the device overflow if we
597309b9c6f2SSascha Wildner 	 * don't read them regularly.
597409b9c6f2SSascha Wildner 	 */
597509b9c6f2SSascha Wildner 	usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]);
597609b9c6f2SSascha Wildner 
597709b9c6f2SSascha Wildner 	lockmgr(&chan->lock, LK_RELEASE);
597809b9c6f2SSascha Wildner 
597909b9c6f2SSascha Wildner 	return (0);			/* success */
598009b9c6f2SSascha Wildner 
598109b9c6f2SSascha Wildner detach:
598209b9c6f2SSascha Wildner 	return (ENXIO);			/* failure */
598309b9c6f2SSascha Wildner }
598409b9c6f2SSascha Wildner 
598509b9c6f2SSascha Wildner static int
umidi_detach(device_t dev)598609b9c6f2SSascha Wildner umidi_detach(device_t dev)
598709b9c6f2SSascha Wildner {
598809b9c6f2SSascha Wildner 	struct uaudio_softc *sc = device_get_softc(dev);
598909b9c6f2SSascha Wildner 	struct umidi_chan *chan = &sc->sc_midi_chan;
599009b9c6f2SSascha Wildner 	uint32_t n;
599109b9c6f2SSascha Wildner 
5992a963377aSSascha Wildner 	for (n = 0; n < UMIDI_EMB_JACK_MAX; n++)
599309b9c6f2SSascha Wildner 		usb_fifo_detach(&chan->sub[n].fifo);
599409b9c6f2SSascha Wildner 
599509b9c6f2SSascha Wildner 	lockmgr(&chan->lock, LK_EXCLUSIVE);
599609b9c6f2SSascha Wildner 
599709b9c6f2SSascha Wildner 	usbd_transfer_stop(chan->xfer[UMIDI_RX_TRANSFER]);
599809b9c6f2SSascha Wildner 
599909b9c6f2SSascha Wildner 	lockmgr(&chan->lock, LK_RELEASE);
600009b9c6f2SSascha Wildner 
600109b9c6f2SSascha Wildner 	usbd_transfer_unsetup(chan->xfer, UMIDI_N_TRANSFER);
600209b9c6f2SSascha Wildner 
600309b9c6f2SSascha Wildner 	lockuninit(&chan->lock);
600409b9c6f2SSascha Wildner 
600509b9c6f2SSascha Wildner 	return (0);
600609b9c6f2SSascha Wildner }
600709b9c6f2SSascha Wildner 
6008a963377aSSascha Wildner static void
uaudio_hid_rx_callback(struct usb_xfer * xfer,usb_error_t error)6009a963377aSSascha Wildner uaudio_hid_rx_callback(struct usb_xfer *xfer, usb_error_t error)
6010a963377aSSascha Wildner {
6011a963377aSSascha Wildner 	struct uaudio_softc *sc = usbd_xfer_softc(xfer);
6012a963377aSSascha Wildner 	const uint8_t *buffer = usbd_xfer_get_frame_buffer(xfer, 0);
6013a963377aSSascha Wildner 	struct snd_mixer *m;
6014a963377aSSascha Wildner 	uint8_t id;
6015a963377aSSascha Wildner 	int actlen;
6016a963377aSSascha Wildner 
6017a963377aSSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
6018a963377aSSascha Wildner 
6019a963377aSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
6020a963377aSSascha Wildner 	case USB_ST_TRANSFERRED:
6021a963377aSSascha Wildner 		DPRINTF("actlen=%d\n", actlen);
6022a963377aSSascha Wildner 
6023a963377aSSascha Wildner 		if (actlen != 0 &&
6024a963377aSSascha Wildner 		    (sc->sc_hid.flags & UAUDIO_HID_HAS_ID)) {
6025a963377aSSascha Wildner 			id = *buffer;
6026a963377aSSascha Wildner 			buffer++;
6027a963377aSSascha Wildner 			actlen--;
6028a963377aSSascha Wildner 		} else {
6029a963377aSSascha Wildner 			id = 0;
6030a963377aSSascha Wildner 		}
6031a963377aSSascha Wildner 
6032a963377aSSascha Wildner 		m = sc->sc_mixer_dev;
6033a963377aSSascha Wildner 
6034a963377aSSascha Wildner 		if ((sc->sc_hid.flags & UAUDIO_HID_HAS_MUTE) &&
6035a963377aSSascha Wildner 		    (sc->sc_hid.mute_id == id) &&
6036a963377aSSascha Wildner 		    hid_get_data(buffer, actlen,
6037a963377aSSascha Wildner 		    &sc->sc_hid.mute_loc)) {
6038a963377aSSascha Wildner 
6039a963377aSSascha Wildner 			DPRINTF("Mute toggle\n");
6040a963377aSSascha Wildner 
6041a963377aSSascha Wildner 			mixer_hwvol_mute_locked(m);
6042a963377aSSascha Wildner 		}
6043a963377aSSascha Wildner 
6044a963377aSSascha Wildner 		if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_UP) &&
6045a963377aSSascha Wildner 		    (sc->sc_hid.volume_up_id == id) &&
6046a963377aSSascha Wildner 		    hid_get_data(buffer, actlen,
6047a963377aSSascha Wildner 		    &sc->sc_hid.volume_up_loc)) {
6048a963377aSSascha Wildner 
6049a963377aSSascha Wildner 			DPRINTF("Volume Up\n");
6050a963377aSSascha Wildner 
6051a963377aSSascha Wildner 			mixer_hwvol_step_locked(m, 1, 1);
6052a963377aSSascha Wildner 		}
6053a963377aSSascha Wildner 
6054a963377aSSascha Wildner 		if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_DOWN) &&
6055a963377aSSascha Wildner 		    (sc->sc_hid.volume_down_id == id) &&
6056a963377aSSascha Wildner 		    hid_get_data(buffer, actlen,
6057a963377aSSascha Wildner 		    &sc->sc_hid.volume_down_loc)) {
6058a963377aSSascha Wildner 
6059a963377aSSascha Wildner 			DPRINTF("Volume Down\n");
6060a963377aSSascha Wildner 
6061a963377aSSascha Wildner 			mixer_hwvol_step_locked(m, -1, -1);
6062a963377aSSascha Wildner 		}
6063a963377aSSascha Wildner 
6064a963377aSSascha Wildner 	case USB_ST_SETUP:
6065a963377aSSascha Wildner tr_setup:
6066a963377aSSascha Wildner 		/* check if we can put more data into the FIFO */
6067a963377aSSascha Wildner 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
6068a963377aSSascha Wildner 		usbd_transfer_submit(xfer);
6069a963377aSSascha Wildner 		break;
6070a963377aSSascha Wildner 
6071a963377aSSascha Wildner 	default:			/* Error */
6072a963377aSSascha Wildner 
6073a963377aSSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
6074a963377aSSascha Wildner 
6075a963377aSSascha Wildner 		if (error != USB_ERR_CANCELLED) {
6076a963377aSSascha Wildner 			/* try to clear stall first */
6077a963377aSSascha Wildner 			usbd_xfer_set_stall(xfer);
6078a963377aSSascha Wildner 			goto tr_setup;
6079a963377aSSascha Wildner 		}
6080a963377aSSascha Wildner 		break;
6081a963377aSSascha Wildner 	}
6082a963377aSSascha Wildner }
6083a963377aSSascha Wildner 
6084a963377aSSascha Wildner static int
uaudio_hid_probe(struct uaudio_softc * sc,struct usb_attach_arg * uaa)6085a963377aSSascha Wildner uaudio_hid_probe(struct uaudio_softc *sc,
6086a963377aSSascha Wildner     struct usb_attach_arg *uaa)
6087a963377aSSascha Wildner {
6088a963377aSSascha Wildner 	void *d_ptr;
6089a963377aSSascha Wildner 	uint32_t flags;
6090a963377aSSascha Wildner 	uint16_t d_len;
6091a963377aSSascha Wildner 	uint8_t id;
6092a963377aSSascha Wildner 	int error;
6093a963377aSSascha Wildner 
6094a963377aSSascha Wildner 	if (!(sc->sc_hid.flags & UAUDIO_HID_VALID))
6095a963377aSSascha Wildner 		return (-1);
6096a963377aSSascha Wildner 
6097a963377aSSascha Wildner 	if (sc->sc_mixer_lock == NULL)
6098a963377aSSascha Wildner 		return (-1);
6099a963377aSSascha Wildner 
6100a963377aSSascha Wildner 	/* Get HID descriptor */
6101a963377aSSascha Wildner 	error = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
6102a963377aSSascha Wildner 	    &d_len, M_TEMP, sc->sc_hid.iface_index);
6103a963377aSSascha Wildner 
6104a963377aSSascha Wildner 	if (error) {
6105a963377aSSascha Wildner 		DPRINTF("error reading report description\n");
6106a963377aSSascha Wildner 		return (-1);
6107a963377aSSascha Wildner 	}
6108a963377aSSascha Wildner 
6109a963377aSSascha Wildner 	/* check if there is an ID byte */
6110a963377aSSascha Wildner 	hid_report_size(d_ptr, d_len, hid_input, &id);
6111a963377aSSascha Wildner 
6112a963377aSSascha Wildner 	if (id != 0)
6113a963377aSSascha Wildner 		sc->sc_hid.flags |= UAUDIO_HID_HAS_ID;
6114a963377aSSascha Wildner 
6115a963377aSSascha Wildner 	if (hid_locate(d_ptr, d_len,
6116a963377aSSascha Wildner 	    HID_USAGE2(HUP_CONSUMER, 0xE9 /* Volume Increment */),
6117a963377aSSascha Wildner 	    hid_input, 0, &sc->sc_hid.volume_up_loc, &flags,
6118a963377aSSascha Wildner 	    &sc->sc_hid.volume_up_id)) {
6119a963377aSSascha Wildner 		if (flags & HIO_VARIABLE)
6120a963377aSSascha Wildner 			sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_UP;
6121a963377aSSascha Wildner 		DPRINTFN(1, "Found Volume Up key\n");
6122a963377aSSascha Wildner 	}
6123a963377aSSascha Wildner 
6124a963377aSSascha Wildner 	if (hid_locate(d_ptr, d_len,
6125a963377aSSascha Wildner 	    HID_USAGE2(HUP_CONSUMER, 0xEA /* Volume Decrement */),
6126a963377aSSascha Wildner 	    hid_input, 0, &sc->sc_hid.volume_down_loc, &flags,
6127a963377aSSascha Wildner 	    &sc->sc_hid.volume_down_id)) {
6128a963377aSSascha Wildner 		if (flags & HIO_VARIABLE)
6129a963377aSSascha Wildner 			sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_DOWN;
6130a963377aSSascha Wildner 		DPRINTFN(1, "Found Volume Down key\n");
6131a963377aSSascha Wildner 	}
6132a963377aSSascha Wildner 
6133a963377aSSascha Wildner 	if (hid_locate(d_ptr, d_len,
6134a963377aSSascha Wildner 	    HID_USAGE2(HUP_CONSUMER, 0xE2 /* Mute */),
6135a963377aSSascha Wildner 	    hid_input, 0, &sc->sc_hid.mute_loc, &flags,
6136a963377aSSascha Wildner 	    &sc->sc_hid.mute_id)) {
6137a963377aSSascha Wildner 		if (flags & HIO_VARIABLE)
6138a963377aSSascha Wildner 			sc->sc_hid.flags |= UAUDIO_HID_HAS_MUTE;
6139a963377aSSascha Wildner 		DPRINTFN(1, "Found Mute key\n");
6140a963377aSSascha Wildner 	}
6141a963377aSSascha Wildner 
6142a963377aSSascha Wildner 	kfree(d_ptr, M_TEMP);
6143a963377aSSascha Wildner 
6144a963377aSSascha Wildner 	if (!(sc->sc_hid.flags & (UAUDIO_HID_HAS_VOLUME_UP |
6145a963377aSSascha Wildner 	    UAUDIO_HID_HAS_VOLUME_DOWN |
6146a963377aSSascha Wildner 	    UAUDIO_HID_HAS_MUTE))) {
6147a963377aSSascha Wildner 		DPRINTFN(1, "Did not find any volume related keys\n");
6148a963377aSSascha Wildner 		return (-1);
6149a963377aSSascha Wildner 	}
6150a963377aSSascha Wildner 
6151a963377aSSascha Wildner 	/* prevent the uhid driver from attaching */
6152a963377aSSascha Wildner 	usbd_set_parent_iface(uaa->device, sc->sc_hid.iface_index,
6153a963377aSSascha Wildner 	    sc->sc_mixer_iface_index);
6154a963377aSSascha Wildner 
6155a963377aSSascha Wildner 	/* allocate USB transfers */
6156a963377aSSascha Wildner 	error = usbd_transfer_setup(uaa->device, &sc->sc_hid.iface_index,
6157a963377aSSascha Wildner 	    sc->sc_hid.xfer, uaudio_hid_config, UAUDIO_HID_N_TRANSFER,
6158a963377aSSascha Wildner 	    sc, sc->sc_mixer_lock);
6159a963377aSSascha Wildner 	if (error) {
6160a963377aSSascha Wildner 		DPRINTF("error=%s\n", usbd_errstr(error));
6161a963377aSSascha Wildner 		return (-1);
6162a963377aSSascha Wildner 	}
6163a963377aSSascha Wildner 	return (0);
6164a963377aSSascha Wildner }
6165a963377aSSascha Wildner 
6166a963377aSSascha Wildner static void
uaudio_hid_detach(struct uaudio_softc * sc)6167a963377aSSascha Wildner uaudio_hid_detach(struct uaudio_softc *sc)
6168a963377aSSascha Wildner {
6169a963377aSSascha Wildner 	usbd_transfer_unsetup(sc->sc_hid.xfer, UAUDIO_HID_N_TRANSFER);
6170a963377aSSascha Wildner }
6171a963377aSSascha Wildner 
6172d38ad084SSascha Wildner DRIVER_MODULE_ORDERED(uaudio, uhub, uaudio_driver, &uaudio_devclass, NULL, NULL, SI_ORDER_ANY);
617309b9c6f2SSascha Wildner MODULE_DEPEND(uaudio, usb, 1, 1, 1);
617409b9c6f2SSascha Wildner MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
617509b9c6f2SSascha Wildner MODULE_VERSION(uaudio, 1);
6176