1184610Salfred/*	$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $	*/
2184610Salfred/*	$FreeBSD$ */
3184610Salfred
4184610Salfred/*-
5184610Salfred * Copyright (c) 1999 The NetBSD Foundation, Inc.
6184610Salfred * All rights reserved.
7184610Salfred *
8184610Salfred * This code is derived from software contributed to The NetBSD Foundation
9184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at
10184610Salfred * Carlstedt Research & Technology.
11184610Salfred *
12184610Salfred * Redistribution and use in source and binary forms, with or without
13184610Salfred * modification, are permitted provided that the following conditions
14184610Salfred * are met:
15184610Salfred * 1. Redistributions of source code must retain the above copyright
16184610Salfred *    notice, this list of conditions and the following disclaimer.
17184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
18184610Salfred *    notice, this list of conditions and the following disclaimer in the
19184610Salfred *    documentation and/or other materials provided with the distribution.
20184610Salfred *
21184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24184610Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31184610Salfred * POSSIBILITY OF SUCH DAMAGE.
32184610Salfred */
33184610Salfred
34227843Smarius#include <sys/cdefs.h>
35227843Smarius__FBSDID("$FreeBSD$");
36227843Smarius
37184610Salfred/*
38184610Salfred * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
39184610Salfred *                  http://www.usb.org/developers/devclass_docs/frmts10.pdf
40184610Salfred *                  http://www.usb.org/developers/devclass_docs/termt10.pdf
41184610Salfred */
42184610Salfred
43184610Salfred/*
44184610Salfred * Also merged:
45184610Salfred *  $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $
46184610Salfred *  $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $
47184610Salfred *  $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $
48184610Salfred *  $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $
49184610Salfred */
50184610Salfred
51194677Sthompsa#include <sys/stdint.h>
52194677Sthompsa#include <sys/stddef.h>
53194677Sthompsa#include <sys/param.h>
54194677Sthompsa#include <sys/queue.h>
55194677Sthompsa#include <sys/types.h>
56194677Sthompsa#include <sys/systm.h>
57194677Sthompsa#include <sys/kernel.h>
58194677Sthompsa#include <sys/bus.h>
59194677Sthompsa#include <sys/module.h>
60194677Sthompsa#include <sys/lock.h>
61194677Sthompsa#include <sys/mutex.h>
62194677Sthompsa#include <sys/condvar.h>
63194677Sthompsa#include <sys/sysctl.h>
64194677Sthompsa#include <sys/sx.h>
65194677Sthompsa#include <sys/unistd.h>
66194677Sthompsa#include <sys/callout.h>
67194677Sthompsa#include <sys/malloc.h>
68194677Sthompsa#include <sys/priv.h>
69194677Sthompsa
70188746Sthompsa#include "usbdevs.h"
71188942Sthompsa#include <dev/usb/usb.h>
72194677Sthompsa#include <dev/usb/usbdi.h>
73194677Sthompsa#include <dev/usb/usbdi_util.h>
74246421Shselasky#include <dev/usb/usbhid.h>
75242438Shselasky#include <dev/usb/usb_request.h>
76249796Shselasky#include <dev/usb/usb_process.h>
77184610Salfred
78184610Salfred#define	USB_DEBUG_VAR uaudio_debug
79188942Sthompsa#include <dev/usb/usb_debug.h>
80184610Salfred
81188942Sthompsa#include <dev/usb/quirk/usb_quirk.h>
82184610Salfred
83184610Salfred#include <sys/reboot.h>			/* for bootverbose */
84184610Salfred
85193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
86193640Sariff#include "opt_snd.h"
87193640Sariff#endif
88193640Sariff
89184610Salfred#include <dev/sound/pcm/sound.h>
90188957Sthompsa#include <dev/sound/usb/uaudioreg.h>
91188957Sthompsa#include <dev/sound/usb/uaudio.h>
92184610Salfred#include <dev/sound/chip.h>
93184610Salfred#include "feeder_if.h"
94184610Salfred
95200825Sthompsastatic int uaudio_default_rate = 0;		/* use rate list */
96186730Salfredstatic int uaudio_default_bits = 32;
97200825Sthompsastatic int uaudio_default_channels = 0;		/* use default */
98186730Salfred
99207077Sthompsa#ifdef USB_DEBUG
100184610Salfredstatic int uaudio_debug = 0;
101184610Salfred
102227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio");
103200825Sthompsa
104192505SthompsaSYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW,
105184610Salfred    &uaudio_debug, 0, "uaudio debug level");
106200825Sthompsa
107200825SthompsaTUNABLE_INT("hw.usb.uaudio.default_rate", &uaudio_default_rate);
108192505SthompsaSYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_rate, CTLFLAG_RW,
109186730Salfred    &uaudio_default_rate, 0, "uaudio default sample rate");
110200825Sthompsa
111200825SthompsaTUNABLE_INT("hw.usb.uaudio.default_bits", &uaudio_default_bits);
112192505SthompsaSYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_bits, CTLFLAG_RW,
113186730Salfred    &uaudio_default_bits, 0, "uaudio default sample bits");
114200825Sthompsa
115200825SthompsaTUNABLE_INT("hw.usb.uaudio.default_channels", &uaudio_default_channels);
116192505SthompsaSYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW,
117186730Salfred    &uaudio_default_channels, 0, "uaudio default sample channels");
118184610Salfred#endif
119184610Salfred
120199060Sthompsa#define	UAUDIO_NFRAMES		64	/* must be factor of 8 due HS-USB */
121240609Shselasky#define	UAUDIO_NCHANBUFS	2	/* number of outstanding request */
122240609Shselasky#define	UAUDIO_RECURSE_LIMIT	255	/* rounds */
123184610Salfred
124184610Salfred#define	MAKE_WORD(h,l) (((h) << 8) | (l))
125184610Salfred#define	BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1)
126193465Sthompsa#define	UAUDIO_MAX_CHAN(x) (x)
127244567Shselasky#define	MIX(sc) ((sc)->sc_mixer_node)
128184610Salfred
129240609Shselaskyunion uaudio_asid {
130240609Shselasky	const struct usb_audio_streaming_interface_descriptor *v1;
131240609Shselasky	const struct usb_audio20_streaming_interface_descriptor *v2;
132240609Shselasky};
133240609Shselasky
134240609Shselaskyunion uaudio_asf1d {
135240609Shselasky	const struct usb_audio_streaming_type1_descriptor *v1;
136240609Shselasky	const struct usb_audio20_streaming_type1_descriptor *v2;
137240609Shselasky};
138240609Shselasky
139240609Shselaskyunion uaudio_sed {
140240609Shselasky	const struct usb_audio_streaming_endpoint_descriptor *v1;
141240609Shselasky	const struct usb_audio20_streaming_endpoint_descriptor *v2;
142240609Shselasky};
143240609Shselasky
144184610Salfredstruct uaudio_mixer_node {
145242438Shselasky	const char *name;
146242438Shselasky
147184610Salfred	int32_t	minval;
148184610Salfred	int32_t	maxval;
149242438Shselasky#define	MIX_MAX_CHAN 16
150184610Salfred	int32_t	wValue[MIX_MAX_CHAN];	/* using nchan */
151184610Salfred	uint32_t mul;
152184610Salfred	uint32_t ctl;
153184610Salfred
154242438Shselasky	int wData[MIX_MAX_CHAN];	/* using nchan */
155184610Salfred	uint16_t wIndex;
156184610Salfred
157184610Salfred	uint8_t	update[(MIX_MAX_CHAN + 7) / 8];
158184610Salfred	uint8_t	nchan;
159184610Salfred	uint8_t	type;
160184610Salfred#define	MIX_ON_OFF	1
161184610Salfred#define	MIX_SIGNED_16	2
162184610Salfred#define	MIX_UNSIGNED_16	3
163184610Salfred#define	MIX_SIGNED_8	4
164184610Salfred#define	MIX_SELECTOR	5
165184610Salfred#define	MIX_UNKNOWN     6
166184610Salfred#define	MIX_SIZE(n) ((((n) == MIX_SIGNED_16) || \
167184610Salfred		      ((n) == MIX_UNSIGNED_16)) ? 2 : 1)
168184610Salfred#define	MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16)
169184610Salfred
170184610Salfred#define	MAX_SELECTOR_INPUT_PIN 256
171184610Salfred	uint8_t	slctrtype[MAX_SELECTOR_INPUT_PIN];
172184610Salfred	uint8_t	class;
173242453Shselasky	uint8_t val_default;
174184610Salfred
175242438Shselasky	uint8_t desc[64];
176242438Shselasky
177184610Salfred	struct uaudio_mixer_node *next;
178184610Salfred};
179184610Salfred
180249796Shselaskystruct uaudio_configure_msg {
181249796Shselasky	struct usb_proc_msg hdr;
182249796Shselasky	struct uaudio_softc *sc;
183249796Shselasky};
184249796Shselasky
185263642Shselasky#define	CHAN_MAX_ALT 24
186249796Shselasky
187249796Shselaskystruct uaudio_chan_alt {
188249796Shselasky	union uaudio_asf1d p_asf1d;
189249796Shselasky	union uaudio_sed p_sed;
190249796Shselasky	const usb_endpoint_descriptor_audio_t *p_ed1;
191249796Shselasky	const struct uaudio_format *p_fmt;
192249796Shselasky	const struct usb_config *usb_cfg;
193249796Shselasky	uint32_t sample_rate;	/* in Hz */
194249796Shselasky	uint16_t sample_size;
195249796Shselasky	uint8_t	iface_index;
196249796Shselasky	uint8_t	iface_alt_index;
197249796Shselasky	uint8_t channels;
198249796Shselasky};
199249796Shselasky
200184610Salfredstruct uaudio_chan {
201184610Salfred	struct pcmchan_caps pcm_cap;	/* capabilities */
202249796Shselasky	struct uaudio_chan_alt usb_alt[CHAN_MAX_ALT];
203184610Salfred	struct snd_dbuf *pcm_buf;
204184610Salfred	struct mtx *pcm_mtx;		/* lock protecting this structure */
205184610Salfred	struct uaudio_softc *priv_sc;
206184610Salfred	struct pcm_channel *pcm_ch;
207242223Shselasky	struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1];
208184610Salfred
209184610Salfred	uint8_t *buf;			/* pointer to buffer */
210184610Salfred	uint8_t *start;			/* upper layer buffer start */
211184610Salfred	uint8_t *end;			/* upper layer buffer end */
212184610Salfred	uint8_t *cur;			/* current position in upper layer
213184610Salfred					 * buffer */
214184610Salfred
215199060Sthompsa	uint32_t intr_frames;		/* in units */
216200825Sthompsa	uint32_t frames_per_second;
217200825Sthompsa	uint32_t sample_rem;
218200825Sthompsa	uint32_t sample_curr;
219249796Shselasky	uint32_t max_buf;
220200825Sthompsa
221184610Salfred	uint32_t pcm_format[2];
222184610Salfred
223200825Sthompsa	uint16_t bytes_per_frame[2];
224184610Salfred
225249796Shselasky	uint8_t	num_alt;
226249796Shselasky	uint8_t cur_alt;
227249796Shselasky	uint8_t set_alt;
228249796Shselasky	uint8_t operation;
229249796Shselasky#define	CHAN_OP_NONE 0
230249796Shselasky#define	CHAN_OP_START 1
231249796Shselasky#define	CHAN_OP_STOP 2
232249796Shselasky#define	CHAN_OP_DRAIN 3
233200825Sthompsa
234242223Shselasky	uint8_t last_sync_time;
235242223Shselasky	uint8_t last_sync_state;
236242223Shselasky#define	UAUDIO_SYNC_NONE 0
237242223Shselasky#define	UAUDIO_SYNC_MORE 1
238242223Shselasky#define	UAUDIO_SYNC_LESS 2
239184610Salfred};
240184610Salfred
241272423Shselasky#define	UMIDI_EMB_JACK_MAX   16		/* units */
242224024Shselasky#define	UMIDI_TX_FRAMES	   256		/* units */
243223727Shselasky#define	UMIDI_TX_BUFFER    (UMIDI_TX_FRAMES * 4)	/* bytes */
244184610Salfred
245218791Shselaskyenum {
246218791Shselasky	UMIDI_TX_TRANSFER,
247218791Shselasky	UMIDI_RX_TRANSFER,
248218791Shselasky	UMIDI_N_TRANSFER,
249218791Shselasky};
250218791Shselasky
251184610Salfredstruct umidi_sub_chan {
252192984Sthompsa	struct usb_fifo_sc fifo;
253184610Salfred	uint8_t *temp_cmd;
254184610Salfred	uint8_t	temp_0[4];
255184610Salfred	uint8_t	temp_1[4];
256184610Salfred	uint8_t	state;
257184610Salfred#define	UMIDI_ST_UNKNOWN   0		/* scan for command */
258184610Salfred#define	UMIDI_ST_1PARAM    1
259184610Salfred#define	UMIDI_ST_2PARAM_1  2
260184610Salfred#define	UMIDI_ST_2PARAM_2  3
261184610Salfred#define	UMIDI_ST_SYSEX_0   4
262184610Salfred#define	UMIDI_ST_SYSEX_1   5
263184610Salfred#define	UMIDI_ST_SYSEX_2   6
264184610Salfred
265184610Salfred	uint8_t	read_open:1;
266184610Salfred	uint8_t	write_open:1;
267184610Salfred	uint8_t	unused:6;
268184610Salfred};
269184610Salfred
270184610Salfredstruct umidi_chan {
271184610Salfred
272272423Shselasky	struct umidi_sub_chan sub[UMIDI_EMB_JACK_MAX];
273184610Salfred	struct mtx mtx;
274184610Salfred
275192984Sthompsa	struct usb_xfer *xfer[UMIDI_N_TRANSFER];
276184610Salfred
277184610Salfred	uint8_t	iface_index;
278184610Salfred	uint8_t	iface_alt_index;
279184610Salfred
280184610Salfred	uint8_t	read_open_refcount;
281184610Salfred	uint8_t	write_open_refcount;
282184610Salfred
283184610Salfred	uint8_t	curr_cable;
284272423Shselasky	uint8_t	max_emb_jack;
285184610Salfred	uint8_t	valid;
286223736Shselasky	uint8_t single_command;
287184610Salfred};
288184610Salfred
289240609Shselaskystruct uaudio_search_result {
290240609Shselasky	uint8_t	bit_input[(256 + 7) / 8];
291240609Shselasky	uint8_t	bit_output[(256 + 7) / 8];
292240609Shselasky	uint8_t	recurse_level;
293240609Shselasky	uint8_t	id_max;
294240609Shselasky	uint8_t is_input;
295240609Shselasky};
296240609Shselasky
297246421Shselaskyenum {
298246421Shselasky	UAUDIO_HID_RX_TRANSFER,
299246421Shselasky	UAUDIO_HID_N_TRANSFER,
300246421Shselasky};
301246421Shselasky
302246421Shselaskystruct uaudio_hid {
303246421Shselasky	struct usb_xfer *xfer[UAUDIO_HID_N_TRANSFER];
304246421Shselasky	struct hid_location volume_up_loc;
305246421Shselasky	struct hid_location volume_down_loc;
306246454Shselasky	struct hid_location mute_loc;
307246421Shselasky	uint32_t flags;
308246421Shselasky#define	UAUDIO_HID_VALID		0x0001
309246421Shselasky#define	UAUDIO_HID_HAS_ID		0x0002
310246421Shselasky#define	UAUDIO_HID_HAS_VOLUME_UP	0x0004
311246421Shselasky#define	UAUDIO_HID_HAS_VOLUME_DOWN	0x0008
312246454Shselasky#define	UAUDIO_HID_HAS_MUTE		0x0010
313246421Shselasky	uint8_t iface_index;
314246421Shselasky	uint8_t volume_up_id;
315246421Shselasky	uint8_t volume_down_id;
316246454Shselasky	uint8_t mute_id;
317246421Shselasky};
318246421Shselasky
319184610Salfredstruct uaudio_softc {
320184610Salfred	struct sbuf sc_sndstat;
321184610Salfred	struct sndcard_func sc_sndcard_func;
322184610Salfred	struct uaudio_chan sc_rec_chan;
323184610Salfred	struct uaudio_chan sc_play_chan;
324184610Salfred	struct umidi_chan sc_midi_chan;
325246421Shselasky	struct uaudio_hid sc_hid;
326240609Shselasky	struct uaudio_search_result sc_mixer_clocks;
327244567Shselasky	struct uaudio_mixer_node sc_mixer_node;
328249796Shselasky	struct uaudio_configure_msg sc_config_msg[2];
329184610Salfred
330242438Shselasky	struct mtx *sc_mixer_lock;
331246421Shselasky	struct snd_mixer *sc_mixer_dev;
332192984Sthompsa	struct usb_device *sc_udev;
333192984Sthompsa	struct usb_xfer *sc_mixer_xfer[1];
334184610Salfred	struct uaudio_mixer_node *sc_mixer_root;
335184610Salfred	struct uaudio_mixer_node *sc_mixer_curr;
336184610Salfred
337184610Salfred	uint32_t sc_mix_info;
338184610Salfred	uint32_t sc_recsrc_info;
339184610Salfred
340184610Salfred	uint16_t sc_audio_rev;
341184610Salfred	uint16_t sc_mixer_count;
342184610Salfred
343184610Salfred	uint8_t	sc_sndstat_valid;
344184610Salfred	uint8_t	sc_mixer_iface_index;
345184610Salfred	uint8_t	sc_mixer_iface_no;
346184610Salfred	uint8_t	sc_mixer_chan;
347184610Salfred	uint8_t	sc_pcm_registered:1;
348184610Salfred	uint8_t	sc_mixer_init:1;
349184610Salfred	uint8_t	sc_uq_audio_swap_lr:1;
350184610Salfred	uint8_t	sc_uq_au_inp_async:1;
351184610Salfred	uint8_t	sc_uq_au_no_xu:1;
352184610Salfred	uint8_t	sc_uq_bad_adc:1;
353218988Shselasky	uint8_t	sc_uq_au_vendor_class:1;
354184610Salfred};
355184610Salfred
356184610Salfredstruct uaudio_terminal_node {
357184610Salfred	union {
358192984Sthompsa		const struct usb_descriptor *desc;
359240609Shselasky		const struct usb_audio_input_terminal *it_v1;
360240609Shselasky		const struct usb_audio_output_terminal *ot_v1;
361240609Shselasky		const struct usb_audio_mixer_unit_0 *mu_v1;
362240609Shselasky		const struct usb_audio_selector_unit *su_v1;
363240609Shselasky		const struct usb_audio_feature_unit *fu_v1;
364240609Shselasky		const struct usb_audio_processing_unit_0 *pu_v1;
365240609Shselasky		const struct usb_audio_extension_unit_0 *eu_v1;
366240609Shselasky		const struct usb_audio20_clock_source_unit *csrc_v2;
367240609Shselasky		const struct usb_audio20_clock_selector_unit_0 *csel_v2;
368240609Shselasky		const struct usb_audio20_clock_multiplier_unit *cmul_v2;
369240609Shselasky		const struct usb_audio20_input_terminal *it_v2;
370240609Shselasky		const struct usb_audio20_output_terminal *ot_v2;
371240609Shselasky		const struct usb_audio20_mixer_unit_0 *mu_v2;
372240609Shselasky		const struct usb_audio20_selector_unit *su_v2;
373240609Shselasky		const struct usb_audio20_feature_unit *fu_v2;
374240609Shselasky		const struct usb_audio20_sample_rate_unit *ru_v2;
375240609Shselasky		const struct usb_audio20_processing_unit_0 *pu_v2;
376240609Shselasky		const struct usb_audio20_extension_unit_0 *eu_v2;
377240609Shselasky		const struct usb_audio20_effect_unit *ef_v2;
378184610Salfred	}	u;
379184610Salfred	struct uaudio_search_result usr;
380184610Salfred	struct uaudio_terminal_node *root;
381184610Salfred};
382184610Salfred
383184610Salfredstruct uaudio_format {
384184610Salfred	uint16_t wFormat;
385184610Salfred	uint8_t	bPrecision;
386184610Salfred	uint32_t freebsd_fmt;
387184610Salfred	const char *description;
388184610Salfred};
389184610Salfred
390240609Shselaskystatic const struct uaudio_format uaudio10_formats[] = {
391184610Salfred
392184610Salfred	{UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
393184610Salfred	{UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
394184610Salfred	{UA_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
395184610Salfred	{UA_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
396184610Salfred
397184610Salfred	{UA_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
398184610Salfred	{UA_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
399184610Salfred	{UA_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
400184610Salfred	{UA_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
401184610Salfred
402184610Salfred	{UA_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
403184610Salfred	{UA_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
404184610Salfred
405184610Salfred	{0, 0, 0, NULL}
406184610Salfred};
407184610Salfred
408240609Shselaskystatic const struct uaudio_format uaudio20_formats[] = {
409240609Shselasky
410240609Shselasky	{UA20_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
411240609Shselasky	{UA20_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
412240609Shselasky	{UA20_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
413240609Shselasky	{UA20_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
414240609Shselasky
415240609Shselasky	{UA20_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
416240609Shselasky	{UA20_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
417240609Shselasky	{UA20_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
418240609Shselasky	{UA20_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
419240609Shselasky
420240609Shselasky	{UA20_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
421240609Shselasky	{UA20_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
422240609Shselasky
423240609Shselasky	{0, 0, 0, NULL}
424240609Shselasky};
425240609Shselasky
426184610Salfred#define	UAC_OUTPUT	0
427184610Salfred#define	UAC_INPUT	1
428184610Salfred#define	UAC_EQUAL	2
429184610Salfred#define	UAC_RECORD	3
430184610Salfred#define	UAC_NCLASSES	4
431184610Salfred
432207077Sthompsa#ifdef USB_DEBUG
433184610Salfredstatic const char *uac_names[] = {
434184610Salfred	"outputs", "inputs", "equalization", "record"
435184610Salfred};
436184610Salfred
437184610Salfred#endif
438184610Salfred
439184610Salfred/* prototypes */
440184610Salfred
441184610Salfredstatic device_probe_t uaudio_probe;
442184610Salfredstatic device_attach_t uaudio_attach;
443184610Salfredstatic device_detach_t uaudio_detach;
444184610Salfred
445193045Sthompsastatic usb_callback_t uaudio_chan_play_callback;
446242223Shselaskystatic usb_callback_t uaudio_chan_play_sync_callback;
447193045Sthompsastatic usb_callback_t uaudio_chan_record_callback;
448242223Shselaskystatic usb_callback_t uaudio_chan_record_sync_callback;
449193045Sthompsastatic usb_callback_t uaudio_mixer_write_cfg_callback;
450193045Sthompsastatic usb_callback_t umidi_bulk_read_callback;
451193045Sthompsastatic usb_callback_t umidi_bulk_write_callback;
452246421Shselaskystatic usb_callback_t uaudio_hid_rx_callback;
453184610Salfred
454249796Shselaskystatic usb_proc_callback_t uaudio_configure_msg;
455249796Shselasky
456242438Shselasky/* ==== USB mixer ==== */
457242438Shselasky
458242438Shselaskystatic int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS);
459242438Shselaskystatic void uaudio_mixer_ctl_free(struct uaudio_softc *);
460242438Shselaskystatic void uaudio_mixer_register_sysctl(struct uaudio_softc *, device_t);
461242438Shselaskystatic void uaudio_mixer_reload_all(struct uaudio_softc *);
462242453Shselaskystatic void uaudio_mixer_controls_create_ftu(struct uaudio_softc *);
463242438Shselasky
464240609Shselasky/* ==== USB audio v1.0 ==== */
465240609Shselasky
466185948Sthompsastatic void	uaudio_mixer_add_mixer(struct uaudio_softc *,
467185948Sthompsa		    const struct uaudio_terminal_node *, int);
468185948Sthompsastatic void	uaudio_mixer_add_selector(struct uaudio_softc *,
469185948Sthompsa		    const struct uaudio_terminal_node *, int);
470185948Sthompsastatic uint32_t	uaudio_mixer_feature_get_bmaControls(
471203678Sbrucec		    const struct usb_audio_feature_unit *, uint8_t);
472185948Sthompsastatic void	uaudio_mixer_add_feature(struct uaudio_softc *,
473185948Sthompsa		    const struct uaudio_terminal_node *, int);
474185948Sthompsastatic void	uaudio_mixer_add_processing_updown(struct uaudio_softc *,
475185948Sthompsa		    const struct uaudio_terminal_node *, int);
476185948Sthompsastatic void	uaudio_mixer_add_processing(struct uaudio_softc *,
477185948Sthompsa		    const struct uaudio_terminal_node *, int);
478185948Sthompsastatic void	uaudio_mixer_add_extension(struct uaudio_softc *,
479185948Sthompsa		    const struct uaudio_terminal_node *, int);
480203678Sbrucecstatic struct	usb_audio_cluster uaudio_mixer_get_cluster(uint8_t,
481185948Sthompsa		    const struct uaudio_terminal_node *);
482185948Sthompsastatic uint16_t	uaudio_mixer_determine_class(const struct uaudio_terminal_node *,
483185948Sthompsa		    struct uaudio_mixer_node *);
484185948Sthompsastatic uint16_t	uaudio_mixer_feature_name(const struct uaudio_terminal_node *,
485185948Sthompsa		    struct uaudio_mixer_node *);
486185948Sthompsastatic void	uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *,
487185948Sthompsa		    const uint8_t *, uint8_t, struct uaudio_search_result *);
488240609Shselaskystatic const void *uaudio_mixer_verify_desc(const void *, uint32_t);
489240609Shselaskystatic usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t);
490240609Shselaskystatic int	uaudio_mixer_get(struct usb_device *, uint16_t, uint8_t,
491240609Shselasky		    struct uaudio_mixer_node *);
492240609Shselasky
493240609Shselasky/* ==== USB audio v2.0 ==== */
494240609Shselasky
495240609Shselaskystatic void	uaudio20_mixer_add_mixer(struct uaudio_softc *,
496240609Shselasky		    const struct uaudio_terminal_node *, int);
497240609Shselaskystatic void	uaudio20_mixer_add_selector(struct uaudio_softc *,
498240609Shselasky		    const struct uaudio_terminal_node *, int);
499240609Shselaskystatic void	uaudio20_mixer_add_feature(struct uaudio_softc *,
500240609Shselasky		    const struct uaudio_terminal_node *, int);
501240609Shselaskystatic struct	usb_audio20_cluster uaudio20_mixer_get_cluster(uint8_t,
502240609Shselasky		    const struct uaudio_terminal_node *);
503240609Shselaskystatic uint16_t	uaudio20_mixer_determine_class(const struct uaudio_terminal_node *,
504240609Shselasky		    struct uaudio_mixer_node *);
505240609Shselaskystatic uint16_t	uaudio20_mixer_feature_name(const struct uaudio_terminal_node *,
506240609Shselasky		    struct uaudio_mixer_node *);
507240609Shselaskystatic void	uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *,
508240609Shselasky		    const uint8_t *, uint8_t, struct uaudio_search_result *);
509240609Shselaskystatic const void *uaudio20_mixer_verify_desc(const void *, uint32_t);
510240609Shselaskystatic usb_error_t uaudio20_set_speed(struct usb_device *, uint8_t,
511240609Shselasky		    uint8_t, uint32_t);
512240609Shselasky
513240609Shselasky/* USB audio v1.0 and v2.0 */
514240609Shselasky
515240609Shselaskystatic void	uaudio_chan_fill_info_sub(struct uaudio_softc *,
516240609Shselasky		    struct usb_device *, uint32_t, uint8_t, uint8_t);
517240609Shselaskystatic void	uaudio_chan_fill_info(struct uaudio_softc *,
518240609Shselasky		    struct usb_device *);
519240609Shselaskystatic void	uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
520240609Shselasky		    struct uaudio_mixer_node *);
521240609Shselaskystatic void	uaudio_mixer_add_ctl(struct uaudio_softc *,
522240609Shselasky		    struct uaudio_mixer_node *);
523185948Sthompsastatic void	uaudio_mixer_fill_info(struct uaudio_softc *,
524192984Sthompsa		    struct usb_device *, void *);
525185948Sthompsastatic void	uaudio_mixer_ctl_set(struct uaudio_softc *,
526185948Sthompsa		    struct uaudio_mixer_node *, uint8_t, int32_t val);
527185948Sthompsastatic int	uaudio_mixer_signext(uint8_t, int);
528185948Sthompsastatic int	uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val);
529185948Sthompsastatic void	uaudio_mixer_init(struct uaudio_softc *);
530240609Shselaskystatic const struct uaudio_terminal_node *uaudio_mixer_get_input(
531240609Shselasky		    const struct uaudio_terminal_node *, uint8_t);
532240609Shselaskystatic const struct uaudio_terminal_node *uaudio_mixer_get_output(
533240609Shselasky		    const struct uaudio_terminal_node *, uint8_t);
534240609Shselaskystatic void	uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *,
535240609Shselasky		    uint8_t, uint8_t, struct uaudio_search_result *);
536185948Sthompsastatic uint8_t	umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t);
537192984Sthompsastatic struct	umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *);
538192984Sthompsastatic void	umidi_start_read(struct usb_fifo *);
539192984Sthompsastatic void	umidi_stop_read(struct usb_fifo *);
540192984Sthompsastatic void	umidi_start_write(struct usb_fifo *);
541192984Sthompsastatic void	umidi_stop_write(struct usb_fifo *);
542192984Sthompsastatic int	umidi_open(struct usb_fifo *, int);
543192984Sthompsastatic int	umidi_ioctl(struct usb_fifo *, u_long cmd, void *, int);
544192984Sthompsastatic void	umidi_close(struct usb_fifo *, int);
545185948Sthompsastatic void	umidi_init(device_t dev);
546218988Shselaskystatic int	umidi_probe(device_t dev);
547218988Shselaskystatic int	umidi_detach(device_t dev);
548246421Shselaskystatic int	uaudio_hid_probe(struct uaudio_softc *sc,
549246421Shselasky		    struct usb_attach_arg *uaa);
550246421Shselaskystatic void	uaudio_hid_detach(struct uaudio_softc *sc);
551184610Salfred
552207077Sthompsa#ifdef USB_DEBUG
553185948Sthompsastatic void	uaudio_chan_dump_ep_desc(
554203678Sbrucec		    const usb_endpoint_descriptor_audio_t *);
555184610Salfred#endif
556184610Salfred
557192984Sthompsastatic const struct usb_config
558242223Shselasky	uaudio_cfg_record[UAUDIO_NCHANBUFS + 1] = {
559184610Salfred	[0] = {
560184610Salfred		.type = UE_ISOCHRONOUS,
561184610Salfred		.endpoint = UE_ADDR_ANY,
562184610Salfred		.direction = UE_DIR_IN,
563190734Sthompsa		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
564199060Sthompsa		.frames = UAUDIO_NFRAMES,
565190734Sthompsa		.flags = {.short_xfer_ok = 1,},
566190734Sthompsa		.callback = &uaudio_chan_record_callback,
567184610Salfred	},
568184610Salfred
569184610Salfred	[1] = {
570184610Salfred		.type = UE_ISOCHRONOUS,
571184610Salfred		.endpoint = UE_ADDR_ANY,
572184610Salfred		.direction = UE_DIR_IN,
573190734Sthompsa		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
574199060Sthompsa		.frames = UAUDIO_NFRAMES,
575190734Sthompsa		.flags = {.short_xfer_ok = 1,},
576190734Sthompsa		.callback = &uaudio_chan_record_callback,
577184610Salfred	},
578242223Shselasky
579242223Shselasky	[2] = {
580242223Shselasky		.type = UE_ISOCHRONOUS,
581242223Shselasky		.endpoint = UE_ADDR_ANY,
582242223Shselasky		.direction = UE_DIR_OUT,
583242223Shselasky		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
584242223Shselasky		.frames = 1,
585242223Shselasky		.flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
586242223Shselasky		.callback = &uaudio_chan_record_sync_callback,
587242223Shselasky	},
588184610Salfred};
589184610Salfred
590192984Sthompsastatic const struct usb_config
591242223Shselasky	uaudio_cfg_play[UAUDIO_NCHANBUFS + 1] = {
592184610Salfred	[0] = {
593184610Salfred		.type = UE_ISOCHRONOUS,
594184610Salfred		.endpoint = UE_ADDR_ANY,
595184610Salfred		.direction = UE_DIR_OUT,
596190734Sthompsa		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
597199060Sthompsa		.frames = UAUDIO_NFRAMES,
598190734Sthompsa		.flags = {.short_xfer_ok = 1,},
599190734Sthompsa		.callback = &uaudio_chan_play_callback,
600184610Salfred	},
601184610Salfred
602184610Salfred	[1] = {
603184610Salfred		.type = UE_ISOCHRONOUS,
604184610Salfred		.endpoint = UE_ADDR_ANY,
605184610Salfred		.direction = UE_DIR_OUT,
606190734Sthompsa		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
607199060Sthompsa		.frames = UAUDIO_NFRAMES,
608190734Sthompsa		.flags = {.short_xfer_ok = 1,},
609190734Sthompsa		.callback = &uaudio_chan_play_callback,
610184610Salfred	},
611242223Shselasky
612242223Shselasky	[2] = {
613242223Shselasky		.type = UE_ISOCHRONOUS,
614242223Shselasky		.endpoint = UE_ADDR_ANY,
615242223Shselasky		.direction = UE_DIR_IN,
616242223Shselasky		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
617242223Shselasky		.frames = 1,
618242223Shselasky		.flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
619242223Shselasky		.callback = &uaudio_chan_play_sync_callback,
620242223Shselasky	},
621184610Salfred};
622184610Salfred
623192984Sthompsastatic const struct usb_config
624184610Salfred	uaudio_mixer_config[1] = {
625184610Salfred	[0] = {
626184610Salfred		.type = UE_CONTROL,
627184610Salfred		.endpoint = 0x00,	/* Control pipe */
628184610Salfred		.direction = UE_DIR_ANY,
629192984Sthompsa		.bufsize = (sizeof(struct usb_device_request) + 4),
630190734Sthompsa		.callback = &uaudio_mixer_write_cfg_callback,
631190734Sthompsa		.timeout = 1000,	/* 1 second */
632184610Salfred	},
633184610Salfred};
634184610Salfred
635184610Salfredstatic const
636184610Salfreduint8_t	umidi_cmd_to_len[16] = {
637184610Salfred	[0x0] = 0,			/* reserved */
638184610Salfred	[0x1] = 0,			/* reserved */
639184610Salfred	[0x2] = 2,			/* bytes */
640184610Salfred	[0x3] = 3,			/* bytes */
641184610Salfred	[0x4] = 3,			/* bytes */
642184610Salfred	[0x5] = 1,			/* bytes */
643184610Salfred	[0x6] = 2,			/* bytes */
644184610Salfred	[0x7] = 3,			/* bytes */
645184610Salfred	[0x8] = 3,			/* bytes */
646184610Salfred	[0x9] = 3,			/* bytes */
647184610Salfred	[0xA] = 3,			/* bytes */
648184610Salfred	[0xB] = 3,			/* bytes */
649184610Salfred	[0xC] = 2,			/* bytes */
650184610Salfred	[0xD] = 2,			/* bytes */
651184610Salfred	[0xE] = 3,			/* bytes */
652184610Salfred	[0xF] = 1,			/* bytes */
653184610Salfred};
654184610Salfred
655192984Sthompsastatic const struct usb_config
656184610Salfred	umidi_config[UMIDI_N_TRANSFER] = {
657218791Shselasky	[UMIDI_TX_TRANSFER] = {
658184610Salfred		.type = UE_BULK,
659184610Salfred		.endpoint = UE_ADDR_ANY,
660184610Salfred		.direction = UE_DIR_OUT,
661223727Shselasky		.bufsize = UMIDI_TX_BUFFER,
662190734Sthompsa		.callback = &umidi_bulk_write_callback,
663184610Salfred	},
664184610Salfred
665218791Shselasky	[UMIDI_RX_TRANSFER] = {
666184610Salfred		.type = UE_BULK,
667184610Salfred		.endpoint = UE_ADDR_ANY,
668184610Salfred		.direction = UE_DIR_IN,
669209450Sthompsa		.bufsize = 4,	/* bytes */
670223727Shselasky		.flags = {.short_xfer_ok = 1,.proxy_buffer = 1,},
671190734Sthompsa		.callback = &umidi_bulk_read_callback,
672184610Salfred	},
673184610Salfred};
674184610Salfred
675246421Shselaskystatic const struct usb_config
676246421Shselasky	uaudio_hid_config[UAUDIO_HID_N_TRANSFER] = {
677246421Shselasky	[UAUDIO_HID_RX_TRANSFER] = {
678246421Shselasky		.type = UE_INTERRUPT,
679246421Shselasky		.endpoint = UE_ADDR_ANY,
680246421Shselasky		.direction = UE_DIR_IN,
681246421Shselasky		.bufsize = 0,	/* use wMaxPacketSize */
682246421Shselasky		.flags = {.short_xfer_ok = 1,},
683246421Shselasky		.callback = &uaudio_hid_rx_callback,
684246421Shselasky	},
685246421Shselasky};
686246421Shselasky
687184610Salfredstatic devclass_t uaudio_devclass;
688184610Salfred
689184610Salfredstatic device_method_t uaudio_methods[] = {
690184610Salfred	DEVMETHOD(device_probe, uaudio_probe),
691184610Salfred	DEVMETHOD(device_attach, uaudio_attach),
692184610Salfred	DEVMETHOD(device_detach, uaudio_detach),
693184610Salfred	DEVMETHOD(device_suspend, bus_generic_suspend),
694184610Salfred	DEVMETHOD(device_resume, bus_generic_resume),
695184610Salfred	DEVMETHOD(device_shutdown, bus_generic_shutdown),
696227843Smarius
697227843Smarius	DEVMETHOD_END
698184610Salfred};
699184610Salfred
700184610Salfredstatic driver_t uaudio_driver = {
701184610Salfred	.name = "uaudio",
702184610Salfred	.methods = uaudio_methods,
703184610Salfred	.size = sizeof(struct uaudio_softc),
704184610Salfred};
705184610Salfred
706244027Shselasky/* The following table is derived from Linux's quirks-table.h */
707244027Shselaskystatic const STRUCT_USB_HOST_ID uaudio_vendor_midi[] = {
708244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1000, 0) }, /* UX256 */
709244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1001, 0) }, /* MU1000 */
710244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1002, 0) }, /* MU2000 */
711244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1003, 0) }, /* MU500 */
712244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1004, 3) }, /* UW500 */
713244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1005, 0) }, /* MOTIF6 */
714244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1006, 0) }, /* MOTIF7 */
715244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1007, 0) }, /* MOTIF8 */
716244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1008, 0) }, /* UX96 */
717244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1009, 0) }, /* UX16 */
718244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100a, 3) }, /* EOS BX */
719244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100c, 0) }, /* UC-MX */
720244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100d, 0) }, /* UC-KX */
721244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100e, 0) }, /* S08 */
722244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x100f, 0) }, /* CLP-150 */
723244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1010, 0) }, /* CLP-170 */
724244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1011, 0) }, /* P-250 */
725244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1012, 0) }, /* TYROS */
726244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1013, 0) }, /* PF-500 */
727244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1014, 0) }, /* S90 */
728244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1015, 0) }, /* MOTIF-R */
729244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1016, 0) }, /* MDP-5 */
730244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1017, 0) }, /* CVP-204 */
731244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1018, 0) }, /* CVP-206 */
732244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1019, 0) }, /* CVP-208 */
733244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101a, 0) }, /* CVP-210 */
734244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101b, 0) }, /* PSR-1100 */
735244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101c, 0) }, /* PSR-2100 */
736244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101d, 0) }, /* CLP-175 */
737244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101e, 0) }, /* PSR-K1 */
738244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x101f, 0) }, /* EZ-J24 */
739244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1020, 0) }, /* EZ-250i */
740244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1021, 0) }, /* MOTIF ES 6 */
741244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1022, 0) }, /* MOTIF ES 7 */
742244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1023, 0) }, /* MOTIF ES 8 */
743244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1024, 0) }, /* CVP-301 */
744244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1025, 0) }, /* CVP-303 */
745244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1026, 0) }, /* CVP-305 */
746244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1027, 0) }, /* CVP-307 */
747244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1028, 0) }, /* CVP-309 */
748244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1029, 0) }, /* CVP-309GP */
749244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x102a, 0) }, /* PSR-1500 */
750244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x102b, 0) }, /* PSR-3000 */
751244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x102e, 0) }, /* ELS-01/01C */
752244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1030, 0) }, /* PSR-295/293 */
753244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1031, 0) }, /* DGX-205/203 */
754244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1032, 0) }, /* DGX-305 */
755244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1033, 0) }, /* DGX-505 */
756244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1034, 0) }, /* NULL */
757244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1035, 0) }, /* NULL */
758244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1036, 0) }, /* NULL */
759244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1037, 0) }, /* NULL */
760244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1038, 0) }, /* NULL */
761244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1039, 0) }, /* NULL */
762244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103a, 0) }, /* NULL */
763244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103b, 0) }, /* NULL */
764244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103c, 0) }, /* NULL */
765244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103d, 0) }, /* NULL */
766244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103e, 0) }, /* NULL */
767244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x103f, 0) }, /* NULL */
768244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1040, 0) }, /* NULL */
769244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1041, 0) }, /* NULL */
770244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1042, 0) }, /* NULL */
771244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1043, 0) }, /* NULL */
772244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1044, 0) }, /* NULL */
773244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1045, 0) }, /* NULL */
774244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x104e, 0) }, /* NULL */
775244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x104f, 0) }, /* NULL */
776244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1050, 0) }, /* NULL */
777244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1051, 0) }, /* NULL */
778244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1052, 0) }, /* NULL */
779244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1053, 0) }, /* NULL */
780244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1054, 0) }, /* NULL */
781244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1055, 0) }, /* NULL */
782244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1056, 0) }, /* NULL */
783244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1057, 0) }, /* NULL */
784244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1058, 0) }, /* NULL */
785244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1059, 0) }, /* NULL */
786244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x105a, 0) }, /* NULL */
787244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x105b, 0) }, /* NULL */
788244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x105c, 0) }, /* NULL */
789244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x105d, 0) }, /* NULL */
790244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x1503, 3) }, /* MOX6/MOX8 */
791244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x2000, 0) }, /* DGP-7 */
792244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x2001, 0) }, /* DGP-5 */
793244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x2002, 0) }, /* NULL */
794244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x2003, 0) }, /* NULL */
795244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5000, 0) }, /* CS1D */
796244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5001, 0) }, /* DSP1D */
797244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5002, 0) }, /* DME32 */
798244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5003, 0) }, /* DM2000 */
799244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5004, 0) }, /* 02R96 */
800244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5005, 0) }, /* ACU16-C */
801244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5006, 0) }, /* NHB32-C */
802244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5007, 0) }, /* DM1000 */
803244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5008, 0) }, /* 01V96 */
804244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x5009, 0) }, /* SPX2000 */
805244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500a, 0) }, /* PM5D */
806244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500b, 0) }, /* DME64N */
807244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500c, 0) }, /* DME24N */
808244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500d, 0) }, /* NULL */
809244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500e, 0) }, /* NULL */
810244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x500f, 0) }, /* NULL */
811244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x7000, 0) }, /* DTX */
812244027Shselasky	{ USB_VPI(USB_VENDOR_YAMAHA, 0x7010, 0) }, /* UB99 */
813244027Shselasky};
814244027Shselasky
815223486Shselaskystatic const STRUCT_USB_HOST_ID __used uaudio_devs[] = {
816223486Shselasky	/* Generic USB audio class match */
817223486Shselasky	{USB_IFACE_CLASS(UICLASS_AUDIO),
818223486Shselasky	 USB_IFACE_SUBCLASS(UISUBCLASS_AUDIOCONTROL),},
819223486Shselasky	/* Generic USB MIDI class match */
820223486Shselasky	{USB_IFACE_CLASS(UICLASS_AUDIO),
821223486Shselasky	 USB_IFACE_SUBCLASS(UISUBCLASS_MIDISTREAM),},
822223486Shselasky};
823223486Shselasky
824184610Salfredstatic int
825184610Salfreduaudio_probe(device_t dev)
826184610Salfred{
827192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
828184610Salfred
829192505Sthompsa	if (uaa->usb_mode != USB_MODE_HOST)
830184610Salfred		return (ENXIO);
831188416Sthompsa
832244027Shselasky	/* lookup non-standard device(s) */
833184610Salfred
834244027Shselasky	if (usbd_lookup_id_by_uaa(uaudio_vendor_midi,
835244027Shselasky	    sizeof(uaudio_vendor_midi), uaa) == 0) {
836244027Shselasky		return (BUS_PROBE_SPECIFIC);
837244027Shselasky	}
838244027Shselasky
839218988Shselasky	if (uaa->info.bInterfaceClass != UICLASS_AUDIO) {
840242129Shselasky		if (uaa->info.bInterfaceClass != UICLASS_VENDOR ||
841242129Shselasky		    usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0)
842218988Shselasky			return (ENXIO);
843218988Shselasky	}
844218988Shselasky
845218988Shselasky	/* check for AUDIO control interface */
846218988Shselasky
847218988Shselasky	if (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL) {
848194228Sthompsa		if (usb_test_quirk(uaa, UQ_BAD_AUDIO))
849184610Salfred			return (ENXIO);
850184610Salfred		else
851222051Savg			return (BUS_PROBE_GENERIC);
852184610Salfred	}
853199677Sthompsa
854199677Sthompsa	/* check for MIDI stream */
855199677Sthompsa
856218988Shselasky	if (uaa->info.bInterfaceSubClass == UISUBCLASS_MIDISTREAM) {
857218988Shselasky		if (usb_test_quirk(uaa, UQ_BAD_MIDI))
858218988Shselasky			return (ENXIO);
859218988Shselasky		else
860222051Savg			return (BUS_PROBE_GENERIC);
861199677Sthompsa	}
862184610Salfred	return (ENXIO);
863184610Salfred}
864184610Salfred
865184610Salfredstatic int
866184610Salfreduaudio_attach(device_t dev)
867184610Salfred{
868192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
869184610Salfred	struct uaudio_softc *sc = device_get_softc(dev);
870192984Sthompsa	struct usb_interface_descriptor *id;
871250763Shselasky	usb_error_t err;
872184610Salfred	device_t child;
873184610Salfred
874184610Salfred	sc->sc_play_chan.priv_sc = sc;
875184610Salfred	sc->sc_rec_chan.priv_sc = sc;
876184610Salfred	sc->sc_udev = uaa->device;
877199060Sthompsa	sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
878199060Sthompsa	sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
879249796Shselasky	sc->sc_config_msg[0].hdr.pm_callback = &uaudio_configure_msg;
880249796Shselasky	sc->sc_config_msg[0].sc = sc;
881249796Shselasky	sc->sc_config_msg[1].hdr.pm_callback = &uaudio_configure_msg;
882249796Shselasky	sc->sc_config_msg[1].sc = sc;
883184610Salfred
884194228Sthompsa	if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR))
885184610Salfred		sc->sc_uq_audio_swap_lr = 1;
886184610Salfred
887194228Sthompsa	if (usb_test_quirk(uaa, UQ_AU_INP_ASYNC))
888184610Salfred		sc->sc_uq_au_inp_async = 1;
889184610Salfred
890194228Sthompsa	if (usb_test_quirk(uaa, UQ_AU_NO_XU))
891184610Salfred		sc->sc_uq_au_no_xu = 1;
892184610Salfred
893194228Sthompsa	if (usb_test_quirk(uaa, UQ_BAD_ADC))
894184610Salfred		sc->sc_uq_bad_adc = 1;
895184610Salfred
896218988Shselasky	if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS))
897218988Shselasky		sc->sc_uq_au_vendor_class = 1;
898218988Shselasky
899184610Salfred	umidi_init(dev);
900184610Salfred
901194228Sthompsa	device_set_usb_desc(dev);
902184610Salfred
903194228Sthompsa	id = usbd_get_interface_descriptor(uaa->iface);
904184610Salfred
905240609Shselasky	/* must fill mixer info before channel info */
906240609Shselasky	uaudio_mixer_fill_info(sc, uaa->device, id);
907240609Shselasky
908240609Shselasky	/* fill channel info */
909184610Salfred	uaudio_chan_fill_info(sc, uaa->device);
910184610Salfred
911184610Salfred	DPRINTF("audio rev %d.%02x\n",
912184610Salfred	    sc->sc_audio_rev >> 8,
913184610Salfred	    sc->sc_audio_rev & 0xff);
914184610Salfred
915242453Shselasky	if (sc->sc_mixer_count == 0) {
916242453Shselasky		if (uaa->info.idVendor == USB_VENDOR_MAUDIO &&
917242453Shselasky		    (uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA ||
918242453Shselasky		    uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA8R)) {
919242453Shselasky			DPRINTF("Generating mixer descriptors\n");
920242453Shselasky			uaudio_mixer_controls_create_ftu(sc);
921242453Shselasky		}
922242453Shselasky	}
923242453Shselasky
924184610Salfred	DPRINTF("%d mixer controls\n",
925184610Salfred	    sc->sc_mixer_count);
926184610Salfred
927249796Shselasky	if (sc->sc_play_chan.num_alt > 0) {
928249796Shselasky		uint8_t x;
929250763Shselasky
930250763Shselasky		/*
931250763Shselasky		 * Need to set a default alternate interface, else
932250763Shselasky		 * some USB audio devices might go into an infinte
933250763Shselasky		 * re-enumeration loop:
934250763Shselasky		 */
935250763Shselasky		err = usbd_set_alt_interface_index(sc->sc_udev,
936250763Shselasky		    sc->sc_play_chan.usb_alt[0].iface_index,
937250763Shselasky		    sc->sc_play_chan.usb_alt[0].iface_alt_index);
938250763Shselasky		if (err) {
939250763Shselasky			DPRINTF("setting of alternate index failed: %s!\n",
940250763Shselasky			    usbd_errstr(err));
941250763Shselasky		}
942249796Shselasky		for (x = 0; x != sc->sc_play_chan.num_alt; x++) {
943249796Shselasky			device_printf(dev, "Play: %d Hz, %d ch, %s format, "
944249796Shselasky			    "2x8ms buffer.\n",
945249796Shselasky			    sc->sc_play_chan.usb_alt[x].sample_rate,
946249796Shselasky			    sc->sc_play_chan.usb_alt[x].channels,
947249796Shselasky			    sc->sc_play_chan.usb_alt[x].p_fmt->description);
948249796Shselasky		}
949184610Salfred	} else {
950232039Shselasky		device_printf(dev, "No playback.\n");
951184610Salfred	}
952184610Salfred
953249796Shselasky	if (sc->sc_rec_chan.num_alt > 0) {
954249796Shselasky		uint8_t x;
955250763Shselasky
956250763Shselasky		/*
957250763Shselasky		 * Need to set a default alternate interface, else
958250763Shselasky		 * some USB audio devices might go into an infinte
959250763Shselasky		 * re-enumeration loop:
960250763Shselasky		 */
961250763Shselasky		err = usbd_set_alt_interface_index(sc->sc_udev,
962250763Shselasky		    sc->sc_rec_chan.usb_alt[0].iface_index,
963250763Shselasky		    sc->sc_rec_chan.usb_alt[0].iface_alt_index);
964250763Shselasky		if (err) {
965250763Shselasky			DPRINTF("setting of alternate index failed: %s!\n",
966250763Shselasky			    usbd_errstr(err));
967250763Shselasky		}
968249796Shselasky		for (x = 0; x != sc->sc_rec_chan.num_alt; x++) {
969249796Shselasky			device_printf(dev, "Record: %d Hz, %d ch, %s format, "
970249796Shselasky			    "2x8ms buffer.\n",
971249796Shselasky			    sc->sc_rec_chan.usb_alt[x].sample_rate,
972249796Shselasky			    sc->sc_rec_chan.usb_alt[x].channels,
973249796Shselasky			    sc->sc_rec_chan.usb_alt[x].p_fmt->description);
974249796Shselasky		}
975184610Salfred	} else {
976232039Shselasky		device_printf(dev, "No recording.\n");
977184610Salfred	}
978184610Salfred
979244027Shselasky	if (sc->sc_midi_chan.valid == 0) {
980244027Shselasky		if (usbd_lookup_id_by_uaa(uaudio_vendor_midi,
981244027Shselasky		    sizeof(uaudio_vendor_midi), uaa) == 0) {
982244027Shselasky			sc->sc_midi_chan.iface_index =
983244027Shselasky			    (uint8_t)uaa->driver_info;
984244027Shselasky			sc->sc_midi_chan.iface_alt_index = 0;
985244027Shselasky			sc->sc_midi_chan.valid = 1;
986244027Shselasky		}
987244027Shselasky	}
988244027Shselasky
989184610Salfred	if (sc->sc_midi_chan.valid) {
990184610Salfred
991184610Salfred		if (umidi_probe(dev)) {
992184610Salfred			goto detach;
993184610Salfred		}
994232039Shselasky		device_printf(dev, "MIDI sequencer.\n");
995184610Salfred	} else {
996246421Shselasky		device_printf(dev, "No MIDI sequencer.\n");
997184610Salfred	}
998184610Salfred
999184610Salfred	DPRINTF("doing child attach\n");
1000184610Salfred
1001184610Salfred	/* attach the children */
1002184610Salfred
1003184610Salfred	sc->sc_sndcard_func.func = SCF_PCM;
1004184610Salfred
1005232039Shselasky	/*
1006232039Shselasky	 * Only attach a PCM device if we have a playback, recording
1007232039Shselasky	 * or mixer device present:
1008232039Shselasky	 */
1009249796Shselasky	if (sc->sc_play_chan.num_alt > 0 ||
1010249796Shselasky	    sc->sc_rec_chan.num_alt > 0 ||
1011232039Shselasky	    sc->sc_mix_info) {
1012232039Shselasky		child = device_add_child(dev, "pcm", -1);
1013184610Salfred
1014232039Shselasky		if (child == NULL) {
1015232039Shselasky			DPRINTF("out of memory\n");
1016232039Shselasky			goto detach;
1017232039Shselasky		}
1018232039Shselasky		device_set_ivars(child, &sc->sc_sndcard_func);
1019184610Salfred	}
1020184610Salfred
1021184610Salfred	if (bus_generic_attach(dev)) {
1022184610Salfred		DPRINTF("child attach failed\n");
1023184610Salfred		goto detach;
1024184610Salfred	}
1025242438Shselasky
1026246421Shselasky	if (uaudio_hid_probe(sc, uaa) == 0) {
1027246421Shselasky		device_printf(dev, "HID volume keys found.\n");
1028246421Shselasky	} else {
1029246421Shselasky		device_printf(dev, "No HID volume keys found.\n");
1030246421Shselasky	}
1031246421Shselasky
1032242438Shselasky	/* reload all mixer settings */
1033242438Shselasky	uaudio_mixer_reload_all(sc);
1034242438Shselasky
1035184610Salfred	return (0);			/* success */
1036184610Salfred
1037184610Salfreddetach:
1038184610Salfred	uaudio_detach(dev);
1039184610Salfred	return (ENXIO);
1040184610Salfred}
1041184610Salfred
1042184610Salfredstatic void
1043184610Salfreduaudio_pcm_setflags(device_t dev, uint32_t flags)
1044184610Salfred{
1045184610Salfred	pcm_setflags(dev, pcm_getflags(dev) | flags);
1046184610Salfred}
1047184610Salfred
1048184610Salfredint
1049184610Salfreduaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class)
1050184610Salfred{
1051184610Salfred	struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
1052184610Salfred	char status[SND_STATUSLEN];
1053184610Salfred
1054184610Salfred	uaudio_mixer_init(sc);
1055184610Salfred
1056184610Salfred	if (sc->sc_uq_audio_swap_lr) {
1057184610Salfred		DPRINTF("hardware has swapped left and right\n");
1058193640Sariff		/* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */
1059184610Salfred	}
1060184610Salfred	if (!(sc->sc_mix_info & SOUND_MASK_PCM)) {
1061184610Salfred
1062184610Salfred		DPRINTF("emulating master volume\n");
1063184610Salfred
1064184610Salfred		/*
1065184610Salfred		 * Emulate missing pcm mixer controller
1066184610Salfred		 * through FEEDER_VOLUME
1067184610Salfred		 */
1068184610Salfred		uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL);
1069184610Salfred	}
1070242438Shselasky	if (mixer_init(dev, mixer_class, sc))
1071184610Salfred		goto detach;
1072184610Salfred	sc->sc_mixer_init = 1;
1073184610Salfred
1074246454Shselasky	mixer_hwvol_init(dev);
1075246454Shselasky
1076184610Salfred	snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
1077184610Salfred
1078184610Salfred	if (pcm_register(dev, sc,
1079249796Shselasky	    (sc->sc_play_chan.num_alt > 0) ? 1 : 0,
1080249796Shselasky	    (sc->sc_rec_chan.num_alt > 0) ? 1 : 0)) {
1081184610Salfred		goto detach;
1082184610Salfred	}
1083193640Sariff
1084193640Sariff	uaudio_pcm_setflags(dev, SD_F_MPSAFE);
1085184610Salfred	sc->sc_pcm_registered = 1;
1086184610Salfred
1087249796Shselasky	if (sc->sc_play_chan.num_alt > 0) {
1088184610Salfred		pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc);
1089184610Salfred	}
1090249796Shselasky	if (sc->sc_rec_chan.num_alt > 0) {
1091184610Salfred		pcm_addchan(dev, PCMDIR_REC, chan_class, sc);
1092184610Salfred	}
1093184610Salfred	pcm_setstatus(dev, status);
1094184610Salfred
1095242438Shselasky	uaudio_mixer_register_sysctl(sc, dev);
1096242438Shselasky
1097184610Salfred	return (0);			/* success */
1098184610Salfred
1099184610Salfreddetach:
1100184610Salfred	uaudio_detach_sub(dev);
1101184610Salfred	return (ENXIO);
1102184610Salfred}
1103184610Salfred
1104184610Salfredint
1105184610Salfreduaudio_detach_sub(device_t dev)
1106184610Salfred{
1107184610Salfred	struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
1108184610Salfred	int error = 0;
1109184610Salfred
1110184610Salfredrepeat:
1111184610Salfred	if (sc->sc_pcm_registered) {
1112184610Salfred		error = pcm_unregister(dev);
1113184610Salfred	} else {
1114184610Salfred		if (sc->sc_mixer_init) {
1115184610Salfred			error = mixer_uninit(dev);
1116184610Salfred		}
1117184610Salfred	}
1118184610Salfred
1119184610Salfred	if (error) {
1120184610Salfred		device_printf(dev, "Waiting for sound application to exit!\n");
1121194228Sthompsa		usb_pause_mtx(NULL, 2 * hz);
1122184610Salfred		goto repeat;		/* try again */
1123184610Salfred	}
1124184610Salfred	return (0);			/* success */
1125184610Salfred}
1126184610Salfred
1127184610Salfredstatic int
1128184610Salfreduaudio_detach(device_t dev)
1129184610Salfred{
1130184610Salfred	struct uaudio_softc *sc = device_get_softc(dev);
1131184610Salfred
1132228484Shselasky	/*
1133228484Shselasky	 * Stop USB transfers early so that any audio applications
1134228484Shselasky	 * will time out and close opened /dev/dspX.Y device(s), if
1135228484Shselasky	 * any.
1136228484Shselasky	 */
1137249796Shselasky	usb_proc_explore_lock(sc->sc_udev);
1138249796Shselasky	sc->sc_play_chan.operation = CHAN_OP_DRAIN;
1139249796Shselasky	sc->sc_rec_chan.operation = CHAN_OP_DRAIN;
1140249796Shselasky	usb_proc_explore_mwait(sc->sc_udev,
1141249796Shselasky	    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
1142249796Shselasky	usb_proc_explore_unlock(sc->sc_udev);
1143228484Shselasky
1144249796Shselasky	usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
1145249796Shselasky	usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);
1146249796Shselasky
1147246421Shselasky	uaudio_hid_detach(sc);
1148246421Shselasky
1149228484Shselasky	if (bus_generic_detach(dev) != 0) {
1150184610Salfred		DPRINTF("detach failed!\n");
1151184610Salfred	}
1152184610Salfred	sbuf_delete(&sc->sc_sndstat);
1153184610Salfred	sc->sc_sndstat_valid = 0;
1154184610Salfred
1155184610Salfred	umidi_detach(dev);
1156184610Salfred
1157242438Shselasky	/* free mixer data */
1158242438Shselasky
1159242438Shselasky	uaudio_mixer_ctl_free(sc);
1160242438Shselasky
1161184610Salfred	return (0);
1162184610Salfred}
1163184610Salfred
1164249796Shselaskystatic uint32_t
1165249796Shselaskyuaudio_get_buffer_size(struct uaudio_chan *ch, uint8_t alt)
1166249796Shselasky{
1167249796Shselasky	struct uaudio_chan_alt *chan_alt = &ch->usb_alt[alt];
1168249796Shselasky	/* We use 2 times 8ms of buffer */
1169249796Shselasky	uint32_t buf_size = (((chan_alt->sample_rate * (UAUDIO_NFRAMES / 8)) +
1170249796Shselasky	    1000 - 1) / 1000) * chan_alt->sample_size;
1171249796Shselasky	return (buf_size);
1172249796Shselasky}
1173249796Shselasky
1174249796Shselaskystatic void
1175249796Shselaskyuaudio_configure_msg_sub(struct uaudio_softc *sc,
1176249796Shselasky    struct uaudio_chan *chan, int dir)
1177249796Shselasky{
1178249796Shselasky	struct uaudio_chan_alt *chan_alt;
1179249796Shselasky	uint32_t frames;
1180249796Shselasky	uint32_t buf_size;
1181249796Shselasky	uint16_t fps;
1182249796Shselasky	uint8_t set_alt;
1183249796Shselasky	uint8_t fps_shift;
1184249796Shselasky	uint8_t operation;
1185249796Shselasky	usb_error_t err;
1186249796Shselasky
1187249796Shselasky	if (chan->num_alt <= 0)
1188249796Shselasky		return;
1189249796Shselasky
1190249796Shselasky	DPRINTF("\n");
1191249796Shselasky
1192249796Shselasky	usb_proc_explore_lock(sc->sc_udev);
1193249796Shselasky	operation = chan->operation;
1194249796Shselasky	chan->operation = CHAN_OP_NONE;
1195249796Shselasky	usb_proc_explore_unlock(sc->sc_udev);
1196249796Shselasky
1197249796Shselasky	mtx_lock(chan->pcm_mtx);
1198249796Shselasky	if (chan->cur_alt != chan->set_alt)
1199249796Shselasky		set_alt = chan->set_alt;
1200249796Shselasky	else
1201249796Shselasky		set_alt = CHAN_MAX_ALT;
1202249796Shselasky	mtx_unlock(chan->pcm_mtx);
1203249796Shselasky
1204249796Shselasky	if (set_alt >= chan->num_alt)
1205249796Shselasky		goto done;
1206249796Shselasky
1207249796Shselasky	chan_alt = chan->usb_alt + set_alt;
1208249796Shselasky
1209249796Shselasky	usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
1210249796Shselasky
1211249796Shselasky	err = usbd_set_alt_interface_index(sc->sc_udev,
1212249796Shselasky	    chan_alt->iface_index, chan_alt->iface_alt_index);
1213249796Shselasky	if (err) {
1214249796Shselasky		DPRINTF("setting of alternate index failed: %s!\n",
1215249796Shselasky		    usbd_errstr(err));
1216249796Shselasky		goto error;
1217249796Shselasky	}
1218249796Shselasky
1219249796Shselasky	/*
1220249796Shselasky	 * Only set the sample rate if the channel reports that it
1221249796Shselasky	 * supports the frequency control.
1222249796Shselasky	 */
1223249796Shselasky
1224249796Shselasky	if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
1225249796Shselasky		/* FALLTHROUGH */
1226249796Shselasky	} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
1227249796Shselasky		unsigned int x;
1228249796Shselasky
1229249796Shselasky		for (x = 0; x != 256; x++) {
1230249796Shselasky			if (dir == PCMDIR_PLAY) {
1231249796Shselasky				if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
1232249796Shselasky				    (1 << (x % 8)))) {
1233249796Shselasky					continue;
1234249796Shselasky				}
1235249796Shselasky			} else {
1236249796Shselasky				if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
1237249796Shselasky				    (1 << (x % 8)))) {
1238249796Shselasky					continue;
1239249796Shselasky				}
1240249796Shselasky			}
1241249796Shselasky
1242249796Shselasky			if (uaudio20_set_speed(sc->sc_udev,
1243249796Shselasky			    sc->sc_mixer_iface_no, x, chan_alt->sample_rate)) {
1244249796Shselasky				/*
1245249796Shselasky				 * If the endpoint is adaptive setting
1246249796Shselasky				 * the speed may fail.
1247249796Shselasky				 */
1248249796Shselasky				DPRINTF("setting of sample rate failed! "
1249249796Shselasky				    "(continuing anyway)\n");
1250249796Shselasky			}
1251249796Shselasky		}
1252249796Shselasky	} else if (chan_alt->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) {
1253249796Shselasky		if (uaudio_set_speed(sc->sc_udev,
1254249796Shselasky		    chan_alt->p_ed1->bEndpointAddress, chan_alt->sample_rate)) {
1255249796Shselasky			/*
1256249796Shselasky			 * If the endpoint is adaptive setting the
1257249796Shselasky			 * speed may fail.
1258249796Shselasky			 */
1259249796Shselasky			DPRINTF("setting of sample rate failed! "
1260249796Shselasky			    "(continuing anyway)\n");
1261249796Shselasky		}
1262249796Shselasky	}
1263249796Shselasky	if (usbd_transfer_setup(sc->sc_udev, &chan_alt->iface_index, chan->xfer,
1264249796Shselasky	    chan_alt->usb_cfg, UAUDIO_NCHANBUFS + 1, chan, chan->pcm_mtx)) {
1265249796Shselasky		DPRINTF("could not allocate USB transfers!\n");
1266249796Shselasky		goto error;
1267249796Shselasky	}
1268249796Shselasky
1269249796Shselasky	fps = usbd_get_isoc_fps(sc->sc_udev);
1270249796Shselasky
1271249796Shselasky	if (fps < 8000) {
1272249796Shselasky		/* FULL speed USB */
1273249796Shselasky		frames = 8;
1274249796Shselasky	} else {
1275249796Shselasky		/* HIGH speed USB */
1276249796Shselasky		frames = UAUDIO_NFRAMES;
1277249796Shselasky	}
1278249796Shselasky
1279249796Shselasky	fps_shift = usbd_xfer_get_fps_shift(chan->xfer[0]);
1280249796Shselasky
1281249796Shselasky	/* down shift number of frames per second, if any */
1282249796Shselasky	fps >>= fps_shift;
1283249796Shselasky	frames >>= fps_shift;
1284249796Shselasky
1285249796Shselasky	/* bytes per frame should not be zero */
1286249796Shselasky	chan->bytes_per_frame[0] =
1287249796Shselasky	    ((chan_alt->sample_rate / fps) * chan_alt->sample_size);
1288249796Shselasky	chan->bytes_per_frame[1] =
1289249796Shselasky	    (((chan_alt->sample_rate + fps - 1) / fps) * chan_alt->sample_size);
1290249796Shselasky
1291249796Shselasky	/* setup data rate dithering, if any */
1292249796Shselasky	chan->frames_per_second = fps;
1293249796Shselasky	chan->sample_rem = chan_alt->sample_rate % fps;
1294249796Shselasky	chan->sample_curr = 0;
1295249796Shselasky	chan->frames_per_second = fps;
1296249796Shselasky
1297249796Shselasky	/* compute required buffer size */
1298249796Shselasky	buf_size = (chan->bytes_per_frame[1] * frames);
1299249796Shselasky
1300249796Shselasky	if (buf_size > (chan->end - chan->start)) {
1301249796Shselasky		DPRINTF("buffer size is too big\n");
1302249796Shselasky		goto error;
1303249796Shselasky	}
1304249796Shselasky
1305249796Shselasky	chan->intr_frames = frames;
1306249796Shselasky
1307249796Shselasky	DPRINTF("fps=%d sample_rem=%d\n", (int)fps, (int)chan->sample_rem);
1308249796Shselasky
1309249796Shselasky	if (chan->intr_frames == 0) {
1310249796Shselasky		DPRINTF("frame shift is too high!\n");
1311249796Shselasky		goto error;
1312249796Shselasky	}
1313249796Shselasky
1314249796Shselasky	mtx_lock(chan->pcm_mtx);
1315249796Shselasky	chan->cur_alt = set_alt;
1316249796Shselasky	mtx_unlock(chan->pcm_mtx);
1317249796Shselasky
1318249796Shselaskydone:
1319249796Shselasky#if (UAUDIO_NCHANBUFS != 2)
1320249796Shselasky#error "please update code"
1321249796Shselasky#endif
1322249796Shselasky	switch (operation) {
1323249796Shselasky	case CHAN_OP_START:
1324249796Shselasky		mtx_lock(chan->pcm_mtx);
1325249796Shselasky		usbd_transfer_start(chan->xfer[0]);
1326249796Shselasky		usbd_transfer_start(chan->xfer[1]);
1327249796Shselasky		mtx_unlock(chan->pcm_mtx);
1328249796Shselasky		break;
1329249796Shselasky	case CHAN_OP_STOP:
1330249796Shselasky		mtx_lock(chan->pcm_mtx);
1331249796Shselasky		usbd_transfer_stop(chan->xfer[0]);
1332249796Shselasky		usbd_transfer_stop(chan->xfer[1]);
1333249796Shselasky		mtx_unlock(chan->pcm_mtx);
1334249796Shselasky		break;
1335249796Shselasky	default:
1336249796Shselasky		break;
1337249796Shselasky	}
1338249796Shselasky	return;
1339249796Shselasky
1340249796Shselaskyerror:
1341249796Shselasky	usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1);
1342249796Shselasky
1343249796Shselasky	mtx_lock(chan->pcm_mtx);
1344249796Shselasky	chan->cur_alt = CHAN_MAX_ALT;
1345249796Shselasky	mtx_unlock(chan->pcm_mtx);
1346249796Shselasky}
1347249796Shselasky
1348249796Shselaskystatic void
1349249796Shselaskyuaudio_configure_msg(struct usb_proc_msg *pm)
1350249796Shselasky{
1351249796Shselasky	struct uaudio_softc *sc = ((struct uaudio_configure_msg *)pm)->sc;
1352249796Shselasky
1353249796Shselasky	usb_proc_explore_unlock(sc->sc_udev);
1354249796Shselasky	uaudio_configure_msg_sub(sc, &sc->sc_play_chan, PCMDIR_PLAY);
1355249796Shselasky	uaudio_configure_msg_sub(sc, &sc->sc_rec_chan, PCMDIR_REC);
1356249796Shselasky	usb_proc_explore_lock(sc->sc_udev);
1357249796Shselasky}
1358249796Shselasky
1359184610Salfred/*========================================================================*
1360184610Salfred * AS - Audio Stream - routines
1361184610Salfred *========================================================================*/
1362184610Salfred
1363207077Sthompsa#ifdef USB_DEBUG
1364184610Salfredstatic void
1365203678Sbrucecuaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed)
1366184610Salfred{
1367184610Salfred	if (ed) {
1368184610Salfred		DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n"
1369184610Salfred		    "bEndpointAddress=%d bmAttributes=0x%x \n"
1370184610Salfred		    "wMaxPacketSize=%d bInterval=%d \n"
1371184610Salfred		    "bRefresh=%d bSynchAddress=%d\n",
1372184610Salfred		    ed, ed->bLength, ed->bDescriptorType,
1373184610Salfred		    ed->bEndpointAddress, ed->bmAttributes,
1374184610Salfred		    UGETW(ed->wMaxPacketSize), ed->bInterval,
1375209452Sthompsa		    UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0,
1376209452Sthompsa		    UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0);
1377184610Salfred	}
1378184610Salfred}
1379184610Salfred
1380184610Salfred#endif
1381184610Salfred
1382221695Shselasky/*
1383221695Shselasky * The following is a workaround for broken no-name USB audio devices
1384221695Shselasky * sold by dealextreme called "3D sound". The problem is that the
1385221695Shselasky * manufacturer computed wMaxPacketSize is too small to hold the
1386221695Shselasky * actual data sent. In other words the device sometimes sends more
1387221695Shselasky * data than it actually reports it can send in a single isochronous
1388221695Shselasky * packet.
1389221695Shselasky */
1390184610Salfredstatic void
1391221695Shselaskyuaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep,
1392221695Shselasky    uint32_t xps, uint32_t add)
1393221695Shselasky{
1394221695Shselasky	uint32_t mps;
1395221695Shselasky
1396221695Shselasky	mps = UGETW(ep->wMaxPacketSize);
1397221695Shselasky
1398221695Shselasky	/*
1399221695Shselasky	 * If the device indicates it can send more data than what the
1400221695Shselasky	 * sample rate indicates, we apply the workaround.
1401221695Shselasky	 */
1402221695Shselasky	if (mps > xps) {
1403221695Shselasky
1404221695Shselasky		/* allow additional data */
1405221695Shselasky		xps += add;
1406221695Shselasky
1407221695Shselasky		/* check against the maximum USB 1.x length */
1408221695Shselasky		if (xps > 1023)
1409221695Shselasky			xps = 1023;
1410221695Shselasky
1411221695Shselasky		/* check if we should do an update */
1412221695Shselasky		if (mps < xps) {
1413221695Shselasky			/* simply update the wMaxPacketSize field */
1414221695Shselasky			USETW(ep->wMaxPacketSize, xps);
1415221695Shselasky			DPRINTF("Workaround: Updated wMaxPacketSize "
1416221695Shselasky			    "from %d to %d bytes.\n",
1417221695Shselasky			    (int)mps, (int)xps);
1418221695Shselasky		}
1419221695Shselasky	}
1420221695Shselasky}
1421221695Shselasky
1422240609Shselaskystatic usb_error_t
1423240609Shselaskyuaudio20_check_rate(struct usb_device *udev, uint8_t iface_no,
1424240609Shselasky    uint8_t clockid, uint32_t rate)
1425240609Shselasky{
1426240609Shselasky	struct usb_device_request req;
1427240609Shselasky	usb_error_t error;
1428240609Shselasky	uint8_t data[255];
1429240609Shselasky	uint16_t actlen;
1430240609Shselasky	uint16_t rates;
1431240609Shselasky	uint16_t x;
1432240609Shselasky
1433240609Shselasky	DPRINTFN(6, "ifaceno=%d clockid=%d rate=%u\n",
1434240609Shselasky	    iface_no, clockid, rate);
1435240609Shselasky
1436240609Shselasky	req.bmRequestType = UT_READ_CLASS_INTERFACE;
1437240609Shselasky	req.bRequest = UA20_CS_RANGE;
1438240609Shselasky	USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
1439240609Shselasky	USETW2(req.wIndex, clockid, iface_no);
1440240609Shselasky	USETW(req.wLength, 255);
1441240609Shselasky
1442240609Shselasky        error = usbd_do_request_flags(udev, NULL, &req, data,
1443240609Shselasky	    USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT);
1444240609Shselasky
1445240609Shselasky	if (error != 0 || actlen < 2)
1446240609Shselasky		return (USB_ERR_INVAL);
1447240609Shselasky
1448240609Shselasky	rates = data[0] | (data[1] << 8);
1449240609Shselasky	actlen = (actlen - 2) / 12;
1450240609Shselasky
1451240609Shselasky	if (rates > actlen) {
1452240609Shselasky		DPRINTF("Too many rates\n");
1453240609Shselasky		rates = actlen;
1454240609Shselasky	}
1455240609Shselasky
1456240609Shselasky	for (x = 0; x != rates; x++) {
1457240609Shselasky		uint32_t min = UGETDW(data + 2 + (12 * x));
1458240609Shselasky		uint32_t max = UGETDW(data + 6 + (12 * x));
1459240609Shselasky		uint32_t res = UGETDW(data + 10 + (12 * x));
1460240609Shselasky
1461240609Shselasky		if (res == 0) {
1462240609Shselasky			DPRINTF("Zero residue\n");
1463240609Shselasky			res = 1;
1464240609Shselasky		}
1465240609Shselasky
1466240609Shselasky		if (min > max) {
1467240609Shselasky			DPRINTF("Swapped max and min\n");
1468240609Shselasky			uint32_t temp;
1469240609Shselasky			temp = min;
1470240609Shselasky			min = max;
1471240609Shselasky			max = temp;
1472240609Shselasky		}
1473240609Shselasky
1474240609Shselasky		if (rate >= min && rate <= max &&
1475240609Shselasky		    (((rate - min) % res) == 0)) {
1476240609Shselasky			return (0);
1477240609Shselasky		}
1478240609Shselasky	}
1479240609Shselasky	return (USB_ERR_INVAL);
1480240609Shselasky}
1481240609Shselasky
1482221695Shselaskystatic void
1483192984Sthompsauaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
1484200825Sthompsa    uint32_t rate, uint8_t channels, uint8_t bit_resolution)
1485184610Salfred{
1486192984Sthompsa	struct usb_descriptor *desc = NULL;
1487240609Shselasky	union uaudio_asid asid = { NULL };
1488240609Shselasky	union uaudio_asf1d asf1d = { NULL };
1489240609Shselasky	union uaudio_sed sed = { NULL };
1490272423Shselasky	struct usb_midi_streaming_endpoint_descriptor *msid = NULL;
1491221695Shselasky	usb_endpoint_descriptor_audio_t *ed1 = NULL;
1492240609Shselasky	const struct usb_audio_control_descriptor *acdp = NULL;
1493194228Sthompsa	struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
1494192984Sthompsa	struct usb_interface_descriptor *id;
1495240609Shselasky	const struct uaudio_format *p_fmt = NULL;
1496184610Salfred	struct uaudio_chan *chan;
1497249796Shselasky	struct uaudio_chan_alt *chan_alt;
1498249796Shselasky	uint32_t format;
1499184610Salfred	uint16_t curidx = 0xFFFF;
1500184610Salfred	uint16_t lastidx = 0xFFFF;
1501184610Salfred	uint16_t alt_index = 0;
1502240609Shselasky	uint16_t audio_rev = 0;
1503240609Shselasky	uint16_t x;
1504184610Salfred	uint8_t ep_dir;
1505184610Salfred	uint8_t bChannels;
1506184610Salfred	uint8_t bBitResolution;
1507184610Salfred	uint8_t audio_if = 0;
1508272423Shselasky	uint8_t midi_if = 0;
1509218988Shselasky	uint8_t uma_if_class;
1510184610Salfred
1511194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
1512184610Salfred
1513184610Salfred		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
1514184610Salfred		    (desc->bLength >= sizeof(*id))) {
1515184610Salfred
1516184610Salfred			id = (void *)desc;
1517184610Salfred
1518184610Salfred			if (id->bInterfaceNumber != lastidx) {
1519184610Salfred				lastidx = id->bInterfaceNumber;
1520184610Salfred				curidx++;
1521184610Salfred				alt_index = 0;
1522184610Salfred
1523184610Salfred			} else {
1524184610Salfred				alt_index++;
1525184610Salfred			}
1526184610Salfred
1527246421Shselasky			if ((!(sc->sc_hid.flags & UAUDIO_HID_VALID)) &&
1528246421Shselasky			    (id->bInterfaceClass == UICLASS_HID) &&
1529246421Shselasky			    (id->bInterfaceSubClass == 0) &&
1530246421Shselasky			    (id->bInterfaceProtocol == 0) &&
1531246421Shselasky			    (alt_index == 0) &&
1532246421Shselasky			    usbd_get_iface(udev, curidx) != NULL) {
1533246421Shselasky				DPRINTF("Found HID interface at %d\n",
1534246421Shselasky				    curidx);
1535246421Shselasky				sc->sc_hid.flags |= UAUDIO_HID_VALID;
1536246421Shselasky				sc->sc_hid.iface_index = curidx;
1537246421Shselasky			}
1538246421Shselasky
1539218988Shselasky			uma_if_class =
1540218988Shselasky			    ((id->bInterfaceClass == UICLASS_AUDIO) ||
1541218988Shselasky			    ((id->bInterfaceClass == UICLASS_VENDOR) &&
1542218988Shselasky			    (sc->sc_uq_au_vendor_class != 0)));
1543218988Shselasky
1544272423Shselasky			if ((uma_if_class != 0) &&
1545272423Shselasky			    (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) {
1546184610Salfred				audio_if = 1;
1547184610Salfred			} else {
1548184610Salfred				audio_if = 0;
1549184610Salfred			}
1550184610Salfred
1551218988Shselasky			if ((uma_if_class != 0) &&
1552184610Salfred			    (id->bInterfaceSubClass == UISUBCLASS_MIDISTREAM)) {
1553184610Salfred
1554184610Salfred				/*
1555184610Salfred				 * XXX could allow multiple MIDI interfaces
1556184610Salfred				 */
1557272423Shselasky				midi_if = 1;
1558184610Salfred
1559184610Salfred				if ((sc->sc_midi_chan.valid == 0) &&
1560272423Shselasky				    (usbd_get_iface(udev, curidx) != NULL)) {
1561184610Salfred					sc->sc_midi_chan.iface_index = curidx;
1562184610Salfred					sc->sc_midi_chan.iface_alt_index = alt_index;
1563184610Salfred					sc->sc_midi_chan.valid = 1;
1564184610Salfred				}
1565272423Shselasky			} else {
1566272423Shselasky				midi_if = 0;
1567184610Salfred			}
1568240609Shselasky			asid.v1 = NULL;
1569240609Shselasky			asf1d.v1 = NULL;
1570184610Salfred			ed1 = NULL;
1571240609Shselasky			sed.v1 = NULL;
1572184610Salfred		}
1573240609Shselasky
1574241988Shselasky		if (audio_if == 0) {
1575272423Shselasky			if (midi_if == 0) {
1576272423Shselasky				if ((acdp == NULL) &&
1577272423Shselasky				    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
1578272423Shselasky				    (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) &&
1579272423Shselasky				    (desc->bLength >= sizeof(*acdp))) {
1580272423Shselasky					acdp = (void *)desc;
1581272423Shselasky					audio_rev = UGETW(acdp->bcdADC);
1582272423Shselasky				}
1583272423Shselasky			} else {
1584272423Shselasky				msid = (void *)desc;
1585272423Shselasky
1586272423Shselasky				/* get the maximum number of embedded jacks in use, if any */
1587272423Shselasky				if (msid->bLength >= sizeof(*msid) &&
1588272423Shselasky				    msid->bDescriptorType == UDESC_CS_ENDPOINT &&
1589272423Shselasky				    msid->bDescriptorSubtype == MS_GENERAL &&
1590272423Shselasky				    msid->bNumEmbMIDIJack > sc->sc_midi_chan.max_emb_jack) {
1591272423Shselasky					sc->sc_midi_chan.max_emb_jack = msid->bNumEmbMIDIJack;
1592272423Shselasky				}
1593241988Shselasky			}
1594241988Shselasky			/*
1595241988Shselasky			 * Don't collect any USB audio descriptors if
1596241988Shselasky			 * this is not an USB audio stream interface.
1597241988Shselasky			 */
1598241988Shselasky			continue;
1599240609Shselasky		}
1600240609Shselasky
1601240609Shselasky		if ((acdp != NULL) &&
1602240609Shselasky		    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
1603240609Shselasky		    (desc->bDescriptorSubtype == AS_GENERAL) &&
1604240609Shselasky		    (asid.v1 == NULL)) {
1605240609Shselasky			if (audio_rev >= UAUDIO_VERSION_30) {
1606240609Shselasky				/* FALLTHROUGH */
1607240609Shselasky			} else if (audio_rev >= UAUDIO_VERSION_20) {
1608240609Shselasky				if (desc->bLength >= sizeof(*asid.v2)) {
1609240609Shselasky					asid.v2 = (void *)desc;
1610240609Shselasky				}
1611240609Shselasky			} else {
1612240609Shselasky				if (desc->bLength >= sizeof(*asid.v1)) {
1613240609Shselasky					asid.v1 = (void *)desc;
1614240609Shselasky				}
1615184610Salfred			}
1616184610Salfred		}
1617240609Shselasky		if ((acdp != NULL) &&
1618240609Shselasky		    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
1619184610Salfred		    (desc->bDescriptorSubtype == FORMAT_TYPE) &&
1620240609Shselasky		    (asf1d.v1 == NULL)) {
1621240609Shselasky			if (audio_rev >= UAUDIO_VERSION_30) {
1622240609Shselasky				/* FALLTHROUGH */
1623240609Shselasky			} else if (audio_rev >= UAUDIO_VERSION_20) {
1624240609Shselasky				if (desc->bLength >= sizeof(*asf1d.v2))
1625240609Shselasky					asf1d.v2 = (void *)desc;
1626240609Shselasky			} else {
1627240609Shselasky				if (desc->bLength >= sizeof(*asf1d.v1)) {
1628240609Shselasky					asf1d.v1 = (void *)desc;
1629240609Shselasky
1630240609Shselasky					if (asf1d.v1->bFormatType != FORMAT_TYPE_I) {
1631240609Shselasky						DPRINTFN(11, "ignored bFormatType = %d\n",
1632240609Shselasky						    asf1d.v1->bFormatType);
1633240609Shselasky						asf1d.v1 = NULL;
1634240609Shselasky						continue;
1635240609Shselasky					}
1636240609Shselasky					if (desc->bLength < (sizeof(*asf1d.v1) +
1637240609Shselasky					    ((asf1d.v1->bSamFreqType == 0) ? 6 :
1638240609Shselasky					    (asf1d.v1->bSamFreqType * 3)))) {
1639240609Shselasky						DPRINTFN(11, "invalid descriptor, "
1640240609Shselasky						    "too short\n");
1641240609Shselasky						asf1d.v1 = NULL;
1642240609Shselasky						continue;
1643240609Shselasky					}
1644184610Salfred				}
1645184610Salfred			}
1646184610Salfred		}
1647184610Salfred		if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
1648240609Shselasky		    (desc->bLength >= UEP_MINSIZE) &&
1649240609Shselasky		    (ed1 == NULL)) {
1650240609Shselasky			ed1 = (void *)desc;
1651240609Shselasky			if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) {
1652240609Shselasky				ed1 = NULL;
1653240609Shselasky				continue;
1654184610Salfred			}
1655184610Salfred		}
1656240609Shselasky		if ((acdp != NULL) &&
1657240609Shselasky		    (desc->bDescriptorType == UDESC_CS_ENDPOINT) &&
1658184610Salfred		    (desc->bDescriptorSubtype == AS_GENERAL) &&
1659240609Shselasky		    (sed.v1 == NULL)) {
1660240609Shselasky			if (audio_rev >= UAUDIO_VERSION_30) {
1661240609Shselasky				/* FALLTHROUGH */
1662240609Shselasky			} else if (audio_rev >= UAUDIO_VERSION_20) {
1663240609Shselasky				if (desc->bLength >= sizeof(*sed.v2))
1664240609Shselasky					sed.v2 = (void *)desc;
1665240609Shselasky			} else {
1666240609Shselasky				if (desc->bLength >= sizeof(*sed.v1))
1667240609Shselasky					sed.v1 = (void *)desc;
1668184610Salfred			}
1669184610Salfred		}
1670241988Shselasky		if (asid.v1 == NULL || asf1d.v1 == NULL ||
1671241988Shselasky		    ed1 == NULL || sed.v1 == NULL) {
1672240609Shselasky			/* need more descriptors */
1673240609Shselasky			continue;
1674240609Shselasky		}
1675184610Salfred
1676240609Shselasky		ep_dir = UE_GET_DIR(ed1->bEndpointAddress);
1677184610Salfred
1678240609Shselasky		/* We ignore sync endpoint information until further. */
1679184610Salfred
1680240609Shselasky		if (audio_rev >= UAUDIO_VERSION_30) {
1681240609Shselasky			goto next_ep;
1682240609Shselasky		} else if (audio_rev >= UAUDIO_VERSION_20) {
1683184610Salfred
1684240609Shselasky			uint32_t dwFormat;
1685240609Shselasky
1686240609Shselasky			dwFormat = UGETDW(asid.v2->bmFormats);
1687240609Shselasky			bChannels = asid.v2->bNrChannels;
1688270717Shselasky			bBitResolution = asf1d.v2->bSubslotSize * 8;
1689240609Shselasky
1690240609Shselasky			if ((bChannels != channels) ||
1691240609Shselasky			    (bBitResolution != bit_resolution)) {
1692240609Shselasky				DPRINTF("Wrong number of channels\n");
1693240609Shselasky				goto next_ep;
1694240609Shselasky			}
1695240609Shselasky
1696240609Shselasky			for (p_fmt = uaudio20_formats;
1697240609Shselasky			    p_fmt->wFormat != 0; p_fmt++) {
1698240609Shselasky				if ((p_fmt->wFormat & dwFormat) &&
1699240609Shselasky				    (p_fmt->bPrecision == bBitResolution))
1700240609Shselasky					break;
1701240609Shselasky			}
1702240609Shselasky
1703240609Shselasky			if (p_fmt->wFormat == 0) {
1704240609Shselasky				DPRINTF("Unsupported audio format\n");
1705240609Shselasky				goto next_ep;
1706240609Shselasky			}
1707240609Shselasky
1708240609Shselasky			for (x = 0; x != 256; x++) {
1709240609Shselasky				if (ep_dir == UE_DIR_OUT) {
1710240609Shselasky					if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
1711240609Shselasky					    (1 << (x % 8)))) {
1712240609Shselasky						continue;
1713240609Shselasky					}
1714240609Shselasky				} else {
1715240609Shselasky					if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
1716240609Shselasky					    (1 << (x % 8)))) {
1717240609Shselasky						continue;
1718240609Shselasky					}
1719240609Shselasky				}
1720240609Shselasky
1721240609Shselasky				DPRINTF("Checking clock ID=%d\n", x);
1722240609Shselasky
1723240609Shselasky				if (uaudio20_check_rate(udev,
1724240609Shselasky				    sc->sc_mixer_iface_no, x, rate)) {
1725240609Shselasky					DPRINTF("Unsupported sampling "
1726240609Shselasky					    "rate, id=%d\n", x);
1727240609Shselasky					goto next_ep;
1728240609Shselasky				}
1729240609Shselasky			}
1730240609Shselasky		} else {
1731240609Shselasky			uint16_t wFormat;
1732240609Shselasky
1733240609Shselasky			wFormat = UGETW(asid.v1->wFormatTag);
1734240609Shselasky			bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels);
1735270717Shselasky			bBitResolution = asf1d.v1->bSubFrameSize * 8;
1736240609Shselasky
1737240609Shselasky			if (asf1d.v1->bSamFreqType == 0) {
1738184610Salfred				DPRINTFN(16, "Sample rate: %d-%dHz\n",
1739240609Shselasky				    UA_SAMP_LO(asf1d.v1),
1740240609Shselasky				    UA_SAMP_HI(asf1d.v1));
1741184610Salfred
1742240609Shselasky				if ((rate >= UA_SAMP_LO(asf1d.v1)) &&
1743240609Shselasky				    (rate <= UA_SAMP_HI(asf1d.v1)))
1744184610Salfred					goto found_rate;
1745184610Salfred			} else {
1746184610Salfred
1747240609Shselasky				for (x = 0; x < asf1d.v1->bSamFreqType; x++) {
1748184610Salfred					DPRINTFN(16, "Sample rate = %dHz\n",
1749240609Shselasky					    UA_GETSAMP(asf1d.v1, x));
1750184610Salfred
1751240609Shselasky					if (rate == UA_GETSAMP(asf1d.v1, x))
1752184610Salfred						goto found_rate;
1753184610Salfred				}
1754184610Salfred			}
1755240609Shselasky			goto next_ep;
1756184610Salfred
1757184610Salfred	found_rate:
1758240609Shselasky			for (p_fmt = uaudio10_formats;
1759240609Shselasky			    p_fmt->wFormat != 0; p_fmt++) {
1760184610Salfred				if ((p_fmt->wFormat == wFormat) &&
1761240609Shselasky				    (p_fmt->bPrecision == bBitResolution))
1762240609Shselasky					break;
1763184610Salfred			}
1764240609Shselasky			if (p_fmt->wFormat == 0) {
1765240609Shselasky				DPRINTF("Unsupported audio format\n");
1766240609Shselasky				goto next_ep;
1767240609Shselasky			}
1768184610Salfred
1769240609Shselasky			if ((bChannels != channels) ||
1770240609Shselasky			    (bBitResolution != bit_resolution)) {
1771240609Shselasky				DPRINTF("Wrong number of channels\n");
1772240609Shselasky				goto next_ep;
1773240609Shselasky			}
1774240609Shselasky		}
1775184610Salfred
1776240609Shselasky		chan = (ep_dir == UE_DIR_IN) ?
1777240609Shselasky		    &sc->sc_rec_chan : &sc->sc_play_chan;
1778184610Salfred
1779249796Shselasky		if (usbd_get_iface(udev, curidx) == NULL) {
1780249796Shselasky			DPRINTF("Interface is not valid\n");
1781240609Shselasky			goto next_ep;
1782240609Shselasky		}
1783249796Shselasky		if (chan->num_alt == CHAN_MAX_ALT) {
1784249796Shselasky			DPRINTF("Too many alternate settings\n");
1785249796Shselasky			goto next_ep;
1786249796Shselasky		}
1787249796Shselasky		chan->set_alt = 0;
1788249796Shselasky		chan->cur_alt = CHAN_MAX_ALT;
1789184610Salfred
1790249796Shselasky		chan_alt = &chan->usb_alt[chan->num_alt++];
1791249796Shselasky
1792207077Sthompsa#ifdef USB_DEBUG
1793240609Shselasky		uaudio_chan_dump_ep_desc(ed1);
1794184610Salfred#endif
1795240609Shselasky		DPRINTF("Sample rate = %dHz, channels = %d, "
1796240609Shselasky		    "bits = %d, format = %s\n", rate, channels,
1797240609Shselasky		    bit_resolution, p_fmt->description);
1798184610Salfred
1799249796Shselasky		chan_alt->sample_rate = rate;
1800249796Shselasky		chan_alt->p_asf1d = asf1d;
1801249796Shselasky		chan_alt->p_ed1 = ed1;
1802249796Shselasky		chan_alt->p_fmt = p_fmt;
1803249796Shselasky		chan_alt->p_sed = sed;
1804249796Shselasky		chan_alt->iface_index = curidx;
1805249796Shselasky		chan_alt->iface_alt_index = alt_index;
1806184610Salfred
1807249796Shselasky		usbd_set_parent_iface(sc->sc_udev, curidx,
1808249796Shselasky		    sc->sc_mixer_iface_index);
1809249796Shselasky
1810240609Shselasky		if (ep_dir == UE_DIR_IN)
1811249796Shselasky			chan_alt->usb_cfg = uaudio_cfg_record;
1812240609Shselasky		else
1813249796Shselasky			chan_alt->usb_cfg = uaudio_cfg_play;
1814184610Salfred
1815249796Shselasky		chan_alt->sample_size = (UAUDIO_MAX_CHAN(channels) *
1816240609Shselasky		    p_fmt->bPrecision) / 8;
1817249796Shselasky		chan_alt->channels = channels;
1818184610Salfred
1819240609Shselasky		if (ep_dir == UE_DIR_IN &&
1820240609Shselasky		    usbd_get_speed(udev) == USB_SPEED_FULL) {
1821240609Shselasky			uaudio_record_fix_fs(ed1,
1822249796Shselasky			    chan_alt->sample_size * (rate / 1000),
1823249796Shselasky			    chan_alt->sample_size * (rate / 4000));
1824240609Shselasky		}
1825221695Shselasky
1826249796Shselasky		/* setup play/record format */
1827249796Shselasky
1828249796Shselasky		format = chan_alt->p_fmt->freebsd_fmt;
1829249796Shselasky
1830249796Shselasky		switch (chan_alt->channels) {
1831249796Shselasky		case 2:
1832249796Shselasky			/* stereo */
1833249796Shselasky			format = SND_FORMAT(format, 2, 0);
1834249796Shselasky			break;
1835249796Shselasky		case 1:
1836249796Shselasky			/* mono */
1837249796Shselasky			format = SND_FORMAT(format, 1, 0);
1838249796Shselasky			break;
1839249796Shselasky		default:
1840249796Shselasky			/* surround and more */
1841249796Shselasky			format = feeder_matrix_default_format(
1842249796Shselasky			    SND_FORMAT(format, chan_alt->channels, 0));
1843249796Shselasky			break;
1844249796Shselasky		}
1845249796Shselasky
1846249796Shselasky		/* check if format is not supported */
1847249796Shselasky		if (format == 0) {
1848249796Shselasky			DPRINTF("The selected audio format is not supported\n");
1849249796Shselasky			chan->num_alt--;
1850249796Shselasky			goto next_ep;
1851249796Shselasky		}
1852249845Shselasky		if (chan->num_alt > 1) {
1853249845Shselasky			/* we only accumulate one format at different sample rates */
1854249845Shselasky			if (chan->pcm_format[0] != format) {
1855249845Shselasky				DPRINTF("Multiple formats is not supported\n");
1856249845Shselasky				chan->num_alt--;
1857249845Shselasky				goto next_ep;
1858249845Shselasky			}
1859249845Shselasky			/* ignore if duplicate sample rate entry */
1860249845Shselasky			if (rate == chan->usb_alt[chan->num_alt - 2].sample_rate) {
1861249845Shselasky				DPRINTF("Duplicate sample rate detected\n");
1862249845Shselasky				chan->num_alt--;
1863249845Shselasky				goto next_ep;
1864249845Shselasky			}
1865249796Shselasky		}
1866249796Shselasky		chan->pcm_cap.fmtlist = chan->pcm_format;
1867249796Shselasky		chan->pcm_cap.fmtlist[0] = format;
1868249796Shselasky
1869249796Shselasky		if (rate < chan->pcm_cap.minspeed || chan->pcm_cap.minspeed == 0)
1870249796Shselasky			chan->pcm_cap.minspeed = rate;
1871249796Shselasky		if (rate > chan->pcm_cap.maxspeed || chan->pcm_cap.maxspeed == 0)
1872249796Shselasky			chan->pcm_cap.maxspeed = rate;
1873249796Shselasky
1874240609Shselasky		if (sc->sc_sndstat_valid != 0) {
1875240609Shselasky			sbuf_printf(&sc->sc_sndstat, "\n\t"
1876240609Shselasky			    "mode %d.%d:(%s) %dch, %dbit, %s, %dHz",
1877240609Shselasky			    curidx, alt_index,
1878240609Shselasky			    (ep_dir == UE_DIR_IN) ? "input" : "output",
1879240609Shselasky				    channels, p_fmt->bPrecision,
1880240609Shselasky				    p_fmt->description, rate);
1881184610Salfred		}
1882240609Shselasky
1883240609Shselasky	next_ep:
1884240609Shselasky		sed.v1 = NULL;
1885240609Shselasky		ed1 = NULL;
1886184610Salfred	}
1887184610Salfred}
1888184610Salfred
1889200825Sthompsa/* This structure defines all the supported rates. */
1890200825Sthompsa
1891249796Shselaskystatic const uint32_t uaudio_rate_list[CHAN_MAX_ALT] = {
1892263642Shselasky	384000,
1893263642Shselasky	352800,
1894263642Shselasky	192000,
1895263642Shselasky	176400,
1896200825Sthompsa	96000,
1897249796Shselasky	88200,
1898200825Sthompsa	88000,
1899200825Sthompsa	80000,
1900200825Sthompsa	72000,
1901200825Sthompsa	64000,
1902200825Sthompsa	56000,
1903200825Sthompsa	48000,
1904200825Sthompsa	44100,
1905200825Sthompsa	40000,
1906200825Sthompsa	32000,
1907200825Sthompsa	24000,
1908200825Sthompsa	22050,
1909200825Sthompsa	16000,
1910200825Sthompsa	11025,
1911200825Sthompsa	8000,
1912200825Sthompsa	0
1913200825Sthompsa};
1914200825Sthompsa
1915184610Salfredstatic void
1916192984Sthompsauaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
1917184610Salfred{
1918184610Salfred	uint32_t rate = uaudio_default_rate;
1919200825Sthompsa	uint8_t z;
1920184610Salfred	uint8_t bits = uaudio_default_bits;
1921184610Salfred	uint8_t y;
1922184610Salfred	uint8_t channels = uaudio_default_channels;
1923184610Salfred	uint8_t x;
1924184610Salfred
1925184610Salfred	bits -= (bits % 8);
1926186730Salfred	if ((bits == 0) || (bits > 32)) {
1927186730Salfred		/* set a valid value */
1928186730Salfred		bits = 32;
1929186730Salfred	}
1930200825Sthompsa	if (channels == 0) {
1931200825Sthompsa		switch (usbd_get_speed(udev)) {
1932200825Sthompsa		case USB_SPEED_LOW:
1933200825Sthompsa		case USB_SPEED_FULL:
1934200825Sthompsa			/*
1935200825Sthompsa			 * Due to high bandwidth usage and problems
1936200825Sthompsa			 * with HIGH-speed split transactions we
1937200825Sthompsa			 * disable surround setups on FULL-speed USB
1938200825Sthompsa			 * by default
1939200825Sthompsa			 */
1940241988Shselasky			channels = 4;
1941200825Sthompsa			break;
1942200825Sthompsa		default:
1943200825Sthompsa			channels = 16;
1944200825Sthompsa			break;
1945200825Sthompsa		}
1946200825Sthompsa	} else if (channels > 16) {
1947200825Sthompsa		channels = 16;
1948186730Salfred	}
1949184610Salfred	if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) {
1950184610Salfred		sc->sc_sndstat_valid = 1;
1951184610Salfred	}
1952184610Salfred	/* try to search for a valid config */
1953184610Salfred
1954184610Salfred	for (x = channels; x; x--) {
1955184610Salfred		for (y = bits; y; y -= 8) {
1956184610Salfred
1957200825Sthompsa			/* try user defined rate, if any */
1958200825Sthompsa			if (rate != 0)
1959200825Sthompsa				uaudio_chan_fill_info_sub(sc, udev, rate, x, y);
1960200825Sthompsa
1961200825Sthompsa			/* try find a matching rate, if any */
1962249796Shselasky			for (z = 0; uaudio_rate_list[z]; z++)
1963200825Sthompsa				uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y);
1964184610Salfred		}
1965184610Salfred	}
1966249796Shselasky	if (sc->sc_sndstat_valid)
1967184610Salfred		sbuf_finish(&sc->sc_sndstat);
1968184610Salfred}
1969184610Salfred
1970184610Salfredstatic void
1971242223Shselaskyuaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
1972242223Shselasky{
1973242223Shselasky	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
1974242223Shselasky	struct usb_page_cache *pc;
1975249796Shselasky	uint64_t sample_rate = ch->usb_alt[ch->cur_alt].sample_rate;
1976242223Shselasky	uint8_t buf[4];
1977242223Shselasky	uint64_t temp;
1978242223Shselasky	int len;
1979242223Shselasky	int actlen;
1980242223Shselasky	int nframes;
1981242223Shselasky
1982242223Shselasky	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1983242223Shselasky
1984242223Shselasky	switch (USB_GET_STATE(xfer)) {
1985242223Shselasky	case USB_ST_TRANSFERRED:
1986242223Shselasky
1987242223Shselasky		DPRINTFN(6, "transferred %d bytes\n", actlen);
1988242223Shselasky
1989242223Shselasky		if (nframes == 0)
1990242223Shselasky			break;
1991242223Shselasky		len = usbd_xfer_frame_len(xfer, 0);
1992242223Shselasky		if (len == 0)
1993242223Shselasky			break;
1994242223Shselasky		if (len > sizeof(buf))
1995242223Shselasky			len = sizeof(buf);
1996242223Shselasky
1997242223Shselasky		memset(buf, 0, sizeof(buf));
1998242223Shselasky
1999242223Shselasky		pc = usbd_xfer_get_frame(xfer, 0);
2000242223Shselasky		usbd_copy_out(pc, 0, buf, len);
2001242223Shselasky
2002242223Shselasky		temp = UGETDW(buf);
2003242223Shselasky
2004242223Shselasky		DPRINTF("Value = 0x%08x\n", (int)temp);
2005242223Shselasky
2006242223Shselasky		/* auto-detect SYNC format */
2007242223Shselasky
2008242223Shselasky		if (len == 4)
2009242223Shselasky			temp &= 0x0fffffff;
2010242223Shselasky
2011242223Shselasky		/* check for no data */
2012242223Shselasky
2013242223Shselasky		if (temp == 0)
2014242223Shselasky			break;
2015242223Shselasky
2016242223Shselasky		/* correctly scale value */
2017242223Shselasky
2018242223Shselasky		temp = (temp * 125ULL) - 64;
2019242223Shselasky
2020242223Shselasky		/* auto adjust */
2021242223Shselasky
2022249796Shselasky		while (temp < (sample_rate - (sample_rate / 4)))
2023242223Shselasky			temp *= 2;
2024242223Shselasky
2025249796Shselasky		while (temp > (sample_rate + (sample_rate / 2)))
2026242223Shselasky			temp /= 2;
2027242223Shselasky
2028242223Shselasky		/* compare */
2029242223Shselasky
2030242223Shselasky		DPRINTF("Comparing %d < %d\n",
2031249796Shselasky		    (int)temp, (int)sample_rate);
2032242223Shselasky
2033249796Shselasky		if (temp == sample_rate)
2034242223Shselasky			ch->last_sync_state = UAUDIO_SYNC_NONE;
2035249796Shselasky		else if (temp > sample_rate)
2036242223Shselasky			ch->last_sync_state = UAUDIO_SYNC_MORE;
2037242223Shselasky		else
2038242223Shselasky			ch->last_sync_state = UAUDIO_SYNC_LESS;
2039242223Shselasky		break;
2040242223Shselasky
2041242223Shselasky	case USB_ST_SETUP:
2042242223Shselasky		usbd_xfer_set_frames(xfer, 1);
2043242223Shselasky		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_framelen(xfer));
2044242223Shselasky		usbd_transfer_submit(xfer);
2045242223Shselasky		break;
2046242223Shselasky
2047242223Shselasky	default:			/* Error */
2048242223Shselasky		break;
2049242223Shselasky	}
2050242223Shselasky}
2051242223Shselasky
2052242223Shselaskystatic void
2053194677Sthompsauaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
2054184610Salfred{
2055194677Sthompsa	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
2056194677Sthompsa	struct usb_page_cache *pc;
2057249796Shselasky	uint32_t sample_size = ch->usb_alt[ch->cur_alt].sample_size;
2058242223Shselasky	uint32_t mfl;
2059186730Salfred	uint32_t total;
2060184610Salfred	uint32_t blockcount;
2061184610Salfred	uint32_t n;
2062184610Salfred	uint32_t offset;
2063200825Sthompsa	int actlen;
2064200825Sthompsa	int sumlen;
2065184610Salfred
2066194677Sthompsa	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
2067194677Sthompsa
2068199060Sthompsa	if (ch->end == ch->start) {
2069199060Sthompsa		DPRINTF("no buffer!\n");
2070199060Sthompsa		return;
2071199060Sthompsa	}
2072199060Sthompsa
2073184610Salfred	switch (USB_GET_STATE(xfer)) {
2074184610Salfred	case USB_ST_TRANSFERRED:
2075184610Salfredtr_transferred:
2076194677Sthompsa		if (actlen < sumlen) {
2077184610Salfred			DPRINTF("short transfer, "
2078200825Sthompsa			    "%d of %d bytes\n", actlen, sumlen);
2079184610Salfred		}
2080184610Salfred		chn_intr(ch->pcm_ch);
2081184610Salfred
2082242223Shselasky		/* start SYNC transfer, if any */
2083242223Shselasky		if ((ch->last_sync_time++ & 7) == 0)
2084242223Shselasky			usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]);
2085242223Shselasky
2086184610Salfred	case USB_ST_SETUP:
2087242223Shselasky		mfl = usbd_xfer_max_framelen(xfer);
2088242223Shselasky
2089242223Shselasky		if (ch->bytes_per_frame[1] > mfl) {
2090184610Salfred			DPRINTF("bytes per transfer, %d, "
2091184610Salfred			    "exceeds maximum, %d!\n",
2092200825Sthompsa			    ch->bytes_per_frame[1],
2093242223Shselasky			    mfl);
2094184610Salfred			break;
2095184610Salfred		}
2096200825Sthompsa
2097200825Sthompsa		blockcount = ch->intr_frames;
2098200825Sthompsa
2099200825Sthompsa		/* setup number of frames */
2100194677Sthompsa		usbd_xfer_set_frames(xfer, blockcount);
2101184610Salfred
2102200825Sthompsa		/* reset total length */
2103200825Sthompsa		total = 0;
2104200825Sthompsa
2105200825Sthompsa		/* setup frame lengths */
2106200825Sthompsa		for (n = 0; n != blockcount; n++) {
2107242223Shselasky			uint32_t frame_len;
2108242223Shselasky
2109200825Sthompsa			ch->sample_curr += ch->sample_rem;
2110200825Sthompsa			if (ch->sample_curr >= ch->frames_per_second) {
2111200825Sthompsa				ch->sample_curr -= ch->frames_per_second;
2112242223Shselasky				frame_len = ch->bytes_per_frame[1];
2113200825Sthompsa			} else {
2114242223Shselasky				frame_len = ch->bytes_per_frame[0];
2115200825Sthompsa			}
2116242223Shselasky
2117242223Shselasky			if (n == (blockcount - 1)) {
2118242223Shselasky				switch (ch->last_sync_state) {
2119242223Shselasky				case UAUDIO_SYNC_MORE:
2120242223Shselasky					DPRINTFN(6, "sending one sample more\n");
2121249796Shselasky					if ((frame_len + sample_size) <= mfl)
2122249796Shselasky						frame_len += sample_size;
2123242223Shselasky					ch->last_sync_state = UAUDIO_SYNC_NONE;
2124242223Shselasky					break;
2125242223Shselasky				case UAUDIO_SYNC_LESS:
2126242223Shselasky					DPRINTFN(6, "sending one sample less\n");
2127249796Shselasky					if (frame_len >= sample_size)
2128249796Shselasky						frame_len -= sample_size;
2129242223Shselasky					ch->last_sync_state = UAUDIO_SYNC_NONE;
2130242223Shselasky					break;
2131242223Shselasky				default:
2132242223Shselasky					break;
2133242223Shselasky				}
2134242223Shselasky			}
2135242223Shselasky
2136242223Shselasky			usbd_xfer_set_frame_len(xfer, n, frame_len);
2137242223Shselasky			total += frame_len;
2138200825Sthompsa		}
2139200825Sthompsa
2140184610Salfred		DPRINTFN(6, "transfer %d bytes\n", total);
2141184610Salfred
2142184610Salfred		offset = 0;
2143184610Salfred
2144194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
2145184610Salfred		while (total > 0) {
2146184610Salfred
2147184610Salfred			n = (ch->end - ch->cur);
2148184610Salfred			if (n > total) {
2149184610Salfred				n = total;
2150184610Salfred			}
2151194677Sthompsa			usbd_copy_in(pc, offset, ch->cur, n);
2152184610Salfred
2153184610Salfred			total -= n;
2154184610Salfred			ch->cur += n;
2155184610Salfred			offset += n;
2156184610Salfred
2157184610Salfred			if (ch->cur >= ch->end) {
2158184610Salfred				ch->cur = ch->start;
2159184610Salfred			}
2160184610Salfred		}
2161184610Salfred
2162194228Sthompsa		usbd_transfer_submit(xfer);
2163184610Salfred		break;
2164184610Salfred
2165184610Salfred	default:			/* Error */
2166194677Sthompsa		if (error == USB_ERR_CANCELLED) {
2167184610Salfred			break;
2168184610Salfred		}
2169184610Salfred		goto tr_transferred;
2170184610Salfred	}
2171184610Salfred}
2172184610Salfred
2173184610Salfredstatic void
2174242223Shselaskyuaudio_chan_record_sync_callback(struct usb_xfer *xfer, usb_error_t error)
2175242223Shselasky{
2176242223Shselasky	/* TODO */
2177242223Shselasky}
2178242223Shselasky
2179242223Shselaskystatic void
2180194677Sthompsauaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
2181184610Salfred{
2182194677Sthompsa	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
2183194677Sthompsa	struct usb_page_cache *pc;
2184184610Salfred	uint32_t offset0;
2185184610Salfred	uint32_t offset1;
2186199060Sthompsa	uint32_t mfl;
2187233774Shselasky	int m;
2188233774Shselasky	int n;
2189194677Sthompsa	int len;
2190199060Sthompsa	int actlen;
2191199060Sthompsa	int nframes;
2192233774Shselasky	int blockcount;
2193184610Salfred
2194194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
2195199060Sthompsa	mfl = usbd_xfer_max_framelen(xfer);
2196194677Sthompsa
2197199060Sthompsa	if (ch->end == ch->start) {
2198199060Sthompsa		DPRINTF("no buffer!\n");
2199199060Sthompsa		return;
2200199060Sthompsa	}
2201199060Sthompsa
2202184610Salfred	switch (USB_GET_STATE(xfer)) {
2203184610Salfred	case USB_ST_TRANSFERRED:
2204184610Salfred
2205200825Sthompsa		DPRINTFN(6, "transferred %d bytes\n", actlen);
2206200825Sthompsa
2207184610Salfred		offset0 = 0;
2208199060Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
2209184610Salfred
2210194677Sthompsa		for (n = 0; n != nframes; n++) {
2211184610Salfred
2212184610Salfred			offset1 = offset0;
2213194682Sthompsa			len = usbd_xfer_frame_len(xfer, n);
2214184610Salfred
2215194677Sthompsa			while (len > 0) {
2216184610Salfred
2217184610Salfred				m = (ch->end - ch->cur);
2218184610Salfred
2219233774Shselasky				if (m > len)
2220194677Sthompsa					m = len;
2221233774Shselasky
2222194677Sthompsa				usbd_copy_out(pc, offset1, ch->cur, m);
2223184610Salfred
2224194677Sthompsa				len -= m;
2225184610Salfred				offset1 += m;
2226184610Salfred				ch->cur += m;
2227184610Salfred
2228184610Salfred				if (ch->cur >= ch->end) {
2229184610Salfred					ch->cur = ch->start;
2230184610Salfred				}
2231184610Salfred			}
2232184610Salfred
2233199060Sthompsa			offset0 += mfl;
2234184610Salfred		}
2235184610Salfred
2236184610Salfred		chn_intr(ch->pcm_ch);
2237184610Salfred
2238184610Salfred	case USB_ST_SETUP:
2239199060Sthompsatr_setup:
2240200825Sthompsa		blockcount = ch->intr_frames;
2241200825Sthompsa
2242194677Sthompsa		usbd_xfer_set_frames(xfer, blockcount);
2243194677Sthompsa		for (n = 0; n < blockcount; n++) {
2244199060Sthompsa			usbd_xfer_set_frame_len(xfer, n, mfl);
2245184610Salfred		}
2246184610Salfred
2247194228Sthompsa		usbd_transfer_submit(xfer);
2248199060Sthompsa		break;
2249184610Salfred
2250184610Salfred	default:			/* Error */
2251194677Sthompsa		if (error == USB_ERR_CANCELLED) {
2252199060Sthompsa			break;
2253184610Salfred		}
2254199060Sthompsa		goto tr_setup;
2255184610Salfred	}
2256184610Salfred}
2257184610Salfred
2258184610Salfredvoid   *
2259184610Salfreduaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
2260184610Salfred    struct pcm_channel *c, int dir)
2261184610Salfred{
2262184610Salfred	struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
2263184610Salfred	    &sc->sc_play_chan : &sc->sc_rec_chan);
2264186730Salfred	uint32_t buf_size;
2265249796Shselasky	uint8_t x;
2266184610Salfred
2267249796Shselasky	/* store mutex and PCM channel */
2268200825Sthompsa
2269199576Sthompsa	ch->pcm_ch = c;
2270199576Sthompsa	ch->pcm_mtx = c->lock;
2271199576Sthompsa
2272249796Shselasky	/* compute worst case buffer */
2273184610Salfred
2274249796Shselasky	buf_size = 0;
2275249796Shselasky	for (x = 0; x != ch->num_alt; x++) {
2276249796Shselasky		uint32_t temp = uaudio_get_buffer_size(ch, x);
2277249796Shselasky		if (temp > buf_size)
2278249796Shselasky			buf_size = temp;
2279200825Sthompsa	}
2280200825Sthompsa
2281249796Shselasky	/* allow double buffering */
2282249796Shselasky	buf_size *= 2;
2283184610Salfred
2284249796Shselasky	DPRINTF("Worst case buffer is %d bytes\n", (int)buf_size);
2285200825Sthompsa
2286199060Sthompsa	ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
2287199060Sthompsa	if (ch->buf == NULL)
2288199060Sthompsa		goto error;
2289199060Sthompsa	if (sndbuf_setup(b, ch->buf, buf_size) != 0)
2290199060Sthompsa		goto error;
2291199060Sthompsa
2292199060Sthompsa	ch->start = ch->buf;
2293199060Sthompsa	ch->end = ch->buf + buf_size;
2294199060Sthompsa	ch->cur = ch->buf;
2295199060Sthompsa	ch->pcm_buf = b;
2296249796Shselasky	ch->max_buf = buf_size;
2297199060Sthompsa
2298199060Sthompsa	if (ch->pcm_mtx == NULL) {
2299199060Sthompsa		DPRINTF("ERROR: PCM channels does not have a mutex!\n");
2300199060Sthompsa		goto error;
2301199060Sthompsa	}
2302184610Salfred	return (ch);
2303184610Salfred
2304184610Salfrederror:
2305184610Salfred	uaudio_chan_free(ch);
2306184610Salfred	return (NULL);
2307184610Salfred}
2308184610Salfred
2309184610Salfredint
2310184610Salfreduaudio_chan_free(struct uaudio_chan *ch)
2311184610Salfred{
2312184610Salfred	if (ch->buf != NULL) {
2313184610Salfred		free(ch->buf, M_DEVBUF);
2314184610Salfred		ch->buf = NULL;
2315184610Salfred	}
2316242223Shselasky	usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS + 1);
2317184610Salfred
2318249796Shselasky	ch->num_alt = 0;
2319184610Salfred
2320184610Salfred	return (0);
2321184610Salfred}
2322184610Salfred
2323184610Salfredint
2324184610Salfreduaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
2325184610Salfred{
2326249796Shselasky	uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt);
2327249796Shselasky
2328249796Shselasky	sndbuf_setup(ch->pcm_buf, ch->buf, temp);
2329249796Shselasky
2330249796Shselasky	ch->start = ch->buf;
2331249796Shselasky	ch->end = ch->buf + temp;
2332249796Shselasky	ch->cur = ch->buf;
2333249796Shselasky
2334249796Shselasky	return (temp / 2);
2335184610Salfred}
2336184610Salfred
2337184610Salfredint
2338184610Salfreduaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
2339184610Salfred    uint32_t blockcount)
2340184610Salfred{
2341184610Salfred	return (1);
2342184610Salfred}
2343184610Salfred
2344184610Salfredint
2345184610Salfreduaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
2346184610Salfred{
2347249796Shselasky	uint8_t x;
2348249796Shselasky
2349249796Shselasky	for (x = 0; x < ch->num_alt; x++) {
2350249796Shselasky		if (ch->usb_alt[x].sample_rate < speed) {
2351249796Shselasky			/* sample rate is too low */
2352249796Shselasky			break;
2353249796Shselasky		}
2354184610Salfred	}
2355249796Shselasky
2356249796Shselasky	if (x != 0)
2357249796Shselasky		x--;
2358249796Shselasky
2359249796Shselasky	ch->set_alt = x;
2360249796Shselasky
2361249796Shselasky	DPRINTF("Selecting alt %d\n", (int)x);
2362249796Shselasky
2363249796Shselasky	return (ch->usb_alt[x].sample_rate);
2364184610Salfred}
2365184610Salfred
2366184610Salfredint
2367184610Salfreduaudio_chan_getptr(struct uaudio_chan *ch)
2368184610Salfred{
2369184610Salfred	return (ch->cur - ch->start);
2370184610Salfred}
2371184610Salfred
2372184610Salfredstruct pcmchan_caps *
2373184610Salfreduaudio_chan_getcaps(struct uaudio_chan *ch)
2374184610Salfred{
2375184610Salfred	return (&ch->pcm_cap);
2376184610Salfred}
2377184610Salfred
2378193640Sariffstatic struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = {
2379193640Sariff	.id = SND_CHN_MATRIX_DRV,
2380193640Sariff	.channels = 2,
2381193640Sariff	.ext = 0,
2382193640Sariff	.map = {
2383193640Sariff		/* Right */
2384193640Sariff		[0] = {
2385193640Sariff			.type = SND_CHN_T_FR,
2386193640Sariff			.members =
2387193640Sariff			    SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC |
2388193640Sariff			    SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR |
2389193640Sariff			    SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR
2390193640Sariff		},
2391193640Sariff		/* Left */
2392193640Sariff		[1] = {
2393193640Sariff			.type = SND_CHN_T_FL,
2394193640Sariff			.members =
2395193640Sariff			    SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC |
2396193640Sariff			    SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL |
2397193640Sariff			    SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL
2398193640Sariff		},
2399193640Sariff		[2] = {
2400193640Sariff			.type = SND_CHN_T_MAX,
2401193640Sariff			.members = 0
2402193640Sariff		}
2403193640Sariff	},
2404193640Sariff	.mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL,
2405193640Sariff	.offset = {  1,  0, -1, -1, -1, -1, -1, -1, -1,
2406193640Sariff		    -1, -1, -1, -1, -1, -1, -1, -1, -1  }
2407193640Sariff};
2408193640Sariff
2409193640Sariffstruct pcmchan_matrix *
2410193640Sariffuaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format)
2411193640Sariff{
2412193640Sariff	struct uaudio_softc *sc;
2413193640Sariff
2414193640Sariff	sc = ch->priv_sc;
2415193640Sariff
2416193640Sariff	if (sc != NULL && sc->sc_uq_audio_swap_lr != 0 &&
2417193640Sariff	    AFMT_CHANNEL(format) == 2)
2418193640Sariff		return (&uaudio_chan_matrix_swap_2_0);
2419193640Sariff
2420193640Sariff	return (feeder_matrix_format_map(format));
2421193640Sariff}
2422193640Sariff
2423184610Salfredint
2424184610Salfreduaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
2425184610Salfred{
2426249796Shselasky	DPRINTF("Selecting format 0x%08x\n", (unsigned int)format);
2427184610Salfred	return (0);
2428184610Salfred}
2429184610Salfred
2430184610Salfredint
2431184610Salfreduaudio_chan_start(struct uaudio_chan *ch)
2432184610Salfred{
2433249796Shselasky	struct uaudio_softc *sc = ch->priv_sc;
2434249796Shselasky	int do_start = 0;
2435184610Salfred
2436249796Shselasky	usb_proc_explore_lock(sc->sc_udev);
2437249796Shselasky	if (ch->operation != CHAN_OP_DRAIN) {
2438249796Shselasky		if (ch->cur_alt == ch->set_alt &&
2439249796Shselasky		    ch->operation == CHAN_OP_NONE) {
2440249796Shselasky			/* save doing the explore task */
2441249796Shselasky			do_start = 1;
2442249796Shselasky		} else {
2443249796Shselasky			ch->operation = CHAN_OP_START;
2444249796Shselasky			(void)usb_proc_explore_msignal(sc->sc_udev,
2445249796Shselasky			    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
2446249796Shselasky		}
2447249796Shselasky	}
2448249796Shselasky	usb_proc_explore_unlock(sc->sc_udev);
2449249796Shselasky
2450249796Shselasky	if (do_start) {
2451249796Shselasky		usbd_transfer_start(ch->xfer[0]);
2452249796Shselasky		usbd_transfer_start(ch->xfer[1]);
2453249796Shselasky	}
2454184610Salfred	return (0);
2455184610Salfred}
2456184610Salfred
2457184610Salfredint
2458184610Salfreduaudio_chan_stop(struct uaudio_chan *ch)
2459184610Salfred{
2460249796Shselasky	struct uaudio_softc *sc = ch->priv_sc;
2461249796Shselasky	int do_stop = 0;
2462249796Shselasky
2463249796Shselasky	usb_proc_explore_lock(sc->sc_udev);
2464249796Shselasky	if (ch->operation != CHAN_OP_DRAIN) {
2465249796Shselasky		if (ch->cur_alt == ch->set_alt &&
2466249796Shselasky		    ch->operation == CHAN_OP_NONE) {
2467249796Shselasky			/* save doing the explore task */
2468249796Shselasky			do_stop = 1;
2469249796Shselasky		} else {
2470249796Shselasky			ch->operation = CHAN_OP_STOP;
2471249796Shselasky			(void)usb_proc_explore_msignal(sc->sc_udev,
2472249796Shselasky			    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
2473249796Shselasky		}
2474249796Shselasky	}
2475249796Shselasky	usb_proc_explore_unlock(sc->sc_udev);
2476249796Shselasky
2477249796Shselasky	if (do_stop) {
2478249796Shselasky		usbd_transfer_stop(ch->xfer[0]);
2479249796Shselasky		usbd_transfer_stop(ch->xfer[1]);
2480249796Shselasky	}
2481184610Salfred	return (0);
2482184610Salfred}
2483184610Salfred
2484184610Salfred/*========================================================================*
2485184610Salfred * AC - Audio Controller - routines
2486184610Salfred *========================================================================*/
2487184610Salfred
2488242438Shselaskystatic int
2489242438Shselaskyuaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS)
2490242438Shselasky{
2491242438Shselasky	struct uaudio_softc *sc;
2492242438Shselasky	struct uaudio_mixer_node *pmc;
2493242438Shselasky	int hint;
2494242438Shselasky	int error;
2495242438Shselasky	int temp = 0;
2496242438Shselasky	int chan = 0;
2497242438Shselasky
2498242438Shselasky	sc = (struct uaudio_softc *)oidp->oid_arg1;
2499242438Shselasky	hint = oidp->oid_arg2;
2500242438Shselasky
2501242438Shselasky	if (sc->sc_mixer_lock == NULL)
2502242438Shselasky		return (ENXIO);
2503242438Shselasky
2504242438Shselasky	/* lookup mixer node */
2505242438Shselasky
2506242438Shselasky	mtx_lock(sc->sc_mixer_lock);
2507242438Shselasky	for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) {
2508242438Shselasky		for (chan = 0; chan != (int)pmc->nchan; chan++) {
2509242438Shselasky			if (pmc->wValue[chan] != -1 &&
2510242438Shselasky			    pmc->wValue[chan] == hint) {
2511242438Shselasky				temp = pmc->wData[chan];
2512242438Shselasky				goto found;
2513242438Shselasky			}
2514242438Shselasky		}
2515242438Shselasky	}
2516242438Shselaskyfound:
2517242438Shselasky	mtx_unlock(sc->sc_mixer_lock);
2518242438Shselasky
2519242438Shselasky	error = sysctl_handle_int(oidp, &temp, 0, req);
2520242438Shselasky	if (error != 0 || req->newptr == NULL)
2521242438Shselasky		return (error);
2522242438Shselasky
2523242438Shselasky	/* update mixer value */
2524242438Shselasky
2525242438Shselasky	mtx_lock(sc->sc_mixer_lock);
2526242438Shselasky	if (pmc != NULL &&
2527242438Shselasky	    temp >= pmc->minval &&
2528242438Shselasky	    temp <= pmc->maxval) {
2529242438Shselasky
2530242438Shselasky		pmc->wData[chan] = temp;
2531242438Shselasky		pmc->update[(chan / 8)] |= (1 << (chan % 8));
2532242438Shselasky
2533242438Shselasky		/* start the transfer, if not already started */
2534242438Shselasky		usbd_transfer_start(sc->sc_mixer_xfer[0]);
2535242438Shselasky	}
2536242438Shselasky	mtx_unlock(sc->sc_mixer_lock);
2537242438Shselasky
2538242438Shselasky	return (0);
2539242438Shselasky}
2540242438Shselasky
2541184610Salfredstatic void
2542242438Shselaskyuaudio_mixer_ctl_free(struct uaudio_softc *sc)
2543242438Shselasky{
2544242438Shselasky	struct uaudio_mixer_node *p_mc;
2545242438Shselasky
2546242438Shselasky	while ((p_mc = sc->sc_mixer_root) != NULL) {
2547242438Shselasky		sc->sc_mixer_root = p_mc->next;
2548242438Shselasky		free(p_mc, M_USBDEV);
2549242438Shselasky	}
2550242438Shselasky}
2551242438Shselasky
2552242438Shselaskystatic void
2553242438Shselaskyuaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev)
2554242438Shselasky{
2555242438Shselasky	struct uaudio_mixer_node *pmc;
2556242438Shselasky	struct sysctl_oid *mixer_tree;
2557242438Shselasky	struct sysctl_oid *control_tree;
2558242438Shselasky	char buf[32];
2559242438Shselasky	int chan;
2560242438Shselasky	int n;
2561242438Shselasky
2562242453Shselasky	mixer_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
2563242438Shselasky	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "mixer",
2564242438Shselasky	    CTLFLAG_RD, NULL, "");
2565242438Shselasky
2566242438Shselasky	if (mixer_tree == NULL)
2567242438Shselasky		return;
2568242438Shselasky
2569242438Shselasky	for (n = 0, pmc = sc->sc_mixer_root; pmc != NULL;
2570242438Shselasky	    pmc = pmc->next, n++) {
2571242438Shselasky
2572242438Shselasky		for (chan = 0; chan < pmc->nchan; chan++) {
2573242438Shselasky
2574242438Shselasky			if (pmc->nchan > 1) {
2575242438Shselasky				snprintf(buf, sizeof(buf), "%s_%d_%d",
2576242438Shselasky				    pmc->name, n, chan);
2577242438Shselasky			} else {
2578242438Shselasky				snprintf(buf, sizeof(buf), "%s_%d",
2579242438Shselasky				    pmc->name, n);
2580242438Shselasky			}
2581242438Shselasky
2582242453Shselasky			control_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev),
2583242438Shselasky			    SYSCTL_CHILDREN(mixer_tree), OID_AUTO, buf,
2584242453Shselasky			    CTLFLAG_RD, NULL, "Mixer control nodes");
2585242438Shselasky
2586242438Shselasky			if (control_tree == NULL)
2587242438Shselasky				continue;
2588242438Shselasky
2589242453Shselasky			SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
2590242438Shselasky			    SYSCTL_CHILDREN(control_tree),
2591242438Shselasky			    OID_AUTO, "val", CTLTYPE_INT | CTLFLAG_RW, sc,
2592242438Shselasky			    pmc->wValue[chan],
2593242438Shselasky			    uaudio_mixer_sysctl_handler, "I", "Current value");
2594242438Shselasky
2595242453Shselasky			SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
2596242438Shselasky			    SYSCTL_CHILDREN(control_tree),
2597242438Shselasky			    OID_AUTO, "min", CTLFLAG_RD, 0, pmc->minval,
2598242438Shselasky			    "Minimum value");
2599242438Shselasky
2600242453Shselasky			SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
2601242438Shselasky			    SYSCTL_CHILDREN(control_tree),
2602242438Shselasky			    OID_AUTO, "max", CTLFLAG_RD, 0, pmc->maxval,
2603242438Shselasky			    "Maximum value");
2604242438Shselasky
2605242453Shselasky			SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev),
2606242438Shselasky			    SYSCTL_CHILDREN(control_tree),
2607242438Shselasky			    OID_AUTO, "desc", CTLFLAG_RD, pmc->desc, 0,
2608242438Shselasky			    "Description");
2609242438Shselasky		}
2610242438Shselasky	}
2611242438Shselasky}
2612242438Shselasky
2613242453Shselasky/* M-Audio FastTrack Ultra Mixer Description */
2614242453Shselasky/* Origin: Linux USB Audio driver */
2615242438Shselaskystatic void
2616242453Shselaskyuaudio_mixer_controls_create_ftu(struct uaudio_softc *sc)
2617242453Shselasky{
2618242453Shselasky	int chx;
2619242453Shselasky	int chy;
2620242453Shselasky
2621244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2622244567Shselasky	MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
2623244567Shselasky	MIX(sc).wValue[0] = MAKE_WORD(8, 0);
2624244567Shselasky	MIX(sc).class = UAC_OUTPUT;
2625244567Shselasky	MIX(sc).type = MIX_UNSIGNED_16;
2626244567Shselasky	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2627244567Shselasky	MIX(sc).name = "effect";
2628244567Shselasky	MIX(sc).minval = 0;
2629244567Shselasky	MIX(sc).maxval = 7;
2630244567Shselasky	MIX(sc).mul = 7;
2631244567Shselasky	MIX(sc).nchan = 1;
2632244567Shselasky	MIX(sc).update[0] = 1;
2633244567Shselasky	strlcpy(MIX(sc).desc, "Room1,2,3,Hall1,2,Plate,Delay,Echo", sizeof(MIX(sc).desc));
2634244567Shselasky	uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
2635242453Shselasky
2636244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2637244567Shselasky	MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no);
2638242453Shselasky
2639242453Shselasky	for (chx = 0; chx != 8; chx++) {
2640242453Shselasky		for (chy = 0; chy != 8; chy++) {
2641242453Shselasky
2642244567Shselasky			MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1);
2643244567Shselasky			MIX(sc).type = MIX_SIGNED_16;
2644244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2645244567Shselasky			MIX(sc).name = "mix_rec";
2646244567Shselasky			MIX(sc).nchan = 1;
2647244567Shselasky			MIX(sc).update[0] = 1;
2648244567Shselasky			MIX(sc).val_default = 0;
2649244567Shselasky			snprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2650242453Shselasky			    "AIn%d - Out%d Record Volume", chy + 1, chx + 1);
2651242453Shselasky
2652244567Shselasky			uaudio_mixer_add_ctl(sc, &MIX(sc));
2653242453Shselasky
2654244567Shselasky			MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1 + 8);
2655244567Shselasky			MIX(sc).type = MIX_SIGNED_16;
2656244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2657244567Shselasky			MIX(sc).name = "mix_play";
2658244567Shselasky			MIX(sc).nchan = 1;
2659244567Shselasky			MIX(sc).update[0] = 1;
2660244567Shselasky			MIX(sc).val_default = (chx == chy) ? 2 : 0;
2661244567Shselasky			snprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2662242453Shselasky			    "DIn%d - Out%d Playback Volume", chy + 1, chx + 1);
2663242453Shselasky
2664244567Shselasky			uaudio_mixer_add_ctl(sc, &MIX(sc));
2665242453Shselasky		}
2666242453Shselasky	}
2667242453Shselasky
2668244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2669244567Shselasky	MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
2670244567Shselasky	MIX(sc).wValue[0] = MAKE_WORD(2, 0);
2671244567Shselasky	MIX(sc).class = UAC_OUTPUT;
2672244567Shselasky	MIX(sc).type = MIX_SIGNED_8;
2673244567Shselasky	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2674244567Shselasky	MIX(sc).name = "effect_vol";
2675244567Shselasky	MIX(sc).nchan = 1;
2676244567Shselasky	MIX(sc).update[0] = 1;
2677244567Shselasky	MIX(sc).minval = 0;
2678244567Shselasky	MIX(sc).maxval = 0x7f;
2679244567Shselasky	MIX(sc).mul = 0x7f;
2680244567Shselasky	MIX(sc).nchan = 1;
2681244567Shselasky	MIX(sc).update[0] = 1;
2682244567Shselasky	strlcpy(MIX(sc).desc, "Effect Volume", sizeof(MIX(sc).desc));
2683244567Shselasky	uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
2684242453Shselasky
2685244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2686244567Shselasky	MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
2687244567Shselasky	MIX(sc).wValue[0] = MAKE_WORD(3, 0);
2688244567Shselasky	MIX(sc).class = UAC_OUTPUT;
2689244567Shselasky	MIX(sc).type = MIX_SIGNED_16;
2690244567Shselasky	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2691244567Shselasky	MIX(sc).name = "effect_dur";
2692244567Shselasky	MIX(sc).nchan = 1;
2693244567Shselasky	MIX(sc).update[0] = 1;
2694244567Shselasky	MIX(sc).minval = 0;
2695244567Shselasky	MIX(sc).maxval = 0x7f00;
2696244567Shselasky	MIX(sc).mul = 0x7f00;
2697244567Shselasky	MIX(sc).nchan = 1;
2698244567Shselasky	MIX(sc).update[0] = 1;
2699244567Shselasky	strlcpy(MIX(sc).desc, "Effect Duration", sizeof(MIX(sc).desc));
2700244567Shselasky	uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
2701242453Shselasky
2702244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2703244567Shselasky	MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no);
2704244567Shselasky	MIX(sc).wValue[0] = MAKE_WORD(4, 0);
2705244567Shselasky	MIX(sc).class = UAC_OUTPUT;
2706244567Shselasky	MIX(sc).type = MIX_SIGNED_8;
2707244567Shselasky	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2708244567Shselasky	MIX(sc).name = "effect_fb";
2709244567Shselasky	MIX(sc).nchan = 1;
2710244567Shselasky	MIX(sc).update[0] = 1;
2711244567Shselasky	MIX(sc).minval = 0;
2712244567Shselasky	MIX(sc).maxval = 0x7f;
2713244567Shselasky	MIX(sc).mul = 0x7f;
2714244567Shselasky	MIX(sc).nchan = 1;
2715244567Shselasky	MIX(sc).update[0] = 1;
2716244567Shselasky	strlcpy(MIX(sc).desc, "Effect Feedback Volume", sizeof(MIX(sc).desc));
2717244567Shselasky	uaudio_mixer_add_ctl_sub(sc, &MIX(sc));
2718242453Shselasky
2719244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2720244567Shselasky	MIX(sc).wIndex = MAKE_WORD(7, sc->sc_mixer_iface_no);
2721242453Shselasky	for (chy = 0; chy != 4; chy++) {
2722242453Shselasky
2723244567Shselasky		MIX(sc).wValue[0] = MAKE_WORD(7, chy + 1);
2724244567Shselasky		MIX(sc).type = MIX_SIGNED_16;
2725244567Shselasky		MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2726244567Shselasky		MIX(sc).name = "effect_ret";
2727244567Shselasky		MIX(sc).nchan = 1;
2728244567Shselasky		MIX(sc).update[0] = 1;
2729244567Shselasky		snprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2730242453Shselasky		    "Effect Return %d Volume", chy + 1);
2731242453Shselasky
2732244567Shselasky		uaudio_mixer_add_ctl(sc, &MIX(sc));
2733242453Shselasky	}
2734242453Shselasky
2735244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2736244567Shselasky	MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no);
2737242453Shselasky
2738242453Shselasky	for (chy = 0; chy != 8; chy++) {
2739244567Shselasky		MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1);
2740244567Shselasky		MIX(sc).type = MIX_SIGNED_16;
2741244567Shselasky		MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2742244567Shselasky		MIX(sc).name = "effect_send";
2743244567Shselasky		MIX(sc).nchan = 1;
2744244567Shselasky		MIX(sc).update[0] = 1;
2745244567Shselasky		snprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2746242453Shselasky		    "Effect Send AIn%d Volume", chy + 1);
2747242453Shselasky
2748244567Shselasky		uaudio_mixer_add_ctl(sc, &MIX(sc));
2749242453Shselasky
2750271375Shselasky		MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1 + 8);
2751244567Shselasky		MIX(sc).type = MIX_SIGNED_16;
2752244567Shselasky		MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
2753244567Shselasky		MIX(sc).name = "effect_send";
2754244567Shselasky		MIX(sc).nchan = 1;
2755244567Shselasky		MIX(sc).update[0] = 1;
2756244567Shselasky		snprintf(MIX(sc).desc, sizeof(MIX(sc).desc),
2757271375Shselasky		    "Effect Send DIn%d Volume", chy + 1);
2758242453Shselasky
2759244567Shselasky		uaudio_mixer_add_ctl(sc, &MIX(sc));
2760242453Shselasky	}
2761242453Shselasky}
2762242453Shselasky
2763242453Shselaskystatic void
2764242438Shselaskyuaudio_mixer_reload_all(struct uaudio_softc *sc)
2765242438Shselasky{
2766242438Shselasky	struct uaudio_mixer_node *pmc;
2767242438Shselasky	int chan;
2768242438Shselasky
2769242438Shselasky	if (sc->sc_mixer_lock == NULL)
2770242438Shselasky		return;
2771242438Shselasky
2772242438Shselasky	mtx_lock(sc->sc_mixer_lock);
2773242438Shselasky	for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) {
2774242453Shselasky		/* use reset defaults for non-oss controlled settings */
2775242453Shselasky		if (pmc->ctl == SOUND_MIXER_NRDEVICES)
2776242453Shselasky			continue;
2777242438Shselasky		for (chan = 0; chan < pmc->nchan; chan++)
2778242438Shselasky			pmc->update[chan / 8] |= (1 << (chan % 8));
2779242438Shselasky	}
2780242438Shselasky	usbd_transfer_start(sc->sc_mixer_xfer[0]);
2781246421Shselasky
2782246421Shselasky	/* start HID volume keys, if any */
2783246421Shselasky	usbd_transfer_start(sc->sc_hid.xfer[0]);
2784242438Shselasky	mtx_unlock(sc->sc_mixer_lock);
2785242438Shselasky}
2786242438Shselasky
2787242438Shselaskystatic void
2788184610Salfreduaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
2789184610Salfred{
2790184610Salfred	struct uaudio_mixer_node *p_mc_new =
2791218791Shselasky	    malloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK);
2792242453Shselasky	int ch;
2793184610Salfred
2794218791Shselasky	if (p_mc_new != NULL) {
2795218791Shselasky		memcpy(p_mc_new, mc, sizeof(*p_mc_new));
2796184610Salfred		p_mc_new->next = sc->sc_mixer_root;
2797184610Salfred		sc->sc_mixer_root = p_mc_new;
2798184610Salfred		sc->sc_mixer_count++;
2799242453Shselasky
2800242453Shselasky		/* set default value for all channels */
2801242453Shselasky		for (ch = 0; ch < p_mc_new->nchan; ch++) {
2802242453Shselasky			switch (p_mc_new->val_default) {
2803242453Shselasky			case 1:
2804242455Shselasky				/* 50% */
2805242453Shselasky				p_mc_new->wData[ch] = (p_mc_new->maxval + p_mc_new->minval) / 2;
2806242453Shselasky				break;
2807242453Shselasky			case 2:
2808242455Shselasky				/* 100% */
2809242453Shselasky				p_mc_new->wData[ch] = p_mc_new->maxval;
2810242453Shselasky				break;
2811242453Shselasky			default:
2812242455Shselasky				/* 0% */
2813242453Shselasky				p_mc_new->wData[ch] = p_mc_new->minval;
2814242453Shselasky				break;
2815242453Shselasky			}
2816242453Shselasky		}
2817184610Salfred	} else {
2818184610Salfred		DPRINTF("out of memory\n");
2819184610Salfred	}
2820184610Salfred}
2821184610Salfred
2822184610Salfredstatic void
2823184610Salfreduaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
2824184610Salfred{
2825184610Salfred	int32_t res;
2826184610Salfred
2827184610Salfred	if (mc->class < UAC_NCLASSES) {
2828184610Salfred		DPRINTF("adding %s.%d\n",
2829184610Salfred		    uac_names[mc->class], mc->ctl);
2830184610Salfred	} else {
2831184610Salfred		DPRINTF("adding %d\n", mc->ctl);
2832184610Salfred	}
2833184610Salfred
2834184610Salfred	if (mc->type == MIX_ON_OFF) {
2835184610Salfred		mc->minval = 0;
2836184610Salfred		mc->maxval = 1;
2837184610Salfred	} else if (mc->type == MIX_SELECTOR) {
2838184610Salfred	} else {
2839184610Salfred
2840184610Salfred		/* determine min and max values */
2841184610Salfred
2842240609Shselasky		mc->minval = uaudio_mixer_get(sc->sc_udev,
2843240609Shselasky		    sc->sc_audio_rev, GET_MIN, mc);
2844240609Shselasky		mc->maxval = uaudio_mixer_get(sc->sc_udev,
2845240609Shselasky		    sc->sc_audio_rev, GET_MAX, mc);
2846184610Salfred
2847199060Sthompsa		/* check if max and min was swapped */
2848199060Sthompsa
2849199060Sthompsa		if (mc->maxval < mc->minval) {
2850199060Sthompsa			res = mc->maxval;
2851199060Sthompsa			mc->maxval = mc->minval;
2852199060Sthompsa			mc->minval = res;
2853199060Sthompsa		}
2854199060Sthompsa
2855199060Sthompsa		/* compute value range */
2856184610Salfred		mc->mul = mc->maxval - mc->minval;
2857199060Sthompsa		if (mc->mul == 0)
2858184610Salfred			mc->mul = 1;
2859199060Sthompsa
2860199060Sthompsa		/* compute value alignment */
2861240609Shselasky		res = uaudio_mixer_get(sc->sc_udev,
2862240609Shselasky		    sc->sc_audio_rev, GET_RES, mc);
2863199576Sthompsa
2864199576Sthompsa		DPRINTF("Resolution = %d\n", (int)res);
2865184610Salfred	}
2866184610Salfred
2867184610Salfred	uaudio_mixer_add_ctl_sub(sc, mc);
2868184610Salfred
2869207077Sthompsa#ifdef USB_DEBUG
2870184610Salfred	if (uaudio_debug > 2) {
2871184610Salfred		uint8_t i;
2872184610Salfred
2873184610Salfred		for (i = 0; i < mc->nchan; i++) {
2874184610Salfred			DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]);
2875184610Salfred		}
2876184610Salfred		DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' "
2877184610Salfred		    "min=%d max=%d\n",
2878184610Salfred		    mc->wIndex, mc->type, mc->ctl,
2879184610Salfred		    mc->minval, mc->maxval);
2880184610Salfred	}
2881184610Salfred#endif
2882184610Salfred}
2883184610Salfred
2884184610Salfredstatic void
2885240609Shselaskyuaudio_mixer_add_mixer(struct uaudio_softc *sc,
2886184610Salfred    const struct uaudio_terminal_node *iot, int id)
2887184610Salfred{
2888240609Shselasky	const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu_v1;
2889240609Shselasky	const struct usb_audio_mixer_unit_1 *d1;
2890184610Salfred
2891240609Shselasky	uint32_t bno;			/* bit number */
2892240609Shselasky	uint32_t p;			/* bit number accumulator */
2893240609Shselasky	uint32_t mo;			/* matching outputs */
2894240609Shselasky	uint32_t mc;			/* matching channels */
2895240609Shselasky	uint32_t ichs;			/* input channels */
2896240609Shselasky	uint32_t ochs;			/* output channels */
2897240609Shselasky	uint32_t c;
2898240609Shselasky	uint32_t chs;			/* channels */
2899240609Shselasky	uint32_t i;
2900240609Shselasky	uint32_t o;
2901184610Salfred
2902240609Shselasky	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
2903240609Shselasky	    d0->bUnitId, d0->bNrInPins);
2904240609Shselasky
2905240609Shselasky	/* compute the number of input channels */
2906240609Shselasky
2907240609Shselasky	ichs = 0;
2908240609Shselasky	for (i = 0; i < d0->bNrInPins; i++) {
2909240609Shselasky		ichs += uaudio_mixer_get_cluster(
2910240609Shselasky		    d0->baSourceId[i], iot).bNrChannels;
2911240609Shselasky	}
2912240609Shselasky
2913240609Shselasky	d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
2914240609Shselasky
2915240609Shselasky	/* and the number of output channels */
2916240609Shselasky
2917240609Shselasky	ochs = d1->bNrChannels;
2918240609Shselasky
2919240609Shselasky	DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
2920240609Shselasky
2921244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
2922240609Shselasky
2923244567Shselasky	MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
2924244567Shselasky	uaudio_mixer_determine_class(&iot[id], &MIX(sc));
2925244567Shselasky	MIX(sc).type = MIX_SIGNED_16;
2926240609Shselasky
2927240609Shselasky	if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL)
2928240609Shselasky		return;
2929240609Shselasky
2930240609Shselasky	for (p = i = 0; i < d0->bNrInPins; i++) {
2931240609Shselasky		chs = uaudio_mixer_get_cluster(
2932240609Shselasky		    d0->baSourceId[i], iot).bNrChannels;
2933240609Shselasky		mc = 0;
2934240609Shselasky		for (c = 0; c < chs; c++) {
2935240609Shselasky			mo = 0;
2936240609Shselasky			for (o = 0; o < ochs; o++) {
2937240609Shselasky				bno = ((p + c) * ochs) + o;
2938240609Shselasky				if (BIT_TEST(d1->bmControls, bno))
2939240609Shselasky					mo++;
2940240609Shselasky			}
2941240609Shselasky			if (mo == 1)
2942240609Shselasky				mc++;
2943240609Shselasky		}
2944240609Shselasky		if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
2945240609Shselasky
2946240609Shselasky			/* repeat bit-scan */
2947240609Shselasky
2948240609Shselasky			mc = 0;
2949240609Shselasky			for (c = 0; c < chs; c++) {
2950240609Shselasky				for (o = 0; o < ochs; o++) {
2951240609Shselasky					bno = ((p + c) * ochs) + o;
2952240609Shselasky					if (BIT_TEST(d1->bmControls, bno))
2953244567Shselasky						MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
2954240609Shselasky				}
2955240609Shselasky			}
2956244567Shselasky			MIX(sc).nchan = chs;
2957244567Shselasky			uaudio_mixer_add_ctl(sc, &MIX(sc));
2958240609Shselasky		}
2959240609Shselasky		p += chs;
2960240609Shselasky	}
2961184610Salfred}
2962184610Salfred
2963184610Salfredstatic void
2964240609Shselaskyuaudio20_mixer_add_mixer(struct uaudio_softc *sc,
2965184610Salfred    const struct uaudio_terminal_node *iot, int id)
2966184610Salfred{
2967240609Shselasky	const struct usb_audio20_mixer_unit_0 *d0 = iot[id].u.mu_v2;
2968240609Shselasky	const struct usb_audio20_mixer_unit_1 *d1;
2969184610Salfred
2970184610Salfred	uint32_t bno;			/* bit number */
2971184610Salfred	uint32_t p;			/* bit number accumulator */
2972184610Salfred	uint32_t mo;			/* matching outputs */
2973184610Salfred	uint32_t mc;			/* matching channels */
2974184610Salfred	uint32_t ichs;			/* input channels */
2975184610Salfred	uint32_t ochs;			/* output channels */
2976184610Salfred	uint32_t c;
2977184610Salfred	uint32_t chs;			/* channels */
2978184610Salfred	uint32_t i;
2979184610Salfred	uint32_t o;
2980184610Salfred
2981184610Salfred	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
2982184610Salfred	    d0->bUnitId, d0->bNrInPins);
2983184610Salfred
2984184610Salfred	/* compute the number of input channels */
2985184610Salfred
2986184610Salfred	ichs = 0;
2987184610Salfred	for (i = 0; i < d0->bNrInPins; i++) {
2988240609Shselasky		ichs += uaudio20_mixer_get_cluster(
2989240609Shselasky		    d0->baSourceId[i], iot).bNrChannels;
2990184610Salfred	}
2991184610Salfred
2992184610Salfred	d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
2993184610Salfred
2994184610Salfred	/* and the number of output channels */
2995184610Salfred
2996184610Salfred	ochs = d1->bNrChannels;
2997184610Salfred
2998184610Salfred	DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
2999184610Salfred
3000244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3001184610Salfred
3002244567Shselasky	MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3003244567Shselasky	uaudio20_mixer_determine_class(&iot[id], &MIX(sc));
3004244567Shselasky	MIX(sc).type = MIX_SIGNED_16;
3005184610Salfred
3006240609Shselasky	if (uaudio20_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL)
3007184610Salfred		return;
3008240609Shselasky
3009184610Salfred	for (p = i = 0; i < d0->bNrInPins; i++) {
3010240609Shselasky		chs = uaudio20_mixer_get_cluster(
3011240609Shselasky		    d0->baSourceId[i], iot).bNrChannels;
3012184610Salfred		mc = 0;
3013184610Salfred		for (c = 0; c < chs; c++) {
3014184610Salfred			mo = 0;
3015184610Salfred			for (o = 0; o < ochs; o++) {
3016184610Salfred				bno = ((p + c) * ochs) + o;
3017240609Shselasky				if (BIT_TEST(d1->bmControls, bno))
3018184610Salfred					mo++;
3019184610Salfred			}
3020240609Shselasky			if (mo == 1)
3021184610Salfred				mc++;
3022184610Salfred		}
3023184610Salfred		if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
3024184610Salfred
3025184610Salfred			/* repeat bit-scan */
3026184610Salfred
3027184610Salfred			mc = 0;
3028184610Salfred			for (c = 0; c < chs; c++) {
3029184610Salfred				for (o = 0; o < ochs; o++) {
3030184610Salfred					bno = ((p + c) * ochs) + o;
3031240609Shselasky					if (BIT_TEST(d1->bmControls, bno))
3032244567Shselasky						MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
3033184610Salfred				}
3034184610Salfred			}
3035244567Shselasky			MIX(sc).nchan = chs;
3036244567Shselasky			uaudio_mixer_add_ctl(sc, &MIX(sc));
3037184610Salfred		}
3038184610Salfred		p += chs;
3039184610Salfred	}
3040184610Salfred}
3041184610Salfred
3042184610Salfredstatic void
3043184610Salfreduaudio_mixer_add_selector(struct uaudio_softc *sc,
3044184610Salfred    const struct uaudio_terminal_node *iot, int id)
3045184610Salfred{
3046240609Shselasky	const struct usb_audio_selector_unit *d = iot[id].u.su_v1;
3047184610Salfred	uint16_t i;
3048184610Salfred
3049184610Salfred	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
3050184610Salfred	    d->bUnitId, d->bNrInPins);
3051184610Salfred
3052242438Shselasky	if (d->bNrInPins == 0)
3053184610Salfred		return;
3054242438Shselasky
3055244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3056184610Salfred
3057244567Shselasky	MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
3058244567Shselasky	MIX(sc).wValue[0] = MAKE_WORD(0, 0);
3059244567Shselasky	uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3060244567Shselasky	MIX(sc).nchan = 1;
3061244567Shselasky	MIX(sc).type = MIX_SELECTOR;
3062244567Shselasky	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3063244567Shselasky	MIX(sc).minval = 1;
3064244567Shselasky	MIX(sc).maxval = d->bNrInPins;
3065244567Shselasky	MIX(sc).name = "selector";
3066184610Salfred
3067242438Shselasky	i = d->baSourceId[d->bNrInPins];
3068242438Shselasky	if (i == 0 ||
3069242438Shselasky	    usbd_req_get_string_any(sc->sc_udev, NULL,
3070244567Shselasky	    MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
3071244567Shselasky		MIX(sc).desc[0] = 0;
3072242438Shselasky	}
3073242438Shselasky
3074244567Shselasky	if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN) {
3075244567Shselasky		MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN;
3076184610Salfred	}
3077244567Shselasky	MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval);
3078184610Salfred	for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) {
3079244567Shselasky		MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES;
3080184610Salfred	}
3081184610Salfred
3082244567Shselasky	for (i = 0; i < MIX(sc).maxval; i++) {
3083244567Shselasky		MIX(sc).slctrtype[i] = uaudio_mixer_feature_name(
3084244567Shselasky		    &iot[d->baSourceId[i]], &MIX(sc));
3085184610Salfred	}
3086184610Salfred
3087244567Shselasky	MIX(sc).class = 0;			/* not used */
3088184610Salfred
3089244567Shselasky	uaudio_mixer_add_ctl(sc, &MIX(sc));
3090184610Salfred}
3091184610Salfred
3092240609Shselaskystatic void
3093240609Shselaskyuaudio20_mixer_add_selector(struct uaudio_softc *sc,
3094240609Shselasky    const struct uaudio_terminal_node *iot, int id)
3095240609Shselasky{
3096240609Shselasky	const struct usb_audio20_selector_unit *d = iot[id].u.su_v2;
3097240609Shselasky	uint16_t i;
3098240609Shselasky
3099240609Shselasky	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
3100240609Shselasky	    d->bUnitId, d->bNrInPins);
3101240609Shselasky
3102240609Shselasky	if (d->bNrInPins == 0)
3103240609Shselasky		return;
3104240609Shselasky
3105244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3106240609Shselasky
3107244567Shselasky	MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
3108244567Shselasky	MIX(sc).wValue[0] = MAKE_WORD(0, 0);
3109244567Shselasky	uaudio20_mixer_determine_class(&iot[id], &MIX(sc));
3110244567Shselasky	MIX(sc).nchan = 1;
3111244567Shselasky	MIX(sc).type = MIX_SELECTOR;
3112244567Shselasky	MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3113244567Shselasky	MIX(sc).minval = 1;
3114244567Shselasky	MIX(sc).maxval = d->bNrInPins;
3115244567Shselasky	MIX(sc).name = "selector";
3116240609Shselasky
3117242438Shselasky	i = d->baSourceId[d->bNrInPins];
3118242438Shselasky	if (i == 0 ||
3119242438Shselasky	    usbd_req_get_string_any(sc->sc_udev, NULL,
3120244567Shselasky	    MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
3121244567Shselasky		MIX(sc).desc[0] = 0;
3122242438Shselasky	}
3123242438Shselasky
3124244567Shselasky	if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN)
3125244567Shselasky		MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN;
3126240609Shselasky
3127244567Shselasky	MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval);
3128240609Shselasky	for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++)
3129244567Shselasky		MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES;
3130240609Shselasky
3131244567Shselasky	for (i = 0; i < MIX(sc).maxval; i++) {
3132244567Shselasky		MIX(sc).slctrtype[i] = uaudio20_mixer_feature_name(
3133244567Shselasky		    &iot[d->baSourceId[i]], &MIX(sc));
3134240609Shselasky	}
3135240609Shselasky
3136244567Shselasky	MIX(sc).class = 0;			/* not used */
3137240609Shselasky
3138244567Shselasky	uaudio_mixer_add_ctl(sc, &MIX(sc));
3139240609Shselasky}
3140240609Shselasky
3141184610Salfredstatic uint32_t
3142203678Sbrucecuaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit *d,
3143233774Shselasky    uint8_t i)
3144184610Salfred{
3145184610Salfred	uint32_t temp = 0;
3146233774Shselasky	uint32_t offset = (i * d->bControlSize);
3147184610Salfred
3148184610Salfred	if (d->bControlSize > 0) {
3149184610Salfred		temp |= d->bmaControls[offset];
3150184610Salfred		if (d->bControlSize > 1) {
3151184610Salfred			temp |= d->bmaControls[offset + 1] << 8;
3152184610Salfred			if (d->bControlSize > 2) {
3153184610Salfred				temp |= d->bmaControls[offset + 2] << 16;
3154184610Salfred				if (d->bControlSize > 3) {
3155184610Salfred					temp |= d->bmaControls[offset + 3] << 24;
3156184610Salfred				}
3157184610Salfred			}
3158184610Salfred		}
3159184610Salfred	}
3160184610Salfred	return (temp);
3161184610Salfred}
3162184610Salfred
3163184610Salfredstatic void
3164184610Salfreduaudio_mixer_add_feature(struct uaudio_softc *sc,
3165184610Salfred    const struct uaudio_terminal_node *iot, int id)
3166184610Salfred{
3167240609Shselasky	const struct usb_audio_feature_unit *d = iot[id].u.fu_v1;
3168184610Salfred	uint32_t fumask;
3169184610Salfred	uint32_t mmask;
3170184610Salfred	uint32_t cmask;
3171184610Salfred	uint16_t mixernumber;
3172184610Salfred	uint8_t nchan;
3173184610Salfred	uint8_t chan;
3174184610Salfred	uint8_t ctl;
3175184610Salfred	uint8_t i;
3176184610Salfred
3177242438Shselasky	if (d->bControlSize == 0)
3178184610Salfred		return;
3179242438Shselasky
3180244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3181184610Salfred
3182184610Salfred	nchan = (d->bLength - 7) / d->bControlSize;
3183184610Salfred	mmask = uaudio_mixer_feature_get_bmaControls(d, 0);
3184184610Salfred	cmask = 0;
3185184610Salfred
3186242438Shselasky	if (nchan == 0)
3187184610Salfred		return;
3188242438Shselasky
3189184610Salfred	/* figure out what we can control */
3190184610Salfred
3191184610Salfred	for (chan = 1; chan < nchan; chan++) {
3192184610Salfred		DPRINTFN(10, "chan=%d mask=%x\n",
3193184610Salfred		    chan, uaudio_mixer_feature_get_bmaControls(d, chan));
3194184610Salfred
3195184610Salfred		cmask |= uaudio_mixer_feature_get_bmaControls(d, chan);
3196184610Salfred	}
3197184610Salfred
3198184610Salfred	if (nchan > MIX_MAX_CHAN) {
3199184610Salfred		nchan = MIX_MAX_CHAN;
3200184610Salfred	}
3201244567Shselasky	MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
3202184610Salfred
3203242438Shselasky	i = d->bmaControls[d->bControlSize];
3204242438Shselasky	if (i == 0 ||
3205242438Shselasky	    usbd_req_get_string_any(sc->sc_udev, NULL,
3206244567Shselasky	    MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
3207244567Shselasky		MIX(sc).desc[0] = 0;
3208242438Shselasky	}
3209242438Shselasky
3210184610Salfred	for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) {
3211184610Salfred
3212184610Salfred		fumask = FU_MASK(ctl);
3213184610Salfred
3214184610Salfred		DPRINTFN(5, "ctl=%d fumask=0x%04x\n",
3215184610Salfred		    ctl, fumask);
3216184610Salfred
3217184610Salfred		if (mmask & fumask) {
3218244567Shselasky			MIX(sc).nchan = 1;
3219244567Shselasky			MIX(sc).wValue[0] = MAKE_WORD(ctl, 0);
3220184610Salfred		} else if (cmask & fumask) {
3221244567Shselasky			MIX(sc).nchan = nchan - 1;
3222184610Salfred			for (i = 1; i < nchan; i++) {
3223184610Salfred				if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask)
3224244567Shselasky					MIX(sc).wValue[i - 1] = MAKE_WORD(ctl, i);
3225184610Salfred				else
3226244567Shselasky					MIX(sc).wValue[i - 1] = -1;
3227184610Salfred			}
3228184610Salfred		} else {
3229184610Salfred			continue;
3230184610Salfred		}
3231184610Salfred
3232244567Shselasky		mixernumber = uaudio_mixer_feature_name(&iot[id], &MIX(sc));
3233184610Salfred
3234184610Salfred		switch (ctl) {
3235184610Salfred		case MUTE_CONTROL:
3236244567Shselasky			MIX(sc).type = MIX_ON_OFF;
3237244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3238244567Shselasky			MIX(sc).name = "mute";
3239184610Salfred			break;
3240184610Salfred
3241184610Salfred		case VOLUME_CONTROL:
3242244567Shselasky			MIX(sc).type = MIX_SIGNED_16;
3243244567Shselasky			MIX(sc).ctl = mixernumber;
3244244567Shselasky			MIX(sc).name = "vol";
3245184610Salfred			break;
3246184610Salfred
3247184610Salfred		case BASS_CONTROL:
3248244567Shselasky			MIX(sc).type = MIX_SIGNED_8;
3249244567Shselasky			MIX(sc).ctl = SOUND_MIXER_BASS;
3250244567Shselasky			MIX(sc).name = "bass";
3251184610Salfred			break;
3252184610Salfred
3253184610Salfred		case MID_CONTROL:
3254244567Shselasky			MIX(sc).type = MIX_SIGNED_8;
3255244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3256244567Shselasky			MIX(sc).name = "mid";
3257184610Salfred			break;
3258184610Salfred
3259184610Salfred		case TREBLE_CONTROL:
3260244567Shselasky			MIX(sc).type = MIX_SIGNED_8;
3261244567Shselasky			MIX(sc).ctl = SOUND_MIXER_TREBLE;
3262244567Shselasky			MIX(sc).name = "treble";
3263184610Salfred			break;
3264184610Salfred
3265184610Salfred		case GRAPHIC_EQUALIZER_CONTROL:
3266184610Salfred			continue;	/* XXX don't add anything */
3267184610Salfred
3268184610Salfred		case AGC_CONTROL:
3269244567Shselasky			MIX(sc).type = MIX_ON_OFF;
3270244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3271244567Shselasky			MIX(sc).name = "agc";
3272184610Salfred			break;
3273184610Salfred
3274184610Salfred		case DELAY_CONTROL:
3275244567Shselasky			MIX(sc).type = MIX_UNSIGNED_16;
3276244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3277244567Shselasky			MIX(sc).name = "delay";
3278184610Salfred			break;
3279184610Salfred
3280184610Salfred		case BASS_BOOST_CONTROL:
3281244567Shselasky			MIX(sc).type = MIX_ON_OFF;
3282244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3283244567Shselasky			MIX(sc).name = "boost";
3284184610Salfred			break;
3285184610Salfred
3286184610Salfred		case LOUDNESS_CONTROL:
3287244567Shselasky			MIX(sc).type = MIX_ON_OFF;
3288244567Shselasky			MIX(sc).ctl = SOUND_MIXER_LOUD;	/* Is this correct ? */
3289244567Shselasky			MIX(sc).name = "loudness";
3290184610Salfred			break;
3291184610Salfred
3292184610Salfred		default:
3293244567Shselasky			MIX(sc).type = MIX_UNKNOWN;
3294184610Salfred			break;
3295184610Salfred		}
3296184610Salfred
3297244567Shselasky		if (MIX(sc).type != MIX_UNKNOWN)
3298244567Shselasky			uaudio_mixer_add_ctl(sc, &MIX(sc));
3299240609Shselasky	}
3300240609Shselasky}
3301240609Shselasky
3302240609Shselaskystatic void
3303240609Shselaskyuaudio20_mixer_add_feature(struct uaudio_softc *sc,
3304240609Shselasky    const struct uaudio_terminal_node *iot, int id)
3305240609Shselasky{
3306240609Shselasky	const struct usb_audio20_feature_unit *d = iot[id].u.fu_v2;
3307240609Shselasky	uint32_t ctl;
3308240609Shselasky	uint32_t mmask;
3309240609Shselasky	uint32_t cmask;
3310240609Shselasky	uint16_t mixernumber;
3311240609Shselasky	uint8_t nchan;
3312240609Shselasky	uint8_t chan;
3313240609Shselasky	uint8_t i;
3314240609Shselasky	uint8_t what;
3315240609Shselasky
3316240609Shselasky	if (UGETDW(d->bmaControls[0]) == 0)
3317240609Shselasky		return;
3318240609Shselasky
3319244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3320240609Shselasky
3321240609Shselasky	nchan = (d->bLength - 6) / 4;
3322240609Shselasky	mmask = UGETDW(d->bmaControls[0]);
3323240609Shselasky	cmask = 0;
3324240609Shselasky
3325240609Shselasky	if (nchan == 0)
3326240609Shselasky		return;
3327240609Shselasky
3328240609Shselasky	/* figure out what we can control */
3329240609Shselasky
3330240609Shselasky	for (chan = 1; chan < nchan; chan++)
3331240609Shselasky		cmask |= UGETDW(d->bmaControls[chan]);
3332240609Shselasky
3333240609Shselasky	if (nchan > MIX_MAX_CHAN)
3334240609Shselasky		nchan = MIX_MAX_CHAN;
3335240609Shselasky
3336244567Shselasky	MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
3337240609Shselasky
3338242438Shselasky	i = d->bmaControls[nchan][0];
3339242438Shselasky	if (i == 0 ||
3340242438Shselasky	    usbd_req_get_string_any(sc->sc_udev, NULL,
3341244567Shselasky	    MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) {
3342244567Shselasky		MIX(sc).desc[0] = 0;
3343242438Shselasky	}
3344242438Shselasky
3345240609Shselasky	for (ctl = 3; ctl != 0; ctl <<= 2) {
3346240609Shselasky
3347244567Shselasky		mixernumber = uaudio20_mixer_feature_name(&iot[id], &MIX(sc));
3348240609Shselasky
3349240609Shselasky		switch (ctl) {
3350240609Shselasky		case (3 << 0):
3351244567Shselasky			MIX(sc).type = MIX_ON_OFF;
3352244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;
3353244567Shselasky			MIX(sc).name = "mute";
3354240609Shselasky			what = MUTE_CONTROL;
3355240609Shselasky			break;
3356240609Shselasky		case (3 << 2):
3357244567Shselasky			MIX(sc).type = MIX_SIGNED_16;
3358244567Shselasky			MIX(sc).ctl = mixernumber;
3359244567Shselasky			MIX(sc).name = "vol";
3360240609Shselasky			what = VOLUME_CONTROL;
3361240609Shselasky			break;
3362240609Shselasky		case (3 << 4):
3363244567Shselasky			MIX(sc).type = MIX_SIGNED_8;
3364244567Shselasky			MIX(sc).ctl = SOUND_MIXER_BASS;
3365244567Shselasky			MIX(sc).name = "bass";
3366240609Shselasky			what = BASS_CONTROL;
3367240609Shselasky			break;
3368240609Shselasky		case (3 << 6):
3369244567Shselasky			MIX(sc).type = MIX_SIGNED_8;
3370244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3371244567Shselasky			MIX(sc).name = "mid";
3372240609Shselasky			what = MID_CONTROL;
3373240609Shselasky			break;
3374240609Shselasky		case (3 << 8):
3375244567Shselasky			MIX(sc).type = MIX_SIGNED_8;
3376244567Shselasky			MIX(sc).ctl = SOUND_MIXER_TREBLE;
3377244567Shselasky			MIX(sc).name = "treble";
3378240609Shselasky			what = TREBLE_CONTROL;
3379240609Shselasky			break;
3380240609Shselasky		case (3 << 12):
3381244567Shselasky			MIX(sc).type = MIX_ON_OFF;
3382244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3383244567Shselasky			MIX(sc).name = "agc";
3384240609Shselasky			what = AGC_CONTROL;
3385240609Shselasky			break;
3386240609Shselasky		case (3 << 14):
3387244567Shselasky			MIX(sc).type = MIX_UNSIGNED_16;
3388244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3389244567Shselasky			MIX(sc).name = "delay";
3390240609Shselasky			what = DELAY_CONTROL;
3391240609Shselasky			break;
3392240609Shselasky		case (3 << 16):
3393244567Shselasky			MIX(sc).type = MIX_ON_OFF;
3394244567Shselasky			MIX(sc).ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
3395244567Shselasky			MIX(sc).name = "boost";
3396240609Shselasky			what = BASS_BOOST_CONTROL;
3397240609Shselasky			break;
3398240609Shselasky		case (3 << 18):
3399244567Shselasky			MIX(sc).type = MIX_ON_OFF;
3400244567Shselasky			MIX(sc).ctl = SOUND_MIXER_LOUD;	/* Is this correct ? */
3401244567Shselasky			MIX(sc).name = "loudness";
3402240609Shselasky			what = LOUDNESS_CONTROL;
3403240609Shselasky			break;
3404240609Shselasky		case (3 << 20):
3405244567Shselasky			MIX(sc).type = MIX_SIGNED_16;
3406244567Shselasky			MIX(sc).ctl = mixernumber;
3407244567Shselasky			MIX(sc).name = "igain";
3408240609Shselasky			what = INPUT_GAIN_CONTROL;
3409240609Shselasky			break;
3410240609Shselasky		case (3 << 22):
3411244567Shselasky			MIX(sc).type = MIX_SIGNED_16;
3412244567Shselasky			MIX(sc).ctl = mixernumber;
3413244567Shselasky			MIX(sc).name = "igainpad";
3414240609Shselasky			what = INPUT_GAIN_PAD_CONTROL;
3415240609Shselasky			break;
3416240609Shselasky		default:
3417240609Shselasky			continue;
3418184610Salfred		}
3419240609Shselasky
3420240609Shselasky		if ((mmask & ctl) == ctl) {
3421244567Shselasky			MIX(sc).nchan = 1;
3422244567Shselasky			MIX(sc).wValue[0] = MAKE_WORD(what, 0);
3423240609Shselasky		} else if ((cmask & ctl) == ctl) {
3424244567Shselasky			MIX(sc).nchan = nchan - 1;
3425240609Shselasky			for (i = 1; i < nchan; i++) {
3426240609Shselasky				if ((UGETDW(d->bmaControls[i]) & ctl) == ctl)
3427244567Shselasky					MIX(sc).wValue[i - 1] = MAKE_WORD(what, i);
3428240609Shselasky				else
3429244567Shselasky					MIX(sc).wValue[i - 1] = -1;
3430240609Shselasky			}
3431240609Shselasky		} else {
3432240609Shselasky			continue;
3433240609Shselasky		}
3434240609Shselasky
3435244567Shselasky		if (MIX(sc).type != MIX_UNKNOWN)
3436244567Shselasky			uaudio_mixer_add_ctl(sc, &MIX(sc));
3437184610Salfred	}
3438184610Salfred}
3439184610Salfred
3440184610Salfredstatic void
3441184610Salfreduaudio_mixer_add_processing_updown(struct uaudio_softc *sc,
3442184610Salfred    const struct uaudio_terminal_node *iot, int id)
3443184610Salfred{
3444240609Shselasky	const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1;
3445203678Sbrucec	const struct usb_audio_processing_unit_1 *d1 =
3446244567Shselasky	    (const void *)(d0->baSourceId + d0->bNrInPins);
3447203678Sbrucec	const struct usb_audio_processing_unit_updown *ud =
3448244567Shselasky	    (const void *)(d1->bmControls + d1->bControlSize);
3449184610Salfred	uint8_t i;
3450184610Salfred
3451184610Salfred	if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) {
3452184610Salfred		return;
3453184610Salfred	}
3454184610Salfred	if (uaudio_mixer_verify_desc(d0, sizeof(*ud) + (2 * ud->bNrModes))
3455184610Salfred	    == NULL) {
3456184610Salfred		return;
3457184610Salfred	}
3458184610Salfred	DPRINTFN(3, "bUnitId=%d bNrModes=%d\n",
3459184610Salfred	    d0->bUnitId, ud->bNrModes);
3460184610Salfred
3461184610Salfred	if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) {
3462184610Salfred		DPRINTF("no mode select\n");
3463184610Salfred		return;
3464184610Salfred	}
3465244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3466184610Salfred
3467244567Shselasky	MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3468244567Shselasky	MIX(sc).nchan = 1;
3469244567Shselasky	MIX(sc).wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0);
3470244567Shselasky	uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3471244567Shselasky	MIX(sc).type = MIX_ON_OFF;		/* XXX */
3472184610Salfred
3473184610Salfred	for (i = 0; i < ud->bNrModes; i++) {
3474184610Salfred		DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i]));
3475184610Salfred		/* XXX */
3476184610Salfred	}
3477184610Salfred
3478244567Shselasky	uaudio_mixer_add_ctl(sc, &MIX(sc));
3479184610Salfred}
3480184610Salfred
3481184610Salfredstatic void
3482184610Salfreduaudio_mixer_add_processing(struct uaudio_softc *sc,
3483184610Salfred    const struct uaudio_terminal_node *iot, int id)
3484184610Salfred{
3485240609Shselasky	const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1;
3486203678Sbrucec	const struct usb_audio_processing_unit_1 *d1 =
3487244567Shselasky	    (const void *)(d0->baSourceId + d0->bNrInPins);
3488184610Salfred	uint16_t ptype;
3489184610Salfred
3490244567Shselasky	memset(&MIX(sc), 0, sizeof(MIX(sc)));
3491184610Salfred
3492184610Salfred	ptype = UGETW(d0->wProcessType);
3493184610Salfred
3494184610Salfred	DPRINTFN(3, "wProcessType=%d bUnitId=%d "
3495184610Salfred	    "bNrInPins=%d\n", ptype, d0->bUnitId, d0->bNrInPins);
3496184610Salfred
3497184610Salfred	if (d1->bControlSize == 0) {
3498184610Salfred		return;
3499184610Salfred	}
3500184610Salfred	if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) {
3501244567Shselasky		MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3502244567Shselasky		MIX(sc).nchan = 1;
3503244567Shselasky		MIX(sc).wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0);
3504244567Shselasky		uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3505244567Shselasky		MIX(sc).type = MIX_ON_OFF;
3506244567Shselasky		uaudio_mixer_add_ctl(sc, &MIX(sc));
3507184610Salfred	}
3508184610Salfred	switch (ptype) {
3509184610Salfred	case UPDOWNMIX_PROCESS:
3510184610Salfred		uaudio_mixer_add_processing_updown(sc, iot, id);
3511184610Salfred		break;
3512184610Salfred
3513184610Salfred	case DOLBY_PROLOGIC_PROCESS:
3514184610Salfred	case P3D_STEREO_EXTENDER_PROCESS:
3515184610Salfred	case REVERBATION_PROCESS:
3516184610Salfred	case CHORUS_PROCESS:
3517184610Salfred	case DYN_RANGE_COMP_PROCESS:
3518184610Salfred	default:
3519184610Salfred		DPRINTF("unit %d, type=%d is not implemented\n",
3520184610Salfred		    d0->bUnitId, ptype);
3521184610Salfred		break;
3522184610Salfred	}
3523184610Salfred}
3524184610Salfred
3525184610Salfredstatic void
3526184610Salfreduaudio_mixer_add_extension(struct uaudio_softc *sc,
3527184610Salfred    const struct uaudio_terminal_node *iot, int id)
3528184610Salfred{
3529240609Shselasky	const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu_v1;
3530203678Sbrucec	const struct usb_audio_extension_unit_1 *d1 =
3531244567Shselasky	    (const void *)(d0->baSourceId + d0->bNrInPins);
3532184610Salfred
3533184610Salfred	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
3534184610Salfred	    d0->bUnitId, d0->bNrInPins);
3535184610Salfred
3536184610Salfred	if (sc->sc_uq_au_no_xu) {
3537184610Salfred		return;
3538184610Salfred	}
3539184610Salfred	if (d1->bControlSize == 0) {
3540184610Salfred		return;
3541184610Salfred	}
3542184610Salfred	if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) {
3543184610Salfred
3544244567Shselasky		memset(&MIX(sc), 0, sizeof(MIX(sc)));
3545184610Salfred
3546244567Shselasky		MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
3547244567Shselasky		MIX(sc).nchan = 1;
3548244567Shselasky		MIX(sc).wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0);
3549244567Shselasky		uaudio_mixer_determine_class(&iot[id], &MIX(sc));
3550244567Shselasky		MIX(sc).type = MIX_ON_OFF;
3551184610Salfred
3552244567Shselasky		uaudio_mixer_add_ctl(sc, &MIX(sc));
3553184610Salfred	}
3554184610Salfred}
3555184610Salfred
3556184610Salfredstatic const void *
3557184610Salfreduaudio_mixer_verify_desc(const void *arg, uint32_t len)
3558184610Salfred{
3559203678Sbrucec	const struct usb_audio_mixer_unit_1 *d1;
3560203678Sbrucec	const struct usb_audio_extension_unit_1 *e1;
3561203678Sbrucec	const struct usb_audio_processing_unit_1 *u1;
3562184610Salfred
3563184610Salfred	union {
3564192984Sthompsa		const struct usb_descriptor *desc;
3565203678Sbrucec		const struct usb_audio_input_terminal *it;
3566203678Sbrucec		const struct usb_audio_output_terminal *ot;
3567203678Sbrucec		const struct usb_audio_mixer_unit_0 *mu;
3568203678Sbrucec		const struct usb_audio_selector_unit *su;
3569203678Sbrucec		const struct usb_audio_feature_unit *fu;
3570203678Sbrucec		const struct usb_audio_processing_unit_0 *pu;
3571203678Sbrucec		const struct usb_audio_extension_unit_0 *eu;
3572184610Salfred	}     u;
3573184610Salfred
3574184610Salfred	u.desc = arg;
3575184610Salfred
3576184610Salfred	if (u.desc == NULL) {
3577184610Salfred		goto error;
3578184610Salfred	}
3579184610Salfred	if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) {
3580184610Salfred		goto error;
3581184610Salfred	}
3582184610Salfred	switch (u.desc->bDescriptorSubtype) {
3583184610Salfred	case UDESCSUB_AC_INPUT:
3584184610Salfred		len += sizeof(*u.it);
3585184610Salfred		break;
3586184610Salfred
3587184610Salfred	case UDESCSUB_AC_OUTPUT:
3588184610Salfred		len += sizeof(*u.ot);
3589184610Salfred		break;
3590184610Salfred
3591184610Salfred	case UDESCSUB_AC_MIXER:
3592184610Salfred		len += sizeof(*u.mu);
3593184610Salfred
3594184610Salfred		if (u.desc->bLength < len) {
3595184610Salfred			goto error;
3596184610Salfred		}
3597184610Salfred		len += u.mu->bNrInPins;
3598184610Salfred
3599184610Salfred		if (u.desc->bLength < len) {
3600184610Salfred			goto error;
3601184610Salfred		}
3602184610Salfred		d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins);
3603184610Salfred
3604184610Salfred		len += sizeof(*d1);
3605184610Salfred		break;
3606184610Salfred
3607184610Salfred	case UDESCSUB_AC_SELECTOR:
3608184610Salfred		len += sizeof(*u.su);
3609184610Salfred
3610184610Salfred		if (u.desc->bLength < len) {
3611184610Salfred			goto error;
3612184610Salfred		}
3613242438Shselasky		len += u.su->bNrInPins + 1;
3614184610Salfred		break;
3615184610Salfred
3616184610Salfred	case UDESCSUB_AC_FEATURE:
3617242438Shselasky		len += sizeof(*u.fu) + 1;
3618242438Shselasky
3619242438Shselasky		if (u.desc->bLength < len)
3620242438Shselasky			goto error;
3621242438Shselasky
3622242438Shselasky		len += u.fu->bControlSize;
3623184610Salfred		break;
3624184610Salfred
3625184610Salfred	case UDESCSUB_AC_PROCESSING:
3626184610Salfred		len += sizeof(*u.pu);
3627184610Salfred
3628184610Salfred		if (u.desc->bLength < len) {
3629184610Salfred			goto error;
3630184610Salfred		}
3631184610Salfred		len += u.pu->bNrInPins;
3632184610Salfred
3633184610Salfred		if (u.desc->bLength < len) {
3634184610Salfred			goto error;
3635184610Salfred		}
3636184610Salfred		u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins);
3637184610Salfred
3638184610Salfred		len += sizeof(*u1);
3639184610Salfred
3640184610Salfred		if (u.desc->bLength < len) {
3641184610Salfred			goto error;
3642184610Salfred		}
3643184610Salfred		len += u1->bControlSize;
3644184610Salfred
3645184610Salfred		break;
3646184610Salfred
3647184610Salfred	case UDESCSUB_AC_EXTENSION:
3648184610Salfred		len += sizeof(*u.eu);
3649184610Salfred
3650184610Salfred		if (u.desc->bLength < len) {
3651184610Salfred			goto error;
3652184610Salfred		}
3653184610Salfred		len += u.eu->bNrInPins;
3654184610Salfred
3655184610Salfred		if (u.desc->bLength < len) {
3656184610Salfred			goto error;
3657184610Salfred		}
3658184610Salfred		e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
3659184610Salfred
3660184610Salfred		len += sizeof(*e1);
3661184610Salfred
3662184610Salfred		if (u.desc->bLength < len) {
3663184610Salfred			goto error;
3664184610Salfred		}
3665184610Salfred		len += e1->bControlSize;
3666184610Salfred		break;
3667184610Salfred
3668184610Salfred	default:
3669184610Salfred		goto error;
3670184610Salfred	}
3671184610Salfred
3672184610Salfred	if (u.desc->bLength < len) {
3673184610Salfred		goto error;
3674184610Salfred	}
3675184610Salfred	return (u.desc);
3676184610Salfred
3677184610Salfrederror:
3678184610Salfred	if (u.desc) {
3679184610Salfred		DPRINTF("invalid descriptor, type=%d, "
3680184610Salfred		    "sub_type=%d, len=%d of %d bytes\n",
3681184610Salfred		    u.desc->bDescriptorType,
3682184610Salfred		    u.desc->bDescriptorSubtype,
3683184610Salfred		    u.desc->bLength, len);
3684184610Salfred	}
3685184610Salfred	return (NULL);
3686184610Salfred}
3687184610Salfred
3688240609Shselaskystatic const void *
3689240609Shselaskyuaudio20_mixer_verify_desc(const void *arg, uint32_t len)
3690184610Salfred{
3691240609Shselasky	const struct usb_audio20_mixer_unit_1 *d1;
3692240609Shselasky	const struct usb_audio20_extension_unit_1 *e1;
3693240609Shselasky	const struct usb_audio20_processing_unit_1 *u1;
3694240609Shselasky	const struct usb_audio20_clock_selector_unit_1 *c1;
3695184610Salfred
3696240609Shselasky	union {
3697240609Shselasky		const struct usb_descriptor *desc;
3698240609Shselasky		const struct usb_audio20_clock_source_unit *csrc;
3699240609Shselasky		const struct usb_audio20_clock_selector_unit_0 *csel;
3700240609Shselasky		const struct usb_audio20_clock_multiplier_unit *cmul;
3701240609Shselasky		const struct usb_audio20_input_terminal *it;
3702240609Shselasky		const struct usb_audio20_output_terminal *ot;
3703240609Shselasky		const struct usb_audio20_mixer_unit_0 *mu;
3704240609Shselasky		const struct usb_audio20_selector_unit *su;
3705240609Shselasky		const struct usb_audio20_feature_unit *fu;
3706240609Shselasky		const struct usb_audio20_sample_rate_unit *ru;
3707240609Shselasky		const struct usb_audio20_processing_unit_0 *pu;
3708240609Shselasky		const struct usb_audio20_extension_unit_0 *eu;
3709240609Shselasky		const struct usb_audio20_effect_unit *ef;
3710240609Shselasky	}     u;
3711184610Salfred
3712240609Shselasky	u.desc = arg;
3713184610Salfred
3714240609Shselasky	if (u.desc == NULL)
3715240609Shselasky		goto error;
3716240609Shselasky
3717240609Shselasky	if (u.desc->bDescriptorType != UDESC_CS_INTERFACE)
3718240609Shselasky		goto error;
3719240609Shselasky
3720240609Shselasky	switch (u.desc->bDescriptorSubtype) {
3721240609Shselasky	case UDESCSUB_AC_INPUT:
3722240609Shselasky		len += sizeof(*u.it);
3723240609Shselasky		break;
3724240609Shselasky
3725240609Shselasky	case UDESCSUB_AC_OUTPUT:
3726240609Shselasky		len += sizeof(*u.ot);
3727240609Shselasky		break;
3728240609Shselasky
3729240609Shselasky	case UDESCSUB_AC_MIXER:
3730240609Shselasky		len += sizeof(*u.mu);
3731240609Shselasky
3732240609Shselasky		if (u.desc->bLength < len)
3733240609Shselasky			goto error;
3734240609Shselasky		len += u.mu->bNrInPins;
3735240609Shselasky
3736240609Shselasky		if (u.desc->bLength < len)
3737240609Shselasky			goto error;
3738240609Shselasky
3739240609Shselasky		d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins);
3740240609Shselasky
3741240609Shselasky		len += sizeof(*d1) + d1->bNrChannels;
3742240609Shselasky		break;
3743240609Shselasky
3744240609Shselasky	case UDESCSUB_AC_SELECTOR:
3745240609Shselasky		len += sizeof(*u.su);
3746240609Shselasky
3747240609Shselasky		if (u.desc->bLength < len)
3748240609Shselasky			goto error;
3749240609Shselasky
3750242438Shselasky		len += u.su->bNrInPins + 1;
3751240609Shselasky		break;
3752240609Shselasky
3753240609Shselasky	case UDESCSUB_AC_FEATURE:
3754240609Shselasky		len += sizeof(*u.fu) + 1;
3755240609Shselasky		break;
3756240609Shselasky
3757240609Shselasky	case UDESCSUB_AC_EFFECT:
3758240609Shselasky		len += sizeof(*u.ef) + 4;
3759240609Shselasky		break;
3760240609Shselasky
3761240609Shselasky	case UDESCSUB_AC_PROCESSING_V2:
3762240609Shselasky		len += sizeof(*u.pu);
3763240609Shselasky
3764240609Shselasky		if (u.desc->bLength < len)
3765240609Shselasky			goto error;
3766240609Shselasky
3767240609Shselasky		len += u.pu->bNrInPins;
3768240609Shselasky
3769240609Shselasky		if (u.desc->bLength < len)
3770240609Shselasky			goto error;
3771240609Shselasky
3772240609Shselasky		u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins);
3773240609Shselasky
3774240609Shselasky		len += sizeof(*u1);
3775240609Shselasky		break;
3776240609Shselasky
3777240609Shselasky	case UDESCSUB_AC_EXTENSION_V2:
3778240609Shselasky		len += sizeof(*u.eu);
3779240609Shselasky
3780240609Shselasky		if (u.desc->bLength < len)
3781240609Shselasky			goto error;
3782240609Shselasky
3783240609Shselasky		len += u.eu->bNrInPins;
3784240609Shselasky
3785240609Shselasky		if (u.desc->bLength < len)
3786240609Shselasky			goto error;
3787240609Shselasky
3788240609Shselasky		e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
3789240609Shselasky
3790240609Shselasky		len += sizeof(*e1);
3791240609Shselasky		break;
3792240609Shselasky
3793240609Shselasky	case UDESCSUB_AC_CLOCK_SRC:
3794240609Shselasky		len += sizeof(*u.csrc);
3795240609Shselasky		break;
3796240609Shselasky
3797240609Shselasky	case UDESCSUB_AC_CLOCK_SEL:
3798240609Shselasky		len += sizeof(*u.csel);
3799240609Shselasky
3800240609Shselasky		if (u.desc->bLength < len)
3801240609Shselasky			goto error;
3802240609Shselasky
3803240609Shselasky		len += u.csel->bNrInPins;
3804240609Shselasky
3805240609Shselasky		if (u.desc->bLength < len)
3806240609Shselasky			goto error;
3807240609Shselasky
3808240609Shselasky		c1 = (const void *)(u.csel->baCSourceId + u.csel->bNrInPins);
3809240609Shselasky
3810240609Shselasky		len += sizeof(*c1);
3811240609Shselasky		break;
3812240609Shselasky
3813240609Shselasky	case UDESCSUB_AC_CLOCK_MUL:
3814240609Shselasky		len += sizeof(*u.cmul);
3815240609Shselasky		break;
3816240609Shselasky
3817240609Shselasky	case UDESCSUB_AC_SAMPLE_RT:
3818240609Shselasky		len += sizeof(*u.ru);
3819240609Shselasky		break;
3820240609Shselasky
3821240609Shselasky	default:
3822240609Shselasky		goto error;
3823184610Salfred	}
3824240609Shselasky
3825240609Shselasky	if (u.desc->bLength < len)
3826240609Shselasky		goto error;
3827240609Shselasky
3828240609Shselasky	return (u.desc);
3829240609Shselasky
3830240609Shselaskyerror:
3831240609Shselasky	if (u.desc) {
3832240609Shselasky		DPRINTF("invalid descriptor, type=%d, "
3833240609Shselasky		    "sub_type=%d, len=%d of %d bytes\n",
3834240609Shselasky		    u.desc->bDescriptorType,
3835240609Shselasky		    u.desc->bDescriptorSubtype,
3836240609Shselasky		    u.desc->bLength, len);
3837240609Shselasky	}
3838240609Shselasky	return (NULL);
3839184610Salfred}
3840184610Salfred
3841203678Sbrucecstatic struct usb_audio_cluster
3842184610Salfreduaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
3843184610Salfred{
3844203678Sbrucec	struct usb_audio_cluster r;
3845192984Sthompsa	const struct usb_descriptor *dp;
3846184610Salfred	uint8_t i;
3847184610Salfred
3848184610Salfred	for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) {	/* avoid infinite loops */
3849184610Salfred		dp = iot[id].u.desc;
3850184610Salfred		if (dp == NULL) {
3851184610Salfred			goto error;
3852184610Salfred		}
3853184610Salfred		switch (dp->bDescriptorSubtype) {
3854184610Salfred		case UDESCSUB_AC_INPUT:
3855240609Shselasky			r.bNrChannels = iot[id].u.it_v1->bNrChannels;
3856240609Shselasky			r.wChannelConfig[0] = iot[id].u.it_v1->wChannelConfig[0];
3857240609Shselasky			r.wChannelConfig[1] = iot[id].u.it_v1->wChannelConfig[1];
3858240609Shselasky			r.iChannelNames = iot[id].u.it_v1->iChannelNames;
3859184610Salfred			goto done;
3860184610Salfred
3861184610Salfred		case UDESCSUB_AC_OUTPUT:
3862240609Shselasky			id = iot[id].u.ot_v1->bSourceId;
3863184610Salfred			break;
3864184610Salfred
3865184610Salfred		case UDESCSUB_AC_MIXER:
3866203678Sbrucec			r = *(const struct usb_audio_cluster *)
3867240609Shselasky			    &iot[id].u.mu_v1->baSourceId[
3868240609Shselasky			    iot[id].u.mu_v1->bNrInPins];
3869184610Salfred			goto done;
3870184610Salfred
3871184610Salfred		case UDESCSUB_AC_SELECTOR:
3872240609Shselasky			if (iot[id].u.su_v1->bNrInPins > 0) {
3873184610Salfred				/* XXX This is not really right */
3874240609Shselasky				id = iot[id].u.su_v1->baSourceId[0];
3875184610Salfred			}
3876184610Salfred			break;
3877184610Salfred
3878184610Salfred		case UDESCSUB_AC_FEATURE:
3879240609Shselasky			id = iot[id].u.fu_v1->bSourceId;
3880184610Salfred			break;
3881184610Salfred
3882184610Salfred		case UDESCSUB_AC_PROCESSING:
3883203678Sbrucec			r = *((const struct usb_audio_cluster *)
3884240609Shselasky			    &iot[id].u.pu_v1->baSourceId[
3885240609Shselasky			    iot[id].u.pu_v1->bNrInPins]);
3886184610Salfred			goto done;
3887184610Salfred
3888184610Salfred		case UDESCSUB_AC_EXTENSION:
3889203678Sbrucec			r = *((const struct usb_audio_cluster *)
3890240609Shselasky			    &iot[id].u.eu_v1->baSourceId[
3891240609Shselasky			    iot[id].u.eu_v1->bNrInPins]);
3892184610Salfred			goto done;
3893184610Salfred
3894184610Salfred		default:
3895184610Salfred			goto error;
3896184610Salfred		}
3897184610Salfred	}
3898184610Salfrederror:
3899184610Salfred	DPRINTF("bad data\n");
3900218791Shselasky	memset(&r, 0, sizeof(r));
3901184610Salfreddone:
3902184610Salfred	return (r);
3903184610Salfred}
3904184610Salfred
3905240609Shselaskystatic struct usb_audio20_cluster
3906240609Shselaskyuaudio20_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
3907240609Shselasky{
3908240609Shselasky	struct usb_audio20_cluster r;
3909240609Shselasky	const struct usb_descriptor *dp;
3910240609Shselasky	uint8_t i;
3911184610Salfred
3912240609Shselasky	for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) {	/* avoid infinite loops */
3913240609Shselasky		dp = iot[id].u.desc;
3914240609Shselasky		if (dp == NULL)
3915240609Shselasky			goto error;
3916184610Salfred
3917240609Shselasky		switch (dp->bDescriptorSubtype) {
3918240609Shselasky		case UDESCSUB_AC_INPUT:
3919240609Shselasky			r.bNrChannels = iot[id].u.it_v2->bNrChannels;
3920240609Shselasky			r.bmChannelConfig[0] = iot[id].u.it_v2->bmChannelConfig[0];
3921240609Shselasky			r.bmChannelConfig[1] = iot[id].u.it_v2->bmChannelConfig[1];
3922240609Shselasky			r.bmChannelConfig[2] = iot[id].u.it_v2->bmChannelConfig[2];
3923240609Shselasky			r.bmChannelConfig[3] = iot[id].u.it_v2->bmChannelConfig[3];
3924240609Shselasky			r.iChannelNames = iot[id].u.it_v2->iTerminal;
3925240609Shselasky			goto done;
3926184610Salfred
3927240609Shselasky		case UDESCSUB_AC_OUTPUT:
3928240609Shselasky			id = iot[id].u.ot_v2->bSourceId;
3929240609Shselasky			break;
3930184610Salfred
3931240609Shselasky		case UDESCSUB_AC_MIXER:
3932240609Shselasky			r = *(const struct usb_audio20_cluster *)
3933240609Shselasky			    &iot[id].u.mu_v2->baSourceId[
3934240609Shselasky			    iot[id].u.mu_v2->bNrInPins];
3935240609Shselasky			goto done;
3936184610Salfred
3937240609Shselasky		case UDESCSUB_AC_SELECTOR:
3938240609Shselasky			if (iot[id].u.su_v2->bNrInPins > 0) {
3939240609Shselasky				/* XXX This is not really right */
3940240609Shselasky				id = iot[id].u.su_v2->baSourceId[0];
3941240609Shselasky			}
3942240609Shselasky			break;
3943184610Salfred
3944240609Shselasky		case UDESCSUB_AC_SAMPLE_RT:
3945240609Shselasky			id = iot[id].u.ru_v2->bSourceId;
3946240609Shselasky			break;
3947184610Salfred
3948240609Shselasky		case UDESCSUB_AC_EFFECT:
3949240609Shselasky			id = iot[id].u.ef_v2->bSourceId;
3950240609Shselasky			break;
3951184610Salfred
3952240609Shselasky		case UDESCSUB_AC_FEATURE:
3953240609Shselasky			id = iot[id].u.fu_v2->bSourceId;
3954240609Shselasky			break;
3955184610Salfred
3956240609Shselasky		case UDESCSUB_AC_PROCESSING_V2:
3957240609Shselasky			r = *((const struct usb_audio20_cluster *)
3958240609Shselasky			    &iot[id].u.pu_v2->baSourceId[
3959240609Shselasky			    iot[id].u.pu_v2->bNrInPins]);
3960240609Shselasky			goto done;
3961184610Salfred
3962240609Shselasky		case UDESCSUB_AC_EXTENSION_V2:
3963240609Shselasky			r = *((const struct usb_audio20_cluster *)
3964240609Shselasky			    &iot[id].u.eu_v2->baSourceId[
3965240609Shselasky			    iot[id].u.eu_v2->bNrInPins]);
3966240609Shselasky			goto done;
3967184610Salfred
3968240609Shselasky		default:
3969240609Shselasky			goto error;
3970184610Salfred		}
3971184610Salfred	}
3972240609Shselaskyerror:
3973240609Shselasky	DPRINTF("Bad data!\n");
3974240609Shselasky	memset(&r, 0, sizeof(r));
3975240609Shselaskydone:
3976240609Shselasky	return (r);
3977184610Salfred}
3978184610Salfred
3979184610Salfredstatic uint16_t
3980184610Salfreduaudio_mixer_determine_class(const struct uaudio_terminal_node *iot,
3981184610Salfred    struct uaudio_mixer_node *mix)
3982184610Salfred{
3983184610Salfred	uint16_t terminal_type = 0x0000;
3984184610Salfred	const struct uaudio_terminal_node *input[2];
3985184610Salfred	const struct uaudio_terminal_node *output[2];
3986184610Salfred
3987184610Salfred	input[0] = uaudio_mixer_get_input(iot, 0);
3988184610Salfred	input[1] = uaudio_mixer_get_input(iot, 1);
3989184610Salfred
3990184610Salfred	output[0] = uaudio_mixer_get_output(iot, 0);
3991184610Salfred	output[1] = uaudio_mixer_get_output(iot, 1);
3992184610Salfred
3993184610Salfred	/*
3994184610Salfred	 * check if there is only
3995184610Salfred	 * one output terminal:
3996184610Salfred	 */
3997184610Salfred	if (output[0] && (!output[1])) {
3998240609Shselasky		terminal_type =
3999240609Shselasky		    UGETW(output[0]->u.ot_v1->wTerminalType);
4000184610Salfred	}
4001184610Salfred	/*
4002184610Salfred	 * If the only output terminal is USB,
4003184610Salfred	 * the class is UAC_RECORD.
4004184610Salfred	 */
4005184610Salfred	if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
4006184610Salfred
4007184610Salfred		mix->class = UAC_RECORD;
4008184610Salfred		if (input[0] && (!input[1])) {
4009240609Shselasky			terminal_type =
4010240609Shselasky			    UGETW(input[0]->u.it_v1->wTerminalType);
4011184610Salfred		} else {
4012184610Salfred			terminal_type = 0;
4013184610Salfred		}
4014184610Salfred		goto done;
4015184610Salfred	}
4016184610Salfred	/*
4017184610Salfred	 * if the unit is connected to just
4018184610Salfred	 * one input terminal, the
4019184610Salfred	 * class is UAC_INPUT:
4020184610Salfred	 */
4021184610Salfred	if (input[0] && (!input[1])) {
4022184610Salfred		mix->class = UAC_INPUT;
4023240609Shselasky		terminal_type =
4024240609Shselasky		    UGETW(input[0]->u.it_v1->wTerminalType);
4025184610Salfred		goto done;
4026184610Salfred	}
4027184610Salfred	/*
4028184610Salfred	 * Otherwise, the class is UAC_OUTPUT.
4029184610Salfred	 */
4030184610Salfred	mix->class = UAC_OUTPUT;
4031184610Salfreddone:
4032184610Salfred	return (terminal_type);
4033184610Salfred}
4034184610Salfred
4035240609Shselaskystatic uint16_t
4036240609Shselaskyuaudio20_mixer_determine_class(const struct uaudio_terminal_node *iot,
4037240609Shselasky    struct uaudio_mixer_node *mix)
4038240609Shselasky{
4039240609Shselasky	uint16_t terminal_type = 0x0000;
4040240609Shselasky	const struct uaudio_terminal_node *input[2];
4041240609Shselasky	const struct uaudio_terminal_node *output[2];
4042240609Shselasky
4043240609Shselasky	input[0] = uaudio_mixer_get_input(iot, 0);
4044240609Shselasky	input[1] = uaudio_mixer_get_input(iot, 1);
4045240609Shselasky
4046240609Shselasky	output[0] = uaudio_mixer_get_output(iot, 0);
4047240609Shselasky	output[1] = uaudio_mixer_get_output(iot, 1);
4048240609Shselasky
4049240609Shselasky	/*
4050240609Shselasky	 * check if there is only
4051240609Shselasky	 * one output terminal:
4052240609Shselasky	 */
4053240609Shselasky	if (output[0] && (!output[1]))
4054240609Shselasky		terminal_type = UGETW(output[0]->u.ot_v2->wTerminalType);
4055240609Shselasky	/*
4056240609Shselasky	 * If the only output terminal is USB,
4057240609Shselasky	 * the class is UAC_RECORD.
4058240609Shselasky	 */
4059240609Shselasky	if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
4060240609Shselasky
4061240609Shselasky		mix->class = UAC_RECORD;
4062240609Shselasky		if (input[0] && (!input[1])) {
4063240609Shselasky			terminal_type =
4064240609Shselasky			    UGETW(input[0]->u.it_v2->wTerminalType);
4065240609Shselasky		} else {
4066240609Shselasky			terminal_type = 0;
4067240609Shselasky		}
4068240609Shselasky		goto done;
4069240609Shselasky	}
4070240609Shselasky	/*
4071240609Shselasky	 * if the unit is connected to just
4072240609Shselasky	 * one input terminal, the
4073240609Shselasky	 * class is UAC_INPUT:
4074240609Shselasky	 */
4075240609Shselasky	if (input[0] && (!input[1])) {
4076240609Shselasky		mix->class = UAC_INPUT;
4077240609Shselasky		terminal_type =
4078240609Shselasky		    UGETW(input[0]->u.it_v2->wTerminalType);
4079240609Shselasky		goto done;
4080240609Shselasky	}
4081240609Shselasky	/*
4082240609Shselasky	 * Otherwise, the class is UAC_OUTPUT.
4083240609Shselasky	 */
4084240609Shselasky	mix->class = UAC_OUTPUT;
4085240609Shselaskydone:
4086240609Shselasky	return (terminal_type);
4087240609Shselasky}
4088240609Shselasky
4089184610Salfredstruct uaudio_tt_to_feature {
4090184610Salfred	uint16_t terminal_type;
4091184610Salfred	uint16_t feature;
4092184610Salfred};
4093184610Salfred
4094184610Salfredstatic const struct uaudio_tt_to_feature uaudio_tt_to_feature[] = {
4095184610Salfred
4096184610Salfred	{UAT_STREAM, SOUND_MIXER_PCM},
4097184610Salfred
4098184610Salfred	{UATI_MICROPHONE, SOUND_MIXER_MIC},
4099184610Salfred	{UATI_DESKMICROPHONE, SOUND_MIXER_MIC},
4100184610Salfred	{UATI_PERSONALMICROPHONE, SOUND_MIXER_MIC},
4101184610Salfred	{UATI_OMNIMICROPHONE, SOUND_MIXER_MIC},
4102184610Salfred	{UATI_MICROPHONEARRAY, SOUND_MIXER_MIC},
4103184610Salfred	{UATI_PROCMICROPHONEARR, SOUND_MIXER_MIC},
4104184610Salfred
4105184610Salfred	{UATO_SPEAKER, SOUND_MIXER_SPEAKER},
4106184610Salfred	{UATO_DESKTOPSPEAKER, SOUND_MIXER_SPEAKER},
4107184610Salfred	{UATO_ROOMSPEAKER, SOUND_MIXER_SPEAKER},
4108184610Salfred	{UATO_COMMSPEAKER, SOUND_MIXER_SPEAKER},
4109184610Salfred
4110184610Salfred	{UATE_ANALOGCONN, SOUND_MIXER_LINE},
4111184610Salfred	{UATE_LINECONN, SOUND_MIXER_LINE},
4112184610Salfred	{UATE_LEGACYCONN, SOUND_MIXER_LINE},
4113184610Salfred
4114184610Salfred	{UATE_DIGITALAUIFC, SOUND_MIXER_ALTPCM},
4115184610Salfred	{UATE_SPDIF, SOUND_MIXER_ALTPCM},
4116184610Salfred	{UATE_1394DA, SOUND_MIXER_ALTPCM},
4117184610Salfred	{UATE_1394DV, SOUND_MIXER_ALTPCM},
4118184610Salfred
4119184610Salfred	{UATF_CDPLAYER, SOUND_MIXER_CD},
4120184610Salfred
4121184610Salfred	{UATF_SYNTHESIZER, SOUND_MIXER_SYNTH},
4122184610Salfred
4123184610Salfred	{UATF_VIDEODISCAUDIO, SOUND_MIXER_VIDEO},
4124184610Salfred	{UATF_DVDAUDIO, SOUND_MIXER_VIDEO},
4125184610Salfred	{UATF_TVTUNERAUDIO, SOUND_MIXER_VIDEO},
4126184610Salfred
4127184610Salfred	/* telephony terminal types */
4128184610Salfred	{UATT_UNDEFINED, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
4129184610Salfred	{UATT_PHONELINE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
4130184610Salfred	{UATT_TELEPHONE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
4131184610Salfred	{UATT_DOWNLINEPHONE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
4132184610Salfred
4133184610Salfred	{UATF_RADIORECV, SOUND_MIXER_RADIO},
4134184610Salfred	{UATF_RADIOXMIT, SOUND_MIXER_RADIO},
4135184610Salfred
4136184610Salfred	{UAT_UNDEFINED, SOUND_MIXER_VOLUME},
4137184610Salfred	{UAT_VENDOR, SOUND_MIXER_VOLUME},
4138184610Salfred	{UATI_UNDEFINED, SOUND_MIXER_VOLUME},
4139184610Salfred
4140184610Salfred	/* output terminal types */
4141184610Salfred	{UATO_UNDEFINED, SOUND_MIXER_VOLUME},
4142184610Salfred	{UATO_DISPLAYAUDIO, SOUND_MIXER_VOLUME},
4143184610Salfred	{UATO_SUBWOOFER, SOUND_MIXER_VOLUME},
4144184610Salfred	{UATO_HEADPHONES, SOUND_MIXER_VOLUME},
4145184610Salfred
4146184610Salfred	/* bidir terminal types */
4147184610Salfred	{UATB_UNDEFINED, SOUND_MIXER_VOLUME},
4148184610Salfred	{UATB_HANDSET, SOUND_MIXER_VOLUME},
4149184610Salfred	{UATB_HEADSET, SOUND_MIXER_VOLUME},
4150184610Salfred	{UATB_SPEAKERPHONE, SOUND_MIXER_VOLUME},
4151184610Salfred	{UATB_SPEAKERPHONEESUP, SOUND_MIXER_VOLUME},
4152184610Salfred	{UATB_SPEAKERPHONEECANC, SOUND_MIXER_VOLUME},
4153184610Salfred
4154184610Salfred	/* external terminal types */
4155184610Salfred	{UATE_UNDEFINED, SOUND_MIXER_VOLUME},
4156184610Salfred
4157184610Salfred	/* embedded function terminal types */
4158184610Salfred	{UATF_UNDEFINED, SOUND_MIXER_VOLUME},
4159184610Salfred	{UATF_CALIBNOISE, SOUND_MIXER_VOLUME},
4160184610Salfred	{UATF_EQUNOISE, SOUND_MIXER_VOLUME},
4161184610Salfred	{UATF_DAT, SOUND_MIXER_VOLUME},
4162184610Salfred	{UATF_DCC, SOUND_MIXER_VOLUME},
4163184610Salfred	{UATF_MINIDISK, SOUND_MIXER_VOLUME},
4164184610Salfred	{UATF_ANALOGTAPE, SOUND_MIXER_VOLUME},
4165184610Salfred	{UATF_PHONOGRAPH, SOUND_MIXER_VOLUME},
4166184610Salfred	{UATF_VCRAUDIO, SOUND_MIXER_VOLUME},
4167184610Salfred	{UATF_SATELLITE, SOUND_MIXER_VOLUME},
4168184610Salfred	{UATF_CABLETUNER, SOUND_MIXER_VOLUME},
4169184610Salfred	{UATF_DSS, SOUND_MIXER_VOLUME},
4170184610Salfred	{UATF_MULTITRACK, SOUND_MIXER_VOLUME},
4171184610Salfred	{0xffff, SOUND_MIXER_VOLUME},
4172184610Salfred
4173184610Salfred	/* default */
4174184610Salfred	{0x0000, SOUND_MIXER_VOLUME},
4175184610Salfred};
4176184610Salfred
4177184610Salfredstatic uint16_t
4178184610Salfreduaudio_mixer_feature_name(const struct uaudio_terminal_node *iot,
4179184610Salfred    struct uaudio_mixer_node *mix)
4180184610Salfred{
4181184610Salfred	const struct uaudio_tt_to_feature *uat = uaudio_tt_to_feature;
4182184610Salfred	uint16_t terminal_type = uaudio_mixer_determine_class(iot, mix);
4183184610Salfred
4184184610Salfred	if ((mix->class == UAC_RECORD) && (terminal_type == 0)) {
4185184610Salfred		return (SOUND_MIXER_IMIX);
4186184610Salfred	}
4187184610Salfred	while (uat->terminal_type) {
4188184610Salfred		if (uat->terminal_type == terminal_type) {
4189184610Salfred			break;
4190184610Salfred		}
4191184610Salfred		uat++;
4192184610Salfred	}
4193184610Salfred
4194240609Shselasky	DPRINTF("terminal_type=0x%04x -> %d\n",
4195184610Salfred	    terminal_type, uat->feature);
4196184610Salfred
4197184610Salfred	return (uat->feature);
4198184610Salfred}
4199184610Salfred
4200240609Shselaskystatic uint16_t
4201240609Shselaskyuaudio20_mixer_feature_name(const struct uaudio_terminal_node *iot,
4202240609Shselasky    struct uaudio_mixer_node *mix)
4203240609Shselasky{
4204240609Shselasky	const struct uaudio_tt_to_feature *uat;
4205240609Shselasky	uint16_t terminal_type = uaudio20_mixer_determine_class(iot, mix);
4206240609Shselasky
4207240609Shselasky	if ((mix->class == UAC_RECORD) && (terminal_type == 0))
4208240609Shselasky		return (SOUND_MIXER_IMIX);
4209240609Shselasky
4210240609Shselasky	for (uat = uaudio_tt_to_feature; uat->terminal_type != 0; uat++) {
4211240609Shselasky		if (uat->terminal_type == terminal_type)
4212240609Shselasky			break;
4213240609Shselasky	}
4214240609Shselasky
4215240609Shselasky	DPRINTF("terminal_type=0x%04x -> %d\n",
4216240609Shselasky	    terminal_type, uat->feature);
4217240609Shselasky
4218240609Shselasky	return (uat->feature);
4219240609Shselasky}
4220240609Shselasky
4221233774Shselaskystatic const struct uaudio_terminal_node *
4222233774Shselaskyuaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t i)
4223184610Salfred{
4224184610Salfred	struct uaudio_terminal_node *root = iot->root;
4225184610Salfred	uint8_t n;
4226184610Salfred
4227184610Salfred	n = iot->usr.id_max;
4228184610Salfred	do {
4229184610Salfred		if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) {
4230233774Shselasky			if (!i--)
4231184610Salfred				return (root + n);
4232184610Salfred		}
4233184610Salfred	} while (n--);
4234184610Salfred
4235184610Salfred	return (NULL);
4236184610Salfred}
4237184610Salfred
4238233774Shselaskystatic const struct uaudio_terminal_node *
4239233774Shselaskyuaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t i)
4240184610Salfred{
4241184610Salfred	struct uaudio_terminal_node *root = iot->root;
4242184610Salfred	uint8_t n;
4243184610Salfred
4244184610Salfred	n = iot->usr.id_max;
4245184610Salfred	do {
4246184610Salfred		if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) {
4247233774Shselasky			if (!i--)
4248184610Salfred				return (root + n);
4249184610Salfred		}
4250184610Salfred	} while (n--);
4251184610Salfred
4252184610Salfred	return (NULL);
4253184610Salfred}
4254184610Salfred
4255184610Salfredstatic void
4256184610Salfreduaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
4257184610Salfred    const uint8_t *p_id, uint8_t n_id,
4258184610Salfred    struct uaudio_search_result *info)
4259184610Salfred{
4260184610Salfred	struct uaudio_terminal_node *iot;
4261184610Salfred	uint8_t n;
4262184610Salfred	uint8_t i;
4263240609Shselasky	uint8_t is_last;
4264184610Salfred
4265240609Shselaskytop:
4266184610Salfred	for (n = 0; n < n_id; n++) {
4267184610Salfred
4268184610Salfred		i = p_id[n];
4269184610Salfred
4270240609Shselasky		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
4271184610Salfred			DPRINTF("avoided going into a circle at id=%d!\n", i);
4272240609Shselasky			return;
4273184610Salfred		}
4274184610Salfred
4275240609Shselasky		info->recurse_level++;
4276240609Shselasky
4277184610Salfred		iot = (root + i);
4278184610Salfred
4279240609Shselasky		if (iot->u.desc == NULL)
4280184610Salfred			continue;
4281240609Shselasky
4282240609Shselasky		is_last = ((n + 1) == n_id);
4283240609Shselasky
4284184610Salfred		switch (iot->u.desc->bDescriptorSubtype) {
4285184610Salfred		case UDESCSUB_AC_INPUT:
4286184610Salfred			info->bit_input[i / 8] |= (1 << (i % 8));
4287184610Salfred			break;
4288184610Salfred
4289184610Salfred		case UDESCSUB_AC_FEATURE:
4290240609Shselasky			if (is_last) {
4291240609Shselasky				p_id = &iot->u.fu_v1->bSourceId;
4292240609Shselasky				n_id = 1;
4293240609Shselasky				goto top;
4294240609Shselasky			}
4295240609Shselasky			uaudio_mixer_find_inputs_sub(
4296240609Shselasky			    root, &iot->u.fu_v1->bSourceId, 1, info);
4297184610Salfred			break;
4298184610Salfred
4299184610Salfred		case UDESCSUB_AC_OUTPUT:
4300240609Shselasky			if (is_last) {
4301240609Shselasky				p_id = &iot->u.ot_v1->bSourceId;
4302240609Shselasky				n_id = 1;
4303240609Shselasky				goto top;
4304240609Shselasky			}
4305240609Shselasky			uaudio_mixer_find_inputs_sub(
4306240609Shselasky			    root, &iot->u.ot_v1->bSourceId, 1, info);
4307184610Salfred			break;
4308184610Salfred
4309184610Salfred		case UDESCSUB_AC_MIXER:
4310240609Shselasky			if (is_last) {
4311240609Shselasky				p_id = iot->u.mu_v1->baSourceId;
4312240609Shselasky				n_id = iot->u.mu_v1->bNrInPins;
4313240609Shselasky				goto top;
4314240609Shselasky			}
4315240609Shselasky			uaudio_mixer_find_inputs_sub(
4316240609Shselasky			    root, iot->u.mu_v1->baSourceId,
4317240609Shselasky			    iot->u.mu_v1->bNrInPins, info);
4318184610Salfred			break;
4319184610Salfred
4320184610Salfred		case UDESCSUB_AC_SELECTOR:
4321240609Shselasky			if (is_last) {
4322240609Shselasky				p_id = iot->u.su_v1->baSourceId;
4323240609Shselasky				n_id = iot->u.su_v1->bNrInPins;
4324240609Shselasky				goto top;
4325240609Shselasky			}
4326240609Shselasky			uaudio_mixer_find_inputs_sub(
4327240609Shselasky			    root, iot->u.su_v1->baSourceId,
4328240609Shselasky			    iot->u.su_v1->bNrInPins, info);
4329184610Salfred			break;
4330184610Salfred
4331184610Salfred		case UDESCSUB_AC_PROCESSING:
4332240609Shselasky			if (is_last) {
4333240609Shselasky				p_id = iot->u.pu_v1->baSourceId;
4334240609Shselasky				n_id = iot->u.pu_v1->bNrInPins;
4335240609Shselasky				goto top;
4336240609Shselasky			}
4337240609Shselasky			uaudio_mixer_find_inputs_sub(
4338240609Shselasky			    root, iot->u.pu_v1->baSourceId,
4339240609Shselasky			    iot->u.pu_v1->bNrInPins, info);
4340184610Salfred			break;
4341184610Salfred
4342184610Salfred		case UDESCSUB_AC_EXTENSION:
4343240609Shselasky			if (is_last) {
4344240609Shselasky				p_id = iot->u.eu_v1->baSourceId;
4345240609Shselasky				n_id = iot->u.eu_v1->bNrInPins;
4346240609Shselasky				goto top;
4347240609Shselasky			}
4348240609Shselasky			uaudio_mixer_find_inputs_sub(
4349240609Shselasky			    root, iot->u.eu_v1->baSourceId,
4350240609Shselasky			    iot->u.eu_v1->bNrInPins, info);
4351184610Salfred			break;
4352184610Salfred
4353184610Salfred		default:
4354184610Salfred			break;
4355184610Salfred		}
4356184610Salfred	}
4357184610Salfred}
4358184610Salfred
4359184610Salfredstatic void
4360240609Shselaskyuaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
4361240609Shselasky    const uint8_t *p_id, uint8_t n_id,
4362240609Shselasky    struct uaudio_search_result *info)
4363240609Shselasky{
4364240609Shselasky	struct uaudio_terminal_node *iot;
4365240609Shselasky	uint8_t n;
4366240609Shselasky	uint8_t i;
4367240609Shselasky	uint8_t is_last;
4368240609Shselasky
4369240609Shselaskytop:
4370240609Shselasky	for (n = 0; n < n_id; n++) {
4371240609Shselasky
4372240609Shselasky		i = p_id[n];
4373240609Shselasky
4374240609Shselasky		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
4375240609Shselasky			DPRINTF("avoided going into a circle at id=%d!\n", i);
4376240609Shselasky			return;
4377240609Shselasky		}
4378240609Shselasky
4379240609Shselasky		info->recurse_level++;
4380240609Shselasky
4381240609Shselasky		iot = (root + i);
4382240609Shselasky
4383240609Shselasky		if (iot->u.desc == NULL)
4384240609Shselasky			continue;
4385240609Shselasky
4386240609Shselasky		is_last = ((n + 1) == n_id);
4387240609Shselasky
4388240609Shselasky		switch (iot->u.desc->bDescriptorSubtype) {
4389240609Shselasky		case UDESCSUB_AC_INPUT:
4390240609Shselasky			info->bit_input[i / 8] |= (1 << (i % 8));
4391240609Shselasky			break;
4392240609Shselasky
4393240609Shselasky		case UDESCSUB_AC_OUTPUT:
4394240609Shselasky			if (is_last) {
4395240609Shselasky				p_id = &iot->u.ot_v2->bSourceId;
4396240609Shselasky				n_id = 1;
4397240609Shselasky				goto top;
4398240609Shselasky			}
4399240609Shselasky			uaudio20_mixer_find_inputs_sub(
4400240609Shselasky			    root, &iot->u.ot_v2->bSourceId, 1, info);
4401240609Shselasky			break;
4402240609Shselasky
4403240609Shselasky		case UDESCSUB_AC_MIXER:
4404240609Shselasky			if (is_last) {
4405240609Shselasky				p_id = iot->u.mu_v2->baSourceId;
4406240609Shselasky				n_id = iot->u.mu_v2->bNrInPins;
4407240609Shselasky				goto top;
4408240609Shselasky			}
4409240609Shselasky			uaudio20_mixer_find_inputs_sub(
4410240609Shselasky			    root, iot->u.mu_v2->baSourceId,
4411240609Shselasky			    iot->u.mu_v2->bNrInPins, info);
4412240609Shselasky			break;
4413240609Shselasky
4414240609Shselasky		case UDESCSUB_AC_SELECTOR:
4415240609Shselasky			if (is_last) {
4416240609Shselasky				p_id = iot->u.su_v2->baSourceId;
4417240609Shselasky				n_id = iot->u.su_v2->bNrInPins;
4418240609Shselasky				goto top;
4419240609Shselasky			}
4420240609Shselasky			uaudio20_mixer_find_inputs_sub(
4421240609Shselasky			    root, iot->u.su_v2->baSourceId,
4422240609Shselasky			    iot->u.su_v2->bNrInPins, info);
4423240609Shselasky			break;
4424240609Shselasky
4425240609Shselasky		case UDESCSUB_AC_SAMPLE_RT:
4426240609Shselasky			if (is_last) {
4427240609Shselasky				p_id = &iot->u.ru_v2->bSourceId;
4428240609Shselasky				n_id = 1;
4429240609Shselasky				goto top;
4430240609Shselasky			}
4431240609Shselasky			uaudio20_mixer_find_inputs_sub(
4432240609Shselasky			    root, &iot->u.ru_v2->bSourceId,
4433240609Shselasky			    1, info);
4434240609Shselasky			break;
4435240609Shselasky
4436240609Shselasky		case UDESCSUB_AC_EFFECT:
4437240609Shselasky			if (is_last) {
4438240609Shselasky				p_id = &iot->u.ef_v2->bSourceId;
4439240609Shselasky				n_id = 1;
4440240609Shselasky				goto top;
4441240609Shselasky			}
4442240609Shselasky			uaudio20_mixer_find_inputs_sub(
4443240609Shselasky			    root, &iot->u.ef_v2->bSourceId,
4444240609Shselasky			    1, info);
4445240609Shselasky			break;
4446240609Shselasky
4447240609Shselasky		case UDESCSUB_AC_FEATURE:
4448240609Shselasky			if (is_last) {
4449240609Shselasky				p_id = &iot->u.fu_v2->bSourceId;
4450240609Shselasky				n_id = 1;
4451240609Shselasky				goto top;
4452240609Shselasky			}
4453240609Shselasky			uaudio20_mixer_find_inputs_sub(
4454240609Shselasky			    root, &iot->u.fu_v2->bSourceId, 1, info);
4455240609Shselasky			break;
4456240609Shselasky
4457240609Shselasky		case UDESCSUB_AC_PROCESSING_V2:
4458240609Shselasky			if (is_last) {
4459240609Shselasky				p_id = iot->u.pu_v2->baSourceId;
4460240609Shselasky				n_id = iot->u.pu_v2->bNrInPins;
4461240609Shselasky				goto top;
4462240609Shselasky			}
4463240609Shselasky			uaudio20_mixer_find_inputs_sub(
4464240609Shselasky			    root, iot->u.pu_v2->baSourceId,
4465240609Shselasky			    iot->u.pu_v2->bNrInPins, info);
4466240609Shselasky			break;
4467240609Shselasky
4468240609Shselasky		case UDESCSUB_AC_EXTENSION_V2:
4469240609Shselasky			if (is_last) {
4470240609Shselasky				p_id = iot->u.eu_v2->baSourceId;
4471240609Shselasky				n_id = iot->u.eu_v2->bNrInPins;
4472240609Shselasky				goto top;
4473240609Shselasky			}
4474240609Shselasky			uaudio20_mixer_find_inputs_sub(
4475240609Shselasky			    root, iot->u.eu_v2->baSourceId,
4476240609Shselasky			    iot->u.eu_v2->bNrInPins, info);
4477240609Shselasky			break;
4478240609Shselasky		default:
4479240609Shselasky			break;
4480240609Shselasky		}
4481240609Shselasky	}
4482240609Shselasky}
4483240609Shselasky
4484240609Shselaskystatic void
4485240609Shselaskyuaudio20_mixer_find_clocks_sub(struct uaudio_terminal_node *root,
4486240609Shselasky    const uint8_t *p_id, uint8_t n_id,
4487240609Shselasky    struct uaudio_search_result *info)
4488240609Shselasky{
4489240609Shselasky	struct uaudio_terminal_node *iot;
4490240609Shselasky	uint8_t n;
4491240609Shselasky	uint8_t i;
4492240609Shselasky	uint8_t is_last;
4493240609Shselasky	uint8_t id;
4494240609Shselasky
4495240609Shselaskytop:
4496240609Shselasky	for (n = 0; n < n_id; n++) {
4497240609Shselasky
4498240609Shselasky		i = p_id[n];
4499240609Shselasky
4500240609Shselasky		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
4501240609Shselasky			DPRINTF("avoided going into a circle at id=%d!\n", i);
4502240609Shselasky			return;
4503240609Shselasky		}
4504240609Shselasky
4505240609Shselasky		info->recurse_level++;
4506240609Shselasky
4507240609Shselasky		iot = (root + i);
4508240609Shselasky
4509240609Shselasky		if (iot->u.desc == NULL)
4510240609Shselasky			continue;
4511240609Shselasky
4512240609Shselasky		is_last = ((n + 1) == n_id);
4513240609Shselasky
4514240609Shselasky		switch (iot->u.desc->bDescriptorSubtype) {
4515240609Shselasky		case UDESCSUB_AC_INPUT:
4516240609Shselasky			info->is_input = 1;
4517240609Shselasky			if (is_last) {
4518240609Shselasky				p_id = &iot->u.it_v2->bCSourceId;
4519240609Shselasky				n_id = 1;
4520240609Shselasky				goto top;
4521240609Shselasky			}
4522240609Shselasky			uaudio20_mixer_find_clocks_sub(root,
4523240609Shselasky			    &iot->u.it_v2->bCSourceId, 1, info);
4524240609Shselasky			break;
4525240609Shselasky
4526240609Shselasky		case UDESCSUB_AC_OUTPUT:
4527240609Shselasky			info->is_input = 0;
4528240609Shselasky			if (is_last) {
4529240609Shselasky				p_id = &iot->u.ot_v2->bCSourceId;
4530240609Shselasky				n_id = 1;
4531240609Shselasky				goto top;
4532240609Shselasky			}
4533240609Shselasky			uaudio20_mixer_find_clocks_sub(root,
4534240609Shselasky			    &iot->u.ot_v2->bCSourceId, 1, info);
4535240609Shselasky			break;
4536240609Shselasky
4537240609Shselasky		case UDESCSUB_AC_CLOCK_SEL:
4538240609Shselasky			if (is_last) {
4539240609Shselasky				p_id = iot->u.csel_v2->baCSourceId;
4540240609Shselasky				n_id = iot->u.csel_v2->bNrInPins;
4541240609Shselasky				goto top;
4542240609Shselasky			}
4543240609Shselasky			uaudio20_mixer_find_clocks_sub(root,
4544240609Shselasky			    iot->u.csel_v2->baCSourceId,
4545240609Shselasky			    iot->u.csel_v2->bNrInPins, info);
4546240609Shselasky			break;
4547240609Shselasky
4548240609Shselasky		case UDESCSUB_AC_CLOCK_MUL:
4549240609Shselasky			if (is_last) {
4550240609Shselasky				p_id = &iot->u.cmul_v2->bCSourceId;
4551240609Shselasky				n_id = 1;
4552240609Shselasky				goto top;
4553240609Shselasky			}
4554240609Shselasky			uaudio20_mixer_find_clocks_sub(root,
4555240609Shselasky			    &iot->u.cmul_v2->bCSourceId,
4556240609Shselasky			    1, info);
4557240609Shselasky			break;
4558240609Shselasky
4559240609Shselasky		case UDESCSUB_AC_CLOCK_SRC:
4560240609Shselasky
4561240609Shselasky			id = iot->u.csrc_v2->bClockId;
4562240609Shselasky
4563240609Shselasky			switch (info->is_input) {
4564240609Shselasky			case 0:
4565240609Shselasky				info->bit_output[id / 8] |= (1 << (id % 8));
4566240609Shselasky				break;
4567240609Shselasky			case 1:
4568240609Shselasky				info->bit_input[id / 8] |= (1 << (id % 8));
4569240609Shselasky				break;
4570240609Shselasky			default:
4571240609Shselasky				break;
4572240609Shselasky			}
4573240609Shselasky			break;
4574240609Shselasky
4575240609Shselasky		default:
4576240609Shselasky			break;
4577240609Shselasky		}
4578240609Shselasky	}
4579240609Shselasky}
4580240609Shselasky
4581240609Shselaskystatic void
4582184610Salfreduaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id,
4583184610Salfred    uint8_t n_id, struct uaudio_search_result *info)
4584184610Salfred{
4585184610Salfred	struct uaudio_terminal_node *iot = (root + id);
4586184610Salfred	uint8_t j;
4587184610Salfred
4588184610Salfred	j = n_id;
4589184610Salfred	do {
4590184610Salfred		if ((j != id) && ((root + j)->u.desc) &&
4591184610Salfred		    ((root + j)->u.desc->bDescriptorSubtype == UDESCSUB_AC_OUTPUT)) {
4592184610Salfred
4593184610Salfred			/*
4594184610Salfred			 * "j" (output) <--- virtual wire <--- "id" (input)
4595184610Salfred			 *
4596184610Salfred			 * if "j" has "id" on the input, then "id" have "j" on
4597184610Salfred			 * the output, because they are connected:
4598184610Salfred			 */
4599184610Salfred			if ((root + j)->usr.bit_input[id / 8] & (1 << (id % 8))) {
4600184610Salfred				iot->usr.bit_output[j / 8] |= (1 << (j % 8));
4601184610Salfred			}
4602184610Salfred		}
4603184610Salfred	} while (j--);
4604184610Salfred}
4605184610Salfred
4606184610Salfredstatic void
4607240609Shselaskyuaudio_mixer_fill_info(struct uaudio_softc *sc,
4608240609Shselasky    struct usb_device *udev, void *desc)
4609184610Salfred{
4610203678Sbrucec	const struct usb_audio_control_descriptor *acdp;
4611194228Sthompsa	struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
4612192984Sthompsa	const struct usb_descriptor *dp;
4613203678Sbrucec	const struct usb_audio_unit *au;
4614184610Salfred	struct uaudio_terminal_node *iot = NULL;
4615184610Salfred	uint16_t wTotalLen;
4616184610Salfred	uint8_t ID_max = 0;		/* inclusive */
4617184610Salfred	uint8_t i;
4618184610Salfred
4619194228Sthompsa	desc = usb_desc_foreach(cd, desc);
4620184610Salfred
4621184610Salfred	if (desc == NULL) {
4622184610Salfred		DPRINTF("no Audio Control header\n");
4623184610Salfred		goto done;
4624184610Salfred	}
4625184610Salfred	acdp = desc;
4626184610Salfred
4627184610Salfred	if ((acdp->bLength < sizeof(*acdp)) ||
4628184610Salfred	    (acdp->bDescriptorType != UDESC_CS_INTERFACE) ||
4629184610Salfred	    (acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)) {
4630184610Salfred		DPRINTF("invalid Audio Control header\n");
4631184610Salfred		goto done;
4632184610Salfred	}
4633186730Salfred	/* "wTotalLen" is allowed to be corrupt */
4634186730Salfred	wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength;
4635186730Salfred
4636186730Salfred	/* get USB audio revision */
4637184610Salfred	sc->sc_audio_rev = UGETW(acdp->bcdADC);
4638184610Salfred
4639184610Salfred	DPRINTFN(3, "found AC header, vers=%03x, len=%d\n",
4640184610Salfred	    sc->sc_audio_rev, wTotalLen);
4641184610Salfred
4642184610Salfred	iot = malloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP,
4643184610Salfred	    M_WAITOK | M_ZERO);
4644184610Salfred
4645184610Salfred	if (iot == NULL) {
4646184610Salfred		DPRINTF("no memory!\n");
4647184610Salfred		goto done;
4648184610Salfred	}
4649194228Sthompsa	while ((desc = usb_desc_foreach(cd, desc))) {
4650184610Salfred
4651184610Salfred		dp = desc;
4652184610Salfred
4653184610Salfred		if (dp->bLength > wTotalLen) {
4654184610Salfred			break;
4655184610Salfred		} else {
4656184610Salfred			wTotalLen -= dp->bLength;
4657184610Salfred		}
4658184610Salfred
4659240609Shselasky		if (sc->sc_audio_rev >= UAUDIO_VERSION_30)
4660240609Shselasky			au = NULL;
4661240609Shselasky		else if (sc->sc_audio_rev >= UAUDIO_VERSION_20)
4662240609Shselasky			au = uaudio20_mixer_verify_desc(dp, 0);
4663240609Shselasky		else
4664240609Shselasky			au = uaudio_mixer_verify_desc(dp, 0);
4665184610Salfred
4666184610Salfred		if (au) {
4667184610Salfred			iot[au->bUnitId].u.desc = (const void *)au;
4668240609Shselasky			if (au->bUnitId > ID_max)
4669184610Salfred				ID_max = au->bUnitId;
4670184610Salfred		}
4671184610Salfred	}
4672184610Salfred
4673184610Salfred	DPRINTF("Maximum ID=%d\n", ID_max);
4674184610Salfred
4675184610Salfred	/*
4676184610Salfred	 * determine sourcing inputs for
4677184610Salfred	 * all nodes in the tree:
4678184610Salfred	 */
4679184610Salfred	i = ID_max;
4680184610Salfred	do {
4681240609Shselasky		if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
4682240609Shselasky			/* FALLTHROUGH */
4683240609Shselasky		} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
4684240609Shselasky			uaudio20_mixer_find_inputs_sub(iot,
4685240609Shselasky			    &i, 1, &((iot + i)->usr));
4686240609Shselasky
4687240609Shselasky			sc->sc_mixer_clocks.is_input = 255;
4688240609Shselasky			sc->sc_mixer_clocks.recurse_level = 0;
4689240609Shselasky
4690240609Shselasky			uaudio20_mixer_find_clocks_sub(iot,
4691240609Shselasky			    &i, 1, &sc->sc_mixer_clocks);
4692240609Shselasky		} else {
4693240609Shselasky			uaudio_mixer_find_inputs_sub(iot,
4694240609Shselasky			    &i, 1, &((iot + i)->usr));
4695240609Shselasky		}
4696184610Salfred	} while (i--);
4697184610Salfred
4698184610Salfred	/*
4699184610Salfred	 * determine outputs for
4700184610Salfred	 * all nodes in the tree:
4701184610Salfred	 */
4702184610Salfred	i = ID_max;
4703184610Salfred	do {
4704240609Shselasky		uaudio_mixer_find_outputs_sub(iot,
4705240609Shselasky		    i, ID_max, &((iot + i)->usr));
4706184610Salfred	} while (i--);
4707184610Salfred
4708184610Salfred	/* set "id_max" and "root" */
4709184610Salfred
4710184610Salfred	i = ID_max;
4711184610Salfred	do {
4712184610Salfred		(iot + i)->usr.id_max = ID_max;
4713184610Salfred		(iot + i)->root = iot;
4714184610Salfred	} while (i--);
4715184610Salfred
4716240609Shselasky	/*
4717240609Shselasky	 * Scan the config to create a linked list of "mixer" nodes:
4718240609Shselasky	 */
4719240609Shselasky
4720184610Salfred	i = ID_max;
4721184610Salfred	do {
4722240609Shselasky		dp = iot[i].u.desc;
4723184610Salfred
4724240609Shselasky		if (dp == NULL)
4725184610Salfred			continue;
4726184610Salfred
4727240609Shselasky		DPRINTFN(11, "id=%d subtype=%d\n",
4728240609Shselasky		    i, dp->bDescriptorSubtype);
4729184610Salfred
4730240609Shselasky		if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
4731240609Shselasky			continue;
4732240609Shselasky		} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
4733184610Salfred
4734240609Shselasky			switch (dp->bDescriptorSubtype) {
4735240609Shselasky			case UDESCSUB_AC_HEADER:
4736240609Shselasky				DPRINTF("unexpected AC header\n");
4737240609Shselasky				break;
4738184610Salfred
4739240609Shselasky			case UDESCSUB_AC_INPUT:
4740240609Shselasky			case UDESCSUB_AC_OUTPUT:
4741240609Shselasky			case UDESCSUB_AC_PROCESSING_V2:
4742240609Shselasky			case UDESCSUB_AC_EXTENSION_V2:
4743240609Shselasky			case UDESCSUB_AC_EFFECT:
4744240609Shselasky			case UDESCSUB_AC_CLOCK_SRC:
4745240609Shselasky			case UDESCSUB_AC_CLOCK_SEL:
4746240609Shselasky			case UDESCSUB_AC_CLOCK_MUL:
4747240609Shselasky			case UDESCSUB_AC_SAMPLE_RT:
4748240609Shselasky				break;
4749184610Salfred
4750240609Shselasky			case UDESCSUB_AC_MIXER:
4751240609Shselasky				uaudio20_mixer_add_mixer(sc, iot, i);
4752240609Shselasky				break;
4753184610Salfred
4754240609Shselasky			case UDESCSUB_AC_SELECTOR:
4755240609Shselasky				uaudio20_mixer_add_selector(sc, iot, i);
4756240609Shselasky				break;
4757184610Salfred
4758240609Shselasky			case UDESCSUB_AC_FEATURE:
4759240609Shselasky				uaudio20_mixer_add_feature(sc, iot, i);
4760240609Shselasky				break;
4761184610Salfred
4762240609Shselasky			default:
4763240609Shselasky				DPRINTF("bad AC desc subtype=0x%02x\n",
4764240609Shselasky				    dp->bDescriptorSubtype);
4765240609Shselasky				break;
4766184610Salfred			}
4767184610Salfred			continue;
4768184610Salfred		}
4769184610Salfred
4770184610Salfred		switch (dp->bDescriptorSubtype) {
4771184610Salfred		case UDESCSUB_AC_HEADER:
4772184610Salfred			DPRINTF("unexpected AC header\n");
4773184610Salfred			break;
4774184610Salfred
4775184610Salfred		case UDESCSUB_AC_INPUT:
4776184610Salfred		case UDESCSUB_AC_OUTPUT:
4777184610Salfred			break;
4778184610Salfred
4779184610Salfred		case UDESCSUB_AC_MIXER:
4780184610Salfred			uaudio_mixer_add_mixer(sc, iot, i);
4781184610Salfred			break;
4782184610Salfred
4783184610Salfred		case UDESCSUB_AC_SELECTOR:
4784184610Salfred			uaudio_mixer_add_selector(sc, iot, i);
4785184610Salfred			break;
4786184610Salfred
4787184610Salfred		case UDESCSUB_AC_FEATURE:
4788184610Salfred			uaudio_mixer_add_feature(sc, iot, i);
4789184610Salfred			break;
4790184610Salfred
4791184610Salfred		case UDESCSUB_AC_PROCESSING:
4792184610Salfred			uaudio_mixer_add_processing(sc, iot, i);
4793184610Salfred			break;
4794184610Salfred
4795184610Salfred		case UDESCSUB_AC_EXTENSION:
4796184610Salfred			uaudio_mixer_add_extension(sc, iot, i);
4797184610Salfred			break;
4798184610Salfred
4799184610Salfred		default:
4800184610Salfred			DPRINTF("bad AC desc subtype=0x%02x\n",
4801184610Salfred			    dp->bDescriptorSubtype);
4802184610Salfred			break;
4803184610Salfred		}
4804184610Salfred
4805184610Salfred	} while (i--);
4806184610Salfred
4807184610Salfreddone:
4808240609Shselasky	free(iot, M_TEMP);
4809184610Salfred}
4810184610Salfred
4811240609Shselaskystatic int
4812240609Shselaskyuaudio_mixer_get(struct usb_device *udev, uint16_t audio_rev,
4813240609Shselasky    uint8_t what, struct uaudio_mixer_node *mc)
4814184610Salfred{
4815192984Sthompsa	struct usb_device_request req;
4816240609Shselasky	int val;
4817240609Shselasky	uint8_t data[2 + (2 * 3)];
4818193045Sthompsa	usb_error_t err;
4819184610Salfred
4820240609Shselasky	if (mc->wValue[0] == -1)
4821184610Salfred		return (0);
4822240609Shselasky
4823240609Shselasky	if (audio_rev >= UAUDIO_VERSION_30)
4824240609Shselasky		return (0);
4825240609Shselasky	else if (audio_rev >= UAUDIO_VERSION_20) {
4826240609Shselasky		if (what == GET_CUR) {
4827240609Shselasky			req.bRequest = UA20_CS_CUR;
4828240609Shselasky			USETW(req.wLength, 2);
4829240609Shselasky		} else {
4830240609Shselasky			req.bRequest = UA20_CS_RANGE;
4831240609Shselasky			USETW(req.wLength, 8);
4832240609Shselasky		}
4833240609Shselasky	} else {
4834240609Shselasky		uint16_t len = MIX_SIZE(mc->type);
4835240609Shselasky
4836240609Shselasky		req.bRequest = what;
4837240609Shselasky		USETW(req.wLength, len);
4838184610Salfred	}
4839240609Shselasky
4840184610Salfred	req.bmRequestType = UT_READ_CLASS_INTERFACE;
4841184610Salfred	USETW(req.wValue, mc->wValue[0]);
4842184610Salfred	USETW(req.wIndex, mc->wIndex);
4843184610Salfred
4844240609Shselasky	memset(data, 0, sizeof(data));
4845240609Shselasky
4846196487Salfred	err = usbd_do_request(udev, NULL, &req, data);
4847184610Salfred	if (err) {
4848194228Sthompsa		DPRINTF("err=%s\n", usbd_errstr(err));
4849184610Salfred		return (0);
4850184610Salfred	}
4851240609Shselasky
4852240609Shselasky	if (audio_rev >= UAUDIO_VERSION_30) {
4853240609Shselasky		val = 0;
4854240609Shselasky	} else if (audio_rev >= UAUDIO_VERSION_20) {
4855240609Shselasky		switch (what) {
4856240609Shselasky		case GET_CUR:
4857240609Shselasky			val = (data[0] | (data[1] << 8));
4858240609Shselasky			break;
4859240609Shselasky		case GET_MIN:
4860240609Shselasky			val = (data[2] | (data[3] << 8));
4861240609Shselasky			break;
4862240609Shselasky		case GET_MAX:
4863240609Shselasky			val = (data[4] | (data[5] << 8));
4864240609Shselasky			break;
4865240609Shselasky		case GET_RES:
4866240609Shselasky			val = (data[6] | (data[7] << 8));
4867240609Shselasky			break;
4868240609Shselasky		default:
4869240609Shselasky			val = 0;
4870240609Shselasky			break;
4871240609Shselasky		}
4872240609Shselasky	} else {
4873240609Shselasky		val = (data[0] | (data[1] << 8));
4874184610Salfred	}
4875184610Salfred
4876240609Shselasky	if (what == GET_CUR || what == GET_MIN || what == GET_MAX)
4877240609Shselasky		val = uaudio_mixer_signext(mc->type, val);
4878240609Shselasky
4879184610Salfred	DPRINTFN(3, "val=%d\n", val);
4880184610Salfred
4881184610Salfred	return (val);
4882184610Salfred}
4883184610Salfred
4884184610Salfredstatic void
4885194677Sthompsauaudio_mixer_write_cfg_callback(struct usb_xfer *xfer, usb_error_t error)
4886184610Salfred{
4887192984Sthompsa	struct usb_device_request req;
4888194677Sthompsa	struct uaudio_softc *sc = usbd_xfer_softc(xfer);
4889184610Salfred	struct uaudio_mixer_node *mc = sc->sc_mixer_curr;
4890194677Sthompsa	struct usb_page_cache *pc;
4891184610Salfred	uint16_t len;
4892184610Salfred	uint8_t repeat = 1;
4893184610Salfred	uint8_t update;
4894184610Salfred	uint8_t chan;
4895184610Salfred	uint8_t buf[2];
4896184610Salfred
4897187165Sthompsa	DPRINTF("\n");
4898187165Sthompsa
4899184610Salfred	switch (USB_GET_STATE(xfer)) {
4900184610Salfred	case USB_ST_TRANSFERRED:
4901184610Salfredtr_transferred:
4902184610Salfred	case USB_ST_SETUP:
4903184610Salfredtr_setup:
4904184610Salfred
4905184610Salfred		if (mc == NULL) {
4906184610Salfred			mc = sc->sc_mixer_root;
4907184610Salfred			sc->sc_mixer_curr = mc;
4908184610Salfred			sc->sc_mixer_chan = 0;
4909184610Salfred			repeat = 0;
4910184610Salfred		}
4911184610Salfred		while (mc) {
4912184610Salfred			while (sc->sc_mixer_chan < mc->nchan) {
4913184610Salfred
4914184610Salfred				chan = sc->sc_mixer_chan;
4915184610Salfred
4916184610Salfred				sc->sc_mixer_chan++;
4917184610Salfred
4918184610Salfred				update = ((mc->update[chan / 8] & (1 << (chan % 8))) &&
4919184610Salfred				    (mc->wValue[chan] != -1));
4920184610Salfred
4921184610Salfred				mc->update[chan / 8] &= ~(1 << (chan % 8));
4922184610Salfred
4923184610Salfred				if (update) {
4924184610Salfred
4925184610Salfred					req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
4926184610Salfred					USETW(req.wValue, mc->wValue[chan]);
4927184610Salfred					USETW(req.wIndex, mc->wIndex);
4928184610Salfred
4929240609Shselasky					if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
4930240609Shselasky						return;
4931240609Shselasky					} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
4932240609Shselasky						len = 2;
4933240609Shselasky						req.bRequest = UA20_CS_CUR;
4934240609Shselasky						USETW(req.wLength, len);
4935240609Shselasky					} else {
4936240609Shselasky						len = MIX_SIZE(mc->type);
4937240609Shselasky						req.bRequest = SET_CUR;
4938240609Shselasky						USETW(req.wLength, len);
4939184610Salfred					}
4940240609Shselasky
4941240609Shselasky					buf[0] = (mc->wData[chan] & 0xFF);
4942240609Shselasky					buf[1] = (mc->wData[chan] >> 8) & 0xFF;
4943240609Shselasky
4944194677Sthompsa					pc = usbd_xfer_get_frame(xfer, 0);
4945194677Sthompsa					usbd_copy_in(pc, 0, &req, sizeof(req));
4946194677Sthompsa					pc = usbd_xfer_get_frame(xfer, 1);
4947194677Sthompsa					usbd_copy_in(pc, 0, buf, len);
4948184610Salfred
4949194677Sthompsa					usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
4950194677Sthompsa					usbd_xfer_set_frame_len(xfer, 1, len);
4951194677Sthompsa					usbd_xfer_set_frames(xfer, len ? 2 : 1);
4952194228Sthompsa					usbd_transfer_submit(xfer);
4953184610Salfred					return;
4954184610Salfred				}
4955184610Salfred			}
4956184610Salfred
4957184610Salfred			mc = mc->next;
4958184610Salfred			sc->sc_mixer_curr = mc;
4959184610Salfred			sc->sc_mixer_chan = 0;
4960184610Salfred		}
4961184610Salfred
4962184610Salfred		if (repeat) {
4963184610Salfred			goto tr_setup;
4964184610Salfred		}
4965187165Sthompsa		break;
4966184610Salfred
4967184610Salfred	default:			/* Error */
4968194677Sthompsa		DPRINTF("error=%s\n", usbd_errstr(error));
4969194677Sthompsa		if (error == USB_ERR_CANCELLED) {
4970187165Sthompsa			/* do nothing - we are detaching */
4971187165Sthompsa			break;
4972187165Sthompsa		}
4973184610Salfred		goto tr_transferred;
4974184610Salfred	}
4975184610Salfred}
4976184610Salfred
4977193045Sthompsastatic usb_error_t
4978192984Sthompsauaudio_set_speed(struct usb_device *udev, uint8_t endpt, uint32_t speed)
4979184610Salfred{
4980192984Sthompsa	struct usb_device_request req;
4981184610Salfred	uint8_t data[3];
4982184610Salfred
4983184610Salfred	DPRINTFN(6, "endpt=%d speed=%u\n", endpt, speed);
4984184610Salfred
4985184610Salfred	req.bmRequestType = UT_WRITE_CLASS_ENDPOINT;
4986184610Salfred	req.bRequest = SET_CUR;
4987184610Salfred	USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0);
4988184610Salfred	USETW(req.wIndex, endpt);
4989184610Salfred	USETW(req.wLength, 3);
4990184610Salfred	data[0] = speed;
4991184610Salfred	data[1] = speed >> 8;
4992184610Salfred	data[2] = speed >> 16;
4993184610Salfred
4994196487Salfred	return (usbd_do_request(udev, NULL, &req, data));
4995184610Salfred}
4996184610Salfred
4997240609Shselaskystatic usb_error_t
4998240609Shselaskyuaudio20_set_speed(struct usb_device *udev, uint8_t iface_no,
4999240609Shselasky    uint8_t clockid, uint32_t speed)
5000240609Shselasky{
5001240609Shselasky	struct usb_device_request req;
5002240609Shselasky	uint8_t data[4];
5003240609Shselasky
5004240609Shselasky	DPRINTFN(6, "ifaceno=%d clockid=%d speed=%u\n",
5005240609Shselasky	    iface_no, clockid, speed);
5006240609Shselasky
5007240609Shselasky	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
5008240609Shselasky	req.bRequest = UA20_CS_CUR;
5009240609Shselasky	USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
5010240609Shselasky	USETW2(req.wIndex, clockid, iface_no);
5011240609Shselasky	USETW(req.wLength, 4);
5012240609Shselasky	data[0] = speed;
5013240609Shselasky	data[1] = speed >> 8;
5014240609Shselasky	data[2] = speed >> 16;
5015240609Shselasky	data[3] = speed >> 24;
5016240609Shselasky
5017240609Shselasky	return (usbd_do_request(udev, NULL, &req, data));
5018240609Shselasky}
5019240609Shselasky
5020184610Salfredstatic int
5021184610Salfreduaudio_mixer_signext(uint8_t type, int val)
5022184610Salfred{
5023184610Salfred	if (!MIX_UNSIGNED(type)) {
5024184610Salfred		if (MIX_SIZE(type) == 2) {
5025184610Salfred			val = (int16_t)val;
5026184610Salfred		} else {
5027184610Salfred			val = (int8_t)val;
5028184610Salfred		}
5029184610Salfred	}
5030184610Salfred	return (val);
5031184610Salfred}
5032184610Salfred
5033184610Salfredstatic int
5034184610Salfreduaudio_mixer_bsd2value(struct uaudio_mixer_node *mc, int32_t val)
5035184610Salfred{
5036184610Salfred	if (mc->type == MIX_ON_OFF) {
5037184610Salfred		val = (val != 0);
5038184610Salfred	} else if (mc->type == MIX_SELECTOR) {
5039184610Salfred		if ((val < mc->minval) ||
5040184610Salfred		    (val > mc->maxval)) {
5041184610Salfred			val = mc->minval;
5042184610Salfred		}
5043184610Salfred	} else {
5044199060Sthompsa
5045199060Sthompsa		/* compute actual volume */
5046199060Sthompsa		val = (val * mc->mul) / 255;
5047199060Sthompsa
5048199060Sthompsa		/* add lower offset */
5049199060Sthompsa		val = val + mc->minval;
5050199060Sthompsa
5051199060Sthompsa		/* make sure we don't write a value out of range */
5052199060Sthompsa		if (val > mc->maxval)
5053199060Sthompsa			val = mc->maxval;
5054199060Sthompsa		else if (val < mc->minval)
5055199060Sthompsa			val = mc->minval;
5056184610Salfred	}
5057184610Salfred
5058185087Salfred	DPRINTFN(6, "type=0x%03x val=%d min=%d max=%d val=%d\n",
5059185087Salfred	    mc->type, val, mc->minval, mc->maxval, val);
5060184610Salfred	return (val);
5061184610Salfred}
5062184610Salfred
5063184610Salfredstatic void
5064184610Salfreduaudio_mixer_ctl_set(struct uaudio_softc *sc, struct uaudio_mixer_node *mc,
5065184610Salfred    uint8_t chan, int32_t val)
5066184610Salfred{
5067184610Salfred	val = uaudio_mixer_bsd2value(mc, val);
5068184610Salfred
5069184610Salfred	mc->update[chan / 8] |= (1 << (chan % 8));
5070184610Salfred	mc->wData[chan] = val;
5071184610Salfred
5072184610Salfred	/* start the transfer, if not already started */
5073184610Salfred
5074194228Sthompsa	usbd_transfer_start(sc->sc_mixer_xfer[0]);
5075184610Salfred}
5076184610Salfred
5077184610Salfredstatic void
5078184610Salfreduaudio_mixer_init(struct uaudio_softc *sc)
5079184610Salfred{
5080184610Salfred	struct uaudio_mixer_node *mc;
5081184610Salfred	int32_t i;
5082184610Salfred
5083184610Salfred	for (mc = sc->sc_mixer_root; mc;
5084184610Salfred	    mc = mc->next) {
5085184610Salfred
5086184610Salfred		if (mc->ctl != SOUND_MIXER_NRDEVICES) {
5087184610Salfred			/*
5088184610Salfred			 * Set device mask bits. See
5089184610Salfred			 * /usr/include/machine/soundcard.h
5090184610Salfred			 */
5091184610Salfred			sc->sc_mix_info |= (1 << mc->ctl);
5092184610Salfred		}
5093184610Salfred		if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
5094184610Salfred		    (mc->type == MIX_SELECTOR)) {
5095184610Salfred
5096184610Salfred			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
5097184610Salfred				if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) {
5098184610Salfred					continue;
5099184610Salfred				}
5100184610Salfred				sc->sc_recsrc_info |= 1 << mc->slctrtype[i - 1];
5101184610Salfred			}
5102184610Salfred		}
5103184610Salfred	}
5104184610Salfred}
5105184610Salfred
5106184610Salfredint
5107184610Salfreduaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m)
5108184610Salfred{
5109184610Salfred	DPRINTF("\n");
5110184610Salfred
5111242438Shselasky	sc->sc_mixer_lock = mixer_get_lock(m);
5112246421Shselasky	sc->sc_mixer_dev = m;
5113242438Shselasky
5114194228Sthompsa	if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index,
5115184610Salfred	    sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc,
5116242438Shselasky	    sc->sc_mixer_lock)) {
5117184610Salfred		DPRINTFN(0, "could not allocate USB "
5118184610Salfred		    "transfer for audio mixer!\n");
5119184610Salfred		return (ENOMEM);
5120184610Salfred	}
5121184610Salfred	if (!(sc->sc_mix_info & SOUND_MASK_VOLUME)) {
5122184610Salfred		mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM);
5123184610Salfred		mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
5124184610Salfred	}
5125184610Salfred	mix_setdevs(m, sc->sc_mix_info);
5126184610Salfred	mix_setrecdevs(m, sc->sc_recsrc_info);
5127184610Salfred	return (0);
5128184610Salfred}
5129184610Salfred
5130184610Salfredint
5131184610Salfreduaudio_mixer_uninit_sub(struct uaudio_softc *sc)
5132184610Salfred{
5133184610Salfred	DPRINTF("\n");
5134184610Salfred
5135194228Sthompsa	usbd_transfer_unsetup(sc->sc_mixer_xfer, 1);
5136184610Salfred
5137242438Shselasky	sc->sc_mixer_lock = NULL;
5138242438Shselasky
5139184610Salfred	return (0);
5140184610Salfred}
5141184610Salfred
5142184610Salfredvoid
5143184610Salfreduaudio_mixer_set(struct uaudio_softc *sc, unsigned type,
5144184610Salfred    unsigned left, unsigned right)
5145184610Salfred{
5146184610Salfred	struct uaudio_mixer_node *mc;
5147242438Shselasky	int chan;
5148184610Salfred
5149242438Shselasky	for (mc = sc->sc_mixer_root; mc != NULL; mc = mc->next) {
5150184610Salfred
5151184610Salfred		if (mc->ctl == type) {
5152242438Shselasky			for (chan = 0; chan < mc->nchan; chan++) {
5153242438Shselasky				uaudio_mixer_ctl_set(sc, mc, chan,
5154242438Shselasky				    (int)((chan == 0 ? left : right) *
5155242438Shselasky				    255) / 100);
5156184610Salfred			}
5157184610Salfred		}
5158184610Salfred	}
5159184610Salfred}
5160184610Salfred
5161184610Salfreduint32_t
5162184610Salfreduaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src)
5163184610Salfred{
5164184610Salfred	struct uaudio_mixer_node *mc;
5165184610Salfred	uint32_t mask;
5166184610Salfred	uint32_t temp;
5167184610Salfred	int32_t i;
5168184610Salfred
5169184610Salfred	for (mc = sc->sc_mixer_root; mc;
5170184610Salfred	    mc = mc->next) {
5171184610Salfred
5172184610Salfred		if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
5173184610Salfred		    (mc->type == MIX_SELECTOR)) {
5174184610Salfred
5175184610Salfred			/* compute selector mask */
5176184610Salfred
5177184610Salfred			mask = 0;
5178184610Salfred			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
5179184610Salfred				mask |= (1 << mc->slctrtype[i - 1]);
5180184610Salfred			}
5181184610Salfred
5182184610Salfred			temp = mask & src;
5183184610Salfred			if (temp == 0) {
5184184610Salfred				continue;
5185184610Salfred			}
5186184610Salfred			/* find the first set bit */
5187184610Salfred			temp = (-temp) & temp;
5188184610Salfred
5189184610Salfred			/* update "src" */
5190184610Salfred			src &= ~mask;
5191184610Salfred			src |= temp;
5192184610Salfred
5193184610Salfred			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
5194184610Salfred				if (temp != (1 << mc->slctrtype[i - 1])) {
5195184610Salfred					continue;
5196184610Salfred				}
5197184610Salfred				uaudio_mixer_ctl_set(sc, mc, 0, i);
5198184610Salfred				break;
5199184610Salfred			}
5200184610Salfred		}
5201184610Salfred	}
5202184610Salfred	return (src);
5203184610Salfred}
5204184610Salfred
5205184610Salfred/*========================================================================*
5206184610Salfred * MIDI support routines
5207184610Salfred *========================================================================*/
5208184610Salfred
5209184610Salfredstatic void
5210194677Sthompsaumidi_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
5211184610Salfred{
5212194677Sthompsa	struct umidi_chan *chan = usbd_xfer_softc(xfer);
5213184610Salfred	struct umidi_sub_chan *sub;
5214194677Sthompsa	struct usb_page_cache *pc;
5215218791Shselasky	uint8_t buf[4];
5216184610Salfred	uint8_t cmd_len;
5217184610Salfred	uint8_t cn;
5218184610Salfred	uint16_t pos;
5219194677Sthompsa	int actlen;
5220184610Salfred
5221194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
5222194677Sthompsa
5223184610Salfred	switch (USB_GET_STATE(xfer)) {
5224184610Salfred	case USB_ST_TRANSFERRED:
5225184610Salfred
5226194677Sthompsa		DPRINTF("actlen=%d bytes\n", actlen);
5227184610Salfred
5228184610Salfred		pos = 0;
5229194677Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
5230184610Salfred
5231194677Sthompsa		while (actlen >= 4) {
5232184610Salfred
5233218791Shselasky			/* copy out the MIDI data */
5234218791Shselasky			usbd_copy_out(pc, pos, buf, 4);
5235218791Shselasky			/* command length */
5236218791Shselasky			cmd_len = umidi_cmd_to_len[buf[0] & 0xF];
5237218791Shselasky			/* cable number */
5238218791Shselasky			cn = buf[0] >> 4;
5239218791Shselasky			/*
5240218791Shselasky			 * Lookup sub-channel. The index is range
5241218791Shselasky			 * checked below.
5242218791Shselasky			 */
5243184610Salfred			sub = &chan->sub[cn];
5244184610Salfred
5245272423Shselasky			if ((cmd_len != 0) && (cn < chan->max_emb_jack) &&
5246218791Shselasky			    (sub->read_open != 0)) {
5247218791Shselasky
5248218791Shselasky				/* Send data to the application */
5249218791Shselasky				usb_fifo_put_data_linear(
5250218791Shselasky				    sub->fifo.fp[USB_FIFO_RX],
5251218791Shselasky				    buf + 1, cmd_len, 1);
5252184610Salfred			}
5253194677Sthompsa			actlen -= 4;
5254184610Salfred			pos += 4;
5255184610Salfred		}
5256184610Salfred
5257184610Salfred	case USB_ST_SETUP:
5258184610Salfred		DPRINTF("start\n");
5259218791Shselaskytr_setup:
5260194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
5261194228Sthompsa		usbd_transfer_submit(xfer);
5262218791Shselasky		break;
5263184610Salfred
5264184610Salfred	default:
5265194677Sthompsa		DPRINTF("error=%s\n", usbd_errstr(error));
5266184610Salfred
5267194677Sthompsa		if (error != USB_ERR_CANCELLED) {
5268184610Salfred			/* try to clear stall first */
5269218791Shselasky			usbd_xfer_set_stall(xfer);
5270218791Shselasky			goto tr_setup;
5271184610Salfred		}
5272218791Shselasky		break;
5273184610Salfred	}
5274184610Salfred}
5275184610Salfred
5276184610Salfred/*
5277184610Salfred * The following statemachine, that converts MIDI commands to
5278184610Salfred * USB MIDI packets, derives from Linux's usbmidi.c, which
5279184610Salfred * was written by "Clemens Ladisch":
5280184610Salfred *
5281184610Salfred * Returns:
5282184610Salfred *    0: No command
5283184610Salfred * Else: Command is complete
5284184610Salfred */
5285184610Salfredstatic uint8_t
5286184610Salfredumidi_convert_to_usb(struct umidi_sub_chan *sub, uint8_t cn, uint8_t b)
5287184610Salfred{
5288184610Salfred	uint8_t p0 = (cn << 4);
5289184610Salfred
5290184610Salfred	if (b >= 0xf8) {
5291184610Salfred		sub->temp_0[0] = p0 | 0x0f;
5292184610Salfred		sub->temp_0[1] = b;
5293184610Salfred		sub->temp_0[2] = 0;
5294184610Salfred		sub->temp_0[3] = 0;
5295184610Salfred		sub->temp_cmd = sub->temp_0;
5296184610Salfred		return (1);
5297184610Salfred
5298184610Salfred	} else if (b >= 0xf0) {
5299184610Salfred		switch (b) {
5300184610Salfred		case 0xf0:		/* system exclusive begin */
5301184610Salfred			sub->temp_1[1] = b;
5302184610Salfred			sub->state = UMIDI_ST_SYSEX_1;
5303184610Salfred			break;
5304184610Salfred		case 0xf1:		/* MIDI time code */
5305184610Salfred		case 0xf3:		/* song select */
5306184610Salfred			sub->temp_1[1] = b;
5307184610Salfred			sub->state = UMIDI_ST_1PARAM;
5308184610Salfred			break;
5309184610Salfred		case 0xf2:		/* song position pointer */
5310184610Salfred			sub->temp_1[1] = b;
5311184610Salfred			sub->state = UMIDI_ST_2PARAM_1;
5312184610Salfred			break;
5313184610Salfred		case 0xf4:		/* unknown */
5314184610Salfred		case 0xf5:		/* unknown */
5315184610Salfred			sub->state = UMIDI_ST_UNKNOWN;
5316184610Salfred			break;
5317184610Salfred		case 0xf6:		/* tune request */
5318184610Salfred			sub->temp_1[0] = p0 | 0x05;
5319184610Salfred			sub->temp_1[1] = 0xf6;
5320184610Salfred			sub->temp_1[2] = 0;
5321184610Salfred			sub->temp_1[3] = 0;
5322184610Salfred			sub->temp_cmd = sub->temp_1;
5323184610Salfred			sub->state = UMIDI_ST_UNKNOWN;
5324184610Salfred			return (1);
5325184610Salfred
5326184610Salfred		case 0xf7:		/* system exclusive end */
5327184610Salfred			switch (sub->state) {
5328184610Salfred			case UMIDI_ST_SYSEX_0:
5329184610Salfred				sub->temp_1[0] = p0 | 0x05;
5330184610Salfred				sub->temp_1[1] = 0xf7;
5331184610Salfred				sub->temp_1[2] = 0;
5332184610Salfred				sub->temp_1[3] = 0;
5333184610Salfred				sub->temp_cmd = sub->temp_1;
5334184610Salfred				sub->state = UMIDI_ST_UNKNOWN;
5335184610Salfred				return (1);
5336184610Salfred			case UMIDI_ST_SYSEX_1:
5337184610Salfred				sub->temp_1[0] = p0 | 0x06;
5338184610Salfred				sub->temp_1[2] = 0xf7;
5339184610Salfred				sub->temp_1[3] = 0;
5340184610Salfred				sub->temp_cmd = sub->temp_1;
5341184610Salfred				sub->state = UMIDI_ST_UNKNOWN;
5342184610Salfred				return (1);
5343184610Salfred			case UMIDI_ST_SYSEX_2:
5344184610Salfred				sub->temp_1[0] = p0 | 0x07;
5345184610Salfred				sub->temp_1[3] = 0xf7;
5346184610Salfred				sub->temp_cmd = sub->temp_1;
5347184610Salfred				sub->state = UMIDI_ST_UNKNOWN;
5348184610Salfred				return (1);
5349184610Salfred			}
5350184610Salfred			sub->state = UMIDI_ST_UNKNOWN;
5351184610Salfred			break;
5352184610Salfred		}
5353184610Salfred	} else if (b >= 0x80) {
5354184610Salfred		sub->temp_1[1] = b;
5355184610Salfred		if ((b >= 0xc0) && (b <= 0xdf)) {
5356184610Salfred			sub->state = UMIDI_ST_1PARAM;
5357184610Salfred		} else {
5358184610Salfred			sub->state = UMIDI_ST_2PARAM_1;
5359184610Salfred		}
5360184610Salfred	} else {			/* b < 0x80 */
5361184610Salfred		switch (sub->state) {
5362184610Salfred		case UMIDI_ST_1PARAM:
5363184610Salfred			if (sub->temp_1[1] < 0xf0) {
5364184610Salfred				p0 |= sub->temp_1[1] >> 4;
5365184610Salfred			} else {
5366184610Salfred				p0 |= 0x02;
5367184610Salfred				sub->state = UMIDI_ST_UNKNOWN;
5368184610Salfred			}
5369184610Salfred			sub->temp_1[0] = p0;
5370184610Salfred			sub->temp_1[2] = b;
5371184610Salfred			sub->temp_1[3] = 0;
5372184610Salfred			sub->temp_cmd = sub->temp_1;
5373184610Salfred			return (1);
5374184610Salfred		case UMIDI_ST_2PARAM_1:
5375184610Salfred			sub->temp_1[2] = b;
5376184610Salfred			sub->state = UMIDI_ST_2PARAM_2;
5377184610Salfred			break;
5378184610Salfred		case UMIDI_ST_2PARAM_2:
5379184610Salfred			if (sub->temp_1[1] < 0xf0) {
5380184610Salfred				p0 |= sub->temp_1[1] >> 4;
5381184610Salfred				sub->state = UMIDI_ST_2PARAM_1;
5382184610Salfred			} else {
5383184610Salfred				p0 |= 0x03;
5384184610Salfred				sub->state = UMIDI_ST_UNKNOWN;
5385184610Salfred			}
5386184610Salfred			sub->temp_1[0] = p0;
5387184610Salfred			sub->temp_1[3] = b;
5388184610Salfred			sub->temp_cmd = sub->temp_1;
5389184610Salfred			return (1);
5390184610Salfred		case UMIDI_ST_SYSEX_0:
5391184610Salfred			sub->temp_1[1] = b;
5392184610Salfred			sub->state = UMIDI_ST_SYSEX_1;
5393184610Salfred			break;
5394184610Salfred		case UMIDI_ST_SYSEX_1:
5395184610Salfred			sub->temp_1[2] = b;
5396184610Salfred			sub->state = UMIDI_ST_SYSEX_2;
5397184610Salfred			break;
5398184610Salfred		case UMIDI_ST_SYSEX_2:
5399184610Salfred			sub->temp_1[0] = p0 | 0x04;
5400184610Salfred			sub->temp_1[3] = b;
5401184610Salfred			sub->temp_cmd = sub->temp_1;
5402184610Salfred			sub->state = UMIDI_ST_SYSEX_0;
5403184610Salfred			return (1);
5404218791Shselasky		default:
5405218791Shselasky			break;
5406184610Salfred		}
5407184610Salfred	}
5408184610Salfred	return (0);
5409184610Salfred}
5410184610Salfred
5411184610Salfredstatic void
5412194677Sthompsaumidi_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
5413184610Salfred{
5414194677Sthompsa	struct umidi_chan *chan = usbd_xfer_softc(xfer);
5415184610Salfred	struct umidi_sub_chan *sub;
5416194677Sthompsa	struct usb_page_cache *pc;
5417184610Salfred	uint32_t actlen;
5418223727Shselasky	uint16_t nframes;
5419184610Salfred	uint8_t buf;
5420184610Salfred	uint8_t start_cable;
5421184610Salfred	uint8_t tr_any;
5422194677Sthompsa	int len;
5423184610Salfred
5424194677Sthompsa	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
5425194677Sthompsa
5426223727Shselasky	/*
5427223727Shselasky	 * NOTE: Some MIDI devices only accept 4 bytes of data per
5428223727Shselasky	 * short terminated USB transfer.
5429223727Shselasky	 */
5430184610Salfred	switch (USB_GET_STATE(xfer)) {
5431184610Salfred	case USB_ST_TRANSFERRED:
5432194677Sthompsa		DPRINTF("actlen=%d bytes\n", len);
5433184610Salfred
5434184610Salfred	case USB_ST_SETUP:
5435218791Shselaskytr_setup:
5436184610Salfred		DPRINTF("start\n");
5437184610Salfred
5438223727Shselasky		nframes = 0;	/* reset */
5439184610Salfred		start_cable = chan->curr_cable;
5440184610Salfred		tr_any = 0;
5441223736Shselasky		pc = usbd_xfer_get_frame(xfer, 0);
5442184610Salfred
5443184610Salfred		while (1) {
5444184610Salfred
5445184610Salfred			/* round robin de-queueing */
5446184610Salfred
5447184610Salfred			sub = &chan->sub[chan->curr_cable];
5448184610Salfred
5449184610Salfred			if (sub->write_open) {
5450223727Shselasky				usb_fifo_get_data_linear(sub->fifo.fp[USB_FIFO_TX],
5451223727Shselasky				    &buf, 1, &actlen, 0);
5452184610Salfred			} else {
5453184610Salfred				actlen = 0;
5454184610Salfred			}
5455184610Salfred
5456184610Salfred			if (actlen) {
5457184610Salfred
5458184610Salfred				tr_any = 1;
5459184610Salfred
5460223727Shselasky				DPRINTF("byte=0x%02x from FIFO %u\n", buf,
5461223727Shselasky				    (unsigned int)chan->curr_cable);
5462184610Salfred
5463184610Salfred				if (umidi_convert_to_usb(sub, chan->curr_cable, buf)) {
5464184610Salfred
5465223727Shselasky					DPRINTF("sub=0x%02x 0x%02x 0x%02x 0x%02x\n",
5466184610Salfred					    sub->temp_cmd[0], sub->temp_cmd[1],
5467184610Salfred					    sub->temp_cmd[2], sub->temp_cmd[3]);
5468184610Salfred
5469223736Shselasky					usbd_copy_in(pc, nframes * 4, sub->temp_cmd, 4);
5470184610Salfred
5471223736Shselasky					nframes++;
5472184610Salfred
5473223736Shselasky					if ((nframes >= UMIDI_TX_FRAMES) || (chan->single_command != 0))
5474184610Salfred						break;
5475184610Salfred				} else {
5476184610Salfred					continue;
5477184610Salfred				}
5478184610Salfred			}
5479223727Shselasky
5480184610Salfred			chan->curr_cable++;
5481272423Shselasky			if (chan->curr_cable >= chan->max_emb_jack)
5482184610Salfred				chan->curr_cable = 0;
5483223727Shselasky
5484184610Salfred			if (chan->curr_cable == start_cable) {
5485223727Shselasky				if (tr_any == 0)
5486184610Salfred					break;
5487184610Salfred				tr_any = 0;
5488184610Salfred			}
5489184610Salfred		}
5490184610Salfred
5491223736Shselasky		if (nframes != 0) {
5492223727Shselasky			DPRINTF("Transferring %d frames\n", (int)nframes);
5493223736Shselasky			usbd_xfer_set_frame_len(xfer, 0, 4 * nframes);
5494194228Sthompsa			usbd_transfer_submit(xfer);
5495184610Salfred		}
5496218791Shselasky		break;
5497184610Salfred
5498184610Salfred	default:			/* Error */
5499184610Salfred
5500194677Sthompsa		DPRINTF("error=%s\n", usbd_errstr(error));
5501184610Salfred
5502194677Sthompsa		if (error != USB_ERR_CANCELLED) {
5503184610Salfred			/* try to clear stall first */
5504218791Shselasky			usbd_xfer_set_stall(xfer);
5505218791Shselasky			goto tr_setup;
5506184610Salfred		}
5507218791Shselasky		break;
5508184610Salfred	}
5509184610Salfred}
5510184610Salfred
5511184610Salfredstatic struct umidi_sub_chan *
5512192984Sthompsaumidi_sub_by_fifo(struct usb_fifo *fifo)
5513184610Salfred{
5514194677Sthompsa	struct umidi_chan *chan = usb_fifo_softc(fifo);
5515184610Salfred	struct umidi_sub_chan *sub;
5516184610Salfred	uint32_t n;
5517184610Salfred
5518272423Shselasky	for (n = 0; n < UMIDI_EMB_JACK_MAX; n++) {
5519184610Salfred		sub = &chan->sub[n];
5520184610Salfred		if ((sub->fifo.fp[USB_FIFO_RX] == fifo) ||
5521184610Salfred		    (sub->fifo.fp[USB_FIFO_TX] == fifo)) {
5522184610Salfred			return (sub);
5523184610Salfred		}
5524184610Salfred	}
5525184610Salfred
5526192984Sthompsa	panic("%s:%d cannot find usb_fifo!\n",
5527184610Salfred	    __FILE__, __LINE__);
5528184610Salfred
5529184610Salfred	return (NULL);
5530184610Salfred}
5531184610Salfred
5532184610Salfredstatic void
5533192984Sthompsaumidi_start_read(struct usb_fifo *fifo)
5534184610Salfred{
5535194677Sthompsa	struct umidi_chan *chan = usb_fifo_softc(fifo);
5536184610Salfred
5537218791Shselasky	usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]);
5538184610Salfred}
5539184610Salfred
5540184610Salfredstatic void
5541192984Sthompsaumidi_stop_read(struct usb_fifo *fifo)
5542184610Salfred{
5543194677Sthompsa	struct umidi_chan *chan = usb_fifo_softc(fifo);
5544184610Salfred	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
5545184610Salfred
5546184610Salfred	DPRINTF("\n");
5547184610Salfred
5548184610Salfred	sub->read_open = 0;
5549184610Salfred
5550184610Salfred	if (--(chan->read_open_refcount) == 0) {
5551184610Salfred		/*
5552184610Salfred		 * XXX don't stop the read transfer here, hence that causes
5553184610Salfred		 * problems with some MIDI adapters
5554184610Salfred		 */
5555184610Salfred		DPRINTF("(stopping read transfer)\n");
5556184610Salfred	}
5557184610Salfred}
5558184610Salfred
5559184610Salfredstatic void
5560192984Sthompsaumidi_start_write(struct usb_fifo *fifo)
5561184610Salfred{
5562194677Sthompsa	struct umidi_chan *chan = usb_fifo_softc(fifo);
5563184610Salfred
5564218791Shselasky	usbd_transfer_start(chan->xfer[UMIDI_TX_TRANSFER]);
5565184610Salfred}
5566184610Salfred
5567184610Salfredstatic void
5568192984Sthompsaumidi_stop_write(struct usb_fifo *fifo)
5569184610Salfred{
5570194677Sthompsa	struct umidi_chan *chan = usb_fifo_softc(fifo);
5571184610Salfred	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
5572184610Salfred
5573184610Salfred	DPRINTF("\n");
5574184610Salfred
5575184610Salfred	sub->write_open = 0;
5576184610Salfred
5577184610Salfred	if (--(chan->write_open_refcount) == 0) {
5578184610Salfred		DPRINTF("(stopping write transfer)\n");
5579218791Shselasky		usbd_transfer_stop(chan->xfer[UMIDI_TX_TRANSFER]);
5580184610Salfred	}
5581184610Salfred}
5582184610Salfred
5583184610Salfredstatic int
5584192984Sthompsaumidi_open(struct usb_fifo *fifo, int fflags)
5585184610Salfred{
5586194677Sthompsa	struct umidi_chan *chan = usb_fifo_softc(fifo);
5587184610Salfred	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
5588184610Salfred
5589184610Salfred	if (fflags & FREAD) {
5590194228Sthompsa		if (usb_fifo_alloc_buffer(fifo, 4, (1024 / 4))) {
5591184610Salfred			return (ENOMEM);
5592184610Salfred		}
5593195120Sthompsa		mtx_lock(&chan->mtx);
5594184610Salfred		chan->read_open_refcount++;
5595184610Salfred		sub->read_open = 1;
5596195120Sthompsa		mtx_unlock(&chan->mtx);
5597184610Salfred	}
5598184610Salfred	if (fflags & FWRITE) {
5599194228Sthompsa		if (usb_fifo_alloc_buffer(fifo, 32, (1024 / 32))) {
5600184610Salfred			return (ENOMEM);
5601184610Salfred		}
5602184610Salfred		/* clear stall first */
5603195120Sthompsa		mtx_lock(&chan->mtx);
5604184610Salfred		chan->write_open_refcount++;
5605184610Salfred		sub->write_open = 1;
5606184610Salfred
5607184610Salfred		/* reset */
5608184610Salfred		sub->state = UMIDI_ST_UNKNOWN;
5609195120Sthompsa		mtx_unlock(&chan->mtx);
5610184610Salfred	}
5611184610Salfred	return (0);			/* success */
5612184610Salfred}
5613184610Salfred
5614184610Salfredstatic void
5615192984Sthompsaumidi_close(struct usb_fifo *fifo, int fflags)
5616184610Salfred{
5617184610Salfred	if (fflags & FREAD) {
5618194228Sthompsa		usb_fifo_free_buffer(fifo);
5619184610Salfred	}
5620184610Salfred	if (fflags & FWRITE) {
5621194228Sthompsa		usb_fifo_free_buffer(fifo);
5622184610Salfred	}
5623184610Salfred}
5624184610Salfred
5625184610Salfred
5626184610Salfredstatic int
5627192984Sthompsaumidi_ioctl(struct usb_fifo *fifo, u_long cmd, void *data,
5628189110Sthompsa    int fflags)
5629184610Salfred{
5630184610Salfred	return (ENODEV);
5631184610Salfred}
5632184610Salfred
5633184610Salfredstatic void
5634184610Salfredumidi_init(device_t dev)
5635184610Salfred{
5636184610Salfred	struct uaudio_softc *sc = device_get_softc(dev);
5637184610Salfred	struct umidi_chan *chan = &sc->sc_midi_chan;
5638184610Salfred
5639184610Salfred	mtx_init(&chan->mtx, "umidi lock", NULL, MTX_DEF | MTX_RECURSE);
5640184610Salfred}
5641184610Salfred
5642192984Sthompsastatic struct usb_fifo_methods umidi_fifo_methods = {
5643184610Salfred	.f_start_read = &umidi_start_read,
5644184610Salfred	.f_start_write = &umidi_start_write,
5645184610Salfred	.f_stop_read = &umidi_stop_read,
5646184610Salfred	.f_stop_write = &umidi_stop_write,
5647184610Salfred	.f_open = &umidi_open,
5648184610Salfred	.f_close = &umidi_close,
5649184610Salfred	.f_ioctl = &umidi_ioctl,
5650184610Salfred	.basename[0] = "umidi",
5651184610Salfred};
5652184610Salfred
5653218988Shselaskystatic int
5654184610Salfredumidi_probe(device_t dev)
5655184610Salfred{
5656184610Salfred	struct uaudio_softc *sc = device_get_softc(dev);
5657192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
5658184610Salfred	struct umidi_chan *chan = &sc->sc_midi_chan;
5659184610Salfred	struct umidi_sub_chan *sub;
5660184610Salfred	int unit = device_get_unit(dev);
5661184610Salfred	int error;
5662184610Salfred	uint32_t n;
5663184610Salfred
5664223736Shselasky	if (usb_test_quirk(uaa, UQ_SINGLE_CMD_MIDI))
5665223736Shselasky		chan->single_command = 1;
5666223736Shselasky
5667194228Sthompsa	if (usbd_set_alt_interface_index(sc->sc_udev, chan->iface_index,
5668184610Salfred	    chan->iface_alt_index)) {
5669184610Salfred		DPRINTF("setting of alternate index failed!\n");
5670184610Salfred		goto detach;
5671184610Salfred	}
5672218791Shselasky	usbd_set_parent_iface(sc->sc_udev, chan->iface_index,
5673218791Shselasky	    sc->sc_mixer_iface_index);
5674184610Salfred
5675194228Sthompsa	error = usbd_transfer_setup(uaa->device, &chan->iface_index,
5676184610Salfred	    chan->xfer, umidi_config, UMIDI_N_TRANSFER,
5677184610Salfred	    chan, &chan->mtx);
5678184610Salfred	if (error) {
5679194228Sthompsa		DPRINTF("error=%s\n", usbd_errstr(error));
5680184610Salfred		goto detach;
5681184610Salfred	}
5682263643Shselasky
5683263643Shselasky	/*
5684263643Shselasky	 * Some USB MIDI device makers couldn't resist using
5685263643Shselasky	 * wMaxPacketSize = 4 for RX and TX BULK endpoints, although
5686263643Shselasky	 * that size is an unsupported value for FULL speed BULK
5687263643Shselasky	 * endpoints. The same applies to some HIGH speed MIDI devices
5688263643Shselasky	 * which are using a wMaxPacketSize different from 512 bytes.
5689263643Shselasky	 *
5690263643Shselasky	 * Refer to section 5.8.3 in USB 2.0 PDF: Cite: "All Host
5691263643Shselasky	 * Controllers are required to have support for 8-, 16-, 32-,
5692263643Shselasky	 * and 64-byte maximum packet sizes for full-speed bulk
5693263643Shselasky	 * endpoints and 512 bytes for high-speed bulk endpoints."
5694263643Shselasky	 */
5695263643Shselasky	if (usbd_xfer_maxp_was_clamped(chan->xfer[UMIDI_TX_TRANSFER]))
5696263643Shselasky		chan->single_command = 1;
5697263643Shselasky
5698263643Shselasky	if (chan->single_command != 0)
5699263643Shselasky		device_printf(dev, "Single command MIDI quirk enabled\n");
5700263643Shselasky
5701272423Shselasky	if ((chan->max_emb_jack == 0) ||
5702272423Shselasky	    (chan->max_emb_jack > UMIDI_EMB_JACK_MAX)) {
5703272423Shselasky		chan->max_emb_jack = UMIDI_EMB_JACK_MAX;
5704184610Salfred	}
5705184610Salfred
5706272423Shselasky	for (n = 0; n < chan->max_emb_jack; n++) {
5707184610Salfred
5708184610Salfred		sub = &chan->sub[n];
5709184610Salfred
5710194228Sthompsa		error = usb_fifo_attach(sc->sc_udev, chan, &chan->mtx,
5711184610Salfred		    &umidi_fifo_methods, &sub->fifo, unit, n,
5712189110Sthompsa		    chan->iface_index,
5713189110Sthompsa		    UID_ROOT, GID_OPERATOR, 0644);
5714184610Salfred		if (error) {
5715184610Salfred			goto detach;
5716184610Salfred		}
5717184610Salfred	}
5718184610Salfred
5719184610Salfred	mtx_lock(&chan->mtx);
5720184610Salfred
5721184610Salfred	/*
5722218791Shselasky	 * NOTE: At least one device will not work properly unless the
5723218791Shselasky	 * BULK IN pipe is open all the time. This might have to do
5724218791Shselasky	 * about that the internal queues of the device overflow if we
5725218791Shselasky	 * don't read them regularly.
5726184610Salfred	 */
5727218791Shselasky	usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]);
5728184610Salfred
5729184610Salfred	mtx_unlock(&chan->mtx);
5730184610Salfred
5731184610Salfred	return (0);			/* success */
5732184610Salfred
5733184610Salfreddetach:
5734184610Salfred	return (ENXIO);			/* failure */
5735184610Salfred}
5736184610Salfred
5737218988Shselaskystatic int
5738184610Salfredumidi_detach(device_t dev)
5739184610Salfred{
5740184610Salfred	struct uaudio_softc *sc = device_get_softc(dev);
5741184610Salfred	struct umidi_chan *chan = &sc->sc_midi_chan;
5742184610Salfred	uint32_t n;
5743184610Salfred
5744272423Shselasky	for (n = 0; n < UMIDI_EMB_JACK_MAX; n++)
5745194228Sthompsa		usb_fifo_detach(&chan->sub[n].fifo);
5746184610Salfred
5747184610Salfred	mtx_lock(&chan->mtx);
5748184610Salfred
5749218791Shselasky	usbd_transfer_stop(chan->xfer[UMIDI_RX_TRANSFER]);
5750184610Salfred
5751184610Salfred	mtx_unlock(&chan->mtx);
5752184610Salfred
5753194228Sthompsa	usbd_transfer_unsetup(chan->xfer, UMIDI_N_TRANSFER);
5754184610Salfred
5755184610Salfred	mtx_destroy(&chan->mtx);
5756184610Salfred
5757184610Salfred	return (0);
5758184610Salfred}
5759184610Salfred
5760246421Shselaskystatic void
5761246421Shselaskyuaudio_hid_rx_callback(struct usb_xfer *xfer, usb_error_t error)
5762246421Shselasky{
5763246421Shselasky	struct uaudio_softc *sc = usbd_xfer_softc(xfer);
5764246421Shselasky	const uint8_t *buffer = usbd_xfer_get_frame_buffer(xfer, 0);
5765246421Shselasky	struct snd_mixer *m;
5766246421Shselasky	uint8_t id;
5767246421Shselasky	int actlen;
5768246421Shselasky
5769246421Shselasky	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
5770246421Shselasky
5771246421Shselasky	switch (USB_GET_STATE(xfer)) {
5772246421Shselasky	case USB_ST_TRANSFERRED:
5773246421Shselasky		DPRINTF("actlen=%d\n", actlen);
5774246421Shselasky
5775246421Shselasky		if (actlen != 0 &&
5776246421Shselasky		    (sc->sc_hid.flags & UAUDIO_HID_HAS_ID)) {
5777246421Shselasky			id = *buffer;
5778246421Shselasky			buffer++;
5779246421Shselasky			actlen--;
5780246421Shselasky		} else {
5781246421Shselasky			id = 0;
5782246421Shselasky		}
5783246421Shselasky
5784246421Shselasky		m = sc->sc_mixer_dev;
5785246421Shselasky
5786246454Shselasky		if ((sc->sc_hid.flags & UAUDIO_HID_HAS_MUTE) &&
5787246454Shselasky		    (sc->sc_hid.mute_id == id) &&
5788246454Shselasky		    hid_get_data(buffer, actlen,
5789246454Shselasky		    &sc->sc_hid.mute_loc)) {
5790246454Shselasky
5791246454Shselasky			DPRINTF("Mute toggle\n");
5792246454Shselasky
5793246454Shselasky			mixer_hwvol_mute_locked(m);
5794246454Shselasky		}
5795246454Shselasky
5796246421Shselasky		if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_UP) &&
5797246421Shselasky		    (sc->sc_hid.volume_up_id == id) &&
5798246421Shselasky		    hid_get_data(buffer, actlen,
5799246421Shselasky		    &sc->sc_hid.volume_up_loc)) {
5800246421Shselasky
5801246421Shselasky			DPRINTF("Volume Up\n");
5802246421Shselasky
5803246454Shselasky			mixer_hwvol_step_locked(m, 1, 1);
5804246421Shselasky		}
5805246421Shselasky
5806246421Shselasky		if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_DOWN) &&
5807246421Shselasky		    (sc->sc_hid.volume_down_id == id) &&
5808246421Shselasky		    hid_get_data(buffer, actlen,
5809246421Shselasky		    &sc->sc_hid.volume_down_loc)) {
5810246421Shselasky
5811246421Shselasky			DPRINTF("Volume Down\n");
5812246421Shselasky
5813246454Shselasky			mixer_hwvol_step_locked(m, -1, -1);
5814246421Shselasky		}
5815246421Shselasky
5816246421Shselasky	case USB_ST_SETUP:
5817246421Shselaskytr_setup:
5818246421Shselasky		/* check if we can put more data into the FIFO */
5819246421Shselasky		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
5820246421Shselasky		usbd_transfer_submit(xfer);
5821246421Shselasky		break;
5822246421Shselasky
5823246421Shselasky	default:			/* Error */
5824250765Shselasky
5825250765Shselasky		DPRINTF("error=%s\n", usbd_errstr(error));
5826250765Shselasky
5827246421Shselasky		if (error != USB_ERR_CANCELLED) {
5828250765Shselasky			/* try to clear stall first */
5829246421Shselasky			usbd_xfer_set_stall(xfer);
5830246421Shselasky			goto tr_setup;
5831246421Shselasky		}
5832246421Shselasky		break;
5833246421Shselasky	}
5834246421Shselasky}
5835246421Shselasky
5836246421Shselaskystatic int
5837246421Shselaskyuaudio_hid_probe(struct uaudio_softc *sc,
5838246421Shselasky    struct usb_attach_arg *uaa)
5839246421Shselasky{
5840246421Shselasky	void *d_ptr;
5841246421Shselasky	uint32_t flags;
5842246421Shselasky	uint16_t d_len;
5843246421Shselasky	uint8_t id;
5844246421Shselasky	int error;
5845246421Shselasky
5846246421Shselasky	if (!(sc->sc_hid.flags & UAUDIO_HID_VALID))
5847246421Shselasky		return (-1);
5848246421Shselasky
5849246421Shselasky	if (sc->sc_mixer_lock == NULL)
5850246421Shselasky		return (-1);
5851246421Shselasky
5852246421Shselasky	/* Get HID descriptor */
5853246421Shselasky	error = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
5854246421Shselasky	    &d_len, M_TEMP, sc->sc_hid.iface_index);
5855246421Shselasky
5856246421Shselasky	if (error) {
5857246421Shselasky		DPRINTF("error reading report description\n");
5858246421Shselasky		return (-1);
5859246421Shselasky	}
5860246421Shselasky
5861246421Shselasky	/* check if there is an ID byte */
5862246421Shselasky	hid_report_size(d_ptr, d_len, hid_input, &id);
5863246421Shselasky
5864246421Shselasky	if (id != 0)
5865246421Shselasky		sc->sc_hid.flags |= UAUDIO_HID_HAS_ID;
5866246421Shselasky
5867246421Shselasky	if (hid_locate(d_ptr, d_len,
5868246421Shselasky	    HID_USAGE2(HUP_CONSUMER, 0xE9 /* Volume Increment */),
5869246421Shselasky	    hid_input, 0, &sc->sc_hid.volume_up_loc, &flags,
5870246421Shselasky	    &sc->sc_hid.volume_up_id)) {
5871246421Shselasky		if (flags & HIO_VARIABLE)
5872246421Shselasky			sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_UP;
5873246421Shselasky		DPRINTFN(1, "Found Volume Up key\n");
5874246421Shselasky	}
5875246421Shselasky
5876246421Shselasky	if (hid_locate(d_ptr, d_len,
5877246421Shselasky	    HID_USAGE2(HUP_CONSUMER, 0xEA /* Volume Decrement */),
5878246421Shselasky	    hid_input, 0, &sc->sc_hid.volume_down_loc, &flags,
5879246421Shselasky	    &sc->sc_hid.volume_down_id)) {
5880246421Shselasky		if (flags & HIO_VARIABLE)
5881246421Shselasky			sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_DOWN;
5882246421Shselasky		DPRINTFN(1, "Found Volume Down key\n");
5883246421Shselasky	}
5884246421Shselasky
5885246454Shselasky	if (hid_locate(d_ptr, d_len,
5886246454Shselasky	    HID_USAGE2(HUP_CONSUMER, 0xE2 /* Mute */),
5887246454Shselasky	    hid_input, 0, &sc->sc_hid.mute_loc, &flags,
5888246454Shselasky	    &sc->sc_hid.mute_id)) {
5889246454Shselasky		if (flags & HIO_VARIABLE)
5890246454Shselasky			sc->sc_hid.flags |= UAUDIO_HID_HAS_MUTE;
5891246454Shselasky		DPRINTFN(1, "Found Mute key\n");
5892246454Shselasky	}
5893246454Shselasky
5894246421Shselasky	free(d_ptr, M_TEMP);
5895246421Shselasky
5896246421Shselasky	if (!(sc->sc_hid.flags & (UAUDIO_HID_HAS_VOLUME_UP |
5897246454Shselasky	    UAUDIO_HID_HAS_VOLUME_DOWN |
5898246454Shselasky	    UAUDIO_HID_HAS_MUTE))) {
5899246421Shselasky		DPRINTFN(1, "Did not find any volume related keys\n");
5900246421Shselasky		return (-1);
5901246421Shselasky	}
5902246421Shselasky
5903246421Shselasky	/* prevent the uhid driver from attaching */
5904246421Shselasky	usbd_set_parent_iface(uaa->device, sc->sc_hid.iface_index,
5905246421Shselasky	    sc->sc_mixer_iface_index);
5906246421Shselasky
5907246421Shselasky	/* allocate USB transfers */
5908246421Shselasky	error = usbd_transfer_setup(uaa->device, &sc->sc_hid.iface_index,
5909246421Shselasky	    sc->sc_hid.xfer, uaudio_hid_config, UAUDIO_HID_N_TRANSFER,
5910246421Shselasky	    sc, sc->sc_mixer_lock);
5911246421Shselasky	if (error) {
5912246421Shselasky		DPRINTF("error=%s\n", usbd_errstr(error));
5913246421Shselasky		return (-1);
5914246421Shselasky	}
5915246421Shselasky	return (0);
5916246421Shselasky}
5917246421Shselasky
5918246421Shselaskystatic void
5919246421Shselaskyuaudio_hid_detach(struct uaudio_softc *sc)
5920246421Shselasky{
5921246421Shselasky	usbd_transfer_unsetup(sc->sc_hid.xfer, UAUDIO_HID_N_TRANSFER);
5922246421Shselasky}
5923246421Shselasky
5924266485ShselaskyDRIVER_MODULE_ORDERED(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, 0, SI_ORDER_ANY);
5925188942SthompsaMODULE_DEPEND(uaudio, usb, 1, 1, 1);
5926184610SalfredMODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
5927184610SalfredMODULE_VERSION(uaudio, 1);
5928