uaudio.c revision 242223
1181641Skmacy/*	$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $	*/
2181641Skmacy/*	$FreeBSD: head/sys/dev/sound/usb/uaudio.c 242223 2012-10-28 14:37:17Z hselasky $ */
3181641Skmacy
4181641Skmacy/*-
5181641Skmacy * Copyright (c) 1999 The NetBSD Foundation, Inc.
6181641Skmacy * All rights reserved.
7181641Skmacy *
8181641Skmacy * This code is derived from software contributed to The NetBSD Foundation
9181641Skmacy * by Lennart Augustsson (lennart@augustsson.net) at
10181641Skmacy * Carlstedt Research & Technology.
11181641Skmacy *
12181641Skmacy * Redistribution and use in source and binary forms, with or without
13181641Skmacy * modification, are permitted provided that the following conditions
14181641Skmacy * are met:
15181641Skmacy * 1. Redistributions of source code must retain the above copyright
16181641Skmacy *    notice, this list of conditions and the following disclaimer.
17181641Skmacy * 2. Redistributions in binary form must reproduce the above copyright
18181641Skmacy *    notice, this list of conditions and the following disclaimer in the
19181641Skmacy *    documentation and/or other materials provided with the distribution.
20181641Skmacy *
21181641Skmacy * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22181641Skmacy * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23181641Skmacy * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24181641Skmacy * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25181641Skmacy * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26181641Skmacy * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27181641Skmacy * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28181641Skmacy * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29181641Skmacy * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30181641Skmacy * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31181641Skmacy * POSSIBILITY OF SUCH DAMAGE.
32181641Skmacy */
33181641Skmacy
34181641Skmacy#include <sys/cdefs.h>
35181641Skmacy__FBSDID("$FreeBSD: head/sys/dev/sound/usb/uaudio.c 242223 2012-10-28 14:37:17Z hselasky $");
36181641Skmacy
37181641Skmacy/*
38181641Skmacy * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
39181641Skmacy *                  http://www.usb.org/developers/devclass_docs/frmts10.pdf
40181641Skmacy *                  http://www.usb.org/developers/devclass_docs/termt10.pdf
41181641Skmacy */
42181641Skmacy
43181641Skmacy/*
44181641Skmacy * Also merged:
45181641Skmacy *  $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $
46181641Skmacy *  $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $
47181641Skmacy *  $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $
48181641Skmacy *  $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $
49181641Skmacy */
50181641Skmacy
51181641Skmacy#include <sys/stdint.h>
52181641Skmacy#include <sys/stddef.h>
53181641Skmacy#include <sys/param.h>
54181641Skmacy#include <sys/queue.h>
55181641Skmacy#include <sys/types.h>
56181641Skmacy#include <sys/systm.h>
57181641Skmacy#include <sys/kernel.h>
58181641Skmacy#include <sys/bus.h>
59181641Skmacy#include <sys/module.h>
60181641Skmacy#include <sys/lock.h>
61181641Skmacy#include <sys/mutex.h>
62181641Skmacy#include <sys/condvar.h>
63181641Skmacy#include <sys/sysctl.h>
64181641Skmacy#include <sys/sx.h>
65181641Skmacy#include <sys/unistd.h>
66181641Skmacy#include <sys/callout.h>
67181641Skmacy#include <sys/malloc.h>
68181641Skmacy#include <sys/priv.h>
69181641Skmacy
70181641Skmacy#include "usbdevs.h"
71181641Skmacy#include <dev/usb/usb.h>
72181641Skmacy#include <dev/usb/usbdi.h>
73181641Skmacy#include <dev/usb/usbdi_util.h>
74181641Skmacy
75181641Skmacy#define	USB_DEBUG_VAR uaudio_debug
76181641Skmacy#include <dev/usb/usb_debug.h>
77181641Skmacy
78181641Skmacy#include <dev/usb/quirk/usb_quirk.h>
79181641Skmacy
80181641Skmacy#include <sys/reboot.h>			/* for bootverbose */
81181641Skmacy
82181641Skmacy#ifdef HAVE_KERNEL_OPTION_HEADERS
83181641Skmacy#include "opt_snd.h"
84181641Skmacy#endif
85181641Skmacy
86181641Skmacy#include <dev/sound/pcm/sound.h>
87181641Skmacy#include <dev/sound/usb/uaudioreg.h>
88181641Skmacy#include <dev/sound/usb/uaudio.h>
89181641Skmacy#include <dev/sound/chip.h>
90181641Skmacy#include "feeder_if.h"
91181641Skmacy
92181641Skmacystatic int uaudio_default_rate = 0;		/* use rate list */
93181641Skmacystatic int uaudio_default_bits = 32;
94181641Skmacystatic int uaudio_default_channels = 0;		/* use default */
95181641Skmacy
96181641Skmacy#ifdef USB_DEBUG
97181641Skmacystatic int uaudio_debug = 0;
98181641Skmacy
99181641Skmacystatic SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio");
100181641Skmacy
101181641SkmacySYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW,
102181641Skmacy    &uaudio_debug, 0, "uaudio debug level");
103181641Skmacy
104181641SkmacyTUNABLE_INT("hw.usb.uaudio.default_rate", &uaudio_default_rate);
105181641SkmacySYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_rate, CTLFLAG_RW,
106181641Skmacy    &uaudio_default_rate, 0, "uaudio default sample rate");
107181641Skmacy
108181641SkmacyTUNABLE_INT("hw.usb.uaudio.default_bits", &uaudio_default_bits);
109181641SkmacySYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_bits, CTLFLAG_RW,
110181641Skmacy    &uaudio_default_bits, 0, "uaudio default sample bits");
111181641Skmacy
112181641SkmacyTUNABLE_INT("hw.usb.uaudio.default_channels", &uaudio_default_channels);
113181641SkmacySYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW,
114181641Skmacy    &uaudio_default_channels, 0, "uaudio default sample channels");
115181641Skmacy#endif
116181641Skmacy
117181641Skmacy#define	UAUDIO_NFRAMES		64	/* must be factor of 8 due HS-USB */
118181641Skmacy#define	UAUDIO_NCHANBUFS	2	/* number of outstanding request */
119181641Skmacy#define	UAUDIO_RECURSE_LIMIT	255	/* rounds */
120181641Skmacy
121181641Skmacy#define	MAKE_WORD(h,l) (((h) << 8) | (l))
122181641Skmacy#define	BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1)
123181641Skmacy#define	UAUDIO_MAX_CHAN(x) (x)
124195949Skib
125181641Skmacyunion uaudio_asid {
126181641Skmacy	const struct usb_audio_streaming_interface_descriptor *v1;
127181641Skmacy	const struct usb_audio20_streaming_interface_descriptor *v2;
128181641Skmacy};
129181641Skmacy
130181641Skmacyunion uaudio_asf1d {
131181641Skmacy	const struct usb_audio_streaming_type1_descriptor *v1;
132181641Skmacy	const struct usb_audio20_streaming_type1_descriptor *v2;
133181641Skmacy};
134181641Skmacy
135181641Skmacyunion uaudio_sed {
136181641Skmacy	const struct usb_audio_streaming_endpoint_descriptor *v1;
137181641Skmacy	const struct usb_audio20_streaming_endpoint_descriptor *v2;
138181641Skmacy};
139181641Skmacy
140181641Skmacystruct uaudio_mixer_node {
141181641Skmacy	int32_t	minval;
142181641Skmacy	int32_t	maxval;
143181641Skmacy#define	MIX_MAX_CHAN 8
144181641Skmacy	int32_t	wValue[MIX_MAX_CHAN];	/* using nchan */
145181641Skmacy	uint32_t mul;
146181641Skmacy	uint32_t ctl;
147181641Skmacy
148181641Skmacy	uint16_t wData[MIX_MAX_CHAN];	/* using nchan */
149181641Skmacy	uint16_t wIndex;
150181641Skmacy
151181641Skmacy	uint8_t	update[(MIX_MAX_CHAN + 7) / 8];
152181641Skmacy	uint8_t	nchan;
153181641Skmacy	uint8_t	type;
154181641Skmacy#define	MIX_ON_OFF	1
155181641Skmacy#define	MIX_SIGNED_16	2
156181641Skmacy#define	MIX_UNSIGNED_16	3
157181641Skmacy#define	MIX_SIGNED_8	4
158186557Skmacy#define	MIX_SELECTOR	5
159181641Skmacy#define	MIX_UNKNOWN     6
160181641Skmacy#define	MIX_SIZE(n) ((((n) == MIX_SIGNED_16) || \
161181641Skmacy		      ((n) == MIX_UNSIGNED_16)) ? 2 : 1)
162181641Skmacy#define	MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16)
163181641Skmacy
164181641Skmacy#define	MAX_SELECTOR_INPUT_PIN 256
165181641Skmacy	uint8_t	slctrtype[MAX_SELECTOR_INPUT_PIN];
166181641Skmacy	uint8_t	class;
167181641Skmacy
168181641Skmacy	struct uaudio_mixer_node *next;
169181641Skmacy};
170181641Skmacy
171181641Skmacystruct uaudio_chan {
172181641Skmacy	struct pcmchan_caps pcm_cap;	/* capabilities */
173181641Skmacy
174181641Skmacy	struct snd_dbuf *pcm_buf;
175181641Skmacy	const struct usb_config *usb_cfg;
176204041Sed	struct mtx *pcm_mtx;		/* lock protecting this structure */
177204041Sed	struct uaudio_softc *priv_sc;
178204041Sed	struct pcm_channel *pcm_ch;
179202628Sed	struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1];
180204041Sed	union uaudio_asf1d p_asf1d;
181181641Skmacy	union uaudio_sed p_sed;
182181641Skmacy	const usb_endpoint_descriptor_audio_t *p_ed1;
183181641Skmacy	const struct uaudio_format *p_fmt;
184181641Skmacy
185181641Skmacy	uint8_t *buf;			/* pointer to buffer */
186181641Skmacy	uint8_t *start;			/* upper layer buffer start */
187181641Skmacy	uint8_t *end;			/* upper layer buffer end */
188181641Skmacy	uint8_t *cur;			/* current position in upper layer
189181641Skmacy					 * buffer */
190181641Skmacy
191181641Skmacy	uint32_t intr_size;		/* in bytes */
192181747Skmacy	uint32_t intr_frames;		/* in units */
193181747Skmacy	uint32_t sample_rate;
194181747Skmacy	uint32_t frames_per_second;
195181641Skmacy	uint32_t sample_rem;
196181641Skmacy	uint32_t sample_curr;
197181641Skmacy
198181641Skmacy	uint32_t format;
199181641Skmacy	uint32_t pcm_format[2];
200181641Skmacy
201181641Skmacy	uint16_t bytes_per_frame[2];
202181641Skmacy
203181641Skmacy	uint16_t sample_size;
204181641Skmacy
205181641Skmacy	uint8_t	valid;
206181641Skmacy	uint8_t	iface_index;
207181641Skmacy	uint8_t	iface_alt_index;
208181641Skmacy	uint8_t channels;
209181641Skmacy
210181641Skmacy	uint8_t last_sync_time;
211181641Skmacy	uint8_t last_sync_state;
212181641Skmacy#define	UAUDIO_SYNC_NONE 0
213181641Skmacy#define	UAUDIO_SYNC_MORE 1
214181641Skmacy#define	UAUDIO_SYNC_LESS 2
215181641Skmacy};
216181641Skmacy
217181641Skmacy#define	UMIDI_CABLES_MAX   16		/* units */
218181641Skmacy#define	UMIDI_TX_FRAMES	   256		/* units */
219182902Skmacy#define	UMIDI_TX_BUFFER    (UMIDI_TX_FRAMES * 4)	/* bytes */
220181641Skmacy
221181641Skmacyenum {
222181641Skmacy	UMIDI_TX_TRANSFER,
223181641Skmacy	UMIDI_RX_TRANSFER,
224181641Skmacy	UMIDI_N_TRANSFER,
225181641Skmacy};
226181641Skmacy
227181641Skmacystruct umidi_sub_chan {
228181641Skmacy	struct usb_fifo_sc fifo;
229181641Skmacy	uint8_t *temp_cmd;
230196726Sadrian	uint8_t	temp_0[4];
231196726Sadrian	uint8_t	temp_1[4];
232181641Skmacy	uint8_t	state;
233181641Skmacy#define	UMIDI_ST_UNKNOWN   0		/* scan for command */
234181641Skmacy#define	UMIDI_ST_1PARAM    1
235181641Skmacy#define	UMIDI_ST_2PARAM_1  2
236181747Skmacy#define	UMIDI_ST_2PARAM_2  3
237181641Skmacy#define	UMIDI_ST_SYSEX_0   4
238181641Skmacy#define	UMIDI_ST_SYSEX_1   5
239181641Skmacy#define	UMIDI_ST_SYSEX_2   6
240181641Skmacy
241181641Skmacy	uint8_t	read_open:1;
242181641Skmacy	uint8_t	write_open:1;
243181641Skmacy	uint8_t	unused:6;
244181641Skmacy};
245181641Skmacy
246181641Skmacystruct umidi_chan {
247181641Skmacy
248181641Skmacy	struct umidi_sub_chan sub[UMIDI_CABLES_MAX];
249181641Skmacy	struct mtx mtx;
250181641Skmacy
251181641Skmacy	struct usb_xfer *xfer[UMIDI_N_TRANSFER];
252181641Skmacy
253181641Skmacy	uint8_t	iface_index;
254181641Skmacy	uint8_t	iface_alt_index;
255204160Skmacy
256181641Skmacy	uint8_t	read_open_refcount;
257181641Skmacy	uint8_t	write_open_refcount;
258181641Skmacy
259181641Skmacy	uint8_t	curr_cable;
260181641Skmacy	uint8_t	max_cable;
261181641Skmacy	uint8_t	valid;
262181641Skmacy	uint8_t single_command;
263181641Skmacy};
264181641Skmacy
265181641Skmacystruct uaudio_search_result {
266181641Skmacy	uint8_t	bit_input[(256 + 7) / 8];
267181641Skmacy	uint8_t	bit_output[(256 + 7) / 8];
268181641Skmacy	uint8_t	recurse_level;
269181641Skmacy	uint8_t	id_max;
270181641Skmacy	uint8_t is_input;
271181641Skmacy};
272181641Skmacy
273181641Skmacystruct uaudio_softc {
274181641Skmacy	struct sbuf sc_sndstat;
275181641Skmacy	struct sndcard_func sc_sndcard_func;
276181641Skmacy	struct uaudio_chan sc_rec_chan;
277181641Skmacy	struct uaudio_chan sc_play_chan;
278181641Skmacy	struct umidi_chan sc_midi_chan;
279181641Skmacy	struct uaudio_search_result sc_mixer_clocks;
280181641Skmacy
281181641Skmacy	struct usb_device *sc_udev;
282181641Skmacy	struct usb_xfer *sc_mixer_xfer[1];
283181747Skmacy	struct uaudio_mixer_node *sc_mixer_root;
284181747Skmacy	struct uaudio_mixer_node *sc_mixer_curr;
285199184Savg
286181747Skmacy	uint32_t sc_mix_info;
287181747Skmacy	uint32_t sc_recsrc_info;
288181747Skmacy
289181747Skmacy	uint16_t sc_audio_rev;
290181747Skmacy	uint16_t sc_mixer_count;
291181747Skmacy
292207419Skmacy	uint8_t	sc_sndstat_valid;
293207419Skmacy	uint8_t	sc_mixer_iface_index;
294181747Skmacy	uint8_t	sc_mixer_iface_no;
295207419Skmacy	uint8_t	sc_mixer_chan;
296207419Skmacy	uint8_t	sc_pcm_registered:1;
297207419Skmacy	uint8_t	sc_mixer_init:1;
298207419Skmacy	uint8_t	sc_uq_audio_swap_lr:1;
299181641Skmacy	uint8_t	sc_uq_au_inp_async:1;
300181641Skmacy	uint8_t	sc_uq_au_no_xu:1;
301181641Skmacy	uint8_t	sc_uq_bad_adc:1;
302181641Skmacy	uint8_t	sc_uq_au_vendor_class:1;
303181641Skmacy};
304181641Skmacy
305181641Skmacystruct uaudio_terminal_node {
306181641Skmacy	union {
307181641Skmacy		const struct usb_descriptor *desc;
308181641Skmacy		const struct usb_audio_input_terminal *it_v1;
309181641Skmacy		const struct usb_audio_output_terminal *ot_v1;
310181641Skmacy		const struct usb_audio_mixer_unit_0 *mu_v1;
311181641Skmacy		const struct usb_audio_selector_unit *su_v1;
312181641Skmacy		const struct usb_audio_feature_unit *fu_v1;
313181641Skmacy		const struct usb_audio_processing_unit_0 *pu_v1;
314181641Skmacy		const struct usb_audio_extension_unit_0 *eu_v1;
315181641Skmacy		const struct usb_audio20_clock_source_unit *csrc_v2;
316181641Skmacy		const struct usb_audio20_clock_selector_unit_0 *csel_v2;
317181641Skmacy		const struct usb_audio20_clock_multiplier_unit *cmul_v2;
318181641Skmacy		const struct usb_audio20_input_terminal *it_v2;
319181641Skmacy		const struct usb_audio20_output_terminal *ot_v2;
320181641Skmacy		const struct usb_audio20_mixer_unit_0 *mu_v2;
321181641Skmacy		const struct usb_audio20_selector_unit *su_v2;
322181641Skmacy		const struct usb_audio20_feature_unit *fu_v2;
323181747Skmacy		const struct usb_audio20_sample_rate_unit *ru_v2;
324181641Skmacy		const struct usb_audio20_processing_unit_0 *pu_v2;
325196725Sadrian		const struct usb_audio20_extension_unit_0 *eu_v2;
326181747Skmacy		const struct usb_audio20_effect_unit *ef_v2;
327181641Skmacy	}	u;
328181641Skmacy	struct uaudio_search_result usr;
329181641Skmacy	struct uaudio_terminal_node *root;
330201804Sbz};
331201751Salc
332201804Sbzstruct uaudio_format {
333181641Skmacy	uint16_t wFormat;
334181641Skmacy	uint8_t	bPrecision;
335181641Skmacy	uint32_t freebsd_fmt;
336181641Skmacy	const char *description;
337181641Skmacy};
338181641Skmacy
339181641Skmacystatic const struct uaudio_format uaudio10_formats[] = {
340181641Skmacy
341181641Skmacy	{UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
342181641Skmacy	{UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
343181641Skmacy	{UA_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
344181641Skmacy	{UA_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
345181641Skmacy
346181641Skmacy	{UA_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
347181641Skmacy	{UA_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
348181641Skmacy	{UA_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
349181641Skmacy	{UA_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
350181641Skmacy
351181641Skmacy	{UA_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
352181641Skmacy	{UA_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
353181641Skmacy
354181641Skmacy	{0, 0, 0, NULL}
355181641Skmacy};
356181641Skmacy
357181641Skmacystatic const struct uaudio_format uaudio20_formats[] = {
358181641Skmacy
359181641Skmacy	{UA20_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
360181641Skmacy	{UA20_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
361181641Skmacy	{UA20_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
362181641Skmacy	{UA20_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
363181641Skmacy
364181641Skmacy	{UA20_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
365181641Skmacy	{UA20_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
366181641Skmacy	{UA20_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
367181641Skmacy	{UA20_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
368181641Skmacy
369181641Skmacy	{UA20_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
370181641Skmacy	{UA20_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
371181641Skmacy
372181641Skmacy	{0, 0, 0, NULL}
373181641Skmacy};
374181641Skmacy
375181641Skmacy#define	UAC_OUTPUT	0
376181641Skmacy#define	UAC_INPUT	1
377181641Skmacy#define	UAC_EQUAL	2
378181641Skmacy#define	UAC_RECORD	3
379181641Skmacy#define	UAC_NCLASSES	4
380181641Skmacy
381181641Skmacy#ifdef USB_DEBUG
382181641Skmacystatic const char *uac_names[] = {
383181641Skmacy	"outputs", "inputs", "equalization", "record"
384181641Skmacy};
385181641Skmacy
386181641Skmacy#endif
387181641Skmacy
388181641Skmacy/* prototypes */
389181641Skmacy
390181641Skmacystatic device_probe_t uaudio_probe;
391181641Skmacystatic device_attach_t uaudio_attach;
392181641Skmacystatic device_detach_t uaudio_detach;
393181641Skmacy
394181641Skmacystatic usb_callback_t uaudio_chan_play_callback;
395181641Skmacystatic usb_callback_t uaudio_chan_play_sync_callback;
396181641Skmacystatic usb_callback_t uaudio_chan_record_callback;
397181641Skmacystatic usb_callback_t uaudio_chan_record_sync_callback;
398181641Skmacystatic usb_callback_t uaudio_mixer_write_cfg_callback;
399181641Skmacystatic usb_callback_t umidi_bulk_read_callback;
400181641Skmacystatic usb_callback_t umidi_bulk_write_callback;
401181641Skmacy
402181641Skmacy/* ==== USB audio v1.0 ==== */
403181641Skmacy
404181641Skmacystatic void	uaudio_mixer_add_mixer(struct uaudio_softc *,
405181641Skmacy		    const struct uaudio_terminal_node *, int);
406181641Skmacystatic void	uaudio_mixer_add_selector(struct uaudio_softc *,
407181641Skmacy		    const struct uaudio_terminal_node *, int);
408181641Skmacystatic uint32_t	uaudio_mixer_feature_get_bmaControls(
409181641Skmacy		    const struct usb_audio_feature_unit *, uint8_t);
410181641Skmacystatic void	uaudio_mixer_add_feature(struct uaudio_softc *,
411181641Skmacy		    const struct uaudio_terminal_node *, int);
412181641Skmacystatic void	uaudio_mixer_add_processing_updown(struct uaudio_softc *,
413181641Skmacy		    const struct uaudio_terminal_node *, int);
414181641Skmacystatic void	uaudio_mixer_add_processing(struct uaudio_softc *,
415181641Skmacy		    const struct uaudio_terminal_node *, int);
416181641Skmacystatic void	uaudio_mixer_add_extension(struct uaudio_softc *,
417181641Skmacy		    const struct uaudio_terminal_node *, int);
418181641Skmacystatic struct	usb_audio_cluster uaudio_mixer_get_cluster(uint8_t,
419181641Skmacy		    const struct uaudio_terminal_node *);
420181641Skmacystatic uint16_t	uaudio_mixer_determine_class(const struct uaudio_terminal_node *,
421181641Skmacy		    struct uaudio_mixer_node *);
422181641Skmacystatic uint16_t	uaudio_mixer_feature_name(const struct uaudio_terminal_node *,
423181641Skmacy		    struct uaudio_mixer_node *);
424181641Skmacystatic void	uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *,
425181641Skmacy		    const uint8_t *, uint8_t, struct uaudio_search_result *);
426181641Skmacystatic const void *uaudio_mixer_verify_desc(const void *, uint32_t);
427181641Skmacystatic usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t);
428181641Skmacystatic int	uaudio_mixer_get(struct usb_device *, uint16_t, uint8_t,
429181641Skmacy		    struct uaudio_mixer_node *);
430181641Skmacy
431181641Skmacy/* ==== USB audio v2.0 ==== */
432181641Skmacy
433181641Skmacystatic void	uaudio20_mixer_add_mixer(struct uaudio_softc *,
434181641Skmacy		    const struct uaudio_terminal_node *, int);
435181641Skmacystatic void	uaudio20_mixer_add_selector(struct uaudio_softc *,
436181641Skmacy		    const struct uaudio_terminal_node *, int);
437181641Skmacystatic void	uaudio20_mixer_add_feature(struct uaudio_softc *,
438181641Skmacy		    const struct uaudio_terminal_node *, int);
439181641Skmacystatic struct	usb_audio20_cluster uaudio20_mixer_get_cluster(uint8_t,
440183342Skmacy		    const struct uaudio_terminal_node *);
441183342Skmacystatic uint16_t	uaudio20_mixer_determine_class(const struct uaudio_terminal_node *,
442181641Skmacy		    struct uaudio_mixer_node *);
443181641Skmacystatic uint16_t	uaudio20_mixer_feature_name(const struct uaudio_terminal_node *,
444181641Skmacy		    struct uaudio_mixer_node *);
445181641Skmacystatic void	uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *,
446181641Skmacy		    const uint8_t *, uint8_t, struct uaudio_search_result *);
447181641Skmacystatic const void *uaudio20_mixer_verify_desc(const void *, uint32_t);
448181641Skmacystatic usb_error_t uaudio20_set_speed(struct usb_device *, uint8_t,
449181641Skmacy		    uint8_t, uint32_t);
450181641Skmacy
451181641Skmacy/* USB audio v1.0 and v2.0 */
452181641Skmacy
453181641Skmacystatic void	uaudio_chan_fill_info_sub(struct uaudio_softc *,
454181641Skmacy		    struct usb_device *, uint32_t, uint8_t, uint8_t);
455181641Skmacystatic void	uaudio_chan_fill_info(struct uaudio_softc *,
456181641Skmacy		    struct usb_device *);
457181641Skmacystatic void	uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
458181641Skmacy		    struct uaudio_mixer_node *);
459181641Skmacystatic void	uaudio_mixer_add_ctl(struct uaudio_softc *,
460181641Skmacy		    struct uaudio_mixer_node *);
461181641Skmacystatic void	uaudio_mixer_fill_info(struct uaudio_softc *,
462204160Skmacy		    struct usb_device *, void *);
463204160Skmacystatic void	uaudio_mixer_ctl_set(struct uaudio_softc *,
464181641Skmacy		    struct uaudio_mixer_node *, uint8_t, int32_t val);
465181641Skmacystatic int	uaudio_mixer_signext(uint8_t, int);
466181641Skmacystatic int	uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val);
467181641Skmacystatic void	uaudio_mixer_init(struct uaudio_softc *);
468181641Skmacystatic const struct uaudio_terminal_node *uaudio_mixer_get_input(
469181641Skmacy		    const struct uaudio_terminal_node *, uint8_t);
470181641Skmacystatic const struct uaudio_terminal_node *uaudio_mixer_get_output(
471181641Skmacy		    const struct uaudio_terminal_node *, uint8_t);
472181641Skmacystatic void	uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *,
473181641Skmacy		    uint8_t, uint8_t, struct uaudio_search_result *);
474181641Skmacystatic uint8_t	umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t);
475181641Skmacystatic struct	umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *);
476181641Skmacystatic void	umidi_start_read(struct usb_fifo *);
477181641Skmacystatic void	umidi_stop_read(struct usb_fifo *);
478181641Skmacystatic void	umidi_start_write(struct usb_fifo *);
479181641Skmacystatic void	umidi_stop_write(struct usb_fifo *);
480181641Skmacystatic int	umidi_open(struct usb_fifo *, int);
481181641Skmacystatic int	umidi_ioctl(struct usb_fifo *, u_long cmd, void *, int);
482181641Skmacystatic void	umidi_close(struct usb_fifo *, int);
483181641Skmacystatic void	umidi_init(device_t dev);
484181641Skmacystatic int	umidi_probe(device_t dev);
485181641Skmacystatic int	umidi_detach(device_t dev);
486181641Skmacy
487181641Skmacy#ifdef USB_DEBUG
488181641Skmacystatic void	uaudio_chan_dump_ep_desc(
489181641Skmacy		    const usb_endpoint_descriptor_audio_t *);
490181641Skmacy#endif
491181641Skmacy
492181641Skmacystatic const struct usb_config
493181641Skmacy	uaudio_cfg_record[UAUDIO_NCHANBUFS + 1] = {
494181641Skmacy	[0] = {
495181641Skmacy		.type = UE_ISOCHRONOUS,
496181641Skmacy		.endpoint = UE_ADDR_ANY,
497181641Skmacy		.direction = UE_DIR_IN,
498181641Skmacy		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
499181641Skmacy		.frames = UAUDIO_NFRAMES,
500181641Skmacy		.flags = {.short_xfer_ok = 1,},
501181641Skmacy		.callback = &uaudio_chan_record_callback,
502181641Skmacy	},
503181641Skmacy
504181641Skmacy	[1] = {
505181641Skmacy		.type = UE_ISOCHRONOUS,
506181641Skmacy		.endpoint = UE_ADDR_ANY,
507181641Skmacy		.direction = UE_DIR_IN,
508181641Skmacy		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
509181641Skmacy		.frames = UAUDIO_NFRAMES,
510181641Skmacy		.flags = {.short_xfer_ok = 1,},
511181641Skmacy		.callback = &uaudio_chan_record_callback,
512181641Skmacy	},
513181641Skmacy
514181641Skmacy	[2] = {
515181641Skmacy		.type = UE_ISOCHRONOUS,
516181641Skmacy		.endpoint = UE_ADDR_ANY,
517181641Skmacy		.direction = UE_DIR_OUT,
518181641Skmacy		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
519181641Skmacy		.frames = 1,
520181641Skmacy		.flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
521181641Skmacy		.callback = &uaudio_chan_record_sync_callback,
522181641Skmacy	},
523181641Skmacy};
524181641Skmacy
525181641Skmacystatic const struct usb_config
526181641Skmacy	uaudio_cfg_play[UAUDIO_NCHANBUFS + 1] = {
527181641Skmacy	[0] = {
528181641Skmacy		.type = UE_ISOCHRONOUS,
529181641Skmacy		.endpoint = UE_ADDR_ANY,
530181641Skmacy		.direction = UE_DIR_OUT,
531196726Sadrian		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
532197070Sjkim		.frames = UAUDIO_NFRAMES,
533196726Sadrian		.flags = {.short_xfer_ok = 1,},
534196726Sadrian		.callback = &uaudio_chan_play_callback,
535196726Sadrian	},
536196726Sadrian
537196726Sadrian	[1] = {
538196726Sadrian		.type = UE_ISOCHRONOUS,
539196726Sadrian		.endpoint = UE_ADDR_ANY,
540196726Sadrian		.direction = UE_DIR_OUT,
541196726Sadrian		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
542196726Sadrian		.frames = UAUDIO_NFRAMES,
543196726Sadrian		.flags = {.short_xfer_ok = 1,},
544196726Sadrian		.callback = &uaudio_chan_play_callback,
545196726Sadrian	},
546196726Sadrian
547196726Sadrian	[2] = {
548196726Sadrian		.type = UE_ISOCHRONOUS,
549196726Sadrian		.endpoint = UE_ADDR_ANY,
550196726Sadrian		.direction = UE_DIR_IN,
551196726Sadrian		.bufsize = 0,	/* use "wMaxPacketSize * frames" */
552196726Sadrian		.frames = 1,
553196726Sadrian		.flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,},
554196726Sadrian		.callback = &uaudio_chan_play_sync_callback,
555196726Sadrian	},
556196726Sadrian};
557196726Sadrian
558196726Sadrianstatic const struct usb_config
559196726Sadrian	uaudio_mixer_config[1] = {
560196726Sadrian	[0] = {
561181641Skmacy		.type = UE_CONTROL,
562181641Skmacy		.endpoint = 0x00,	/* Control pipe */
563181641Skmacy		.direction = UE_DIR_ANY,
564201804Sbz		.bufsize = (sizeof(struct usb_device_request) + 4),
565181641Skmacy		.callback = &uaudio_mixer_write_cfg_callback,
566181641Skmacy		.timeout = 1000,	/* 1 second */
567181641Skmacy	},
568201751Salc};
569181641Skmacy
570181641Skmacystatic const
571181641Skmacyuint8_t	umidi_cmd_to_len[16] = {
572181641Skmacy	[0x0] = 0,			/* reserved */
573181641Skmacy	[0x1] = 0,			/* reserved */
574181641Skmacy	[0x2] = 2,			/* bytes */
575181641Skmacy	[0x3] = 3,			/* bytes */
576181641Skmacy	[0x4] = 3,			/* bytes */
577181641Skmacy	[0x5] = 1,			/* bytes */
578181641Skmacy	[0x6] = 2,			/* bytes */
579181641Skmacy	[0x7] = 3,			/* bytes */
580181641Skmacy	[0x8] = 3,			/* bytes */
581181641Skmacy	[0x9] = 3,			/* bytes */
582181641Skmacy	[0xA] = 3,			/* bytes */
583181641Skmacy	[0xB] = 3,			/* bytes */
584181641Skmacy	[0xC] = 2,			/* bytes */
585181641Skmacy	[0xD] = 2,			/* bytes */
586181641Skmacy	[0xE] = 3,			/* bytes */
587181641Skmacy	[0xF] = 1,			/* bytes */
588181641Skmacy};
589181641Skmacy
590181641Skmacystatic const struct usb_config
591181641Skmacy	umidi_config[UMIDI_N_TRANSFER] = {
592181641Skmacy	[UMIDI_TX_TRANSFER] = {
593181641Skmacy		.type = UE_BULK,
594181641Skmacy		.endpoint = UE_ADDR_ANY,
595181641Skmacy		.direction = UE_DIR_OUT,
596181641Skmacy		.bufsize = UMIDI_TX_BUFFER,
597181641Skmacy		.callback = &umidi_bulk_write_callback,
598181641Skmacy	},
599181641Skmacy
600181641Skmacy	[UMIDI_RX_TRANSFER] = {
601181641Skmacy		.type = UE_BULK,
602181641Skmacy		.endpoint = UE_ADDR_ANY,
603201804Sbz		.direction = UE_DIR_IN,
604181641Skmacy		.bufsize = 4,	/* bytes */
605181641Skmacy		.flags = {.short_xfer_ok = 1,.proxy_buffer = 1,},
606181641Skmacy		.callback = &umidi_bulk_read_callback,
607181641Skmacy	},
608181641Skmacy};
609181641Skmacy
610181641Skmacystatic devclass_t uaudio_devclass;
611181641Skmacy
612181641Skmacystatic device_method_t uaudio_methods[] = {
613195649Salc	DEVMETHOD(device_probe, uaudio_probe),
614181641Skmacy	DEVMETHOD(device_attach, uaudio_attach),
615181641Skmacy	DEVMETHOD(device_detach, uaudio_detach),
616181641Skmacy	DEVMETHOD(device_suspend, bus_generic_suspend),
617181641Skmacy	DEVMETHOD(device_resume, bus_generic_resume),
618181641Skmacy	DEVMETHOD(device_shutdown, bus_generic_shutdown),
619181641Skmacy
620195385Salc	DEVMETHOD_END
621195385Salc};
622195385Salc
623195385Salcstatic driver_t uaudio_driver = {
624195649Salc	.name = "uaudio",
625181641Skmacy	.methods = uaudio_methods,
626181641Skmacy	.size = sizeof(struct uaudio_softc),
627181641Skmacy};
628181641Skmacy
629181641Skmacystatic const STRUCT_USB_HOST_ID __used uaudio_devs[] = {
630181641Skmacy	/* Generic USB audio class match */
631181641Skmacy	{USB_IFACE_CLASS(UICLASS_AUDIO),
632181641Skmacy	 USB_IFACE_SUBCLASS(UISUBCLASS_AUDIOCONTROL),},
633181641Skmacy	/* Generic USB MIDI class match */
634181641Skmacy	{USB_IFACE_CLASS(UICLASS_AUDIO),
635181641Skmacy	 USB_IFACE_SUBCLASS(UISUBCLASS_MIDISTREAM),},
636181641Skmacy};
637181641Skmacy
638181641Skmacystatic int
639181641Skmacyuaudio_probe(device_t dev)
640181641Skmacy{
641181641Skmacy	struct usb_attach_arg *uaa = device_get_ivars(dev);
642181641Skmacy
643181641Skmacy	if (uaa->usb_mode != USB_MODE_HOST)
644181641Skmacy		return (ENXIO);
645181641Skmacy
646181641Skmacy	/* lookup non-standard device */
647181641Skmacy
648181641Skmacy	if (uaa->info.bInterfaceClass != UICLASS_AUDIO) {
649181641Skmacy		if (uaa->info.bInterfaceClass != UICLASS_VENDOR ||
650181641Skmacy		    usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0)
651181641Skmacy			return (ENXIO);
652181641Skmacy	}
653181641Skmacy
654181641Skmacy	/* check for AUDIO control interface */
655181641Skmacy
656181641Skmacy	if (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL) {
657181641Skmacy		if (usb_test_quirk(uaa, UQ_BAD_AUDIO))
658181641Skmacy			return (ENXIO);
659181641Skmacy		else
660181641Skmacy			return (BUS_PROBE_GENERIC);
661181641Skmacy	}
662181641Skmacy
663181641Skmacy	/* check for MIDI stream */
664181641Skmacy
665181641Skmacy	if (uaa->info.bInterfaceSubClass == UISUBCLASS_MIDISTREAM) {
666181641Skmacy		if (usb_test_quirk(uaa, UQ_BAD_MIDI))
667181641Skmacy			return (ENXIO);
668181641Skmacy		else
669181641Skmacy			return (BUS_PROBE_GENERIC);
670181641Skmacy	}
671181641Skmacy	return (ENXIO);
672181641Skmacy}
673181641Skmacy
674181641Skmacystatic int
675181641Skmacyuaudio_attach(device_t dev)
676181641Skmacy{
677181641Skmacy	struct usb_attach_arg *uaa = device_get_ivars(dev);
678181641Skmacy	struct uaudio_softc *sc = device_get_softc(dev);
679181641Skmacy	struct usb_interface_descriptor *id;
680181641Skmacy	device_t child;
681181641Skmacy
682181641Skmacy	sc->sc_play_chan.priv_sc = sc;
683181641Skmacy	sc->sc_rec_chan.priv_sc = sc;
684181641Skmacy	sc->sc_udev = uaa->device;
685181641Skmacy	sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
686181641Skmacy	sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
687181641Skmacy
688181641Skmacy	if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR))
689181641Skmacy		sc->sc_uq_audio_swap_lr = 1;
690181641Skmacy
691181641Skmacy	if (usb_test_quirk(uaa, UQ_AU_INP_ASYNC))
692181641Skmacy		sc->sc_uq_au_inp_async = 1;
693181641Skmacy
694181641Skmacy	if (usb_test_quirk(uaa, UQ_AU_NO_XU))
695181641Skmacy		sc->sc_uq_au_no_xu = 1;
696181747Skmacy
697181747Skmacy	if (usb_test_quirk(uaa, UQ_BAD_ADC))
698181747Skmacy		sc->sc_uq_bad_adc = 1;
699181641Skmacy
700181641Skmacy	if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS))
701181747Skmacy		sc->sc_uq_au_vendor_class = 1;
702181747Skmacy
703181747Skmacy	umidi_init(dev);
704181747Skmacy
705181808Skmacy	device_set_usb_desc(dev);
706181747Skmacy
707181747Skmacy	id = usbd_get_interface_descriptor(uaa->iface);
708181747Skmacy
709181747Skmacy	/* must fill mixer info before channel info */
710181808Skmacy	uaudio_mixer_fill_info(sc, uaa->device, id);
711181747Skmacy
712181747Skmacy	/* fill channel info */
713181747Skmacy	uaudio_chan_fill_info(sc, uaa->device);
714181641Skmacy
715181641Skmacy	DPRINTF("audio rev %d.%02x\n",
716181641Skmacy	    sc->sc_audio_rev >> 8,
717181641Skmacy	    sc->sc_audio_rev & 0xff);
718181641Skmacy
719181641Skmacy	DPRINTF("%d mixer controls\n",
720181641Skmacy	    sc->sc_mixer_count);
721181641Skmacy
722181641Skmacy	if (sc->sc_play_chan.valid) {
723181641Skmacy		device_printf(dev, "Play: %d Hz, %d ch, %s format.\n",
724181747Skmacy		    sc->sc_play_chan.sample_rate,
725181747Skmacy		    sc->sc_play_chan.channels,
726181747Skmacy		    sc->sc_play_chan.p_fmt->description);
727181747Skmacy	} else {
728181747Skmacy		device_printf(dev, "No playback.\n");
729181747Skmacy	}
730181747Skmacy
731181747Skmacy	if (sc->sc_rec_chan.valid) {
732181747Skmacy		device_printf(dev, "Record: %d Hz, %d ch, %s format.\n",
733181747Skmacy		    sc->sc_rec_chan.sample_rate,
734181747Skmacy		    sc->sc_play_chan.channels,
735181747Skmacy		    sc->sc_rec_chan.p_fmt->description);
736181747Skmacy	} else {
737181747Skmacy		device_printf(dev, "No recording.\n");
738181747Skmacy	}
739181747Skmacy
740181747Skmacy	if (sc->sc_midi_chan.valid) {
741181747Skmacy
742181747Skmacy		if (umidi_probe(dev)) {
743181747Skmacy			goto detach;
744181641Skmacy		}
745181641Skmacy		device_printf(dev, "MIDI sequencer.\n");
746181641Skmacy	} else {
747181641Skmacy		device_printf(dev, "No midi sequencer.\n");
748181641Skmacy	}
749181641Skmacy
750181641Skmacy	DPRINTF("doing child attach\n");
751181641Skmacy
752181641Skmacy	/* attach the children */
753181641Skmacy
754181641Skmacy	sc->sc_sndcard_func.func = SCF_PCM;
755181641Skmacy
756181641Skmacy	/*
757181641Skmacy	 * Only attach a PCM device if we have a playback, recording
758181641Skmacy	 * or mixer device present:
759181641Skmacy	 */
760181641Skmacy	if (sc->sc_play_chan.valid ||
761181641Skmacy	    sc->sc_rec_chan.valid ||
762181641Skmacy	    sc->sc_mix_info) {
763181641Skmacy		child = device_add_child(dev, "pcm", -1);
764181641Skmacy
765181641Skmacy		if (child == NULL) {
766181641Skmacy			DPRINTF("out of memory\n");
767195949Skib			goto detach;
768181641Skmacy		}
769181641Skmacy		device_set_ivars(child, &sc->sc_sndcard_func);
770181641Skmacy	}
771181641Skmacy
772181641Skmacy	if (bus_generic_attach(dev)) {
773181641Skmacy		DPRINTF("child attach failed\n");
774181641Skmacy		goto detach;
775181641Skmacy	}
776181641Skmacy	return (0);			/* success */
777181641Skmacy
778181641Skmacydetach:
779181641Skmacy	uaudio_detach(dev);
780181641Skmacy	return (ENXIO);
781181641Skmacy}
782181641Skmacy
783181641Skmacystatic void
784181641Skmacyuaudio_pcm_setflags(device_t dev, uint32_t flags)
785181641Skmacy{
786181641Skmacy	pcm_setflags(dev, pcm_getflags(dev) | flags);
787181641Skmacy}
788181641Skmacy
789181641Skmacyint
790181641Skmacyuaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class)
791196726Sadrian{
792196726Sadrian	struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
793196726Sadrian	char status[SND_STATUSLEN];
794196726Sadrian
795196726Sadrian	uaudio_mixer_init(sc);
796196726Sadrian
797196726Sadrian	if (sc->sc_uq_audio_swap_lr) {
798196726Sadrian		DPRINTF("hardware has swapped left and right\n");
799196726Sadrian		/* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */
800196726Sadrian	}
801196726Sadrian	if (!(sc->sc_mix_info & SOUND_MASK_PCM)) {
802196726Sadrian
803196726Sadrian		DPRINTF("emulating master volume\n");
804196726Sadrian
805196726Sadrian		/*
806196726Sadrian		 * Emulate missing pcm mixer controller
807196726Sadrian		 * through FEEDER_VOLUME
808196726Sadrian		 */
809196726Sadrian		uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL);
810196726Sadrian	}
811196726Sadrian	if (mixer_init(dev, mixer_class, sc)) {
812196726Sadrian		goto detach;
813196726Sadrian	}
814196726Sadrian	sc->sc_mixer_init = 1;
815196726Sadrian
816196726Sadrian	snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
817196726Sadrian
818196726Sadrian	if (pcm_register(dev, sc,
819196726Sadrian	    sc->sc_play_chan.valid ? 1 : 0,
820196726Sadrian	    sc->sc_rec_chan.valid ? 1 : 0)) {
821196726Sadrian		goto detach;
822196726Sadrian	}
823196726Sadrian
824196726Sadrian	uaudio_pcm_setflags(dev, SD_F_MPSAFE);
825196726Sadrian	sc->sc_pcm_registered = 1;
826196726Sadrian
827196726Sadrian	if (sc->sc_play_chan.valid) {
828196726Sadrian		pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc);
829196726Sadrian	}
830196726Sadrian	if (sc->sc_rec_chan.valid) {
831196726Sadrian		pcm_addchan(dev, PCMDIR_REC, chan_class, sc);
832196726Sadrian	}
833181641Skmacy	pcm_setstatus(dev, status);
834181641Skmacy
835181641Skmacy	return (0);			/* success */
836181641Skmacy
837181641Skmacydetach:
838181641Skmacy	uaudio_detach_sub(dev);
839181641Skmacy	return (ENXIO);
840181641Skmacy}
841181641Skmacy
842181641Skmacyint
843181641Skmacyuaudio_detach_sub(device_t dev)
844181641Skmacy{
845181641Skmacy	struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
846181641Skmacy	int error = 0;
847181641Skmacy
848181641Skmacyrepeat:
849181641Skmacy	if (sc->sc_pcm_registered) {
850181641Skmacy		error = pcm_unregister(dev);
851181641Skmacy	} else {
852181641Skmacy		if (sc->sc_mixer_init) {
853181641Skmacy			error = mixer_uninit(dev);
854181641Skmacy		}
855181641Skmacy	}
856181641Skmacy
857181641Skmacy	if (error) {
858181641Skmacy		device_printf(dev, "Waiting for sound application to exit!\n");
859181641Skmacy		usb_pause_mtx(NULL, 2 * hz);
860181641Skmacy		goto repeat;		/* try again */
861181641Skmacy	}
862181641Skmacy	return (0);			/* success */
863181641Skmacy}
864181641Skmacy
865181641Skmacystatic int
866181641Skmacyuaudio_detach(device_t dev)
867181641Skmacy{
868181641Skmacy	struct uaudio_softc *sc = device_get_softc(dev);
869181641Skmacy
870181641Skmacy	/*
871181641Skmacy	 * Stop USB transfers early so that any audio applications
872181641Skmacy	 * will time out and close opened /dev/dspX.Y device(s), if
873181641Skmacy	 * any.
874181641Skmacy	 */
875181641Skmacy	if (sc->sc_play_chan.valid)
876181641Skmacy		usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1);
877181641Skmacy	if (sc->sc_rec_chan.valid)
878181641Skmacy		usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1);
879181641Skmacy
880181641Skmacy	if (bus_generic_detach(dev) != 0) {
881181641Skmacy		DPRINTF("detach failed!\n");
882181641Skmacy	}
883181641Skmacy	sbuf_delete(&sc->sc_sndstat);
884181641Skmacy	sc->sc_sndstat_valid = 0;
885181641Skmacy
886181641Skmacy	umidi_detach(dev);
887181641Skmacy
888181641Skmacy	return (0);
889181641Skmacy}
890181641Skmacy
891181641Skmacy/*========================================================================*
892181641Skmacy * AS - Audio Stream - routines
893181641Skmacy *========================================================================*/
894181641Skmacy
895181641Skmacy#ifdef USB_DEBUG
896181641Skmacystatic void
897181641Skmacyuaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed)
898181641Skmacy{
899181641Skmacy	if (ed) {
900181641Skmacy		DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n"
901181641Skmacy		    "bEndpointAddress=%d bmAttributes=0x%x \n"
902181641Skmacy		    "wMaxPacketSize=%d bInterval=%d \n"
903181641Skmacy		    "bRefresh=%d bSynchAddress=%d\n",
904181641Skmacy		    ed, ed->bLength, ed->bDescriptorType,
905181641Skmacy		    ed->bEndpointAddress, ed->bmAttributes,
906181641Skmacy		    UGETW(ed->wMaxPacketSize), ed->bInterval,
907181641Skmacy		    UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0,
908181641Skmacy		    UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0);
909181641Skmacy	}
910181641Skmacy}
911181641Skmacy
912181641Skmacy#endif
913181641Skmacy
914181641Skmacy/*
915181641Skmacy * The following is a workaround for broken no-name USB audio devices
916181641Skmacy * sold by dealextreme called "3D sound". The problem is that the
917181641Skmacy * manufacturer computed wMaxPacketSize is too small to hold the
918181641Skmacy * actual data sent. In other words the device sometimes sends more
919181641Skmacy * data than it actually reports it can send in a single isochronous
920181641Skmacy * packet.
921181641Skmacy */
922181641Skmacystatic void
923181641Skmacyuaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep,
924181641Skmacy    uint32_t xps, uint32_t add)
925181641Skmacy{
926181641Skmacy	uint32_t mps;
927181641Skmacy
928181641Skmacy	mps = UGETW(ep->wMaxPacketSize);
929181641Skmacy
930181641Skmacy	/*
931181641Skmacy	 * If the device indicates it can send more data than what the
932181641Skmacy	 * sample rate indicates, we apply the workaround.
933181641Skmacy	 */
934181641Skmacy	if (mps > xps) {
935181641Skmacy
936181641Skmacy		/* allow additional data */
937181641Skmacy		xps += add;
938181641Skmacy
939181641Skmacy		/* check against the maximum USB 1.x length */
940181641Skmacy		if (xps > 1023)
941181641Skmacy			xps = 1023;
942181641Skmacy
943181641Skmacy		/* check if we should do an update */
944181641Skmacy		if (mps < xps) {
945181641Skmacy			/* simply update the wMaxPacketSize field */
946181641Skmacy			USETW(ep->wMaxPacketSize, xps);
947181641Skmacy			DPRINTF("Workaround: Updated wMaxPacketSize "
948181641Skmacy			    "from %d to %d bytes.\n",
949181641Skmacy			    (int)mps, (int)xps);
950181641Skmacy		}
951181641Skmacy	}
952181641Skmacy}
953181641Skmacy
954181641Skmacystatic usb_error_t
955181641Skmacyuaudio20_check_rate(struct usb_device *udev, uint8_t iface_no,
956181641Skmacy    uint8_t clockid, uint32_t rate)
957181641Skmacy{
958181641Skmacy	struct usb_device_request req;
959181641Skmacy	usb_error_t error;
960181641Skmacy	uint8_t data[255];
961181641Skmacy	uint16_t actlen;
962181641Skmacy	uint16_t rates;
963181641Skmacy	uint16_t x;
964181641Skmacy
965181641Skmacy	DPRINTFN(6, "ifaceno=%d clockid=%d rate=%u\n",
966181641Skmacy	    iface_no, clockid, rate);
967181641Skmacy
968181641Skmacy	req.bmRequestType = UT_READ_CLASS_INTERFACE;
969181641Skmacy	req.bRequest = UA20_CS_RANGE;
970181641Skmacy	USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
971181641Skmacy	USETW2(req.wIndex, clockid, iface_no);
972181641Skmacy	USETW(req.wLength, 255);
973181641Skmacy
974181641Skmacy        error = usbd_do_request_flags(udev, NULL, &req, data,
975181641Skmacy	    USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT);
976181641Skmacy
977181641Skmacy	if (error != 0 || actlen < 2)
978181641Skmacy		return (USB_ERR_INVAL);
979181641Skmacy
980181641Skmacy	rates = data[0] | (data[1] << 8);
981181641Skmacy	actlen = (actlen - 2) / 12;
982181641Skmacy
983181641Skmacy	if (rates > actlen) {
984181641Skmacy		DPRINTF("Too many rates\n");
985181641Skmacy		rates = actlen;
986181641Skmacy	}
987181641Skmacy
988181641Skmacy	for (x = 0; x != rates; x++) {
989181641Skmacy		uint32_t min = UGETDW(data + 2 + (12 * x));
990181641Skmacy		uint32_t max = UGETDW(data + 6 + (12 * x));
991181641Skmacy		uint32_t res = UGETDW(data + 10 + (12 * x));
992181641Skmacy
993181641Skmacy		if (res == 0) {
994181641Skmacy			DPRINTF("Zero residue\n");
995181641Skmacy			res = 1;
996181641Skmacy		}
997181641Skmacy
998181641Skmacy		if (min > max) {
999181641Skmacy			DPRINTF("Swapped max and min\n");
1000195949Skib			uint32_t temp;
1001195949Skib			temp = min;
1002195949Skib			min = max;
1003195949Skib			max = temp;
1004195949Skib		}
1005195949Skib
1006195949Skib		if (rate >= min && rate <= max &&
1007195949Skib		    (((rate - min) % res) == 0)) {
1008195949Skib			return (0);
1009195949Skib		}
1010195949Skib	}
1011195949Skib	return (USB_ERR_INVAL);
1012195949Skib}
1013195949Skib
1014195949Skibstatic void
1015195949Skibuaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev,
1016195949Skib    uint32_t rate, uint8_t channels, uint8_t bit_resolution)
1017195949Skib{
1018195949Skib	struct usb_descriptor *desc = NULL;
1019195949Skib	union uaudio_asid asid = { NULL };
1020195949Skib	union uaudio_asf1d asf1d = { NULL };
1021197046Skib	union uaudio_sed sed = { NULL };
1022197046Skib	usb_endpoint_descriptor_audio_t *ed1 = NULL;
1023195949Skib	const struct usb_audio_control_descriptor *acdp = NULL;
1024195949Skib	struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
1025195949Skib	struct usb_interface_descriptor *id;
1026195949Skib	const struct uaudio_format *p_fmt = NULL;
1027195949Skib	struct uaudio_chan *chan;
1028195949Skib	uint16_t curidx = 0xFFFF;
1029195949Skib	uint16_t lastidx = 0xFFFF;
1030195949Skib	uint16_t alt_index = 0;
1031195949Skib	uint16_t audio_rev = 0;
1032195949Skib	uint16_t x;
1033195949Skib	uint8_t ep_dir;
1034181641Skmacy	uint8_t bChannels;
1035181641Skmacy	uint8_t bBitResolution;
1036181641Skmacy	uint8_t audio_if = 0;
1037181641Skmacy	uint8_t uma_if_class;
1038181641Skmacy
1039181641Skmacy	while ((desc = usb_desc_foreach(cd, desc))) {
1040181641Skmacy
1041181641Skmacy		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
1042181641Skmacy		    (desc->bLength >= sizeof(*id))) {
1043181641Skmacy
1044181641Skmacy			id = (void *)desc;
1045181641Skmacy
1046181641Skmacy			if (id->bInterfaceNumber != lastidx) {
1047181641Skmacy				lastidx = id->bInterfaceNumber;
1048181641Skmacy				curidx++;
1049181641Skmacy				alt_index = 0;
1050181641Skmacy
1051181641Skmacy			} else {
1052181641Skmacy				alt_index++;
1053181641Skmacy			}
1054181641Skmacy
1055181641Skmacy			uma_if_class =
1056181641Skmacy			    ((id->bInterfaceClass == UICLASS_AUDIO) ||
1057181641Skmacy			    ((id->bInterfaceClass == UICLASS_VENDOR) &&
1058181641Skmacy			    (sc->sc_uq_au_vendor_class != 0)));
1059181641Skmacy
1060181641Skmacy			if ((uma_if_class != 0) && (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) {
1061181641Skmacy				audio_if = 1;
1062181641Skmacy			} else {
1063181641Skmacy				audio_if = 0;
1064181641Skmacy			}
1065181641Skmacy
1066181641Skmacy			if ((uma_if_class != 0) &&
1067181641Skmacy			    (id->bInterfaceSubClass == UISUBCLASS_MIDISTREAM)) {
1068181641Skmacy
1069204160Skmacy				/*
1070181641Skmacy				 * XXX could allow multiple MIDI interfaces
1071204160Skmacy				 */
1072181641Skmacy
1073181641Skmacy				if ((sc->sc_midi_chan.valid == 0) &&
1074181641Skmacy				    usbd_get_iface(udev, curidx)) {
1075181641Skmacy					sc->sc_midi_chan.iface_index = curidx;
1076181641Skmacy					sc->sc_midi_chan.iface_alt_index = alt_index;
1077181641Skmacy					sc->sc_midi_chan.valid = 1;
1078181641Skmacy				}
1079181641Skmacy			}
1080181641Skmacy			asid.v1 = NULL;
1081181641Skmacy			asf1d.v1 = NULL;
1082181641Skmacy			ed1 = NULL;
1083181641Skmacy			sed.v1 = NULL;
1084181641Skmacy		}
1085181641Skmacy
1086181641Skmacy		if (audio_if == 0) {
1087181641Skmacy			if ((acdp == NULL) &&
1088181641Skmacy			    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
1089181641Skmacy			    (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) &&
1090181641Skmacy			    (desc->bLength >= sizeof(*acdp))) {
1091181641Skmacy				acdp = (void *)desc;
1092181641Skmacy				audio_rev = UGETW(acdp->bcdADC);
1093181641Skmacy			}
1094181641Skmacy
1095181641Skmacy			/*
1096181641Skmacy			 * Don't collect any USB audio descriptors if
1097181641Skmacy			 * this is not an USB audio stream interface.
1098181641Skmacy			 */
1099181641Skmacy			continue;
1100181641Skmacy		}
1101181641Skmacy
1102181641Skmacy		if ((acdp != NULL) &&
1103181641Skmacy		    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
1104181641Skmacy		    (desc->bDescriptorSubtype == AS_GENERAL) &&
1105181641Skmacy		    (asid.v1 == NULL)) {
1106181641Skmacy			if (audio_rev >= UAUDIO_VERSION_30) {
1107181641Skmacy				/* FALLTHROUGH */
1108181641Skmacy			} else if (audio_rev >= UAUDIO_VERSION_20) {
1109181641Skmacy				if (desc->bLength >= sizeof(*asid.v2)) {
1110181641Skmacy					asid.v2 = (void *)desc;
1111181641Skmacy				}
1112181641Skmacy			} else {
1113181641Skmacy				if (desc->bLength >= sizeof(*asid.v1)) {
1114181641Skmacy					asid.v1 = (void *)desc;
1115181641Skmacy				}
1116181641Skmacy			}
1117181641Skmacy		}
1118181641Skmacy		if ((acdp != NULL) &&
1119181641Skmacy		    (desc->bDescriptorType == UDESC_CS_INTERFACE) &&
1120181641Skmacy		    (desc->bDescriptorSubtype == FORMAT_TYPE) &&
1121181641Skmacy		    (asf1d.v1 == NULL)) {
1122181641Skmacy			if (audio_rev >= UAUDIO_VERSION_30) {
1123181641Skmacy				/* FALLTHROUGH */
1124181641Skmacy			} else if (audio_rev >= UAUDIO_VERSION_20) {
1125181641Skmacy				if (desc->bLength >= sizeof(*asf1d.v2))
1126181641Skmacy					asf1d.v2 = (void *)desc;
1127181641Skmacy			} else {
1128181641Skmacy				if (desc->bLength >= sizeof(*asf1d.v1)) {
1129181641Skmacy					asf1d.v1 = (void *)desc;
1130181641Skmacy
1131181641Skmacy					if (asf1d.v1->bFormatType != FORMAT_TYPE_I) {
1132181641Skmacy						DPRINTFN(11, "ignored bFormatType = %d\n",
1133181641Skmacy						    asf1d.v1->bFormatType);
1134181641Skmacy						asf1d.v1 = NULL;
1135181641Skmacy						continue;
1136181641Skmacy					}
1137181641Skmacy					if (desc->bLength < (sizeof(*asf1d.v1) +
1138181641Skmacy					    ((asf1d.v1->bSamFreqType == 0) ? 6 :
1139181641Skmacy					    (asf1d.v1->bSamFreqType * 3)))) {
1140181641Skmacy						DPRINTFN(11, "invalid descriptor, "
1141181641Skmacy						    "too short\n");
1142181641Skmacy						asf1d.v1 = NULL;
1143181641Skmacy						continue;
1144181641Skmacy					}
1145181641Skmacy				}
1146181641Skmacy			}
1147181641Skmacy		}
1148181641Skmacy		if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
1149181641Skmacy		    (desc->bLength >= UEP_MINSIZE) &&
1150181641Skmacy		    (ed1 == NULL)) {
1151181641Skmacy			ed1 = (void *)desc;
1152181641Skmacy			if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) {
1153181641Skmacy				ed1 = NULL;
1154181641Skmacy				continue;
1155181641Skmacy			}
1156181641Skmacy		}
1157181641Skmacy		if ((acdp != NULL) &&
1158181641Skmacy		    (desc->bDescriptorType == UDESC_CS_ENDPOINT) &&
1159181641Skmacy		    (desc->bDescriptorSubtype == AS_GENERAL) &&
1160181641Skmacy		    (sed.v1 == NULL)) {
1161181641Skmacy			if (audio_rev >= UAUDIO_VERSION_30) {
1162181641Skmacy				/* FALLTHROUGH */
1163181641Skmacy			} else if (audio_rev >= UAUDIO_VERSION_20) {
1164181641Skmacy				if (desc->bLength >= sizeof(*sed.v2))
1165181641Skmacy					sed.v2 = (void *)desc;
1166181641Skmacy			} else {
1167181641Skmacy				if (desc->bLength >= sizeof(*sed.v1))
1168181641Skmacy					sed.v1 = (void *)desc;
1169181641Skmacy			}
1170181641Skmacy		}
1171181641Skmacy		if (asid.v1 == NULL || asf1d.v1 == NULL ||
1172181641Skmacy		    ed1 == NULL || sed.v1 == NULL) {
1173181641Skmacy			/* need more descriptors */
1174181641Skmacy			continue;
1175181641Skmacy		}
1176181641Skmacy
1177181641Skmacy		ep_dir = UE_GET_DIR(ed1->bEndpointAddress);
1178181641Skmacy
1179181641Skmacy		/* We ignore sync endpoint information until further. */
1180181641Skmacy
1181181641Skmacy		if (audio_rev >= UAUDIO_VERSION_30) {
1182181641Skmacy			goto next_ep;
1183181641Skmacy		} else if (audio_rev >= UAUDIO_VERSION_20) {
1184181641Skmacy
1185181641Skmacy			uint32_t dwFormat;
1186181641Skmacy			uint8_t bSubslotSize;
1187181641Skmacy
1188181641Skmacy			dwFormat = UGETDW(asid.v2->bmFormats);
1189181641Skmacy			bChannels = asid.v2->bNrChannels;
1190181641Skmacy			bBitResolution = asf1d.v2->bBitResolution;
1191181641Skmacy			bSubslotSize = asf1d.v2->bSubslotSize;
1192181641Skmacy
1193181641Skmacy			if (bBitResolution != (bSubslotSize * 8)) {
1194181641Skmacy				DPRINTF("Invalid bSubslotSize\n");
1195181641Skmacy				goto next_ep;
1196181641Skmacy			}
1197181641Skmacy
1198181641Skmacy			if ((bChannels != channels) ||
1199181641Skmacy			    (bBitResolution != bit_resolution)) {
1200181641Skmacy				DPRINTF("Wrong number of channels\n");
1201181641Skmacy				goto next_ep;
1202181641Skmacy			}
1203181641Skmacy
1204181641Skmacy			for (p_fmt = uaudio20_formats;
1205181641Skmacy			    p_fmt->wFormat != 0; p_fmt++) {
1206181641Skmacy				if ((p_fmt->wFormat & dwFormat) &&
1207181641Skmacy				    (p_fmt->bPrecision == bBitResolution))
1208181641Skmacy					break;
1209181641Skmacy			}
1210181641Skmacy
1211181641Skmacy			if (p_fmt->wFormat == 0) {
1212181641Skmacy				DPRINTF("Unsupported audio format\n");
1213181641Skmacy				goto next_ep;
1214181641Skmacy			}
1215181641Skmacy
1216181641Skmacy			for (x = 0; x != 256; x++) {
1217181641Skmacy				if (ep_dir == UE_DIR_OUT) {
1218181641Skmacy					if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
1219181641Skmacy					    (1 << (x % 8)))) {
1220181641Skmacy						continue;
1221181641Skmacy					}
1222181641Skmacy				} else {
1223181641Skmacy					if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
1224181641Skmacy					    (1 << (x % 8)))) {
1225181641Skmacy						continue;
1226181641Skmacy					}
1227181641Skmacy				}
1228207410Skmacy
1229181641Skmacy				DPRINTF("Checking clock ID=%d\n", x);
1230207410Skmacy
1231181641Skmacy				if (uaudio20_check_rate(udev,
1232181641Skmacy				    sc->sc_mixer_iface_no, x, rate)) {
1233207410Skmacy					DPRINTF("Unsupported sampling "
1234181641Skmacy					    "rate, id=%d\n", x);
1235181641Skmacy					goto next_ep;
1236181641Skmacy				}
1237181641Skmacy			}
1238207410Skmacy		} else {
1239207410Skmacy			uint16_t wFormat;
1240207410Skmacy
1241181641Skmacy			wFormat = UGETW(asid.v1->wFormatTag);
1242181641Skmacy			bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels);
1243181641Skmacy			bBitResolution = asf1d.v1->bBitResolution;
1244181641Skmacy
1245181641Skmacy			if (asf1d.v1->bSamFreqType == 0) {
1246181641Skmacy				DPRINTFN(16, "Sample rate: %d-%dHz\n",
1247181641Skmacy				    UA_SAMP_LO(asf1d.v1),
1248181641Skmacy				    UA_SAMP_HI(asf1d.v1));
1249181641Skmacy
1250181641Skmacy				if ((rate >= UA_SAMP_LO(asf1d.v1)) &&
1251181641Skmacy				    (rate <= UA_SAMP_HI(asf1d.v1)))
1252207410Skmacy					goto found_rate;
1253207410Skmacy			} else {
1254181641Skmacy
1255181641Skmacy				for (x = 0; x < asf1d.v1->bSamFreqType; x++) {
1256181641Skmacy					DPRINTFN(16, "Sample rate = %dHz\n",
1257181641Skmacy					    UA_GETSAMP(asf1d.v1, x));
1258181641Skmacy
1259181641Skmacy					if (rate == UA_GETSAMP(asf1d.v1, x))
1260207410Skmacy						goto found_rate;
1261181641Skmacy				}
1262181641Skmacy			}
1263181641Skmacy			goto next_ep;
1264181641Skmacy
1265181641Skmacy	found_rate:
1266181641Skmacy			for (p_fmt = uaudio10_formats;
1267181641Skmacy			    p_fmt->wFormat != 0; p_fmt++) {
1268181641Skmacy				if ((p_fmt->wFormat == wFormat) &&
1269181641Skmacy				    (p_fmt->bPrecision == bBitResolution))
1270181641Skmacy					break;
1271181641Skmacy			}
1272181641Skmacy			if (p_fmt->wFormat == 0) {
1273181747Skmacy				DPRINTF("Unsupported audio format\n");
1274181641Skmacy				goto next_ep;
1275181641Skmacy			}
1276181641Skmacy
1277181641Skmacy			if ((bChannels != channels) ||
1278181641Skmacy			    (bBitResolution != bit_resolution)) {
1279181747Skmacy				DPRINTF("Wrong number of channels\n");
1280181641Skmacy				goto next_ep;
1281181641Skmacy			}
1282181641Skmacy		}
1283181641Skmacy
1284181641Skmacy		chan = (ep_dir == UE_DIR_IN) ?
1285181641Skmacy		    &sc->sc_rec_chan : &sc->sc_play_chan;
1286181641Skmacy
1287181641Skmacy		if (chan->valid != 0 ||
1288181641Skmacy		    usbd_get_iface(udev, curidx) == NULL) {
1289181747Skmacy			DPRINTF("Channel already exists or "
1290181641Skmacy			    "interface is not valid\n");
1291181641Skmacy			goto next_ep;
1292181641Skmacy		}
1293181641Skmacy
1294181641Skmacy		chan->valid = 1;
1295181641Skmacy#ifdef USB_DEBUG
1296181641Skmacy		uaudio_chan_dump_ep_desc(ed1);
1297181641Skmacy#endif
1298181641Skmacy		DPRINTF("Sample rate = %dHz, channels = %d, "
1299181641Skmacy		    "bits = %d, format = %s\n", rate, channels,
1300181641Skmacy		    bit_resolution, p_fmt->description);
1301181641Skmacy
1302181641Skmacy		chan->sample_rate = rate;
1303181641Skmacy		chan->p_asf1d = asf1d;
1304181641Skmacy		chan->p_ed1 = ed1;
1305181641Skmacy		chan->p_fmt = p_fmt;
1306181641Skmacy		chan->p_sed = sed;
1307181641Skmacy		chan->iface_index = curidx;
1308181641Skmacy		chan->iface_alt_index = alt_index;
1309181641Skmacy
1310181641Skmacy		if (ep_dir == UE_DIR_IN)
1311181641Skmacy			chan->usb_cfg = uaudio_cfg_record;
1312181641Skmacy		else
1313181641Skmacy			chan->usb_cfg = uaudio_cfg_play;
1314181641Skmacy
1315181641Skmacy		chan->sample_size = (UAUDIO_MAX_CHAN(channels) *
1316181641Skmacy		    p_fmt->bPrecision) / 8;
1317181641Skmacy		chan->channels = channels;
1318181641Skmacy
1319181641Skmacy		if (ep_dir == UE_DIR_IN &&
1320181641Skmacy		    usbd_get_speed(udev) == USB_SPEED_FULL) {
1321181641Skmacy			uaudio_record_fix_fs(ed1,
1322181641Skmacy			    chan->sample_size * (rate / 1000),
1323181641Skmacy			    chan->sample_size * (rate / 4000));
1324181641Skmacy		}
1325181641Skmacy
1326181641Skmacy		if (sc->sc_sndstat_valid != 0) {
1327181641Skmacy			sbuf_printf(&sc->sc_sndstat, "\n\t"
1328181641Skmacy			    "mode %d.%d:(%s) %dch, %dbit, %s, %dHz",
1329181641Skmacy			    curidx, alt_index,
1330181641Skmacy			    (ep_dir == UE_DIR_IN) ? "input" : "output",
1331181641Skmacy				    channels, p_fmt->bPrecision,
1332181641Skmacy				    p_fmt->description, rate);
1333181641Skmacy		}
1334181641Skmacy
1335181641Skmacy	next_ep:
1336181641Skmacy		sed.v1 = NULL;
1337181641Skmacy		ed1 = NULL;
1338181641Skmacy	}
1339181641Skmacy}
1340181641Skmacy
1341181641Skmacy/* This structure defines all the supported rates. */
1342181641Skmacy
1343181641Skmacystatic const uint32_t uaudio_rate_list[] = {
1344181641Skmacy	96000,
1345181641Skmacy	88000,
1346181641Skmacy	80000,
1347181641Skmacy	72000,
1348181641Skmacy	64000,
1349181641Skmacy	56000,
1350181641Skmacy	48000,
1351181641Skmacy	44100,
1352181641Skmacy	40000,
1353181641Skmacy	32000,
1354181641Skmacy	24000,
1355181641Skmacy	22050,
1356181641Skmacy	16000,
1357181641Skmacy	11025,
1358181641Skmacy	8000,
1359181641Skmacy	0
1360181641Skmacy};
1361181641Skmacy
1362181641Skmacystatic void
1363181641Skmacyuaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev)
1364181641Skmacy{
1365181641Skmacy	uint32_t rate = uaudio_default_rate;
1366181641Skmacy	uint8_t z;
1367181641Skmacy	uint8_t bits = uaudio_default_bits;
1368181641Skmacy	uint8_t y;
1369181641Skmacy	uint8_t channels = uaudio_default_channels;
1370181641Skmacy	uint8_t x;
1371181641Skmacy
1372181641Skmacy	bits -= (bits % 8);
1373181641Skmacy	if ((bits == 0) || (bits > 32)) {
1374181641Skmacy		/* set a valid value */
1375181641Skmacy		bits = 32;
1376181641Skmacy	}
1377181641Skmacy	if (channels == 0) {
1378181641Skmacy		switch (usbd_get_speed(udev)) {
1379181641Skmacy		case USB_SPEED_LOW:
1380181641Skmacy		case USB_SPEED_FULL:
1381181641Skmacy			/*
1382181641Skmacy			 * Due to high bandwidth usage and problems
1383181641Skmacy			 * with HIGH-speed split transactions we
1384181641Skmacy			 * disable surround setups on FULL-speed USB
1385181641Skmacy			 * by default
1386181641Skmacy			 */
1387181641Skmacy			channels = 4;
1388181641Skmacy			break;
1389181641Skmacy		default:
1390181641Skmacy			channels = 16;
1391181641Skmacy			break;
1392181641Skmacy		}
1393181641Skmacy	} else if (channels > 16) {
1394181641Skmacy		channels = 16;
1395181641Skmacy	}
1396181641Skmacy	if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) {
1397181641Skmacy		sc->sc_sndstat_valid = 1;
1398181641Skmacy	}
1399181641Skmacy	/* try to search for a valid config */
1400181641Skmacy
1401181641Skmacy	for (x = channels; x; x--) {
1402181641Skmacy		for (y = bits; y; y -= 8) {
1403181641Skmacy
1404181641Skmacy			/* try user defined rate, if any */
1405181641Skmacy			if (rate != 0)
1406181641Skmacy				uaudio_chan_fill_info_sub(sc, udev, rate, x, y);
1407181641Skmacy
1408181641Skmacy			/* try find a matching rate, if any */
1409181641Skmacy			for (z = 0; uaudio_rate_list[z]; z++) {
1410181641Skmacy				uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y);
1411181641Skmacy
1412181641Skmacy				if (sc->sc_rec_chan.valid &&
1413181641Skmacy				    sc->sc_play_chan.valid) {
1414181641Skmacy					goto done;
1415181641Skmacy				}
1416181641Skmacy			}
1417181641Skmacy		}
1418181641Skmacy	}
1419181641Skmacy
1420181641Skmacydone:
1421181641Skmacy	if (sc->sc_sndstat_valid) {
1422181641Skmacy		sbuf_finish(&sc->sc_sndstat);
1423181641Skmacy	}
1424181641Skmacy}
1425181641Skmacy
1426181641Skmacystatic void
1427181641Skmacyuaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error)
1428181641Skmacy{
1429181641Skmacy	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
1430181641Skmacy	struct usb_page_cache *pc;
1431181641Skmacy	uint8_t buf[4];
1432181641Skmacy	uint64_t temp;
1433181641Skmacy	int len;
1434181641Skmacy	int actlen;
1435181641Skmacy	int nframes;
1436181641Skmacy
1437181641Skmacy	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1438181641Skmacy
1439181641Skmacy	switch (USB_GET_STATE(xfer)) {
1440181641Skmacy	case USB_ST_TRANSFERRED:
1441181641Skmacy
1442181641Skmacy		DPRINTFN(6, "transferred %d bytes\n", actlen);
1443181641Skmacy
1444181641Skmacy		if (nframes == 0)
1445181641Skmacy			break;
1446181641Skmacy		len = usbd_xfer_frame_len(xfer, 0);
1447181641Skmacy		if (len == 0)
1448181641Skmacy			break;
1449181641Skmacy		if (len > sizeof(buf))
1450181641Skmacy			len = sizeof(buf);
1451181641Skmacy
1452181641Skmacy		memset(buf, 0, sizeof(buf));
1453181641Skmacy
1454181641Skmacy		pc = usbd_xfer_get_frame(xfer, 0);
1455181641Skmacy		usbd_copy_out(pc, 0, buf, len);
1456181641Skmacy
1457181641Skmacy		temp = UGETDW(buf);
1458181641Skmacy
1459181641Skmacy		DPRINTF("Value = 0x%08x\n", (int)temp);
1460181641Skmacy
1461181641Skmacy		/* auto-detect SYNC format */
1462181641Skmacy
1463181641Skmacy		if (len == 4)
1464181641Skmacy			temp &= 0x0fffffff;
1465181641Skmacy
1466181641Skmacy		/* check for no data */
1467181641Skmacy
1468181641Skmacy		if (temp == 0)
1469181641Skmacy			break;
1470181641Skmacy
1471181641Skmacy		/* correctly scale value */
1472181641Skmacy
1473181641Skmacy		temp = (temp * 125ULL) - 64;
1474181641Skmacy
1475181641Skmacy		/* auto adjust */
1476181641Skmacy
1477181641Skmacy		while (temp < (ch->sample_rate - (ch->sample_rate / 4)))
1478181641Skmacy			temp *= 2;
1479181641Skmacy
1480181641Skmacy		while (temp > (ch->sample_rate + (ch->sample_rate / 2)))
1481181641Skmacy			temp /= 2;
1482181641Skmacy
1483181641Skmacy		/* bias */
1484181641Skmacy
1485181641Skmacy		temp += (ch->sample_rate + 1999) / 2000;
1486181641Skmacy
1487181641Skmacy		/* compare */
1488181641Skmacy
1489181641Skmacy		DPRINTF("Comparing %d < %d\n",
1490181641Skmacy		    (int)temp, (int)ch->sample_rate);
1491181641Skmacy
1492181641Skmacy		if (temp == ch->sample_rate)
1493181641Skmacy			ch->last_sync_state = UAUDIO_SYNC_NONE;
1494181641Skmacy		else if (temp > ch->sample_rate)
1495181641Skmacy			ch->last_sync_state = UAUDIO_SYNC_MORE;
1496181641Skmacy		else
1497181641Skmacy			ch->last_sync_state = UAUDIO_SYNC_LESS;
1498181641Skmacy		break;
1499181641Skmacy
1500181641Skmacy	case USB_ST_SETUP:
1501181641Skmacy		usbd_xfer_set_frames(xfer, 1);
1502181641Skmacy		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_framelen(xfer));
1503181641Skmacy		usbd_transfer_submit(xfer);
1504181641Skmacy		break;
1505181641Skmacy
1506181641Skmacy	default:			/* Error */
1507181641Skmacy		break;
1508181641Skmacy	}
1509181641Skmacy}
1510181641Skmacy
1511181641Skmacystatic void
1512181641Skmacyuaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error)
1513181641Skmacy{
1514181641Skmacy	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
1515181641Skmacy	struct usb_page_cache *pc;
1516181641Skmacy	uint32_t mfl;
1517181641Skmacy	uint32_t total;
1518181641Skmacy	uint32_t blockcount;
1519181641Skmacy	uint32_t n;
1520181641Skmacy	uint32_t offset;
1521181641Skmacy	int actlen;
1522181641Skmacy	int sumlen;
1523181641Skmacy
1524181641Skmacy	usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
1525181641Skmacy
1526181641Skmacy	if (ch->end == ch->start) {
1527181641Skmacy		DPRINTF("no buffer!\n");
1528181641Skmacy		return;
1529181641Skmacy	}
1530181641Skmacy
1531181641Skmacy	switch (USB_GET_STATE(xfer)) {
1532181641Skmacy	case USB_ST_TRANSFERRED:
1533181641Skmacytr_transferred:
1534181641Skmacy		if (actlen < sumlen) {
1535181641Skmacy			DPRINTF("short transfer, "
1536181641Skmacy			    "%d of %d bytes\n", actlen, sumlen);
1537181641Skmacy		}
1538181641Skmacy		chn_intr(ch->pcm_ch);
1539181641Skmacy
1540181641Skmacy		/* start SYNC transfer, if any */
1541181641Skmacy		if ((ch->last_sync_time++ & 7) == 0)
1542181641Skmacy			usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]);
1543181641Skmacy
1544181641Skmacy	case USB_ST_SETUP:
1545181641Skmacy		mfl = usbd_xfer_max_framelen(xfer);
1546181641Skmacy
1547181641Skmacy		if (ch->bytes_per_frame[1] > mfl) {
1548181641Skmacy			DPRINTF("bytes per transfer, %d, "
1549181641Skmacy			    "exceeds maximum, %d!\n",
1550181641Skmacy			    ch->bytes_per_frame[1],
1551181641Skmacy			    mfl);
1552181641Skmacy			break;
1553181641Skmacy		}
1554181641Skmacy
1555181641Skmacy		blockcount = ch->intr_frames;
1556181641Skmacy
1557181641Skmacy		/* setup number of frames */
1558181641Skmacy		usbd_xfer_set_frames(xfer, blockcount);
1559181641Skmacy
1560181641Skmacy		/* reset total length */
1561181641Skmacy		total = 0;
1562181641Skmacy
1563181641Skmacy		/* setup frame lengths */
1564181641Skmacy		for (n = 0; n != blockcount; n++) {
1565181641Skmacy			uint32_t frame_len;
1566181641Skmacy
1567181641Skmacy			ch->sample_curr += ch->sample_rem;
1568181641Skmacy			if (ch->sample_curr >= ch->frames_per_second) {
1569181641Skmacy				ch->sample_curr -= ch->frames_per_second;
1570181641Skmacy				frame_len = ch->bytes_per_frame[1];
1571181641Skmacy			} else {
1572181641Skmacy				frame_len = ch->bytes_per_frame[0];
1573181641Skmacy			}
1574181641Skmacy
1575181641Skmacy			if (n == (blockcount - 1)) {
1576181641Skmacy				switch (ch->last_sync_state) {
1577181641Skmacy				case UAUDIO_SYNC_MORE:
1578181641Skmacy					DPRINTFN(6, "sending one sample more\n");
1579181641Skmacy					if ((frame_len + ch->sample_size) <= mfl)
1580181641Skmacy						frame_len += ch->sample_size;
1581181641Skmacy					ch->last_sync_state = UAUDIO_SYNC_NONE;
1582181641Skmacy					break;
1583181641Skmacy				case UAUDIO_SYNC_LESS:
1584181641Skmacy					DPRINTFN(6, "sending one sample less\n");
1585181641Skmacy					if (frame_len >= ch->sample_size)
1586181641Skmacy						frame_len -= ch->sample_size;
1587181641Skmacy					ch->last_sync_state = UAUDIO_SYNC_NONE;
1588181641Skmacy					break;
1589181641Skmacy				default:
1590181641Skmacy					break;
1591181641Skmacy				}
1592181641Skmacy			}
1593181641Skmacy
1594181641Skmacy			usbd_xfer_set_frame_len(xfer, n, frame_len);
1595181641Skmacy			total += frame_len;
1596181641Skmacy		}
1597181641Skmacy
1598181641Skmacy		DPRINTFN(6, "transfer %d bytes\n", total);
1599181641Skmacy
1600181641Skmacy		offset = 0;
1601181641Skmacy
1602181641Skmacy		pc = usbd_xfer_get_frame(xfer, 0);
1603181641Skmacy		while (total > 0) {
1604181641Skmacy
1605181641Skmacy			n = (ch->end - ch->cur);
1606181641Skmacy			if (n > total) {
1607181641Skmacy				n = total;
1608181641Skmacy			}
1609181641Skmacy			usbd_copy_in(pc, offset, ch->cur, n);
1610181641Skmacy
1611181641Skmacy			total -= n;
1612181641Skmacy			ch->cur += n;
1613181641Skmacy			offset += n;
1614181641Skmacy
1615181641Skmacy			if (ch->cur >= ch->end) {
1616181641Skmacy				ch->cur = ch->start;
1617181641Skmacy			}
1618181641Skmacy		}
1619181641Skmacy
1620181641Skmacy		usbd_transfer_submit(xfer);
1621181641Skmacy		break;
1622181641Skmacy
1623181641Skmacy	default:			/* Error */
1624181641Skmacy		if (error == USB_ERR_CANCELLED) {
1625181641Skmacy			break;
1626181641Skmacy		}
1627181641Skmacy		goto tr_transferred;
1628181641Skmacy	}
1629181641Skmacy}
1630181641Skmacy
1631181641Skmacystatic void
1632181641Skmacyuaudio_chan_record_sync_callback(struct usb_xfer *xfer, usb_error_t error)
1633181641Skmacy{
1634181641Skmacy	/* TODO */
1635181641Skmacy}
1636181641Skmacy
1637181641Skmacystatic void
1638181641Skmacyuaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error)
1639181641Skmacy{
1640181641Skmacy	struct uaudio_chan *ch = usbd_xfer_softc(xfer);
1641181641Skmacy	struct usb_page_cache *pc;
1642181641Skmacy	uint32_t offset0;
1643181641Skmacy	uint32_t offset1;
1644181641Skmacy	uint32_t mfl;
1645181641Skmacy	int m;
1646181641Skmacy	int n;
1647181641Skmacy	int len;
1648181641Skmacy	int actlen;
1649181641Skmacy	int nframes;
1650181641Skmacy	int blockcount;
1651181641Skmacy
1652181641Skmacy	usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes);
1653181641Skmacy	mfl = usbd_xfer_max_framelen(xfer);
1654181641Skmacy
1655181641Skmacy	if (ch->end == ch->start) {
1656181641Skmacy		DPRINTF("no buffer!\n");
1657181641Skmacy		return;
1658181641Skmacy	}
1659181641Skmacy
1660181641Skmacy	switch (USB_GET_STATE(xfer)) {
1661181641Skmacy	case USB_ST_TRANSFERRED:
1662181641Skmacy
1663181641Skmacy		DPRINTFN(6, "transferred %d bytes\n", actlen);
1664181641Skmacy
1665181641Skmacy		offset0 = 0;
1666181641Skmacy		pc = usbd_xfer_get_frame(xfer, 0);
1667181641Skmacy
1668181641Skmacy		for (n = 0; n != nframes; n++) {
1669181641Skmacy
1670181641Skmacy			offset1 = offset0;
1671181641Skmacy			len = usbd_xfer_frame_len(xfer, n);
1672181641Skmacy
1673181641Skmacy			while (len > 0) {
1674181641Skmacy
1675181641Skmacy				m = (ch->end - ch->cur);
1676181641Skmacy
1677181641Skmacy				if (m > len)
1678181641Skmacy					m = len;
1679181641Skmacy
1680181641Skmacy				usbd_copy_out(pc, offset1, ch->cur, m);
1681181641Skmacy
1682181641Skmacy				len -= m;
1683181641Skmacy				offset1 += m;
1684181641Skmacy				ch->cur += m;
1685181641Skmacy
1686181641Skmacy				if (ch->cur >= ch->end) {
1687181641Skmacy					ch->cur = ch->start;
1688181641Skmacy				}
1689181641Skmacy			}
1690181641Skmacy
1691181641Skmacy			offset0 += mfl;
1692181641Skmacy		}
1693181641Skmacy
1694181641Skmacy		chn_intr(ch->pcm_ch);
1695181641Skmacy
1696181641Skmacy	case USB_ST_SETUP:
1697181641Skmacytr_setup:
1698181641Skmacy		blockcount = ch->intr_frames;
1699181641Skmacy
1700181641Skmacy		usbd_xfer_set_frames(xfer, blockcount);
1701181641Skmacy		for (n = 0; n < blockcount; n++) {
1702181641Skmacy			usbd_xfer_set_frame_len(xfer, n, mfl);
1703181641Skmacy		}
1704181641Skmacy
1705181641Skmacy		usbd_transfer_submit(xfer);
1706181641Skmacy		break;
1707181641Skmacy
1708181641Skmacy	default:			/* Error */
1709181641Skmacy		if (error == USB_ERR_CANCELLED) {
1710181641Skmacy			break;
1711181641Skmacy		}
1712181641Skmacy		goto tr_setup;
1713181641Skmacy	}
1714181641Skmacy}
1715181641Skmacy
1716181641Skmacyvoid   *
1717181641Skmacyuaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
1718181641Skmacy    struct pcm_channel *c, int dir)
1719181641Skmacy{
1720181641Skmacy	struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
1721181641Skmacy	    &sc->sc_play_chan : &sc->sc_rec_chan);
1722181641Skmacy	uint32_t buf_size;
1723181641Skmacy	uint32_t frames;
1724181641Skmacy	uint32_t format;
1725181641Skmacy	uint16_t fps;
1726181641Skmacy	uint8_t endpoint;
1727181641Skmacy	uint8_t blocks;
1728181641Skmacy	uint8_t iface_index;
1729181641Skmacy	uint8_t alt_index;
1730181641Skmacy	uint8_t fps_shift;
1731181641Skmacy	usb_error_t err;
1732181641Skmacy
1733181641Skmacy	fps = usbd_get_isoc_fps(sc->sc_udev);
1734181641Skmacy
1735181641Skmacy	if (fps < 8000) {
1736181641Skmacy		/* FULL speed USB */
1737181641Skmacy		frames = 8;
1738181641Skmacy	} else {
1739181641Skmacy		/* HIGH speed USB */
1740181641Skmacy		frames = UAUDIO_NFRAMES;
1741181641Skmacy	}
1742181641Skmacy
1743181641Skmacy	/* setup play/record format */
1744181641Skmacy
1745181641Skmacy	ch->pcm_cap.fmtlist = ch->pcm_format;
1746181641Skmacy
1747181641Skmacy	ch->pcm_format[0] = 0;
1748181641Skmacy	ch->pcm_format[1] = 0;
1749181641Skmacy
1750181641Skmacy	ch->pcm_cap.minspeed = ch->sample_rate;
1751181641Skmacy	ch->pcm_cap.maxspeed = ch->sample_rate;
1752181641Skmacy
1753181641Skmacy	/* setup mutex and PCM channel */
1754181641Skmacy
1755196728Sadrian	ch->pcm_ch = c;
1756181641Skmacy	ch->pcm_mtx = c->lock;
1757181641Skmacy
1758181641Skmacy	format = ch->p_fmt->freebsd_fmt;
1759181641Skmacy
1760181641Skmacy	switch (ch->channels) {
1761181641Skmacy	case 2:
1762181641Skmacy		/* stereo */
1763181641Skmacy		format = SND_FORMAT(format, 2, 0);
1764196728Sadrian		break;
1765181641Skmacy	case 1:
1766181641Skmacy		/* mono */
1767181641Skmacy		format = SND_FORMAT(format, 1, 0);
1768181641Skmacy		break;
1769181641Skmacy	default:
1770181641Skmacy		/* surround and more */
1771181641Skmacy		format = feeder_matrix_default_format(
1772181641Skmacy		    SND_FORMAT(format, ch->channels, 0));
1773181641Skmacy		break;
1774181641Skmacy	}
1775181641Skmacy
1776196728Sadrian	ch->pcm_cap.fmtlist[0] = format;
1777181641Skmacy	ch->pcm_cap.fmtlist[1] = 0;
1778181641Skmacy
1779181641Skmacy	/* check if format is not supported */
1780181641Skmacy
1781181641Skmacy	if (format == 0) {
1782181641Skmacy		DPRINTF("The selected audio format is not supported\n");
1783181641Skmacy		goto error;
1784181641Skmacy	}
1785181641Skmacy
1786181641Skmacy	/* set alternate interface corresponding to the mode */
1787181641Skmacy
1788196728Sadrian	endpoint = ch->p_ed1->bEndpointAddress;
1789181641Skmacy	iface_index = ch->iface_index;
1790181641Skmacy	alt_index = ch->iface_alt_index;
1791181641Skmacy
1792181641Skmacy	DPRINTF("endpoint=0x%02x, speed=%d, iface=%d alt=%d\n",
1793181641Skmacy	    endpoint, ch->sample_rate, iface_index, alt_index);
1794181641Skmacy
1795181641Skmacy	err = usbd_set_alt_interface_index(sc->sc_udev, iface_index, alt_index);
1796181641Skmacy	if (err) {
1797181641Skmacy		DPRINTF("setting of alternate index failed: %s!\n",
1798181641Skmacy		    usbd_errstr(err));
1799181641Skmacy		goto error;
1800181641Skmacy	}
1801181641Skmacy	usbd_set_parent_iface(sc->sc_udev, iface_index,
1802181641Skmacy	    sc->sc_mixer_iface_index);
1803181641Skmacy
1804181641Skmacy	/*
1805181641Skmacy	 * Only set the sample rate if the channel reports that it
1806181641Skmacy	 * supports the frequency control.
1807181641Skmacy	 */
1808181641Skmacy
1809181641Skmacy	if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
1810181641Skmacy		/* FALLTHROUGH */
1811181641Skmacy	} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
1812181641Skmacy		unsigned int x;
1813181641Skmacy
1814181641Skmacy		for (x = 0; x != 256; x++) {
1815181641Skmacy			if (dir == PCMDIR_PLAY) {
1816181641Skmacy				if (!(sc->sc_mixer_clocks.bit_output[x / 8] &
1817181641Skmacy				    (1 << (x % 8)))) {
1818181641Skmacy					continue;
1819181641Skmacy				}
1820181641Skmacy			} else {
1821181641Skmacy				if (!(sc->sc_mixer_clocks.bit_input[x / 8] &
1822181641Skmacy				    (1 << (x % 8)))) {
1823181641Skmacy					continue;
1824181641Skmacy				}
1825181641Skmacy			}
1826181641Skmacy
1827181641Skmacy			if (uaudio20_set_speed(sc->sc_udev,
1828181641Skmacy			    sc->sc_mixer_iface_no, x, ch->sample_rate)) {
1829181641Skmacy				/*
1830181641Skmacy				 * If the endpoint is adaptive setting
1831181641Skmacy				 * the speed may fail.
1832181641Skmacy				 */
1833181641Skmacy				DPRINTF("setting of sample rate failed! "
1834181641Skmacy				    "(continuing anyway)\n");
1835181641Skmacy			}
1836181641Skmacy		}
1837181641Skmacy	} else if (ch->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) {
1838181641Skmacy		if (uaudio_set_speed(sc->sc_udev, endpoint, ch->sample_rate)) {
1839181641Skmacy			/*
1840181641Skmacy			 * If the endpoint is adaptive setting the
1841181641Skmacy			 * speed may fail.
1842181641Skmacy			 */
1843181641Skmacy			DPRINTF("setting of sample rate failed! "
1844181641Skmacy			    "(continuing anyway)\n");
1845181641Skmacy		}
1846181641Skmacy	}
1847181641Skmacy	if (usbd_transfer_setup(sc->sc_udev, &iface_index, ch->xfer,
1848181641Skmacy	    ch->usb_cfg, UAUDIO_NCHANBUFS + 1, ch, ch->pcm_mtx)) {
1849181641Skmacy		DPRINTF("could not allocate USB transfers!\n");
1850181641Skmacy		goto error;
1851181641Skmacy	}
1852181641Skmacy
1853181641Skmacy	fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]);
1854181641Skmacy
1855181641Skmacy	/* down shift number of frames per second, if any */
1856181641Skmacy	fps >>= fps_shift;
1857181641Skmacy	frames >>= fps_shift;
1858181641Skmacy
1859181641Skmacy	/* bytes per frame should not be zero */
1860181641Skmacy	ch->bytes_per_frame[0] = ((ch->sample_rate / fps) * ch->sample_size);
1861181641Skmacy	ch->bytes_per_frame[1] = (((ch->sample_rate + fps - 1) / fps) * ch->sample_size);
1862181641Skmacy
1863181641Skmacy	/* setup data rate dithering, if any */
1864181641Skmacy	ch->frames_per_second = fps;
1865181641Skmacy	ch->sample_rem = ch->sample_rate % fps;
1866181641Skmacy	ch->sample_curr = 0;
1867181641Skmacy	ch->frames_per_second = fps;
1868181641Skmacy
1869181641Skmacy	/* compute required buffer size */
1870181641Skmacy	buf_size = (ch->bytes_per_frame[1] * frames);
1871181641Skmacy
1872181641Skmacy	ch->intr_size = buf_size;
1873181641Skmacy	ch->intr_frames = frames;
1874181641Skmacy
1875181641Skmacy	DPRINTF("fps=%d sample_rem=%d\n", fps, ch->sample_rem);
1876181641Skmacy
1877181641Skmacy	if (ch->intr_frames == 0) {
1878181641Skmacy		DPRINTF("frame shift is too high!\n");
1879181641Skmacy		goto error;
1880181641Skmacy	}
1881181641Skmacy
1882181641Skmacy	/* setup double buffering */
1883181641Skmacy	buf_size *= 2;
1884181641Skmacy	blocks = 2;
1885181641Skmacy
1886181641Skmacy	ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
1887181641Skmacy	if (ch->buf == NULL)
1888181641Skmacy		goto error;
1889181641Skmacy	if (sndbuf_setup(b, ch->buf, buf_size) != 0)
1890181641Skmacy		goto error;
1891181641Skmacy	if (sndbuf_resize(b, blocks, ch->intr_size))
1892181641Skmacy		goto error;
1893181641Skmacy
1894181641Skmacy	ch->start = ch->buf;
1895181641Skmacy	ch->end = ch->buf + buf_size;
1896181641Skmacy	ch->cur = ch->buf;
1897181641Skmacy	ch->pcm_buf = b;
1898181641Skmacy
1899181641Skmacy	if (ch->pcm_mtx == NULL) {
1900181641Skmacy		DPRINTF("ERROR: PCM channels does not have a mutex!\n");
1901181641Skmacy		goto error;
1902181641Skmacy	}
1903181641Skmacy
1904181641Skmacy	return (ch);
1905181641Skmacy
1906181641Skmacyerror:
1907181641Skmacy	uaudio_chan_free(ch);
1908181641Skmacy	return (NULL);
1909181641Skmacy}
1910181641Skmacy
1911181641Skmacyint
1912181641Skmacyuaudio_chan_free(struct uaudio_chan *ch)
1913181641Skmacy{
1914181641Skmacy	if (ch->buf != NULL) {
1915181641Skmacy		free(ch->buf, M_DEVBUF);
1916181641Skmacy		ch->buf = NULL;
1917181641Skmacy	}
1918181641Skmacy	usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS + 1);
1919181641Skmacy
1920181641Skmacy	ch->valid = 0;
1921181641Skmacy
1922181641Skmacy	return (0);
1923181641Skmacy}
1924181641Skmacy
1925181641Skmacyint
1926181641Skmacyuaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
1927181641Skmacy{
1928181641Skmacy	return (ch->intr_size);
1929181641Skmacy}
1930181641Skmacy
1931181641Skmacyint
1932181641Skmacyuaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
1933181641Skmacy    uint32_t blockcount)
1934181641Skmacy{
1935181641Skmacy	return (1);
1936181641Skmacy}
1937181641Skmacy
1938181641Skmacyint
1939181641Skmacyuaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
1940181641Skmacy{
1941181641Skmacy	if (speed != ch->sample_rate) {
1942181641Skmacy		DPRINTF("rate conversion required\n");
1943181641Skmacy	}
1944181641Skmacy	return (ch->sample_rate);
1945181641Skmacy}
1946181641Skmacy
1947181641Skmacyint
1948181641Skmacyuaudio_chan_getptr(struct uaudio_chan *ch)
1949181641Skmacy{
1950181641Skmacy	return (ch->cur - ch->start);
1951181641Skmacy}
1952181641Skmacy
1953181641Skmacystruct pcmchan_caps *
1954181641Skmacyuaudio_chan_getcaps(struct uaudio_chan *ch)
1955181641Skmacy{
1956181641Skmacy	return (&ch->pcm_cap);
1957181641Skmacy}
1958181641Skmacy
1959181641Skmacystatic struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = {
1960181641Skmacy	.id = SND_CHN_MATRIX_DRV,
1961181641Skmacy	.channels = 2,
1962181641Skmacy	.ext = 0,
1963181641Skmacy	.map = {
1964181641Skmacy		/* Right */
1965181946Skmacy		[0] = {
1966181641Skmacy			.type = SND_CHN_T_FR,
1967181641Skmacy			.members =
1968181641Skmacy			    SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC |
1969181641Skmacy			    SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR |
1970181641Skmacy			    SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR
1971181641Skmacy		},
1972181946Skmacy		/* Left */
1973181946Skmacy		[1] = {
1974181641Skmacy			.type = SND_CHN_T_FL,
1975181641Skmacy			.members =
1976181641Skmacy			    SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC |
1977181641Skmacy			    SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL |
1978181641Skmacy			    SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL
1979181641Skmacy		},
1980181641Skmacy		[2] = {
1981181641Skmacy			.type = SND_CHN_T_MAX,
1982181641Skmacy			.members = 0
1983181641Skmacy		}
1984181641Skmacy	},
1985181641Skmacy	.mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL,
1986181641Skmacy	.offset = {  1,  0, -1, -1, -1, -1, -1, -1, -1,
1987181641Skmacy		    -1, -1, -1, -1, -1, -1, -1, -1, -1  }
1988181641Skmacy};
1989181641Skmacy
1990181641Skmacystruct pcmchan_matrix *
1991181641Skmacyuaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format)
1992181641Skmacy{
1993181641Skmacy	struct uaudio_softc *sc;
1994181641Skmacy
1995181641Skmacy	sc = ch->priv_sc;
1996181641Skmacy
1997181641Skmacy	if (sc != NULL && sc->sc_uq_audio_swap_lr != 0 &&
1998181641Skmacy	    AFMT_CHANNEL(format) == 2)
1999181641Skmacy		return (&uaudio_chan_matrix_swap_2_0);
2000181641Skmacy
2001181641Skmacy	return (feeder_matrix_format_map(format));
2002181641Skmacy}
2003181641Skmacy
2004181641Skmacyint
2005181641Skmacyuaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
2006181641Skmacy{
2007181641Skmacy	ch->format = format;
2008181641Skmacy	return (0);
2009181641Skmacy}
2010181641Skmacy
2011181641Skmacyint
2012181641Skmacyuaudio_chan_start(struct uaudio_chan *ch)
2013181641Skmacy{
2014181641Skmacy	ch->cur = ch->start;
2015181641Skmacy
2016181641Skmacy#if (UAUDIO_NCHANBUFS != 2)
2017181641Skmacy#error "please update code"
2018181641Skmacy#endif
2019181641Skmacy	usbd_transfer_start(ch->xfer[0]);
2020181641Skmacy	usbd_transfer_start(ch->xfer[1]);
2021181641Skmacy	return (0);
2022181641Skmacy}
2023181641Skmacy
2024181641Skmacyint
2025181641Skmacyuaudio_chan_stop(struct uaudio_chan *ch)
2026181641Skmacy{
2027181641Skmacy#if (UAUDIO_NCHANBUFS != 2)
2028181641Skmacy#error "please update code"
2029181641Skmacy#endif
2030181641Skmacy	usbd_transfer_stop(ch->xfer[0]);
2031181641Skmacy	usbd_transfer_stop(ch->xfer[1]);
2032181641Skmacy	return (0);
2033181641Skmacy}
2034181641Skmacy
2035181641Skmacy/*========================================================================*
2036181641Skmacy * AC - Audio Controller - routines
2037181641Skmacy *========================================================================*/
2038181641Skmacy
2039181641Skmacystatic void
2040181641Skmacyuaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
2041181641Skmacy{
2042181641Skmacy	struct uaudio_mixer_node *p_mc_new =
2043181641Skmacy	    malloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK);
2044181641Skmacy
2045181641Skmacy	if (p_mc_new != NULL) {
2046181641Skmacy		memcpy(p_mc_new, mc, sizeof(*p_mc_new));
2047181641Skmacy		p_mc_new->next = sc->sc_mixer_root;
2048181641Skmacy		sc->sc_mixer_root = p_mc_new;
2049181641Skmacy		sc->sc_mixer_count++;
2050181641Skmacy	} else {
2051181641Skmacy		DPRINTF("out of memory\n");
2052181641Skmacy	}
2053181641Skmacy}
2054181641Skmacy
2055181641Skmacystatic void
2056181641Skmacyuaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
2057181641Skmacy{
2058181641Skmacy	int32_t res;
2059181641Skmacy
2060181641Skmacy	if (mc->class < UAC_NCLASSES) {
2061181641Skmacy		DPRINTF("adding %s.%d\n",
2062181641Skmacy		    uac_names[mc->class], mc->ctl);
2063181641Skmacy	} else {
2064181641Skmacy		DPRINTF("adding %d\n", mc->ctl);
2065181641Skmacy	}
2066181641Skmacy
2067181641Skmacy	if (mc->type == MIX_ON_OFF) {
2068181641Skmacy		mc->minval = 0;
2069181641Skmacy		mc->maxval = 1;
2070181641Skmacy	} else if (mc->type == MIX_SELECTOR) {
2071181641Skmacy	} else {
2072181641Skmacy
2073181641Skmacy		/* determine min and max values */
2074181641Skmacy
2075181641Skmacy		mc->minval = uaudio_mixer_get(sc->sc_udev,
2076181641Skmacy		    sc->sc_audio_rev, GET_MIN, mc);
2077181641Skmacy		mc->maxval = uaudio_mixer_get(sc->sc_udev,
2078181641Skmacy		    sc->sc_audio_rev, GET_MAX, mc);
2079181641Skmacy
2080181641Skmacy		/* check if max and min was swapped */
2081181641Skmacy
2082181641Skmacy		if (mc->maxval < mc->minval) {
2083181641Skmacy			res = mc->maxval;
2084181641Skmacy			mc->maxval = mc->minval;
2085181641Skmacy			mc->minval = res;
2086181641Skmacy		}
2087181641Skmacy
2088181641Skmacy		/* compute value range */
2089181641Skmacy		mc->mul = mc->maxval - mc->minval;
2090181641Skmacy		if (mc->mul == 0)
2091181641Skmacy			mc->mul = 1;
2092181641Skmacy
2093181641Skmacy		/* compute value alignment */
2094181641Skmacy		res = uaudio_mixer_get(sc->sc_udev,
2095181641Skmacy		    sc->sc_audio_rev, GET_RES, mc);
2096181641Skmacy
2097181641Skmacy		DPRINTF("Resolution = %d\n", (int)res);
2098181641Skmacy	}
2099181641Skmacy
2100181641Skmacy	uaudio_mixer_add_ctl_sub(sc, mc);
2101181641Skmacy
2102181641Skmacy#ifdef USB_DEBUG
2103181641Skmacy	if (uaudio_debug > 2) {
2104181641Skmacy		uint8_t i;
2105181641Skmacy
2106181641Skmacy		for (i = 0; i < mc->nchan; i++) {
2107181641Skmacy			DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]);
2108181641Skmacy		}
2109181641Skmacy		DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' "
2110181641Skmacy		    "min=%d max=%d\n",
2111181641Skmacy		    mc->wIndex, mc->type, mc->ctl,
2112181641Skmacy		    mc->minval, mc->maxval);
2113181641Skmacy	}
2114181641Skmacy#endif
2115181641Skmacy}
2116181641Skmacy
2117181641Skmacystatic void
2118181641Skmacyuaudio_mixer_add_mixer(struct uaudio_softc *sc,
2119181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2120181641Skmacy{
2121181641Skmacy	struct uaudio_mixer_node mix;
2122181641Skmacy
2123181641Skmacy	const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu_v1;
2124181641Skmacy	const struct usb_audio_mixer_unit_1 *d1;
2125181641Skmacy
2126181641Skmacy	uint32_t bno;			/* bit number */
2127181641Skmacy	uint32_t p;			/* bit number accumulator */
2128181641Skmacy	uint32_t mo;			/* matching outputs */
2129181641Skmacy	uint32_t mc;			/* matching channels */
2130181641Skmacy	uint32_t ichs;			/* input channels */
2131181641Skmacy	uint32_t ochs;			/* output channels */
2132181641Skmacy	uint32_t c;
2133181641Skmacy	uint32_t chs;			/* channels */
2134181641Skmacy	uint32_t i;
2135181641Skmacy	uint32_t o;
2136181641Skmacy
2137181641Skmacy	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
2138181641Skmacy	    d0->bUnitId, d0->bNrInPins);
2139181641Skmacy
2140181641Skmacy	/* compute the number of input channels */
2141181641Skmacy
2142181641Skmacy	ichs = 0;
2143181641Skmacy	for (i = 0; i < d0->bNrInPins; i++) {
2144181641Skmacy		ichs += uaudio_mixer_get_cluster(
2145181641Skmacy		    d0->baSourceId[i], iot).bNrChannels;
2146181641Skmacy	}
2147181641Skmacy
2148181641Skmacy	d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
2149181641Skmacy
2150181641Skmacy	/* and the number of output channels */
2151181641Skmacy
2152181641Skmacy	ochs = d1->bNrChannels;
2153181641Skmacy
2154181641Skmacy	DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
2155181641Skmacy
2156181641Skmacy	memset(&mix, 0, sizeof(mix));
2157181641Skmacy
2158181641Skmacy	mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
2159181641Skmacy	uaudio_mixer_determine_class(&iot[id], &mix);
2160181641Skmacy	mix.type = MIX_SIGNED_16;
2161181641Skmacy
2162181641Skmacy	if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL)
2163181641Skmacy		return;
2164181641Skmacy
2165181641Skmacy	for (p = i = 0; i < d0->bNrInPins; i++) {
2166181641Skmacy		chs = uaudio_mixer_get_cluster(
2167181641Skmacy		    d0->baSourceId[i], iot).bNrChannels;
2168181641Skmacy		mc = 0;
2169181641Skmacy		for (c = 0; c < chs; c++) {
2170181641Skmacy			mo = 0;
2171181641Skmacy			for (o = 0; o < ochs; o++) {
2172181641Skmacy				bno = ((p + c) * ochs) + o;
2173181641Skmacy				if (BIT_TEST(d1->bmControls, bno))
2174181641Skmacy					mo++;
2175181641Skmacy			}
2176181641Skmacy			if (mo == 1)
2177181641Skmacy				mc++;
2178181641Skmacy		}
2179181641Skmacy		if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
2180181641Skmacy
2181181641Skmacy			/* repeat bit-scan */
2182181641Skmacy
2183181641Skmacy			mc = 0;
2184181641Skmacy			for (c = 0; c < chs; c++) {
2185181641Skmacy				for (o = 0; o < ochs; o++) {
2186181641Skmacy					bno = ((p + c) * ochs) + o;
2187181641Skmacy					if (BIT_TEST(d1->bmControls, bno))
2188181641Skmacy						mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
2189181641Skmacy				}
2190181641Skmacy			}
2191181641Skmacy			mix.nchan = chs;
2192181641Skmacy			uaudio_mixer_add_ctl(sc, &mix);
2193181641Skmacy		}
2194181641Skmacy		p += chs;
2195181641Skmacy	}
2196181641Skmacy}
2197181641Skmacy
2198181641Skmacystatic void
2199181641Skmacyuaudio20_mixer_add_mixer(struct uaudio_softc *sc,
2200181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2201181641Skmacy{
2202181641Skmacy	struct uaudio_mixer_node mix;
2203181641Skmacy
2204181641Skmacy	const struct usb_audio20_mixer_unit_0 *d0 = iot[id].u.mu_v2;
2205181641Skmacy	const struct usb_audio20_mixer_unit_1 *d1;
2206181641Skmacy
2207181641Skmacy	uint32_t bno;			/* bit number */
2208181641Skmacy	uint32_t p;			/* bit number accumulator */
2209181641Skmacy	uint32_t mo;			/* matching outputs */
2210181641Skmacy	uint32_t mc;			/* matching channels */
2211181641Skmacy	uint32_t ichs;			/* input channels */
2212181641Skmacy	uint32_t ochs;			/* output channels */
2213181641Skmacy	uint32_t c;
2214181641Skmacy	uint32_t chs;			/* channels */
2215181641Skmacy	uint32_t i;
2216181641Skmacy	uint32_t o;
2217181641Skmacy
2218181641Skmacy	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
2219181641Skmacy	    d0->bUnitId, d0->bNrInPins);
2220181641Skmacy
2221181641Skmacy	/* compute the number of input channels */
2222181641Skmacy
2223181641Skmacy	ichs = 0;
2224181641Skmacy	for (i = 0; i < d0->bNrInPins; i++) {
2225181641Skmacy		ichs += uaudio20_mixer_get_cluster(
2226181641Skmacy		    d0->baSourceId[i], iot).bNrChannels;
2227181641Skmacy	}
2228181641Skmacy
2229181641Skmacy	d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
2230181641Skmacy
2231181641Skmacy	/* and the number of output channels */
2232181641Skmacy
2233181641Skmacy	ochs = d1->bNrChannels;
2234181641Skmacy
2235181641Skmacy	DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
2236181641Skmacy
2237181641Skmacy	memset(&mix, 0, sizeof(mix));
2238181641Skmacy
2239181641Skmacy	mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
2240181641Skmacy	uaudio20_mixer_determine_class(&iot[id], &mix);
2241181641Skmacy	mix.type = MIX_SIGNED_16;
2242181641Skmacy
2243181641Skmacy	if (uaudio20_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL)
2244181641Skmacy		return;
2245181641Skmacy
2246181641Skmacy	for (p = i = 0; i < d0->bNrInPins; i++) {
2247181641Skmacy		chs = uaudio20_mixer_get_cluster(
2248181641Skmacy		    d0->baSourceId[i], iot).bNrChannels;
2249181641Skmacy		mc = 0;
2250181641Skmacy		for (c = 0; c < chs; c++) {
2251181641Skmacy			mo = 0;
2252181641Skmacy			for (o = 0; o < ochs; o++) {
2253181641Skmacy				bno = ((p + c) * ochs) + o;
2254181641Skmacy				if (BIT_TEST(d1->bmControls, bno))
2255181641Skmacy					mo++;
2256181641Skmacy			}
2257181641Skmacy			if (mo == 1)
2258181641Skmacy				mc++;
2259181641Skmacy		}
2260181641Skmacy		if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
2261181641Skmacy
2262181641Skmacy			/* repeat bit-scan */
2263181641Skmacy
2264181641Skmacy			mc = 0;
2265181641Skmacy			for (c = 0; c < chs; c++) {
2266181641Skmacy				for (o = 0; o < ochs; o++) {
2267181641Skmacy					bno = ((p + c) * ochs) + o;
2268181641Skmacy					if (BIT_TEST(d1->bmControls, bno))
2269181641Skmacy						mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
2270181641Skmacy				}
2271181641Skmacy			}
2272181641Skmacy			mix.nchan = chs;
2273181641Skmacy			uaudio_mixer_add_ctl(sc, &mix);
2274181641Skmacy		}
2275181641Skmacy		p += chs;
2276181641Skmacy	}
2277181641Skmacy}
2278181641Skmacy
2279181641Skmacystatic void
2280181641Skmacyuaudio_mixer_add_selector(struct uaudio_softc *sc,
2281181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2282181641Skmacy{
2283181641Skmacy	const struct usb_audio_selector_unit *d = iot[id].u.su_v1;
2284181641Skmacy	struct uaudio_mixer_node mix;
2285181641Skmacy	uint16_t i;
2286181641Skmacy
2287181641Skmacy	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
2288181641Skmacy	    d->bUnitId, d->bNrInPins);
2289181641Skmacy
2290181641Skmacy	if (d->bNrInPins == 0) {
2291181641Skmacy		return;
2292181641Skmacy	}
2293181641Skmacy	memset(&mix, 0, sizeof(mix));
2294181641Skmacy
2295181641Skmacy	mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
2296181641Skmacy	mix.wValue[0] = MAKE_WORD(0, 0);
2297181641Skmacy	uaudio_mixer_determine_class(&iot[id], &mix);
2298181641Skmacy	mix.nchan = 1;
2299181641Skmacy	mix.type = MIX_SELECTOR;
2300181641Skmacy
2301181641Skmacy	mix.ctl = SOUND_MIXER_NRDEVICES;
2302181641Skmacy	mix.minval = 1;
2303181641Skmacy	mix.maxval = d->bNrInPins;
2304181641Skmacy
2305181641Skmacy	if (mix.maxval > MAX_SELECTOR_INPUT_PIN) {
2306181641Skmacy		mix.maxval = MAX_SELECTOR_INPUT_PIN;
2307181641Skmacy	}
2308181641Skmacy	mix.mul = (mix.maxval - mix.minval);
2309181641Skmacy	for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) {
2310181641Skmacy		mix.slctrtype[i] = SOUND_MIXER_NRDEVICES;
2311181641Skmacy	}
2312181641Skmacy
2313181641Skmacy	for (i = 0; i < mix.maxval; i++) {
2314181641Skmacy		mix.slctrtype[i] = uaudio_mixer_feature_name(
2315181641Skmacy		    &iot[d->baSourceId[i]], &mix);
2316181641Skmacy	}
2317181641Skmacy
2318181641Skmacy	mix.class = 0;			/* not used */
2319181641Skmacy
2320181641Skmacy	uaudio_mixer_add_ctl(sc, &mix);
2321181641Skmacy}
2322181641Skmacy
2323181641Skmacystatic void
2324181641Skmacyuaudio20_mixer_add_selector(struct uaudio_softc *sc,
2325181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2326181641Skmacy{
2327181641Skmacy	const struct usb_audio20_selector_unit *d = iot[id].u.su_v2;
2328181641Skmacy	struct uaudio_mixer_node mix;
2329181641Skmacy	uint16_t i;
2330181641Skmacy
2331181641Skmacy	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
2332181641Skmacy	    d->bUnitId, d->bNrInPins);
2333181641Skmacy
2334181641Skmacy	if (d->bNrInPins == 0)
2335181641Skmacy		return;
2336181641Skmacy
2337181641Skmacy	memset(&mix, 0, sizeof(mix));
2338181641Skmacy
2339181641Skmacy	mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
2340181641Skmacy	mix.wValue[0] = MAKE_WORD(0, 0);
2341181641Skmacy	uaudio20_mixer_determine_class(&iot[id], &mix);
2342181641Skmacy	mix.nchan = 1;
2343181641Skmacy	mix.type = MIX_SELECTOR;
2344181641Skmacy
2345181641Skmacy	mix.ctl = SOUND_MIXER_NRDEVICES;
2346181641Skmacy	mix.minval = 1;
2347181641Skmacy	mix.maxval = d->bNrInPins;
2348181641Skmacy
2349181641Skmacy	if (mix.maxval > MAX_SELECTOR_INPUT_PIN)
2350181641Skmacy		mix.maxval = MAX_SELECTOR_INPUT_PIN;
2351181641Skmacy
2352181641Skmacy	mix.mul = (mix.maxval - mix.minval);
2353181641Skmacy	for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++)
2354181641Skmacy		mix.slctrtype[i] = SOUND_MIXER_NRDEVICES;
2355181641Skmacy
2356181641Skmacy	for (i = 0; i < mix.maxval; i++) {
2357181641Skmacy		mix.slctrtype[i] = uaudio20_mixer_feature_name(
2358181641Skmacy		    &iot[d->baSourceId[i]], &mix);
2359181641Skmacy	}
2360181641Skmacy
2361181641Skmacy	mix.class = 0;			/* not used */
2362181641Skmacy
2363181641Skmacy	uaudio_mixer_add_ctl(sc, &mix);
2364181641Skmacy}
2365181641Skmacy
2366181641Skmacystatic uint32_t
2367181641Skmacyuaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit *d,
2368181641Skmacy    uint8_t i)
2369181641Skmacy{
2370181641Skmacy	uint32_t temp = 0;
2371181641Skmacy	uint32_t offset = (i * d->bControlSize);
2372181641Skmacy
2373181641Skmacy	if (d->bControlSize > 0) {
2374181641Skmacy		temp |= d->bmaControls[offset];
2375181641Skmacy		if (d->bControlSize > 1) {
2376181641Skmacy			temp |= d->bmaControls[offset + 1] << 8;
2377181641Skmacy			if (d->bControlSize > 2) {
2378181641Skmacy				temp |= d->bmaControls[offset + 2] << 16;
2379181641Skmacy				if (d->bControlSize > 3) {
2380181641Skmacy					temp |= d->bmaControls[offset + 3] << 24;
2381181641Skmacy				}
2382181641Skmacy			}
2383181641Skmacy		}
2384181641Skmacy	}
2385181641Skmacy	return (temp);
2386181641Skmacy}
2387181641Skmacy
2388181641Skmacystatic void
2389181641Skmacyuaudio_mixer_add_feature(struct uaudio_softc *sc,
2390181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2391181641Skmacy{
2392181641Skmacy	const struct usb_audio_feature_unit *d = iot[id].u.fu_v1;
2393181641Skmacy	struct uaudio_mixer_node mix;
2394181641Skmacy	uint32_t fumask;
2395181641Skmacy	uint32_t mmask;
2396181641Skmacy	uint32_t cmask;
2397181641Skmacy	uint16_t mixernumber;
2398181641Skmacy	uint8_t nchan;
2399181641Skmacy	uint8_t chan;
2400181641Skmacy	uint8_t ctl;
2401181641Skmacy	uint8_t i;
2402181641Skmacy
2403181641Skmacy	if (d->bControlSize == 0) {
2404181641Skmacy		return;
2405181641Skmacy	}
2406181641Skmacy	memset(&mix, 0, sizeof(mix));
2407181641Skmacy
2408181641Skmacy	nchan = (d->bLength - 7) / d->bControlSize;
2409181641Skmacy	mmask = uaudio_mixer_feature_get_bmaControls(d, 0);
2410181641Skmacy	cmask = 0;
2411181641Skmacy
2412181641Skmacy	if (nchan == 0) {
2413181641Skmacy		return;
2414181641Skmacy	}
2415181641Skmacy	/* figure out what we can control */
2416181641Skmacy
2417181641Skmacy	for (chan = 1; chan < nchan; chan++) {
2418181641Skmacy		DPRINTFN(10, "chan=%d mask=%x\n",
2419181641Skmacy		    chan, uaudio_mixer_feature_get_bmaControls(d, chan));
2420181641Skmacy
2421181641Skmacy		cmask |= uaudio_mixer_feature_get_bmaControls(d, chan);
2422181641Skmacy	}
2423181641Skmacy
2424181641Skmacy	if (nchan > MIX_MAX_CHAN) {
2425181641Skmacy		nchan = MIX_MAX_CHAN;
2426181641Skmacy	}
2427181641Skmacy	mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
2428181641Skmacy
2429181641Skmacy	for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) {
2430181641Skmacy
2431181641Skmacy		fumask = FU_MASK(ctl);
2432181641Skmacy
2433181641Skmacy		DPRINTFN(5, "ctl=%d fumask=0x%04x\n",
2434181641Skmacy		    ctl, fumask);
2435181641Skmacy
2436181641Skmacy		if (mmask & fumask) {
2437181641Skmacy			mix.nchan = 1;
2438181641Skmacy			mix.wValue[0] = MAKE_WORD(ctl, 0);
2439181641Skmacy		} else if (cmask & fumask) {
2440181641Skmacy			mix.nchan = nchan - 1;
2441181641Skmacy			for (i = 1; i < nchan; i++) {
2442181641Skmacy				if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask)
2443181641Skmacy					mix.wValue[i - 1] = MAKE_WORD(ctl, i);
2444181641Skmacy				else
2445181641Skmacy					mix.wValue[i - 1] = -1;
2446181641Skmacy			}
2447181641Skmacy		} else {
2448181641Skmacy			continue;
2449181641Skmacy		}
2450181641Skmacy
2451181641Skmacy		mixernumber = uaudio_mixer_feature_name(&iot[id], &mix);
2452181641Skmacy
2453181641Skmacy		switch (ctl) {
2454181641Skmacy		case MUTE_CONTROL:
2455181641Skmacy			mix.type = MIX_ON_OFF;
2456181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;
2457181641Skmacy			break;
2458181641Skmacy
2459181641Skmacy		case VOLUME_CONTROL:
2460181641Skmacy			mix.type = MIX_SIGNED_16;
2461181641Skmacy			mix.ctl = mixernumber;
2462181641Skmacy			break;
2463181641Skmacy
2464181641Skmacy		case BASS_CONTROL:
2465181641Skmacy			mix.type = MIX_SIGNED_8;
2466181641Skmacy			mix.ctl = SOUND_MIXER_BASS;
2467181641Skmacy			break;
2468181641Skmacy
2469181641Skmacy		case MID_CONTROL:
2470181641Skmacy			mix.type = MIX_SIGNED_8;
2471181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
2472181641Skmacy			break;
2473181641Skmacy
2474181641Skmacy		case TREBLE_CONTROL:
2475181641Skmacy			mix.type = MIX_SIGNED_8;
2476181641Skmacy			mix.ctl = SOUND_MIXER_TREBLE;
2477181641Skmacy			break;
2478181641Skmacy
2479181641Skmacy		case GRAPHIC_EQUALIZER_CONTROL:
2480181641Skmacy			continue;	/* XXX don't add anything */
2481181641Skmacy			break;
2482181641Skmacy
2483181641Skmacy		case AGC_CONTROL:
2484181641Skmacy			mix.type = MIX_ON_OFF;
2485181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
2486181641Skmacy			break;
2487181641Skmacy
2488207796Salc		case DELAY_CONTROL:
2489207796Salc			mix.type = MIX_UNSIGNED_16;
2490207796Salc			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
2491181641Skmacy			break;
2492181641Skmacy
2493181641Skmacy		case BASS_BOOST_CONTROL:
2494181641Skmacy			mix.type = MIX_ON_OFF;
2495181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
2496181641Skmacy			break;
2497181641Skmacy
2498181641Skmacy		case LOUDNESS_CONTROL:
2499181641Skmacy			mix.type = MIX_ON_OFF;
2500181641Skmacy			mix.ctl = SOUND_MIXER_LOUD;	/* Is this correct ? */
2501181641Skmacy			break;
2502181641Skmacy
2503181641Skmacy		default:
2504181641Skmacy			mix.type = MIX_UNKNOWN;
2505181641Skmacy			break;
2506181641Skmacy		}
2507181641Skmacy
2508181641Skmacy		if (mix.type != MIX_UNKNOWN)
2509181641Skmacy			uaudio_mixer_add_ctl(sc, &mix);
2510181641Skmacy	}
2511181641Skmacy}
2512181641Skmacy
2513181641Skmacystatic void
2514181641Skmacyuaudio20_mixer_add_feature(struct uaudio_softc *sc,
2515181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2516181641Skmacy{
2517181641Skmacy	const struct usb_audio20_feature_unit *d = iot[id].u.fu_v2;
2518181641Skmacy	struct uaudio_mixer_node mix;
2519181641Skmacy	uint32_t ctl;
2520181641Skmacy	uint32_t mmask;
2521181641Skmacy	uint32_t cmask;
2522181641Skmacy	uint16_t mixernumber;
2523181641Skmacy	uint8_t nchan;
2524181641Skmacy	uint8_t chan;
2525181641Skmacy	uint8_t i;
2526181641Skmacy	uint8_t what;
2527207796Salc
2528181641Skmacy	if (UGETDW(d->bmaControls[0]) == 0)
2529181641Skmacy		return;
2530181641Skmacy
2531181641Skmacy	memset(&mix, 0, sizeof(mix));
2532181641Skmacy
2533181641Skmacy	nchan = (d->bLength - 6) / 4;
2534181641Skmacy	mmask = UGETDW(d->bmaControls[0]);
2535181641Skmacy	cmask = 0;
2536181641Skmacy
2537181641Skmacy	if (nchan == 0)
2538181641Skmacy		return;
2539181641Skmacy
2540181641Skmacy	/* figure out what we can control */
2541181641Skmacy
2542181641Skmacy	for (chan = 1; chan < nchan; chan++)
2543181641Skmacy		cmask |= UGETDW(d->bmaControls[chan]);
2544181641Skmacy
2545181641Skmacy	if (nchan > MIX_MAX_CHAN)
2546181641Skmacy		nchan = MIX_MAX_CHAN;
2547181641Skmacy
2548181641Skmacy	mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
2549181641Skmacy
2550181641Skmacy	for (ctl = 3; ctl != 0; ctl <<= 2) {
2551181641Skmacy
2552181641Skmacy		mixernumber = uaudio20_mixer_feature_name(&iot[id], &mix);
2553181641Skmacy
2554181641Skmacy		switch (ctl) {
2555181641Skmacy		case (3 << 0):
2556181641Skmacy			mix.type = MIX_ON_OFF;
2557181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;
2558181641Skmacy			what = MUTE_CONTROL;
2559181641Skmacy			break;
2560181641Skmacy		case (3 << 2):
2561181641Skmacy			mix.type = MIX_SIGNED_16;
2562181641Skmacy			mix.ctl = mixernumber;
2563181641Skmacy			what = VOLUME_CONTROL;
2564181641Skmacy			break;
2565181641Skmacy		case (3 << 4):
2566181641Skmacy			mix.type = MIX_SIGNED_8;
2567181641Skmacy			mix.ctl = SOUND_MIXER_BASS;
2568181641Skmacy			what = BASS_CONTROL;
2569181641Skmacy			break;
2570181641Skmacy		case (3 << 6):
2571181641Skmacy			mix.type = MIX_SIGNED_8;
2572181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
2573181641Skmacy			what = MID_CONTROL;
2574181641Skmacy			break;
2575181641Skmacy		case (3 << 8):
2576181641Skmacy			mix.type = MIX_SIGNED_8;
2577181641Skmacy			mix.ctl = SOUND_MIXER_TREBLE;
2578181641Skmacy			what = TREBLE_CONTROL;
2579181641Skmacy			break;
2580181641Skmacy		case (3 << 12):
2581181641Skmacy			mix.type = MIX_ON_OFF;
2582181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
2583181641Skmacy			what = AGC_CONTROL;
2584181641Skmacy			break;
2585181641Skmacy		case (3 << 14):
2586181641Skmacy			mix.type = MIX_UNSIGNED_16;
2587181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
2588181641Skmacy			what = DELAY_CONTROL;
2589181641Skmacy			break;
2590181641Skmacy		case (3 << 16):
2591181641Skmacy			mix.type = MIX_ON_OFF;
2592181641Skmacy			mix.ctl = SOUND_MIXER_NRDEVICES;	/* XXXXX */
2593181641Skmacy			what = BASS_BOOST_CONTROL;
2594181641Skmacy			break;
2595181641Skmacy		case (3 << 18):
2596181641Skmacy			mix.type = MIX_ON_OFF;
2597181641Skmacy			mix.ctl = SOUND_MIXER_LOUD;	/* Is this correct ? */
2598181641Skmacy			what = LOUDNESS_CONTROL;
2599181641Skmacy			break;
2600181641Skmacy		case (3 << 20):
2601181641Skmacy			mix.type = MIX_SIGNED_16;
2602181641Skmacy			mix.ctl = mixernumber;
2603181641Skmacy			what = INPUT_GAIN_CONTROL;
2604181641Skmacy			break;
2605181641Skmacy		case (3 << 22):
2606181641Skmacy			mix.type = MIX_SIGNED_16;
2607181641Skmacy			mix.ctl = mixernumber;
2608181641Skmacy			what = INPUT_GAIN_PAD_CONTROL;
2609181641Skmacy			break;
2610207262Salc		default:
2611207262Salc			continue;
2612207262Salc		}
2613207262Salc
2614207262Salc		if ((mmask & ctl) == ctl) {
2615207262Salc			mix.nchan = 1;
2616181641Skmacy			mix.wValue[0] = MAKE_WORD(what, 0);
2617181641Skmacy		} else if ((cmask & ctl) == ctl) {
2618207262Salc			mix.nchan = nchan - 1;
2619181641Skmacy			for (i = 1; i < nchan; i++) {
2620181641Skmacy				if ((UGETDW(d->bmaControls[i]) & ctl) == ctl)
2621181641Skmacy					mix.wValue[i - 1] = MAKE_WORD(what, i);
2622181641Skmacy				else
2623181641Skmacy					mix.wValue[i - 1] = -1;
2624181641Skmacy			}
2625181641Skmacy		} else {
2626181641Skmacy			continue;
2627181641Skmacy		}
2628181641Skmacy
2629181641Skmacy		if (mix.type != MIX_UNKNOWN)
2630181641Skmacy			uaudio_mixer_add_ctl(sc, &mix);
2631181641Skmacy	}
2632181641Skmacy}
2633181641Skmacy
2634181641Skmacystatic void
2635181641Skmacyuaudio_mixer_add_processing_updown(struct uaudio_softc *sc,
2636181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2637181641Skmacy{
2638181641Skmacy	const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1;
2639181641Skmacy	const struct usb_audio_processing_unit_1 *d1 =
2640181641Skmacy	(const void *)(d0->baSourceId + d0->bNrInPins);
2641181641Skmacy	const struct usb_audio_processing_unit_updown *ud =
2642181641Skmacy	(const void *)(d1->bmControls + d1->bControlSize);
2643181641Skmacy	struct uaudio_mixer_node mix;
2644181641Skmacy	uint8_t i;
2645181641Skmacy
2646181641Skmacy	if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) {
2647181641Skmacy		return;
2648181641Skmacy	}
2649181641Skmacy	if (uaudio_mixer_verify_desc(d0, sizeof(*ud) + (2 * ud->bNrModes))
2650181641Skmacy	    == NULL) {
2651181641Skmacy		return;
2652181641Skmacy	}
2653181641Skmacy	DPRINTFN(3, "bUnitId=%d bNrModes=%d\n",
2654181641Skmacy	    d0->bUnitId, ud->bNrModes);
2655181641Skmacy
2656181641Skmacy	if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) {
2657181641Skmacy		DPRINTF("no mode select\n");
2658181641Skmacy		return;
2659181641Skmacy	}
2660181641Skmacy	memset(&mix, 0, sizeof(mix));
2661181641Skmacy
2662181641Skmacy	mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
2663181641Skmacy	mix.nchan = 1;
2664181641Skmacy	mix.wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0);
2665181641Skmacy	uaudio_mixer_determine_class(&iot[id], &mix);
2666181641Skmacy	mix.type = MIX_ON_OFF;		/* XXX */
2667181641Skmacy
2668181641Skmacy	for (i = 0; i < ud->bNrModes; i++) {
2669181641Skmacy		DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i]));
2670181641Skmacy		/* XXX */
2671181641Skmacy	}
2672181641Skmacy
2673181641Skmacy	uaudio_mixer_add_ctl(sc, &mix);
2674181641Skmacy}
2675181641Skmacy
2676181641Skmacystatic void
2677181641Skmacyuaudio_mixer_add_processing(struct uaudio_softc *sc,
2678181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2679181641Skmacy{
2680181641Skmacy	const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1;
2681181641Skmacy	const struct usb_audio_processing_unit_1 *d1 =
2682181641Skmacy	(const void *)(d0->baSourceId + d0->bNrInPins);
2683181641Skmacy	struct uaudio_mixer_node mix;
2684181641Skmacy	uint16_t ptype;
2685208175Salc
2686208175Salc	memset(&mix, 0, sizeof(mix));
2687208175Salc
2688208175Salc	ptype = UGETW(d0->wProcessType);
2689208175Salc
2690208175Salc	DPRINTFN(3, "wProcessType=%d bUnitId=%d "
2691181641Skmacy	    "bNrInPins=%d\n", ptype, d0->bUnitId, d0->bNrInPins);
2692181641Skmacy
2693181641Skmacy	if (d1->bControlSize == 0) {
2694181641Skmacy		return;
2695181641Skmacy	}
2696181641Skmacy	if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) {
2697181641Skmacy		mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
2698181641Skmacy		mix.nchan = 1;
2699181641Skmacy		mix.wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0);
2700181641Skmacy		uaudio_mixer_determine_class(&iot[id], &mix);
2701181641Skmacy		mix.type = MIX_ON_OFF;
2702181641Skmacy		uaudio_mixer_add_ctl(sc, &mix);
2703181641Skmacy	}
2704181641Skmacy	switch (ptype) {
2705181641Skmacy	case UPDOWNMIX_PROCESS:
2706181641Skmacy		uaudio_mixer_add_processing_updown(sc, iot, id);
2707181641Skmacy		break;
2708181641Skmacy
2709181641Skmacy	case DOLBY_PROLOGIC_PROCESS:
2710181641Skmacy	case P3D_STEREO_EXTENDER_PROCESS:
2711181641Skmacy	case REVERBATION_PROCESS:
2712181641Skmacy	case CHORUS_PROCESS:
2713181641Skmacy	case DYN_RANGE_COMP_PROCESS:
2714181641Skmacy	default:
2715181641Skmacy		DPRINTF("unit %d, type=%d is not implemented\n",
2716181641Skmacy		    d0->bUnitId, ptype);
2717181641Skmacy		break;
2718181641Skmacy	}
2719181641Skmacy}
2720181641Skmacy
2721181641Skmacystatic void
2722181641Skmacyuaudio_mixer_add_extension(struct uaudio_softc *sc,
2723181641Skmacy    const struct uaudio_terminal_node *iot, int id)
2724181641Skmacy{
2725181641Skmacy	const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu_v1;
2726181641Skmacy	const struct usb_audio_extension_unit_1 *d1 =
2727181641Skmacy	(const void *)(d0->baSourceId + d0->bNrInPins);
2728181641Skmacy	struct uaudio_mixer_node mix;
2729181641Skmacy
2730181641Skmacy	DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
2731181641Skmacy	    d0->bUnitId, d0->bNrInPins);
2732181641Skmacy
2733181641Skmacy	if (sc->sc_uq_au_no_xu) {
2734181641Skmacy		return;
2735181641Skmacy	}
2736181641Skmacy	if (d1->bControlSize == 0) {
2737181641Skmacy		return;
2738181641Skmacy	}
2739181641Skmacy	if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) {
2740181641Skmacy
2741181641Skmacy		memset(&mix, 0, sizeof(mix));
2742181641Skmacy
2743181641Skmacy		mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
2744181641Skmacy		mix.nchan = 1;
2745181641Skmacy		mix.wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0);
2746181641Skmacy		uaudio_mixer_determine_class(&iot[id], &mix);
2747181641Skmacy		mix.type = MIX_ON_OFF;
2748181641Skmacy
2749181641Skmacy		uaudio_mixer_add_ctl(sc, &mix);
2750181641Skmacy	}
2751181641Skmacy}
2752181641Skmacy
2753181641Skmacystatic const void *
2754181641Skmacyuaudio_mixer_verify_desc(const void *arg, uint32_t len)
2755181641Skmacy{
2756181641Skmacy	const struct usb_audio_mixer_unit_1 *d1;
2757181641Skmacy	const struct usb_audio_extension_unit_1 *e1;
2758181641Skmacy	const struct usb_audio_processing_unit_1 *u1;
2759181641Skmacy
2760181641Skmacy	union {
2761181641Skmacy		const struct usb_descriptor *desc;
2762181641Skmacy		const struct usb_audio_input_terminal *it;
2763181641Skmacy		const struct usb_audio_output_terminal *ot;
2764181641Skmacy		const struct usb_audio_mixer_unit_0 *mu;
2765181641Skmacy		const struct usb_audio_selector_unit *su;
2766181641Skmacy		const struct usb_audio_feature_unit *fu;
2767181641Skmacy		const struct usb_audio_processing_unit_0 *pu;
2768181641Skmacy		const struct usb_audio_extension_unit_0 *eu;
2769181641Skmacy	}     u;
2770181641Skmacy
2771181641Skmacy	u.desc = arg;
2772181641Skmacy
2773181641Skmacy	if (u.desc == NULL) {
2774181641Skmacy		goto error;
2775181641Skmacy	}
2776181641Skmacy	if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) {
2777181641Skmacy		goto error;
2778181641Skmacy	}
2779181641Skmacy	switch (u.desc->bDescriptorSubtype) {
2780181641Skmacy	case UDESCSUB_AC_INPUT:
2781181641Skmacy		len += sizeof(*u.it);
2782181641Skmacy		break;
2783181641Skmacy
2784181641Skmacy	case UDESCSUB_AC_OUTPUT:
2785181641Skmacy		len += sizeof(*u.ot);
2786181641Skmacy		break;
2787181641Skmacy
2788181641Skmacy	case UDESCSUB_AC_MIXER:
2789181641Skmacy		len += sizeof(*u.mu);
2790181641Skmacy
2791181641Skmacy		if (u.desc->bLength < len) {
2792181641Skmacy			goto error;
2793181641Skmacy		}
2794181641Skmacy		len += u.mu->bNrInPins;
2795181641Skmacy
2796181641Skmacy		if (u.desc->bLength < len) {
2797181641Skmacy			goto error;
2798181641Skmacy		}
2799181641Skmacy		d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins);
2800181641Skmacy
2801181641Skmacy		len += sizeof(*d1);
2802181641Skmacy		break;
2803181641Skmacy
2804181641Skmacy	case UDESCSUB_AC_SELECTOR:
2805181641Skmacy		len += sizeof(*u.su);
2806181641Skmacy
2807181641Skmacy		if (u.desc->bLength < len) {
2808181641Skmacy			goto error;
2809181641Skmacy		}
2810181641Skmacy		len += u.su->bNrInPins;
2811181641Skmacy		break;
2812181641Skmacy
2813181641Skmacy	case UDESCSUB_AC_FEATURE:
2814181641Skmacy		len += (sizeof(*u.fu) + 1);
2815181641Skmacy		break;
2816181641Skmacy
2817181641Skmacy	case UDESCSUB_AC_PROCESSING:
2818181641Skmacy		len += sizeof(*u.pu);
2819181641Skmacy
2820181641Skmacy		if (u.desc->bLength < len) {
2821181641Skmacy			goto error;
2822181641Skmacy		}
2823181641Skmacy		len += u.pu->bNrInPins;
2824181641Skmacy
2825181641Skmacy		if (u.desc->bLength < len) {
2826181641Skmacy			goto error;
2827181641Skmacy		}
2828181641Skmacy		u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins);
2829181641Skmacy
2830181641Skmacy		len += sizeof(*u1);
2831181641Skmacy
2832181641Skmacy		if (u.desc->bLength < len) {
2833181641Skmacy			goto error;
2834181641Skmacy		}
2835181641Skmacy		len += u1->bControlSize;
2836181641Skmacy
2837181641Skmacy		break;
2838181641Skmacy
2839181641Skmacy	case UDESCSUB_AC_EXTENSION:
2840181641Skmacy		len += sizeof(*u.eu);
2841181641Skmacy
2842181641Skmacy		if (u.desc->bLength < len) {
2843181641Skmacy			goto error;
2844181641Skmacy		}
2845181641Skmacy		len += u.eu->bNrInPins;
2846181641Skmacy
2847181641Skmacy		if (u.desc->bLength < len) {
2848181641Skmacy			goto error;
2849181641Skmacy		}
2850181641Skmacy		e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
2851181641Skmacy
2852181641Skmacy		len += sizeof(*e1);
2853181641Skmacy
2854181641Skmacy		if (u.desc->bLength < len) {
2855181641Skmacy			goto error;
2856181641Skmacy		}
2857181641Skmacy		len += e1->bControlSize;
2858181641Skmacy		break;
2859181641Skmacy
2860181641Skmacy	default:
2861181641Skmacy		goto error;
2862181641Skmacy	}
2863181641Skmacy
2864181641Skmacy	if (u.desc->bLength < len) {
2865181641Skmacy		goto error;
2866181641Skmacy	}
2867181641Skmacy	return (u.desc);
2868181641Skmacy
2869181641Skmacyerror:
2870181641Skmacy	if (u.desc) {
2871181641Skmacy		DPRINTF("invalid descriptor, type=%d, "
2872181641Skmacy		    "sub_type=%d, len=%d of %d bytes\n",
2873181641Skmacy		    u.desc->bDescriptorType,
2874181641Skmacy		    u.desc->bDescriptorSubtype,
2875181641Skmacy		    u.desc->bLength, len);
2876181641Skmacy	}
2877181641Skmacy	return (NULL);
2878181641Skmacy}
2879181641Skmacy
2880181641Skmacystatic const void *
2881181641Skmacyuaudio20_mixer_verify_desc(const void *arg, uint32_t len)
2882181641Skmacy{
2883181641Skmacy	const struct usb_audio20_mixer_unit_1 *d1;
2884181641Skmacy	const struct usb_audio20_extension_unit_1 *e1;
2885181641Skmacy	const struct usb_audio20_processing_unit_1 *u1;
2886181641Skmacy	const struct usb_audio20_clock_selector_unit_1 *c1;
2887181641Skmacy
2888181641Skmacy	union {
2889181641Skmacy		const struct usb_descriptor *desc;
2890181641Skmacy		const struct usb_audio20_clock_source_unit *csrc;
2891181641Skmacy		const struct usb_audio20_clock_selector_unit_0 *csel;
2892181641Skmacy		const struct usb_audio20_clock_multiplier_unit *cmul;
2893181641Skmacy		const struct usb_audio20_input_terminal *it;
2894181641Skmacy		const struct usb_audio20_output_terminal *ot;
2895181641Skmacy		const struct usb_audio20_mixer_unit_0 *mu;
2896181641Skmacy		const struct usb_audio20_selector_unit *su;
2897181641Skmacy		const struct usb_audio20_feature_unit *fu;
2898181641Skmacy		const struct usb_audio20_sample_rate_unit *ru;
2899181641Skmacy		const struct usb_audio20_processing_unit_0 *pu;
2900181641Skmacy		const struct usb_audio20_extension_unit_0 *eu;
2901181641Skmacy		const struct usb_audio20_effect_unit *ef;
2902181641Skmacy	}     u;
2903181641Skmacy
2904181641Skmacy	u.desc = arg;
2905181641Skmacy
2906181641Skmacy	if (u.desc == NULL)
2907181641Skmacy		goto error;
2908181641Skmacy
2909181641Skmacy	if (u.desc->bDescriptorType != UDESC_CS_INTERFACE)
2910181641Skmacy		goto error;
2911181641Skmacy
2912181641Skmacy	switch (u.desc->bDescriptorSubtype) {
2913181641Skmacy	case UDESCSUB_AC_INPUT:
2914181641Skmacy		len += sizeof(*u.it);
2915181641Skmacy		break;
2916181641Skmacy
2917181641Skmacy	case UDESCSUB_AC_OUTPUT:
2918181641Skmacy		len += sizeof(*u.ot);
2919181641Skmacy		break;
2920181641Skmacy
2921181641Skmacy	case UDESCSUB_AC_MIXER:
2922181641Skmacy		len += sizeof(*u.mu);
2923181641Skmacy
2924181641Skmacy		if (u.desc->bLength < len)
2925181641Skmacy			goto error;
2926181641Skmacy		len += u.mu->bNrInPins;
2927181641Skmacy
2928181641Skmacy		if (u.desc->bLength < len)
2929181641Skmacy			goto error;
2930181641Skmacy
2931181641Skmacy		d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins);
2932181641Skmacy
2933181641Skmacy		len += sizeof(*d1) + d1->bNrChannels;
2934181641Skmacy		break;
2935181641Skmacy
2936181641Skmacy	case UDESCSUB_AC_SELECTOR:
2937181641Skmacy		len += sizeof(*u.su);
2938181641Skmacy
2939181641Skmacy		if (u.desc->bLength < len)
2940181641Skmacy			goto error;
2941181641Skmacy
2942181641Skmacy		len += u.su->bNrInPins;
2943207796Salc		break;
2944181641Skmacy
2945207796Salc	case UDESCSUB_AC_FEATURE:
2946181641Skmacy		len += sizeof(*u.fu) + 1;
2947181641Skmacy
2948207796Salc		if (u.desc->bLength < len)
2949181641Skmacy			goto error;
2950181641Skmacy		break;
2951181641Skmacy
2952181747Skmacy	case UDESCSUB_AC_EFFECT:
2953181641Skmacy		len += sizeof(*u.ef) + 4;
2954181641Skmacy		break;
2955181641Skmacy
2956181641Skmacy	case UDESCSUB_AC_PROCESSING_V2:
2957181641Skmacy		len += sizeof(*u.pu);
2958181641Skmacy
2959181641Skmacy		if (u.desc->bLength < len)
2960181641Skmacy			goto error;
2961181641Skmacy
2962181641Skmacy		len += u.pu->bNrInPins;
2963181641Skmacy
2964181641Skmacy		if (u.desc->bLength < len)
2965181641Skmacy			goto error;
2966181641Skmacy
2967181641Skmacy		u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins);
2968181641Skmacy
2969181641Skmacy		len += sizeof(*u1);
2970181641Skmacy		break;
2971181641Skmacy
2972181641Skmacy	case UDESCSUB_AC_EXTENSION_V2:
2973181641Skmacy		len += sizeof(*u.eu);
2974181641Skmacy
2975181641Skmacy		if (u.desc->bLength < len)
2976181641Skmacy			goto error;
2977181641Skmacy
2978181641Skmacy		len += u.eu->bNrInPins;
2979181641Skmacy
2980181747Skmacy		if (u.desc->bLength < len)
2981181641Skmacy			goto error;
2982181641Skmacy
2983181641Skmacy		e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
2984181641Skmacy
2985181641Skmacy		len += sizeof(*e1);
2986181641Skmacy		break;
2987181641Skmacy
2988181641Skmacy	case UDESCSUB_AC_CLOCK_SRC:
2989181641Skmacy		len += sizeof(*u.csrc);
2990181641Skmacy		break;
2991181641Skmacy
2992181641Skmacy	case UDESCSUB_AC_CLOCK_SEL:
2993181641Skmacy		len += sizeof(*u.csel);
2994181641Skmacy
2995181641Skmacy		if (u.desc->bLength < len)
2996181641Skmacy			goto error;
2997181641Skmacy
2998181641Skmacy		len += u.csel->bNrInPins;
2999181641Skmacy
3000181641Skmacy		if (u.desc->bLength < len)
3001181641Skmacy			goto error;
3002181641Skmacy
3003181641Skmacy		c1 = (const void *)(u.csel->baCSourceId + u.csel->bNrInPins);
3004181641Skmacy
3005181641Skmacy		len += sizeof(*c1);
3006181641Skmacy		break;
3007181641Skmacy
3008181641Skmacy	case UDESCSUB_AC_CLOCK_MUL:
3009181641Skmacy		len += sizeof(*u.cmul);
3010181641Skmacy		break;
3011181641Skmacy
3012181641Skmacy	case UDESCSUB_AC_SAMPLE_RT:
3013181641Skmacy		len += sizeof(*u.ru);
3014181641Skmacy		break;
3015181641Skmacy
3016181641Skmacy	default:
3017181641Skmacy		goto error;
3018181641Skmacy	}
3019181641Skmacy
3020181641Skmacy	if (u.desc->bLength < len)
3021181641Skmacy		goto error;
3022181641Skmacy
3023181641Skmacy	return (u.desc);
3024181641Skmacy
3025181641Skmacyerror:
3026181641Skmacy	if (u.desc) {
3027181641Skmacy		DPRINTF("invalid descriptor, type=%d, "
3028181641Skmacy		    "sub_type=%d, len=%d of %d bytes\n",
3029181641Skmacy		    u.desc->bDescriptorType,
3030181641Skmacy		    u.desc->bDescriptorSubtype,
3031181641Skmacy		    u.desc->bLength, len);
3032181641Skmacy	}
3033181641Skmacy	return (NULL);
3034181641Skmacy}
3035181641Skmacy
3036181641Skmacystatic struct usb_audio_cluster
3037181641Skmacyuaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
3038181641Skmacy{
3039181641Skmacy	struct usb_audio_cluster r;
3040181641Skmacy	const struct usb_descriptor *dp;
3041181641Skmacy	uint8_t i;
3042181641Skmacy
3043181641Skmacy	for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) {	/* avoid infinite loops */
3044181641Skmacy		dp = iot[id].u.desc;
3045181641Skmacy		if (dp == NULL) {
3046181641Skmacy			goto error;
3047181641Skmacy		}
3048181641Skmacy		switch (dp->bDescriptorSubtype) {
3049181641Skmacy		case UDESCSUB_AC_INPUT:
3050181641Skmacy			r.bNrChannels = iot[id].u.it_v1->bNrChannels;
3051181641Skmacy			r.wChannelConfig[0] = iot[id].u.it_v1->wChannelConfig[0];
3052181641Skmacy			r.wChannelConfig[1] = iot[id].u.it_v1->wChannelConfig[1];
3053181641Skmacy			r.iChannelNames = iot[id].u.it_v1->iChannelNames;
3054181641Skmacy			goto done;
3055181641Skmacy
3056181641Skmacy		case UDESCSUB_AC_OUTPUT:
3057181641Skmacy			id = iot[id].u.ot_v1->bSourceId;
3058181641Skmacy			break;
3059181641Skmacy
3060181641Skmacy		case UDESCSUB_AC_MIXER:
3061181641Skmacy			r = *(const struct usb_audio_cluster *)
3062181641Skmacy			    &iot[id].u.mu_v1->baSourceId[
3063181641Skmacy			    iot[id].u.mu_v1->bNrInPins];
3064181641Skmacy			goto done;
3065181641Skmacy
3066181641Skmacy		case UDESCSUB_AC_SELECTOR:
3067181641Skmacy			if (iot[id].u.su_v1->bNrInPins > 0) {
3068181641Skmacy				/* XXX This is not really right */
3069181641Skmacy				id = iot[id].u.su_v1->baSourceId[0];
3070181641Skmacy			}
3071181641Skmacy			break;
3072181641Skmacy
3073181641Skmacy		case UDESCSUB_AC_FEATURE:
3074181641Skmacy			id = iot[id].u.fu_v1->bSourceId;
3075181641Skmacy			break;
3076181641Skmacy
3077181641Skmacy		case UDESCSUB_AC_PROCESSING:
3078181641Skmacy			r = *((const struct usb_audio_cluster *)
3079181641Skmacy			    &iot[id].u.pu_v1->baSourceId[
3080181641Skmacy			    iot[id].u.pu_v1->bNrInPins]);
3081181641Skmacy			goto done;
3082181641Skmacy
3083181641Skmacy		case UDESCSUB_AC_EXTENSION:
3084181641Skmacy			r = *((const struct usb_audio_cluster *)
3085181641Skmacy			    &iot[id].u.eu_v1->baSourceId[
3086181641Skmacy			    iot[id].u.eu_v1->bNrInPins]);
3087181641Skmacy			goto done;
3088181641Skmacy
3089181641Skmacy		default:
3090181641Skmacy			goto error;
3091181641Skmacy		}
3092181641Skmacy	}
3093181641Skmacyerror:
3094181641Skmacy	DPRINTF("bad data\n");
3095181641Skmacy	memset(&r, 0, sizeof(r));
3096181641Skmacydone:
3097181641Skmacy	return (r);
3098181641Skmacy}
3099181641Skmacy
3100181641Skmacystatic struct usb_audio20_cluster
3101181641Skmacyuaudio20_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
3102181641Skmacy{
3103181641Skmacy	struct usb_audio20_cluster r;
3104181641Skmacy	const struct usb_descriptor *dp;
3105181641Skmacy	uint8_t i;
3106181641Skmacy
3107181641Skmacy	for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) {	/* avoid infinite loops */
3108181641Skmacy		dp = iot[id].u.desc;
3109181641Skmacy		if (dp == NULL)
3110181641Skmacy			goto error;
3111181641Skmacy
3112181641Skmacy		switch (dp->bDescriptorSubtype) {
3113181641Skmacy		case UDESCSUB_AC_INPUT:
3114181641Skmacy			r.bNrChannels = iot[id].u.it_v2->bNrChannels;
3115181641Skmacy			r.bmChannelConfig[0] = iot[id].u.it_v2->bmChannelConfig[0];
3116181641Skmacy			r.bmChannelConfig[1] = iot[id].u.it_v2->bmChannelConfig[1];
3117200346Skmacy			r.bmChannelConfig[2] = iot[id].u.it_v2->bmChannelConfig[2];
3118181641Skmacy			r.bmChannelConfig[3] = iot[id].u.it_v2->bmChannelConfig[3];
3119181641Skmacy			r.iChannelNames = iot[id].u.it_v2->iTerminal;
3120200346Skmacy			goto done;
3121181641Skmacy
3122181641Skmacy		case UDESCSUB_AC_OUTPUT:
3123181641Skmacy			id = iot[id].u.ot_v2->bSourceId;
3124181641Skmacy			break;
3125181641Skmacy
3126181641Skmacy		case UDESCSUB_AC_MIXER:
3127181641Skmacy			r = *(const struct usb_audio20_cluster *)
3128181641Skmacy			    &iot[id].u.mu_v2->baSourceId[
3129181641Skmacy			    iot[id].u.mu_v2->bNrInPins];
3130181641Skmacy			goto done;
3131181641Skmacy
3132181641Skmacy		case UDESCSUB_AC_SELECTOR:
3133181641Skmacy			if (iot[id].u.su_v2->bNrInPins > 0) {
3134181641Skmacy				/* XXX This is not really right */
3135207419Skmacy				id = iot[id].u.su_v2->baSourceId[0];
3136207419Skmacy			}
3137181641Skmacy			break;
3138207419Skmacy
3139181641Skmacy		case UDESCSUB_AC_SAMPLE_RT:
3140181641Skmacy			id = iot[id].u.ru_v2->bSourceId;
3141195840Sjhb			break;
3142181641Skmacy
3143181641Skmacy		case UDESCSUB_AC_EFFECT:
3144207419Skmacy			id = iot[id].u.ef_v2->bSourceId;
3145207419Skmacy			break;
3146207419Skmacy
3147181641Skmacy		case UDESCSUB_AC_FEATURE:
3148207419Skmacy			id = iot[id].u.fu_v2->bSourceId;
3149207419Skmacy			break;
3150207419Skmacy
3151207419Skmacy		case UDESCSUB_AC_PROCESSING_V2:
3152207419Skmacy			r = *((const struct usb_audio20_cluster *)
3153207419Skmacy			    &iot[id].u.pu_v2->baSourceId[
3154207419Skmacy			    iot[id].u.pu_v2->bNrInPins]);
3155181641Skmacy			goto done;
3156181641Skmacy
3157181641Skmacy		case UDESCSUB_AC_EXTENSION_V2:
3158207419Skmacy			r = *((const struct usb_audio20_cluster *)
3159207419Skmacy			    &iot[id].u.eu_v2->baSourceId[
3160207419Skmacy			    iot[id].u.eu_v2->bNrInPins]);
3161207419Skmacy			goto done;
3162207419Skmacy
3163207419Skmacy		default:
3164207419Skmacy			goto error;
3165207419Skmacy		}
3166207419Skmacy	}
3167207419Skmacyerror:
3168207419Skmacy	DPRINTF("Bad data!\n");
3169207419Skmacy	memset(&r, 0, sizeof(r));
3170207419Skmacydone:
3171207419Skmacy	return (r);
3172207419Skmacy}
3173207419Skmacy
3174181641Skmacystatic uint16_t
3175207419Skmacyuaudio_mixer_determine_class(const struct uaudio_terminal_node *iot,
3176207419Skmacy    struct uaudio_mixer_node *mix)
3177207419Skmacy{
3178207419Skmacy	uint16_t terminal_type = 0x0000;
3179207419Skmacy	const struct uaudio_terminal_node *input[2];
3180207419Skmacy	const struct uaudio_terminal_node *output[2];
3181207419Skmacy
3182207419Skmacy	input[0] = uaudio_mixer_get_input(iot, 0);
3183207419Skmacy	input[1] = uaudio_mixer_get_input(iot, 1);
3184207419Skmacy
3185207419Skmacy	output[0] = uaudio_mixer_get_output(iot, 0);
3186207419Skmacy	output[1] = uaudio_mixer_get_output(iot, 1);
3187181641Skmacy
3188181641Skmacy	/*
3189181641Skmacy	 * check if there is only
3190181641Skmacy	 * one output terminal:
3191181641Skmacy	 */
3192181641Skmacy	if (output[0] && (!output[1])) {
3193181641Skmacy		terminal_type =
3194181641Skmacy		    UGETW(output[0]->u.ot_v1->wTerminalType);
3195181641Skmacy	}
3196181641Skmacy	/*
3197181641Skmacy	 * If the only output terminal is USB,
3198181641Skmacy	 * the class is UAC_RECORD.
3199181641Skmacy	 */
3200181641Skmacy	if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
3201181641Skmacy
3202181641Skmacy		mix->class = UAC_RECORD;
3203181641Skmacy		if (input[0] && (!input[1])) {
3204181641Skmacy			terminal_type =
3205181641Skmacy			    UGETW(input[0]->u.it_v1->wTerminalType);
3206181641Skmacy		} else {
3207181641Skmacy			terminal_type = 0;
3208181641Skmacy		}
3209181641Skmacy		goto done;
3210181641Skmacy	}
3211181641Skmacy	/*
3212181641Skmacy	 * if the unit is connected to just
3213181641Skmacy	 * one input terminal, the
3214181641Skmacy	 * class is UAC_INPUT:
3215181641Skmacy	 */
3216181641Skmacy	if (input[0] && (!input[1])) {
3217181641Skmacy		mix->class = UAC_INPUT;
3218181641Skmacy		terminal_type =
3219181641Skmacy		    UGETW(input[0]->u.it_v1->wTerminalType);
3220181641Skmacy		goto done;
3221181641Skmacy	}
3222181641Skmacy	/*
3223181641Skmacy	 * Otherwise, the class is UAC_OUTPUT.
3224181641Skmacy	 */
3225181641Skmacy	mix->class = UAC_OUTPUT;
3226181641Skmacydone:
3227181641Skmacy	return (terminal_type);
3228181641Skmacy}
3229181641Skmacy
3230181641Skmacystatic uint16_t
3231181641Skmacyuaudio20_mixer_determine_class(const struct uaudio_terminal_node *iot,
3232181641Skmacy    struct uaudio_mixer_node *mix)
3233181641Skmacy{
3234181641Skmacy	uint16_t terminal_type = 0x0000;
3235181641Skmacy	const struct uaudio_terminal_node *input[2];
3236181641Skmacy	const struct uaudio_terminal_node *output[2];
3237181641Skmacy
3238181641Skmacy	input[0] = uaudio_mixer_get_input(iot, 0);
3239181641Skmacy	input[1] = uaudio_mixer_get_input(iot, 1);
3240181641Skmacy
3241181641Skmacy	output[0] = uaudio_mixer_get_output(iot, 0);
3242181641Skmacy	output[1] = uaudio_mixer_get_output(iot, 1);
3243181641Skmacy
3244181641Skmacy	/*
3245181641Skmacy	 * check if there is only
3246181641Skmacy	 * one output terminal:
3247181641Skmacy	 */
3248181641Skmacy	if (output[0] && (!output[1]))
3249181641Skmacy		terminal_type = UGETW(output[0]->u.ot_v2->wTerminalType);
3250181641Skmacy	/*
3251181641Skmacy	 * If the only output terminal is USB,
3252181641Skmacy	 * the class is UAC_RECORD.
3253181641Skmacy	 */
3254181641Skmacy	if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
3255181641Skmacy
3256181641Skmacy		mix->class = UAC_RECORD;
3257181641Skmacy		if (input[0] && (!input[1])) {
3258181641Skmacy			terminal_type =
3259181641Skmacy			    UGETW(input[0]->u.it_v2->wTerminalType);
3260181641Skmacy		} else {
3261181641Skmacy			terminal_type = 0;
3262181641Skmacy		}
3263181641Skmacy		goto done;
3264181641Skmacy	}
3265181641Skmacy	/*
3266181641Skmacy	 * if the unit is connected to just
3267181641Skmacy	 * one input terminal, the
3268181641Skmacy	 * class is UAC_INPUT:
3269181641Skmacy	 */
3270181641Skmacy	if (input[0] && (!input[1])) {
3271181641Skmacy		mix->class = UAC_INPUT;
3272181641Skmacy		terminal_type =
3273181641Skmacy		    UGETW(input[0]->u.it_v2->wTerminalType);
3274181641Skmacy		goto done;
3275181641Skmacy	}
3276181641Skmacy	/*
3277181641Skmacy	 * Otherwise, the class is UAC_OUTPUT.
3278181641Skmacy	 */
3279181641Skmacy	mix->class = UAC_OUTPUT;
3280181641Skmacydone:
3281181641Skmacy	return (terminal_type);
3282181641Skmacy}
3283181641Skmacy
3284181641Skmacystruct uaudio_tt_to_feature {
3285181641Skmacy	uint16_t terminal_type;
3286181641Skmacy	uint16_t feature;
3287181641Skmacy};
3288181641Skmacy
3289181641Skmacystatic const struct uaudio_tt_to_feature uaudio_tt_to_feature[] = {
3290181641Skmacy
3291181641Skmacy	{UAT_STREAM, SOUND_MIXER_PCM},
3292181641Skmacy
3293181641Skmacy	{UATI_MICROPHONE, SOUND_MIXER_MIC},
3294181641Skmacy	{UATI_DESKMICROPHONE, SOUND_MIXER_MIC},
3295181641Skmacy	{UATI_PERSONALMICROPHONE, SOUND_MIXER_MIC},
3296181641Skmacy	{UATI_OMNIMICROPHONE, SOUND_MIXER_MIC},
3297181641Skmacy	{UATI_MICROPHONEARRAY, SOUND_MIXER_MIC},
3298181641Skmacy	{UATI_PROCMICROPHONEARR, SOUND_MIXER_MIC},
3299181641Skmacy
3300181641Skmacy	{UATO_SPEAKER, SOUND_MIXER_SPEAKER},
3301181641Skmacy	{UATO_DESKTOPSPEAKER, SOUND_MIXER_SPEAKER},
3302181641Skmacy	{UATO_ROOMSPEAKER, SOUND_MIXER_SPEAKER},
3303181641Skmacy	{UATO_COMMSPEAKER, SOUND_MIXER_SPEAKER},
3304181641Skmacy
3305181641Skmacy	{UATE_ANALOGCONN, SOUND_MIXER_LINE},
3306181641Skmacy	{UATE_LINECONN, SOUND_MIXER_LINE},
3307181641Skmacy	{UATE_LEGACYCONN, SOUND_MIXER_LINE},
3308181641Skmacy
3309181641Skmacy	{UATE_DIGITALAUIFC, SOUND_MIXER_ALTPCM},
3310181641Skmacy	{UATE_SPDIF, SOUND_MIXER_ALTPCM},
3311181641Skmacy	{UATE_1394DA, SOUND_MIXER_ALTPCM},
3312181641Skmacy	{UATE_1394DV, SOUND_MIXER_ALTPCM},
3313181641Skmacy
3314181641Skmacy	{UATF_CDPLAYER, SOUND_MIXER_CD},
3315181641Skmacy
3316181641Skmacy	{UATF_SYNTHESIZER, SOUND_MIXER_SYNTH},
3317181641Skmacy
3318181641Skmacy	{UATF_VIDEODISCAUDIO, SOUND_MIXER_VIDEO},
3319181641Skmacy	{UATF_DVDAUDIO, SOUND_MIXER_VIDEO},
3320181641Skmacy	{UATF_TVTUNERAUDIO, SOUND_MIXER_VIDEO},
3321181641Skmacy
3322181641Skmacy	/* telephony terminal types */
3323181641Skmacy	{UATT_UNDEFINED, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
3324181641Skmacy	{UATT_PHONELINE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
3325181641Skmacy	{UATT_TELEPHONE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
3326181641Skmacy	{UATT_DOWNLINEPHONE, SOUND_MIXER_PHONEIN},	/* SOUND_MIXER_PHONEOUT */
3327181641Skmacy
3328181641Skmacy	{UATF_RADIORECV, SOUND_MIXER_RADIO},
3329181641Skmacy	{UATF_RADIOXMIT, SOUND_MIXER_RADIO},
3330181641Skmacy
3331181641Skmacy	{UAT_UNDEFINED, SOUND_MIXER_VOLUME},
3332181641Skmacy	{UAT_VENDOR, SOUND_MIXER_VOLUME},
3333181641Skmacy	{UATI_UNDEFINED, SOUND_MIXER_VOLUME},
3334181641Skmacy
3335181641Skmacy	/* output terminal types */
3336181641Skmacy	{UATO_UNDEFINED, SOUND_MIXER_VOLUME},
3337181641Skmacy	{UATO_DISPLAYAUDIO, SOUND_MIXER_VOLUME},
3338181641Skmacy	{UATO_SUBWOOFER, SOUND_MIXER_VOLUME},
3339181641Skmacy	{UATO_HEADPHONES, SOUND_MIXER_VOLUME},
3340181641Skmacy
3341181641Skmacy	/* bidir terminal types */
3342181641Skmacy	{UATB_UNDEFINED, SOUND_MIXER_VOLUME},
3343181641Skmacy	{UATB_HANDSET, SOUND_MIXER_VOLUME},
3344181641Skmacy	{UATB_HEADSET, SOUND_MIXER_VOLUME},
3345181641Skmacy	{UATB_SPEAKERPHONE, SOUND_MIXER_VOLUME},
3346181641Skmacy	{UATB_SPEAKERPHONEESUP, SOUND_MIXER_VOLUME},
3347181641Skmacy	{UATB_SPEAKERPHONEECANC, SOUND_MIXER_VOLUME},
3348196723Sadrian
3349196723Sadrian	/* external terminal types */
3350196723Sadrian	{UATE_UNDEFINED, SOUND_MIXER_VOLUME},
3351196723Sadrian
3352196723Sadrian	/* embedded function terminal types */
3353196723Sadrian	{UATF_UNDEFINED, SOUND_MIXER_VOLUME},
3354196723Sadrian	{UATF_CALIBNOISE, SOUND_MIXER_VOLUME},
3355196723Sadrian	{UATF_EQUNOISE, SOUND_MIXER_VOLUME},
3356196723Sadrian	{UATF_DAT, SOUND_MIXER_VOLUME},
3357196723Sadrian	{UATF_DCC, SOUND_MIXER_VOLUME},
3358196723Sadrian	{UATF_MINIDISK, SOUND_MIXER_VOLUME},
3359196723Sadrian	{UATF_ANALOGTAPE, SOUND_MIXER_VOLUME},
3360196723Sadrian	{UATF_PHONOGRAPH, SOUND_MIXER_VOLUME},
3361196723Sadrian	{UATF_VCRAUDIO, SOUND_MIXER_VOLUME},
3362196723Sadrian	{UATF_SATELLITE, SOUND_MIXER_VOLUME},
3363196723Sadrian	{UATF_CABLETUNER, SOUND_MIXER_VOLUME},
3364181641Skmacy	{UATF_DSS, SOUND_MIXER_VOLUME},
3365181641Skmacy	{UATF_MULTITRACK, SOUND_MIXER_VOLUME},
3366181641Skmacy	{0xffff, SOUND_MIXER_VOLUME},
3367181641Skmacy
3368181641Skmacy	/* default */
3369181641Skmacy	{0x0000, SOUND_MIXER_VOLUME},
3370181641Skmacy};
3371181641Skmacy
3372181641Skmacystatic uint16_t
3373181641Skmacyuaudio_mixer_feature_name(const struct uaudio_terminal_node *iot,
3374181641Skmacy    struct uaudio_mixer_node *mix)
3375181641Skmacy{
3376181641Skmacy	const struct uaudio_tt_to_feature *uat = uaudio_tt_to_feature;
3377181641Skmacy	uint16_t terminal_type = uaudio_mixer_determine_class(iot, mix);
3378181641Skmacy
3379181641Skmacy	if ((mix->class == UAC_RECORD) && (terminal_type == 0)) {
3380181641Skmacy		return (SOUND_MIXER_IMIX);
3381181641Skmacy	}
3382181641Skmacy	while (uat->terminal_type) {
3383181641Skmacy		if (uat->terminal_type == terminal_type) {
3384181641Skmacy			break;
3385181641Skmacy		}
3386181641Skmacy		uat++;
3387181641Skmacy	}
3388181641Skmacy
3389181641Skmacy	DPRINTF("terminal_type=0x%04x -> %d\n",
3390181641Skmacy	    terminal_type, uat->feature);
3391181641Skmacy
3392181641Skmacy	return (uat->feature);
3393181641Skmacy}
3394181641Skmacy
3395181641Skmacystatic uint16_t
3396181641Skmacyuaudio20_mixer_feature_name(const struct uaudio_terminal_node *iot,
3397181641Skmacy    struct uaudio_mixer_node *mix)
3398181641Skmacy{
3399181641Skmacy	const struct uaudio_tt_to_feature *uat;
3400181641Skmacy	uint16_t terminal_type = uaudio20_mixer_determine_class(iot, mix);
3401181641Skmacy
3402181641Skmacy	if ((mix->class == UAC_RECORD) && (terminal_type == 0))
3403181641Skmacy		return (SOUND_MIXER_IMIX);
3404181641Skmacy
3405181641Skmacy	for (uat = uaudio_tt_to_feature; uat->terminal_type != 0; uat++) {
3406181641Skmacy		if (uat->terminal_type == terminal_type)
3407181641Skmacy			break;
3408181641Skmacy	}
3409181641Skmacy
3410181641Skmacy	DPRINTF("terminal_type=0x%04x -> %d\n",
3411181641Skmacy	    terminal_type, uat->feature);
3412181641Skmacy
3413181641Skmacy	return (uat->feature);
3414181641Skmacy}
3415181641Skmacy
3416181641Skmacystatic const struct uaudio_terminal_node *
3417181641Skmacyuaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t i)
3418181641Skmacy{
3419181641Skmacy	struct uaudio_terminal_node *root = iot->root;
3420181641Skmacy	uint8_t n;
3421181641Skmacy
3422181641Skmacy	n = iot->usr.id_max;
3423181641Skmacy	do {
3424181641Skmacy		if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) {
3425181641Skmacy			if (!i--)
3426181641Skmacy				return (root + n);
3427181641Skmacy		}
3428181641Skmacy	} while (n--);
3429181641Skmacy
3430181641Skmacy	return (NULL);
3431181641Skmacy}
3432181641Skmacy
3433181641Skmacystatic const struct uaudio_terminal_node *
3434181641Skmacyuaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t i)
3435181641Skmacy{
3436181641Skmacy	struct uaudio_terminal_node *root = iot->root;
3437181641Skmacy	uint8_t n;
3438181641Skmacy
3439181641Skmacy	n = iot->usr.id_max;
3440181641Skmacy	do {
3441181641Skmacy		if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) {
3442181641Skmacy			if (!i--)
3443181641Skmacy				return (root + n);
3444181641Skmacy		}
3445181641Skmacy	} while (n--);
3446181641Skmacy
3447181641Skmacy	return (NULL);
3448181641Skmacy}
3449181641Skmacy
3450181641Skmacystatic void
3451181641Skmacyuaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
3452181641Skmacy    const uint8_t *p_id, uint8_t n_id,
3453181641Skmacy    struct uaudio_search_result *info)
3454181641Skmacy{
3455181641Skmacy	struct uaudio_terminal_node *iot;
3456181641Skmacy	uint8_t n;
3457181641Skmacy	uint8_t i;
3458181641Skmacy	uint8_t is_last;
3459181641Skmacy
3460181641Skmacytop:
3461181641Skmacy	for (n = 0; n < n_id; n++) {
3462181641Skmacy
3463181641Skmacy		i = p_id[n];
3464181641Skmacy
3465181641Skmacy		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
3466181641Skmacy			DPRINTF("avoided going into a circle at id=%d!\n", i);
3467181641Skmacy			return;
3468181641Skmacy		}
3469181641Skmacy
3470181641Skmacy		info->recurse_level++;
3471181641Skmacy
3472181641Skmacy		iot = (root + i);
3473181641Skmacy
3474181641Skmacy		if (iot->u.desc == NULL)
3475181641Skmacy			continue;
3476181641Skmacy
3477181641Skmacy		is_last = ((n + 1) == n_id);
3478181641Skmacy
3479181641Skmacy		switch (iot->u.desc->bDescriptorSubtype) {
3480181641Skmacy		case UDESCSUB_AC_INPUT:
3481181641Skmacy			info->bit_input[i / 8] |= (1 << (i % 8));
3482181641Skmacy			break;
3483181641Skmacy
3484181641Skmacy		case UDESCSUB_AC_FEATURE:
3485181641Skmacy			if (is_last) {
3486181641Skmacy				p_id = &iot->u.fu_v1->bSourceId;
3487181641Skmacy				n_id = 1;
3488181641Skmacy				goto top;
3489181641Skmacy			}
3490181641Skmacy			uaudio_mixer_find_inputs_sub(
3491181641Skmacy			    root, &iot->u.fu_v1->bSourceId, 1, info);
3492181641Skmacy			break;
3493181641Skmacy
3494181641Skmacy		case UDESCSUB_AC_OUTPUT:
3495181641Skmacy			if (is_last) {
3496181641Skmacy				p_id = &iot->u.ot_v1->bSourceId;
3497181641Skmacy				n_id = 1;
3498181641Skmacy				goto top;
3499181641Skmacy			}
3500181641Skmacy			uaudio_mixer_find_inputs_sub(
3501181641Skmacy			    root, &iot->u.ot_v1->bSourceId, 1, info);
3502181641Skmacy			break;
3503207796Salc
3504181641Skmacy		case UDESCSUB_AC_MIXER:
3505181641Skmacy			if (is_last) {
3506181641Skmacy				p_id = iot->u.mu_v1->baSourceId;
3507181641Skmacy				n_id = iot->u.mu_v1->bNrInPins;
3508181641Skmacy				goto top;
3509181641Skmacy			}
3510181641Skmacy			uaudio_mixer_find_inputs_sub(
3511181641Skmacy			    root, iot->u.mu_v1->baSourceId,
3512181641Skmacy			    iot->u.mu_v1->bNrInPins, info);
3513181641Skmacy			break;
3514207796Salc
3515181641Skmacy		case UDESCSUB_AC_SELECTOR:
3516181641Skmacy			if (is_last) {
3517181641Skmacy				p_id = iot->u.su_v1->baSourceId;
3518181641Skmacy				n_id = iot->u.su_v1->bNrInPins;
3519181747Skmacy				goto top;
3520181747Skmacy			}
3521181747Skmacy			uaudio_mixer_find_inputs_sub(
3522181747Skmacy			    root, iot->u.su_v1->baSourceId,
3523181747Skmacy			    iot->u.su_v1->bNrInPins, info);
3524181747Skmacy			break;
3525207796Salc
3526181747Skmacy		case UDESCSUB_AC_PROCESSING:
3527181747Skmacy			if (is_last) {
3528181747Skmacy				p_id = iot->u.pu_v1->baSourceId;
3529207796Salc				n_id = iot->u.pu_v1->bNrInPins;
3530207796Salc				goto top;
3531207796Salc			}
3532207796Salc			uaudio_mixer_find_inputs_sub(
3533207796Salc			    root, iot->u.pu_v1->baSourceId,
3534181747Skmacy			    iot->u.pu_v1->bNrInPins, info);
3535181747Skmacy			break;
3536181747Skmacy
3537181641Skmacy		case UDESCSUB_AC_EXTENSION:
3538181641Skmacy			if (is_last) {
3539181641Skmacy				p_id = iot->u.eu_v1->baSourceId;
3540181641Skmacy				n_id = iot->u.eu_v1->bNrInPins;
3541181641Skmacy				goto top;
3542181641Skmacy			}
3543181641Skmacy			uaudio_mixer_find_inputs_sub(
3544181641Skmacy			    root, iot->u.eu_v1->baSourceId,
3545181641Skmacy			    iot->u.eu_v1->bNrInPins, info);
3546181641Skmacy			break;
3547181641Skmacy
3548181641Skmacy		default:
3549181641Skmacy			break;
3550181641Skmacy		}
3551181641Skmacy	}
3552181641Skmacy}
3553181641Skmacy
3554181641Skmacystatic void
3555181641Skmacyuaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
3556181641Skmacy    const uint8_t *p_id, uint8_t n_id,
3557181641Skmacy    struct uaudio_search_result *info)
3558181641Skmacy{
3559181641Skmacy	struct uaudio_terminal_node *iot;
3560181641Skmacy	uint8_t n;
3561181641Skmacy	uint8_t i;
3562181641Skmacy	uint8_t is_last;
3563181641Skmacy
3564181641Skmacytop:
3565181641Skmacy	for (n = 0; n < n_id; n++) {
3566181641Skmacy
3567181641Skmacy		i = p_id[n];
3568181641Skmacy
3569181641Skmacy		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
3570181641Skmacy			DPRINTF("avoided going into a circle at id=%d!\n", i);
3571181641Skmacy			return;
3572181641Skmacy		}
3573181641Skmacy
3574181641Skmacy		info->recurse_level++;
3575181641Skmacy
3576181641Skmacy		iot = (root + i);
3577181641Skmacy
3578181641Skmacy		if (iot->u.desc == NULL)
3579181641Skmacy			continue;
3580181641Skmacy
3581181641Skmacy		is_last = ((n + 1) == n_id);
3582181641Skmacy
3583181641Skmacy		switch (iot->u.desc->bDescriptorSubtype) {
3584181641Skmacy		case UDESCSUB_AC_INPUT:
3585181641Skmacy			info->bit_input[i / 8] |= (1 << (i % 8));
3586181641Skmacy			break;
3587181641Skmacy
3588181641Skmacy		case UDESCSUB_AC_OUTPUT:
3589181641Skmacy			if (is_last) {
3590181641Skmacy				p_id = &iot->u.ot_v2->bSourceId;
3591181641Skmacy				n_id = 1;
3592181641Skmacy				goto top;
3593181641Skmacy			}
3594181641Skmacy			uaudio20_mixer_find_inputs_sub(
3595181641Skmacy			    root, &iot->u.ot_v2->bSourceId, 1, info);
3596181641Skmacy			break;
3597181641Skmacy
3598181641Skmacy		case UDESCSUB_AC_MIXER:
3599181641Skmacy			if (is_last) {
3600181641Skmacy				p_id = iot->u.mu_v2->baSourceId;
3601181641Skmacy				n_id = iot->u.mu_v2->bNrInPins;
3602181641Skmacy				goto top;
3603181641Skmacy			}
3604181641Skmacy			uaudio20_mixer_find_inputs_sub(
3605181641Skmacy			    root, iot->u.mu_v2->baSourceId,
3606181641Skmacy			    iot->u.mu_v2->bNrInPins, info);
3607181641Skmacy			break;
3608181641Skmacy
3609181641Skmacy		case UDESCSUB_AC_SELECTOR:
3610181641Skmacy			if (is_last) {
3611181641Skmacy				p_id = iot->u.su_v2->baSourceId;
3612181641Skmacy				n_id = iot->u.su_v2->bNrInPins;
3613181641Skmacy				goto top;
3614181641Skmacy			}
3615181641Skmacy			uaudio20_mixer_find_inputs_sub(
3616181641Skmacy			    root, iot->u.su_v2->baSourceId,
3617181641Skmacy			    iot->u.su_v2->bNrInPins, info);
3618181641Skmacy			break;
3619181641Skmacy
3620181641Skmacy		case UDESCSUB_AC_SAMPLE_RT:
3621181641Skmacy			if (is_last) {
3622181641Skmacy				p_id = &iot->u.ru_v2->bSourceId;
3623181641Skmacy				n_id = 1;
3624181641Skmacy				goto top;
3625181641Skmacy			}
3626181641Skmacy			uaudio20_mixer_find_inputs_sub(
3627181641Skmacy			    root, &iot->u.ru_v2->bSourceId,
3628181641Skmacy			    1, info);
3629181641Skmacy			break;
3630181641Skmacy
3631181641Skmacy		case UDESCSUB_AC_EFFECT:
3632181641Skmacy			if (is_last) {
3633181641Skmacy				p_id = &iot->u.ef_v2->bSourceId;
3634181641Skmacy				n_id = 1;
3635181641Skmacy				goto top;
3636181641Skmacy			}
3637181641Skmacy			uaudio20_mixer_find_inputs_sub(
3638181641Skmacy			    root, &iot->u.ef_v2->bSourceId,
3639181641Skmacy			    1, info);
3640181641Skmacy			break;
3641181641Skmacy
3642181641Skmacy		case UDESCSUB_AC_FEATURE:
3643181641Skmacy			if (is_last) {
3644181641Skmacy				p_id = &iot->u.fu_v2->bSourceId;
3645181641Skmacy				n_id = 1;
3646181641Skmacy				goto top;
3647181641Skmacy			}
3648181641Skmacy			uaudio20_mixer_find_inputs_sub(
3649181641Skmacy			    root, &iot->u.fu_v2->bSourceId, 1, info);
3650181641Skmacy			break;
3651181641Skmacy
3652181641Skmacy		case UDESCSUB_AC_PROCESSING_V2:
3653181641Skmacy			if (is_last) {
3654181641Skmacy				p_id = iot->u.pu_v2->baSourceId;
3655181641Skmacy				n_id = iot->u.pu_v2->bNrInPins;
3656181641Skmacy				goto top;
3657181641Skmacy			}
3658181641Skmacy			uaudio20_mixer_find_inputs_sub(
3659181641Skmacy			    root, iot->u.pu_v2->baSourceId,
3660181641Skmacy			    iot->u.pu_v2->bNrInPins, info);
3661181641Skmacy			break;
3662181641Skmacy
3663181641Skmacy		case UDESCSUB_AC_EXTENSION_V2:
3664181641Skmacy			if (is_last) {
3665181641Skmacy				p_id = iot->u.eu_v2->baSourceId;
3666208504Salc				n_id = iot->u.eu_v2->bNrInPins;
3667208504Salc				goto top;
3668181641Skmacy			}
3669208504Salc			uaudio20_mixer_find_inputs_sub(
3670208504Salc			    root, iot->u.eu_v2->baSourceId,
3671208504Salc			    iot->u.eu_v2->bNrInPins, info);
3672208504Salc			break;
3673208504Salc		default:
3674208504Salc			break;
3675208504Salc		}
3676208504Salc	}
3677208504Salc}
3678181641Skmacy
3679208504Salcstatic void
3680181641Skmacyuaudio20_mixer_find_clocks_sub(struct uaudio_terminal_node *root,
3681181641Skmacy    const uint8_t *p_id, uint8_t n_id,
3682181641Skmacy    struct uaudio_search_result *info)
3683181641Skmacy{
3684181641Skmacy	struct uaudio_terminal_node *iot;
3685181641Skmacy	uint8_t n;
3686181641Skmacy	uint8_t i;
3687181641Skmacy	uint8_t is_last;
3688181641Skmacy	uint8_t id;
3689181641Skmacy
3690181641Skmacytop:
3691181641Skmacy	for (n = 0; n < n_id; n++) {
3692181641Skmacy
3693208504Salc		i = p_id[n];
3694181641Skmacy
3695181641Skmacy		if (info->recurse_level == UAUDIO_RECURSE_LIMIT) {
3696181641Skmacy			DPRINTF("avoided going into a circle at id=%d!\n", i);
3697181641Skmacy			return;
3698181641Skmacy		}
3699181641Skmacy
3700181641Skmacy		info->recurse_level++;
3701181641Skmacy
3702181641Skmacy		iot = (root + i);
3703181641Skmacy
3704181641Skmacy		if (iot->u.desc == NULL)
3705181641Skmacy			continue;
3706181641Skmacy
3707181641Skmacy		is_last = ((n + 1) == n_id);
3708181641Skmacy
3709181641Skmacy		switch (iot->u.desc->bDescriptorSubtype) {
3710181641Skmacy		case UDESCSUB_AC_INPUT:
3711181641Skmacy			info->is_input = 1;
3712181641Skmacy			if (is_last) {
3713181641Skmacy				p_id = &iot->u.it_v2->bCSourceId;
3714181641Skmacy				n_id = 1;
3715181641Skmacy				goto top;
3716181641Skmacy			}
3717181641Skmacy			uaudio20_mixer_find_clocks_sub(root,
3718181641Skmacy			    &iot->u.it_v2->bCSourceId, 1, info);
3719181641Skmacy			break;
3720181641Skmacy
3721181641Skmacy		case UDESCSUB_AC_OUTPUT:
3722181641Skmacy			info->is_input = 0;
3723181641Skmacy			if (is_last) {
3724181641Skmacy				p_id = &iot->u.ot_v2->bCSourceId;
3725181641Skmacy				n_id = 1;
3726181641Skmacy				goto top;
3727181641Skmacy			}
3728181641Skmacy			uaudio20_mixer_find_clocks_sub(root,
3729207155Salc			    &iot->u.ot_v2->bCSourceId, 1, info);
3730207155Salc			break;
3731207155Salc
3732207155Salc		case UDESCSUB_AC_CLOCK_SEL:
3733207155Salc			if (is_last) {
3734207155Salc				p_id = iot->u.csel_v2->baCSourceId;
3735207155Salc				n_id = iot->u.csel_v2->bNrInPins;
3736207155Salc				goto top;
3737207155Salc			}
3738207155Salc			uaudio20_mixer_find_clocks_sub(root,
3739207155Salc			    iot->u.csel_v2->baCSourceId,
3740207155Salc			    iot->u.csel_v2->bNrInPins, info);
3741207155Salc			break;
3742207155Salc
3743207155Salc		case UDESCSUB_AC_CLOCK_MUL:
3744207155Salc			if (is_last) {
3745207155Salc				p_id = &iot->u.cmul_v2->bCSourceId;
3746207155Salc				n_id = 1;
3747207155Salc				goto top;
3748207155Salc			}
3749207155Salc			uaudio20_mixer_find_clocks_sub(root,
3750207155Salc			    &iot->u.cmul_v2->bCSourceId,
3751207155Salc			    1, info);
3752207155Salc			break;
3753207155Salc
3754207155Salc		case UDESCSUB_AC_CLOCK_SRC:
3755207155Salc
3756207155Salc			id = iot->u.csrc_v2->bClockId;
3757181641Skmacy
3758181641Skmacy			switch (info->is_input) {
3759181641Skmacy			case 0:
3760181641Skmacy				info->bit_output[id / 8] |= (1 << (id % 8));
3761181641Skmacy				break;
3762181641Skmacy			case 1:
3763181641Skmacy				info->bit_input[id / 8] |= (1 << (id % 8));
3764181641Skmacy				break;
3765181641Skmacy			default:
3766181641Skmacy				break;
3767181641Skmacy			}
3768181641Skmacy			break;
3769181641Skmacy
3770181641Skmacy		default:
3771181641Skmacy			break;
3772181641Skmacy		}
3773181641Skmacy	}
3774181641Skmacy}
3775181641Skmacy
3776181641Skmacystatic void
3777181641Skmacyuaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id,
3778181641Skmacy    uint8_t n_id, struct uaudio_search_result *info)
3779181641Skmacy{
3780181641Skmacy	struct uaudio_terminal_node *iot = (root + id);
3781181641Skmacy	uint8_t j;
3782181641Skmacy
3783181641Skmacy	j = n_id;
3784181641Skmacy	do {
3785181641Skmacy		if ((j != id) && ((root + j)->u.desc) &&
3786181641Skmacy		    ((root + j)->u.desc->bDescriptorSubtype == UDESCSUB_AC_OUTPUT)) {
3787181641Skmacy
3788181641Skmacy			/*
3789181641Skmacy			 * "j" (output) <--- virtual wire <--- "id" (input)
3790181641Skmacy			 *
3791181641Skmacy			 * if "j" has "id" on the input, then "id" have "j" on
3792181641Skmacy			 * the output, because they are connected:
3793208175Salc			 */
3794208175Salc			if ((root + j)->usr.bit_input[id / 8] & (1 << (id % 8))) {
3795208175Salc				iot->usr.bit_output[j / 8] |= (1 << (j % 8));
3796208175Salc			}
3797208175Salc		}
3798208175Salc	} while (j--);
3799208175Salc}
3800208175Salc
3801208175Salcstatic void
3802208175Salcuaudio_mixer_fill_info(struct uaudio_softc *sc,
3803181641Skmacy    struct usb_device *udev, void *desc)
3804181641Skmacy{
3805207796Salc	const struct usb_audio_control_descriptor *acdp;
3806181641Skmacy	struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev);
3807181641Skmacy	const struct usb_descriptor *dp;
3808181641Skmacy	const struct usb_audio_unit *au;
3809181641Skmacy	struct uaudio_terminal_node *iot = NULL;
3810181641Skmacy	uint16_t wTotalLen;
3811181641Skmacy	uint8_t ID_max = 0;		/* inclusive */
3812181641Skmacy	uint8_t i;
3813181641Skmacy
3814188341Skmacy	desc = usb_desc_foreach(cd, desc);
3815188341Skmacy
3816181641Skmacy	if (desc == NULL) {
3817181641Skmacy		DPRINTF("no Audio Control header\n");
3818181641Skmacy		goto done;
3819181641Skmacy	}
3820181641Skmacy	acdp = desc;
3821188341Skmacy
3822188341Skmacy	if ((acdp->bLength < sizeof(*acdp)) ||
3823181641Skmacy	    (acdp->bDescriptorType != UDESC_CS_INTERFACE) ||
3824188341Skmacy	    (acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)) {
3825181641Skmacy		DPRINTF("invalid Audio Control header\n");
3826181641Skmacy		goto done;
3827181641Skmacy	}
3828181641Skmacy	/* "wTotalLen" is allowed to be corrupt */
3829181641Skmacy	wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength;
3830181641Skmacy
3831181641Skmacy	/* get USB audio revision */
3832181641Skmacy	sc->sc_audio_rev = UGETW(acdp->bcdADC);
3833181641Skmacy
3834181641Skmacy	DPRINTFN(3, "found AC header, vers=%03x, len=%d\n",
3835181641Skmacy	    sc->sc_audio_rev, wTotalLen);
3836207796Salc
3837181641Skmacy	iot = malloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP,
3838181641Skmacy	    M_WAITOK | M_ZERO);
3839181641Skmacy
3840181641Skmacy	if (iot == NULL) {
3841181641Skmacy		DPRINTF("no memory!\n");
3842181641Skmacy		goto done;
3843181641Skmacy	}
3844181641Skmacy	while ((desc = usb_desc_foreach(cd, desc))) {
3845181641Skmacy
3846181641Skmacy		dp = desc;
3847181641Skmacy
3848181641Skmacy		if (dp->bLength > wTotalLen) {
3849181641Skmacy			break;
3850181641Skmacy		} else {
3851181641Skmacy			wTotalLen -= dp->bLength;
3852181641Skmacy		}
3853181641Skmacy
3854181641Skmacy		if (sc->sc_audio_rev >= UAUDIO_VERSION_30)
3855181641Skmacy			au = NULL;
3856181641Skmacy		else if (sc->sc_audio_rev >= UAUDIO_VERSION_20)
3857181641Skmacy			au = uaudio20_mixer_verify_desc(dp, 0);
3858181641Skmacy		else
3859181641Skmacy			au = uaudio_mixer_verify_desc(dp, 0);
3860181641Skmacy
3861181641Skmacy		if (au) {
3862181641Skmacy			iot[au->bUnitId].u.desc = (const void *)au;
3863181641Skmacy			if (au->bUnitId > ID_max)
3864181641Skmacy				ID_max = au->bUnitId;
3865181641Skmacy		}
3866181641Skmacy	}
3867181641Skmacy
3868181641Skmacy	DPRINTF("Maximum ID=%d\n", ID_max);
3869181641Skmacy
3870181641Skmacy	/*
3871181641Skmacy	 * determine sourcing inputs for
3872181641Skmacy	 * all nodes in the tree:
3873181641Skmacy	 */
3874181641Skmacy	i = ID_max;
3875181641Skmacy	do {
3876181641Skmacy		if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
3877181641Skmacy			/* FALLTHROUGH */
3878181641Skmacy		} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
3879181641Skmacy			uaudio20_mixer_find_inputs_sub(iot,
3880181641Skmacy			    &i, 1, &((iot + i)->usr));
3881181641Skmacy
3882181641Skmacy			sc->sc_mixer_clocks.is_input = 255;
3883181641Skmacy			sc->sc_mixer_clocks.recurse_level = 0;
3884181641Skmacy
3885181641Skmacy			uaudio20_mixer_find_clocks_sub(iot,
3886181641Skmacy			    &i, 1, &sc->sc_mixer_clocks);
3887181641Skmacy		} else {
3888181641Skmacy			uaudio_mixer_find_inputs_sub(iot,
3889181641Skmacy			    &i, 1, &((iot + i)->usr));
3890181641Skmacy		}
3891181641Skmacy	} while (i--);
3892181641Skmacy
3893181641Skmacy	/*
3894181641Skmacy	 * determine outputs for
3895181641Skmacy	 * all nodes in the tree:
3896181641Skmacy	 */
3897181641Skmacy	i = ID_max;
3898181641Skmacy	do {
3899181641Skmacy		uaudio_mixer_find_outputs_sub(iot,
3900208504Salc		    i, ID_max, &((iot + i)->usr));
3901208504Salc	} while (i--);
3902208504Salc
3903208504Salc	/* set "id_max" and "root" */
3904208504Salc
3905208504Salc	i = ID_max;
3906208504Salc	do {
3907208504Salc		(iot + i)->usr.id_max = ID_max;
3908208504Salc		(iot + i)->root = iot;
3909208504Salc	} while (i--);
3910208504Salc
3911208504Salc	/*
3912181641Skmacy	 * Scan the config to create a linked list of "mixer" nodes:
3913208504Salc	 */
3914181641Skmacy
3915181641Skmacy	i = ID_max;
3916181641Skmacy	do {
3917181641Skmacy		dp = iot[i].u.desc;
3918181641Skmacy
3919181641Skmacy		if (dp == NULL)
3920181641Skmacy			continue;
3921181641Skmacy
3922181641Skmacy		DPRINTFN(11, "id=%d subtype=%d\n",
3923181641Skmacy		    i, dp->bDescriptorSubtype);
3924181641Skmacy
3925181641Skmacy		if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
3926181641Skmacy			continue;
3927181641Skmacy		} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
3928181641Skmacy
3929181641Skmacy			switch (dp->bDescriptorSubtype) {
3930181641Skmacy			case UDESCSUB_AC_HEADER:
3931208504Salc				DPRINTF("unexpected AC header\n");
3932181641Skmacy				break;
3933181641Skmacy
3934181641Skmacy			case UDESCSUB_AC_INPUT:
3935181641Skmacy			case UDESCSUB_AC_OUTPUT:
3936181641Skmacy			case UDESCSUB_AC_PROCESSING_V2:
3937181641Skmacy			case UDESCSUB_AC_EXTENSION_V2:
3938181641Skmacy			case UDESCSUB_AC_EFFECT:
3939181641Skmacy			case UDESCSUB_AC_CLOCK_SRC:
3940181641Skmacy			case UDESCSUB_AC_CLOCK_SEL:
3941181641Skmacy			case UDESCSUB_AC_CLOCK_MUL:
3942181641Skmacy			case UDESCSUB_AC_SAMPLE_RT:
3943181641Skmacy				break;
3944181641Skmacy
3945181641Skmacy			case UDESCSUB_AC_MIXER:
3946208504Salc				uaudio20_mixer_add_mixer(sc, iot, i);
3947208504Salc				break;
3948208504Salc
3949181641Skmacy			case UDESCSUB_AC_SELECTOR:
3950181641Skmacy				uaudio20_mixer_add_selector(sc, iot, i);
3951181641Skmacy				break;
3952181641Skmacy
3953181641Skmacy			case UDESCSUB_AC_FEATURE:
3954181641Skmacy				uaudio20_mixer_add_feature(sc, iot, i);
3955181641Skmacy				break;
3956181641Skmacy
3957181641Skmacy			default:
3958181641Skmacy				DPRINTF("bad AC desc subtype=0x%02x\n",
3959181641Skmacy				    dp->bDescriptorSubtype);
3960181641Skmacy				break;
3961181641Skmacy			}
3962181641Skmacy			continue;
3963181641Skmacy		}
3964181641Skmacy
3965181641Skmacy		switch (dp->bDescriptorSubtype) {
3966208504Salc		case UDESCSUB_AC_HEADER:
3967181641Skmacy			DPRINTF("unexpected AC header\n");
3968181641Skmacy			break;
3969181641Skmacy
3970181641Skmacy		case UDESCSUB_AC_INPUT:
3971181641Skmacy		case UDESCSUB_AC_OUTPUT:
3972181641Skmacy			break;
3973181641Skmacy
3974181641Skmacy		case UDESCSUB_AC_MIXER:
3975181641Skmacy			uaudio_mixer_add_mixer(sc, iot, i);
3976181641Skmacy			break;
3977181641Skmacy
3978181641Skmacy		case UDESCSUB_AC_SELECTOR:
3979181641Skmacy			uaudio_mixer_add_selector(sc, iot, i);
3980181641Skmacy			break;
3981181641Skmacy
3982195949Skib		case UDESCSUB_AC_FEATURE:
3983195949Skib			uaudio_mixer_add_feature(sc, iot, i);
3984181641Skmacy			break;
3985181641Skmacy
3986181641Skmacy		case UDESCSUB_AC_PROCESSING:
3987181641Skmacy			uaudio_mixer_add_processing(sc, iot, i);
3988181641Skmacy			break;
3989181641Skmacy
3990181641Skmacy		case UDESCSUB_AC_EXTENSION:
3991181641Skmacy			uaudio_mixer_add_extension(sc, iot, i);
3992181641Skmacy			break;
3993181641Skmacy
3994181641Skmacy		default:
3995181641Skmacy			DPRINTF("bad AC desc subtype=0x%02x\n",
3996195949Skib			    dp->bDescriptorSubtype);
3997195949Skib			break;
3998195949Skib		}
3999195949Skib
4000181641Skmacy	} while (i--);
4001181641Skmacy
4002181641Skmacydone:
4003181641Skmacy	free(iot, M_TEMP);
4004181641Skmacy}
4005181641Skmacy
4006181641Skmacystatic int
4007181641Skmacyuaudio_mixer_get(struct usb_device *udev, uint16_t audio_rev,
4008181641Skmacy    uint8_t what, struct uaudio_mixer_node *mc)
4009181641Skmacy{
4010181641Skmacy	struct usb_device_request req;
4011181641Skmacy	int val;
4012181641Skmacy	uint8_t data[2 + (2 * 3)];
4013181641Skmacy	usb_error_t err;
4014181641Skmacy
4015181641Skmacy	if (mc->wValue[0] == -1)
4016181641Skmacy		return (0);
4017181641Skmacy
4018181641Skmacy	if (audio_rev >= UAUDIO_VERSION_30)
4019181641Skmacy		return (0);
4020181641Skmacy	else if (audio_rev >= UAUDIO_VERSION_20) {
4021181641Skmacy		if (what == GET_CUR) {
4022181641Skmacy			req.bRequest = UA20_CS_CUR;
4023181641Skmacy			USETW(req.wLength, 2);
4024181641Skmacy		} else {
4025181641Skmacy			req.bRequest = UA20_CS_RANGE;
4026181641Skmacy			USETW(req.wLength, 8);
4027181641Skmacy		}
4028181641Skmacy	} else {
4029181641Skmacy		uint16_t len = MIX_SIZE(mc->type);
4030181641Skmacy
4031181641Skmacy		req.bRequest = what;
4032181641Skmacy		USETW(req.wLength, len);
4033181641Skmacy	}
4034181641Skmacy
4035195774Salc	req.bmRequestType = UT_READ_CLASS_INTERFACE;
4036195774Salc	USETW(req.wValue, mc->wValue[0]);
4037195774Salc	USETW(req.wIndex, mc->wIndex);
4038195774Salc
4039195774Salc	memset(data, 0, sizeof(data));
4040195774Salc
4041195949Skib	err = usbd_do_request(udev, NULL, &req, data);
4042195949Skib	if (err) {
4043195774Salc		DPRINTF("err=%s\n", usbd_errstr(err));
4044195774Salc		return (0);
4045195949Skib	}
4046195949Skib
4047195774Salc	if (audio_rev >= UAUDIO_VERSION_30) {
4048195774Salc		val = 0;
4049195774Salc	} else if (audio_rev >= UAUDIO_VERSION_20) {
4050195949Skib		switch (what) {
4051195949Skib		case GET_CUR:
4052195949Skib			val = (data[0] | (data[1] << 8));
4053195949Skib			break;
4054195949Skib		case GET_MIN:
4055195774Salc			val = (data[2] | (data[3] << 8));
4056195949Skib			break;
4057195949Skib		case GET_MAX:
4058195949Skib			val = (data[4] | (data[5] << 8));
4059195949Skib			break;
4060195949Skib		case GET_RES:
4061195949Skib			val = (data[6] | (data[7] << 8));
4062195949Skib			break;
4063195949Skib		default:
4064195949Skib			val = 0;
4065195949Skib			break;
4066195949Skib		}
4067195949Skib	} else {
4068195949Skib		val = (data[0] | (data[1] << 8));
4069195949Skib	}
4070195949Skib
4071195949Skib	if (what == GET_CUR || what == GET_MIN || what == GET_MAX)
4072195949Skib		val = uaudio_mixer_signext(mc->type, val);
4073195949Skib
4074195949Skib	DPRINTFN(3, "val=%d\n", val);
4075195949Skib
4076195949Skib	return (val);
4077195949Skib}
4078195949Skib
4079195949Skibstatic void
4080195949Skibuaudio_mixer_write_cfg_callback(struct usb_xfer *xfer, usb_error_t error)
4081195949Skib{
4082195949Skib	struct usb_device_request req;
4083195949Skib	struct uaudio_softc *sc = usbd_xfer_softc(xfer);
4084195774Salc	struct uaudio_mixer_node *mc = sc->sc_mixer_curr;
4085195774Salc	struct usb_page_cache *pc;
4086195774Salc	uint16_t len;
4087181641Skmacy	uint8_t repeat = 1;
4088181641Skmacy	uint8_t update;
4089181641Skmacy	uint8_t chan;
4090181641Skmacy	uint8_t buf[2];
4091181641Skmacy
4092181641Skmacy	DPRINTF("\n");
4093181641Skmacy
4094181641Skmacy	switch (USB_GET_STATE(xfer)) {
4095181641Skmacy	case USB_ST_TRANSFERRED:
4096181641Skmacytr_transferred:
4097195949Skib	case USB_ST_SETUP:
4098181641Skmacytr_setup:
4099181641Skmacy
4100181641Skmacy		if (mc == NULL) {
4101181641Skmacy			mc = sc->sc_mixer_root;
4102181641Skmacy			sc->sc_mixer_curr = mc;
4103181641Skmacy			sc->sc_mixer_chan = 0;
4104181641Skmacy			repeat = 0;
4105181641Skmacy		}
4106181641Skmacy		while (mc) {
4107181641Skmacy			while (sc->sc_mixer_chan < mc->nchan) {
4108181641Skmacy
4109181641Skmacy				chan = sc->sc_mixer_chan;
4110181641Skmacy
4111181641Skmacy				sc->sc_mixer_chan++;
4112181641Skmacy
4113181641Skmacy				update = ((mc->update[chan / 8] & (1 << (chan % 8))) &&
4114181641Skmacy				    (mc->wValue[chan] != -1));
4115181641Skmacy
4116181641Skmacy				mc->update[chan / 8] &= ~(1 << (chan % 8));
4117181641Skmacy
4118181641Skmacy				if (update) {
4119195949Skib
4120195949Skib					req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
4121181641Skmacy					USETW(req.wValue, mc->wValue[chan]);
4122181641Skmacy					USETW(req.wIndex, mc->wIndex);
4123181641Skmacy
4124181641Skmacy					if (sc->sc_audio_rev >= UAUDIO_VERSION_30) {
4125181641Skmacy						return;
4126181641Skmacy					} else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) {
4127181641Skmacy						len = 2;
4128181641Skmacy						req.bRequest = UA20_CS_CUR;
4129181641Skmacy						USETW(req.wLength, len);
4130181641Skmacy					} else {
4131181641Skmacy						len = MIX_SIZE(mc->type);
4132181641Skmacy						req.bRequest = SET_CUR;
4133181641Skmacy						USETW(req.wLength, len);
4134181641Skmacy					}
4135181641Skmacy
4136181641Skmacy					buf[0] = (mc->wData[chan] & 0xFF);
4137181641Skmacy					buf[1] = (mc->wData[chan] >> 8) & 0xFF;
4138195949Skib
4139195949Skib					pc = usbd_xfer_get_frame(xfer, 0);
4140181641Skmacy					usbd_copy_in(pc, 0, &req, sizeof(req));
4141181641Skmacy					pc = usbd_xfer_get_frame(xfer, 1);
4142181641Skmacy					usbd_copy_in(pc, 0, buf, len);
4143181641Skmacy
4144181641Skmacy					usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
4145181641Skmacy					usbd_xfer_set_frame_len(xfer, 1, len);
4146181641Skmacy					usbd_xfer_set_frames(xfer, len ? 2 : 1);
4147181641Skmacy					usbd_transfer_submit(xfer);
4148195949Skib					return;
4149195949Skib				}
4150195949Skib			}
4151195949Skib
4152181641Skmacy			mc = mc->next;
4153181641Skmacy			sc->sc_mixer_curr = mc;
4154181641Skmacy			sc->sc_mixer_chan = 0;
4155181641Skmacy		}
4156181641Skmacy
4157181641Skmacy		if (repeat) {
4158181641Skmacy			goto tr_setup;
4159208504Salc		}
4160181641Skmacy		break;
4161181641Skmacy
4162208504Salc	default:			/* Error */
4163208504Salc		DPRINTF("error=%s\n", usbd_errstr(error));
4164181641Skmacy		if (error == USB_ERR_CANCELLED) {
4165181641Skmacy			/* do nothing - we are detaching */
4166208504Salc			break;
4167181641Skmacy		}
4168181641Skmacy		goto tr_transferred;
4169181641Skmacy	}
4170208504Salc}
4171208504Salc
4172208504Salcstatic usb_error_t
4173208504Salcuaudio_set_speed(struct usb_device *udev, uint8_t endpt, uint32_t speed)
4174208504Salc{
4175208504Salc	struct usb_device_request req;
4176208504Salc	uint8_t data[3];
4177208504Salc
4178208504Salc	DPRINTFN(6, "endpt=%d speed=%u\n", endpt, speed);
4179208504Salc
4180208504Salc	req.bmRequestType = UT_WRITE_CLASS_ENDPOINT;
4181208504Salc	req.bRequest = SET_CUR;
4182208504Salc	USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0);
4183208504Salc	USETW(req.wIndex, endpt);
4184208504Salc	USETW(req.wLength, 3);
4185208504Salc	data[0] = speed;
4186208504Salc	data[1] = speed >> 8;
4187181641Skmacy	data[2] = speed >> 16;
4188208504Salc
4189181641Skmacy	return (usbd_do_request(udev, NULL, &req, data));
4190181641Skmacy}
4191181641Skmacy
4192181641Skmacystatic usb_error_t
4193181641Skmacyuaudio20_set_speed(struct usb_device *udev, uint8_t iface_no,
4194181641Skmacy    uint8_t clockid, uint32_t speed)
4195181641Skmacy{
4196181641Skmacy	struct usb_device_request req;
4197181641Skmacy	uint8_t data[4];
4198181641Skmacy
4199181641Skmacy	DPRINTFN(6, "ifaceno=%d clockid=%d speed=%u\n",
4200181641Skmacy	    iface_no, clockid, speed);
4201181641Skmacy
4202181641Skmacy	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
4203181641Skmacy	req.bRequest = UA20_CS_CUR;
4204181641Skmacy	USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0);
4205181641Skmacy	USETW2(req.wIndex, clockid, iface_no);
4206181641Skmacy	USETW(req.wLength, 4);
4207181641Skmacy	data[0] = speed;
4208181641Skmacy	data[1] = speed >> 8;
4209181641Skmacy	data[2] = speed >> 16;
4210181641Skmacy	data[3] = speed >> 24;
4211181641Skmacy
4212181641Skmacy	return (usbd_do_request(udev, NULL, &req, data));
4213181641Skmacy}
4214181641Skmacy
4215181641Skmacystatic int
4216181641Skmacyuaudio_mixer_signext(uint8_t type, int val)
4217181641Skmacy{
4218181641Skmacy	if (!MIX_UNSIGNED(type)) {
4219181641Skmacy		if (MIX_SIZE(type) == 2) {
4220181641Skmacy			val = (int16_t)val;
4221181641Skmacy		} else {
4222198341Smarcel			val = (int8_t)val;
4223198341Smarcel		}
4224198341Smarcel	}
4225198341Smarcel	return (val);
4226198341Smarcel}
4227181747Skmacy
4228181747Skmacystatic int
4229181747Skmacyuaudio_mixer_bsd2value(struct uaudio_mixer_node *mc, int32_t val)
4230181747Skmacy{
4231181747Skmacy	if (mc->type == MIX_ON_OFF) {
4232181747Skmacy		val = (val != 0);
4233181747Skmacy	} else if (mc->type == MIX_SELECTOR) {
4234181641Skmacy		if ((val < mc->minval) ||
4235181747Skmacy		    (val > mc->maxval)) {
4236181641Skmacy			val = mc->minval;
4237181747Skmacy		}
4238181747Skmacy	} else {
4239181747Skmacy
4240181747Skmacy		/* compute actual volume */
4241181747Skmacy		val = (val * mc->mul) / 255;
4242181747Skmacy
4243181747Skmacy		/* add lower offset */
4244181747Skmacy		val = val + mc->minval;
4245181747Skmacy
4246181747Skmacy		/* make sure we don't write a value out of range */
4247181747Skmacy		if (val > mc->maxval)
4248181747Skmacy			val = mc->maxval;
4249181641Skmacy		else if (val < mc->minval)
4250181641Skmacy			val = mc->minval;
4251190627Sdfr	}
4252190627Sdfr
4253190627Sdfr	DPRINTFN(6, "type=0x%03x val=%d min=%d max=%d val=%d\n",
4254190627Sdfr	    mc->type, val, mc->minval, mc->maxval, val);
4255190627Sdfr	return (val);
4256190627Sdfr}
4257190627Sdfr
4258190627Sdfrstatic void
4259190627Sdfruaudio_mixer_ctl_set(struct uaudio_softc *sc, struct uaudio_mixer_node *mc,
4260190627Sdfr    uint8_t chan, int32_t val)
4261190627Sdfr{
4262190627Sdfr	val = uaudio_mixer_bsd2value(mc, val);
4263190627Sdfr
4264190627Sdfr	mc->update[chan / 8] |= (1 << (chan % 8));
4265190627Sdfr	mc->wData[chan] = val;
4266190627Sdfr
4267190627Sdfr	/* start the transfer, if not already started */
4268190627Sdfr
4269190627Sdfr	usbd_transfer_start(sc->sc_mixer_xfer[0]);
4270190627Sdfr}
4271190627Sdfr
4272190627Sdfrstatic void
4273190627Sdfruaudio_mixer_init(struct uaudio_softc *sc)
4274190627Sdfr{
4275190627Sdfr	struct uaudio_mixer_node *mc;
4276190627Sdfr	int32_t i;
4277190627Sdfr
4278190627Sdfr	for (mc = sc->sc_mixer_root; mc;
4279190627Sdfr	    mc = mc->next) {
4280190627Sdfr
4281190627Sdfr		if (mc->ctl != SOUND_MIXER_NRDEVICES) {
4282190627Sdfr			/*
4283190627Sdfr			 * Set device mask bits. See
4284190627Sdfr			 * /usr/include/machine/soundcard.h
4285190627Sdfr			 */
4286190627Sdfr			sc->sc_mix_info |= (1 << mc->ctl);
4287190627Sdfr		}
4288190627Sdfr		if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
4289190627Sdfr		    (mc->type == MIX_SELECTOR)) {
4290190627Sdfr
4291190627Sdfr			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
4292190627Sdfr				if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) {
4293190627Sdfr					continue;
4294190627Sdfr				}
4295190627Sdfr				sc->sc_recsrc_info |= 1 << mc->slctrtype[i - 1];
4296190627Sdfr			}
4297190627Sdfr		}
4298190627Sdfr	}
4299190627Sdfr}
4300190627Sdfr
4301190627Sdfrint
4302190627Sdfruaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m)
4303190627Sdfr{
4304190627Sdfr	DPRINTF("\n");
4305190627Sdfr
4306190627Sdfr	if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index,
4307190627Sdfr	    sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc,
4308190627Sdfr	    mixer_get_lock(m))) {
4309190627Sdfr		DPRINTFN(0, "could not allocate USB "
4310190627Sdfr		    "transfer for audio mixer!\n");
4311190627Sdfr		return (ENOMEM);
4312190627Sdfr	}
4313190627Sdfr	if (!(sc->sc_mix_info & SOUND_MASK_VOLUME)) {
4314190627Sdfr		mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM);
4315190627Sdfr		mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
4316190627Sdfr	}
4317181641Skmacy	mix_setdevs(m, sc->sc_mix_info);
4318181641Skmacy	mix_setrecdevs(m, sc->sc_recsrc_info);
4319181641Skmacy	return (0);
4320181641Skmacy}
4321181641Skmacy
4322181641Skmacyint
4323181641Skmacyuaudio_mixer_uninit_sub(struct uaudio_softc *sc)
4324181641Skmacy{
4325181641Skmacy	DPRINTF("\n");
4326181641Skmacy
4327181641Skmacy	usbd_transfer_unsetup(sc->sc_mixer_xfer, 1);
4328181641Skmacy
4329181641Skmacy	return (0);
4330181641Skmacy}
4331181641Skmacy
4332181641Skmacyvoid
4333181641Skmacyuaudio_mixer_set(struct uaudio_softc *sc, unsigned type,
4334181641Skmacy    unsigned left, unsigned right)
4335181641Skmacy{
4336181641Skmacy	struct uaudio_mixer_node *mc;
4337181641Skmacy
4338181641Skmacy	for (mc = sc->sc_mixer_root; mc;
4339181641Skmacy	    mc = mc->next) {
4340181641Skmacy
4341181641Skmacy		if (mc->ctl == type) {
4342181641Skmacy			if (mc->nchan == 2) {
4343181641Skmacy				/* set Right */
4344181641Skmacy				uaudio_mixer_ctl_set(sc, mc, 1, (int)(right * 255) / 100);
4345181641Skmacy			}
4346181641Skmacy			/* set Left or Mono */
4347181641Skmacy			uaudio_mixer_ctl_set(sc, mc, 0, (int)(left * 255) / 100);
4348181641Skmacy		}
4349181641Skmacy	}
4350181641Skmacy}
4351181641Skmacy
4352181641Skmacyuint32_t
4353181641Skmacyuaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src)
4354181641Skmacy{
4355181641Skmacy	struct uaudio_mixer_node *mc;
4356181641Skmacy	uint32_t mask;
4357181641Skmacy	uint32_t temp;
4358181641Skmacy	int32_t i;
4359181641Skmacy
4360181641Skmacy	for (mc = sc->sc_mixer_root; mc;
4361181641Skmacy	    mc = mc->next) {
4362181641Skmacy
4363181641Skmacy		if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
4364181641Skmacy		    (mc->type == MIX_SELECTOR)) {
4365181641Skmacy
4366181641Skmacy			/* compute selector mask */
4367181641Skmacy
4368181641Skmacy			mask = 0;
4369181641Skmacy			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
4370181641Skmacy				mask |= (1 << mc->slctrtype[i - 1]);
4371181641Skmacy			}
4372181641Skmacy
4373181641Skmacy			temp = mask & src;
4374181641Skmacy			if (temp == 0) {
4375181641Skmacy				continue;
4376181641Skmacy			}
4377181641Skmacy			/* find the first set bit */
4378181641Skmacy			temp = (-temp) & temp;
4379181641Skmacy
4380181641Skmacy			/* update "src" */
4381181641Skmacy			src &= ~mask;
4382181641Skmacy			src |= temp;
4383181641Skmacy
4384181641Skmacy			for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
4385181641Skmacy				if (temp != (1 << mc->slctrtype[i - 1])) {
4386181641Skmacy					continue;
4387181641Skmacy				}
4388181641Skmacy				uaudio_mixer_ctl_set(sc, mc, 0, i);
4389181641Skmacy				break;
4390181641Skmacy			}
4391181641Skmacy		}
4392181641Skmacy	}
4393181641Skmacy	return (src);
4394181641Skmacy}
4395181641Skmacy
4396181641Skmacy/*========================================================================*
4397181641Skmacy * MIDI support routines
4398181641Skmacy *========================================================================*/
4399181641Skmacy
4400181641Skmacystatic void
4401181641Skmacyumidi_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
4402181641Skmacy{
4403181641Skmacy	struct umidi_chan *chan = usbd_xfer_softc(xfer);
4404181641Skmacy	struct umidi_sub_chan *sub;
4405181641Skmacy	struct usb_page_cache *pc;
4406181641Skmacy	uint8_t buf[4];
4407181641Skmacy	uint8_t cmd_len;
4408181641Skmacy	uint8_t cn;
4409181641Skmacy	uint16_t pos;
4410181641Skmacy	int actlen;
4411181641Skmacy
4412181641Skmacy	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
4413181641Skmacy
4414181641Skmacy	switch (USB_GET_STATE(xfer)) {
4415181641Skmacy	case USB_ST_TRANSFERRED:
4416181641Skmacy
4417181641Skmacy		DPRINTF("actlen=%d bytes\n", actlen);
4418181641Skmacy
4419181641Skmacy		pos = 0;
4420181641Skmacy		pc = usbd_xfer_get_frame(xfer, 0);
4421181641Skmacy
4422181641Skmacy		while (actlen >= 4) {
4423181641Skmacy
4424181641Skmacy			/* copy out the MIDI data */
4425			usbd_copy_out(pc, pos, buf, 4);
4426			/* command length */
4427			cmd_len = umidi_cmd_to_len[buf[0] & 0xF];
4428			/* cable number */
4429			cn = buf[0] >> 4;
4430			/*
4431			 * Lookup sub-channel. The index is range
4432			 * checked below.
4433			 */
4434			sub = &chan->sub[cn];
4435
4436			if ((cmd_len != 0) &&
4437			    (cn < chan->max_cable) &&
4438			    (sub->read_open != 0)) {
4439
4440				/* Send data to the application */
4441				usb_fifo_put_data_linear(
4442				    sub->fifo.fp[USB_FIFO_RX],
4443				    buf + 1, cmd_len, 1);
4444			}
4445			actlen -= 4;
4446			pos += 4;
4447		}
4448
4449	case USB_ST_SETUP:
4450		DPRINTF("start\n");
4451tr_setup:
4452		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
4453		usbd_transfer_submit(xfer);
4454		break;
4455
4456	default:
4457		DPRINTF("error=%s\n", usbd_errstr(error));
4458
4459		if (error != USB_ERR_CANCELLED) {
4460			/* try to clear stall first */
4461			usbd_xfer_set_stall(xfer);
4462			goto tr_setup;
4463		}
4464		break;
4465	}
4466}
4467
4468/*
4469 * The following statemachine, that converts MIDI commands to
4470 * USB MIDI packets, derives from Linux's usbmidi.c, which
4471 * was written by "Clemens Ladisch":
4472 *
4473 * Returns:
4474 *    0: No command
4475 * Else: Command is complete
4476 */
4477static uint8_t
4478umidi_convert_to_usb(struct umidi_sub_chan *sub, uint8_t cn, uint8_t b)
4479{
4480	uint8_t p0 = (cn << 4);
4481
4482	if (b >= 0xf8) {
4483		sub->temp_0[0] = p0 | 0x0f;
4484		sub->temp_0[1] = b;
4485		sub->temp_0[2] = 0;
4486		sub->temp_0[3] = 0;
4487		sub->temp_cmd = sub->temp_0;
4488		return (1);
4489
4490	} else if (b >= 0xf0) {
4491		switch (b) {
4492		case 0xf0:		/* system exclusive begin */
4493			sub->temp_1[1] = b;
4494			sub->state = UMIDI_ST_SYSEX_1;
4495			break;
4496		case 0xf1:		/* MIDI time code */
4497		case 0xf3:		/* song select */
4498			sub->temp_1[1] = b;
4499			sub->state = UMIDI_ST_1PARAM;
4500			break;
4501		case 0xf2:		/* song position pointer */
4502			sub->temp_1[1] = b;
4503			sub->state = UMIDI_ST_2PARAM_1;
4504			break;
4505		case 0xf4:		/* unknown */
4506		case 0xf5:		/* unknown */
4507			sub->state = UMIDI_ST_UNKNOWN;
4508			break;
4509		case 0xf6:		/* tune request */
4510			sub->temp_1[0] = p0 | 0x05;
4511			sub->temp_1[1] = 0xf6;
4512			sub->temp_1[2] = 0;
4513			sub->temp_1[3] = 0;
4514			sub->temp_cmd = sub->temp_1;
4515			sub->state = UMIDI_ST_UNKNOWN;
4516			return (1);
4517
4518		case 0xf7:		/* system exclusive end */
4519			switch (sub->state) {
4520			case UMIDI_ST_SYSEX_0:
4521				sub->temp_1[0] = p0 | 0x05;
4522				sub->temp_1[1] = 0xf7;
4523				sub->temp_1[2] = 0;
4524				sub->temp_1[3] = 0;
4525				sub->temp_cmd = sub->temp_1;
4526				sub->state = UMIDI_ST_UNKNOWN;
4527				return (1);
4528			case UMIDI_ST_SYSEX_1:
4529				sub->temp_1[0] = p0 | 0x06;
4530				sub->temp_1[2] = 0xf7;
4531				sub->temp_1[3] = 0;
4532				sub->temp_cmd = sub->temp_1;
4533				sub->state = UMIDI_ST_UNKNOWN;
4534				return (1);
4535			case UMIDI_ST_SYSEX_2:
4536				sub->temp_1[0] = p0 | 0x07;
4537				sub->temp_1[3] = 0xf7;
4538				sub->temp_cmd = sub->temp_1;
4539				sub->state = UMIDI_ST_UNKNOWN;
4540				return (1);
4541			}
4542			sub->state = UMIDI_ST_UNKNOWN;
4543			break;
4544		}
4545	} else if (b >= 0x80) {
4546		sub->temp_1[1] = b;
4547		if ((b >= 0xc0) && (b <= 0xdf)) {
4548			sub->state = UMIDI_ST_1PARAM;
4549		} else {
4550			sub->state = UMIDI_ST_2PARAM_1;
4551		}
4552	} else {			/* b < 0x80 */
4553		switch (sub->state) {
4554		case UMIDI_ST_1PARAM:
4555			if (sub->temp_1[1] < 0xf0) {
4556				p0 |= sub->temp_1[1] >> 4;
4557			} else {
4558				p0 |= 0x02;
4559				sub->state = UMIDI_ST_UNKNOWN;
4560			}
4561			sub->temp_1[0] = p0;
4562			sub->temp_1[2] = b;
4563			sub->temp_1[3] = 0;
4564			sub->temp_cmd = sub->temp_1;
4565			return (1);
4566		case UMIDI_ST_2PARAM_1:
4567			sub->temp_1[2] = b;
4568			sub->state = UMIDI_ST_2PARAM_2;
4569			break;
4570		case UMIDI_ST_2PARAM_2:
4571			if (sub->temp_1[1] < 0xf0) {
4572				p0 |= sub->temp_1[1] >> 4;
4573				sub->state = UMIDI_ST_2PARAM_1;
4574			} else {
4575				p0 |= 0x03;
4576				sub->state = UMIDI_ST_UNKNOWN;
4577			}
4578			sub->temp_1[0] = p0;
4579			sub->temp_1[3] = b;
4580			sub->temp_cmd = sub->temp_1;
4581			return (1);
4582		case UMIDI_ST_SYSEX_0:
4583			sub->temp_1[1] = b;
4584			sub->state = UMIDI_ST_SYSEX_1;
4585			break;
4586		case UMIDI_ST_SYSEX_1:
4587			sub->temp_1[2] = b;
4588			sub->state = UMIDI_ST_SYSEX_2;
4589			break;
4590		case UMIDI_ST_SYSEX_2:
4591			sub->temp_1[0] = p0 | 0x04;
4592			sub->temp_1[3] = b;
4593			sub->temp_cmd = sub->temp_1;
4594			sub->state = UMIDI_ST_SYSEX_0;
4595			return (1);
4596		default:
4597			break;
4598		}
4599	}
4600	return (0);
4601}
4602
4603static void
4604umidi_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
4605{
4606	struct umidi_chan *chan = usbd_xfer_softc(xfer);
4607	struct umidi_sub_chan *sub;
4608	struct usb_page_cache *pc;
4609	uint32_t actlen;
4610	uint16_t nframes;
4611	uint8_t buf;
4612	uint8_t start_cable;
4613	uint8_t tr_any;
4614	int len;
4615
4616	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
4617
4618	/*
4619	 * NOTE: Some MIDI devices only accept 4 bytes of data per
4620	 * short terminated USB transfer.
4621	 */
4622	switch (USB_GET_STATE(xfer)) {
4623	case USB_ST_TRANSFERRED:
4624		DPRINTF("actlen=%d bytes\n", len);
4625
4626	case USB_ST_SETUP:
4627tr_setup:
4628		DPRINTF("start\n");
4629
4630		nframes = 0;	/* reset */
4631		start_cable = chan->curr_cable;
4632		tr_any = 0;
4633		pc = usbd_xfer_get_frame(xfer, 0);
4634
4635		while (1) {
4636
4637			/* round robin de-queueing */
4638
4639			sub = &chan->sub[chan->curr_cable];
4640
4641			if (sub->write_open) {
4642				usb_fifo_get_data_linear(sub->fifo.fp[USB_FIFO_TX],
4643				    &buf, 1, &actlen, 0);
4644			} else {
4645				actlen = 0;
4646			}
4647
4648			if (actlen) {
4649
4650				tr_any = 1;
4651
4652				DPRINTF("byte=0x%02x from FIFO %u\n", buf,
4653				    (unsigned int)chan->curr_cable);
4654
4655				if (umidi_convert_to_usb(sub, chan->curr_cable, buf)) {
4656
4657					DPRINTF("sub=0x%02x 0x%02x 0x%02x 0x%02x\n",
4658					    sub->temp_cmd[0], sub->temp_cmd[1],
4659					    sub->temp_cmd[2], sub->temp_cmd[3]);
4660
4661					usbd_copy_in(pc, nframes * 4, sub->temp_cmd, 4);
4662
4663					nframes++;
4664
4665					if ((nframes >= UMIDI_TX_FRAMES) || (chan->single_command != 0))
4666						break;
4667				} else {
4668					continue;
4669				}
4670			}
4671
4672			chan->curr_cable++;
4673			if (chan->curr_cable >= chan->max_cable)
4674				chan->curr_cable = 0;
4675
4676			if (chan->curr_cable == start_cable) {
4677				if (tr_any == 0)
4678					break;
4679				tr_any = 0;
4680			}
4681		}
4682
4683		if (nframes != 0) {
4684			DPRINTF("Transferring %d frames\n", (int)nframes);
4685			usbd_xfer_set_frame_len(xfer, 0, 4 * nframes);
4686			usbd_transfer_submit(xfer);
4687		}
4688		break;
4689
4690	default:			/* Error */
4691
4692		DPRINTF("error=%s\n", usbd_errstr(error));
4693
4694		if (error != USB_ERR_CANCELLED) {
4695			/* try to clear stall first */
4696			usbd_xfer_set_stall(xfer);
4697			goto tr_setup;
4698		}
4699		break;
4700	}
4701}
4702
4703static struct umidi_sub_chan *
4704umidi_sub_by_fifo(struct usb_fifo *fifo)
4705{
4706	struct umidi_chan *chan = usb_fifo_softc(fifo);
4707	struct umidi_sub_chan *sub;
4708	uint32_t n;
4709
4710	for (n = 0; n < UMIDI_CABLES_MAX; n++) {
4711		sub = &chan->sub[n];
4712		if ((sub->fifo.fp[USB_FIFO_RX] == fifo) ||
4713		    (sub->fifo.fp[USB_FIFO_TX] == fifo)) {
4714			return (sub);
4715		}
4716	}
4717
4718	panic("%s:%d cannot find usb_fifo!\n",
4719	    __FILE__, __LINE__);
4720
4721	return (NULL);
4722}
4723
4724static void
4725umidi_start_read(struct usb_fifo *fifo)
4726{
4727	struct umidi_chan *chan = usb_fifo_softc(fifo);
4728
4729	usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]);
4730}
4731
4732static void
4733umidi_stop_read(struct usb_fifo *fifo)
4734{
4735	struct umidi_chan *chan = usb_fifo_softc(fifo);
4736	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
4737
4738	DPRINTF("\n");
4739
4740	sub->read_open = 0;
4741
4742	if (--(chan->read_open_refcount) == 0) {
4743		/*
4744		 * XXX don't stop the read transfer here, hence that causes
4745		 * problems with some MIDI adapters
4746		 */
4747		DPRINTF("(stopping read transfer)\n");
4748	}
4749}
4750
4751static void
4752umidi_start_write(struct usb_fifo *fifo)
4753{
4754	struct umidi_chan *chan = usb_fifo_softc(fifo);
4755
4756	usbd_transfer_start(chan->xfer[UMIDI_TX_TRANSFER]);
4757}
4758
4759static void
4760umidi_stop_write(struct usb_fifo *fifo)
4761{
4762	struct umidi_chan *chan = usb_fifo_softc(fifo);
4763	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
4764
4765	DPRINTF("\n");
4766
4767	sub->write_open = 0;
4768
4769	if (--(chan->write_open_refcount) == 0) {
4770		DPRINTF("(stopping write transfer)\n");
4771		usbd_transfer_stop(chan->xfer[UMIDI_TX_TRANSFER]);
4772	}
4773}
4774
4775static int
4776umidi_open(struct usb_fifo *fifo, int fflags)
4777{
4778	struct umidi_chan *chan = usb_fifo_softc(fifo);
4779	struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
4780
4781	if (fflags & FREAD) {
4782		if (usb_fifo_alloc_buffer(fifo, 4, (1024 / 4))) {
4783			return (ENOMEM);
4784		}
4785		mtx_lock(&chan->mtx);
4786		chan->read_open_refcount++;
4787		sub->read_open = 1;
4788		mtx_unlock(&chan->mtx);
4789	}
4790	if (fflags & FWRITE) {
4791		if (usb_fifo_alloc_buffer(fifo, 32, (1024 / 32))) {
4792			return (ENOMEM);
4793		}
4794		/* clear stall first */
4795		mtx_lock(&chan->mtx);
4796		usbd_xfer_set_stall(chan->xfer[UMIDI_TX_TRANSFER]);
4797		chan->write_open_refcount++;
4798		sub->write_open = 1;
4799
4800		/* reset */
4801		sub->state = UMIDI_ST_UNKNOWN;
4802		mtx_unlock(&chan->mtx);
4803	}
4804	return (0);			/* success */
4805}
4806
4807static void
4808umidi_close(struct usb_fifo *fifo, int fflags)
4809{
4810	if (fflags & FREAD) {
4811		usb_fifo_free_buffer(fifo);
4812	}
4813	if (fflags & FWRITE) {
4814		usb_fifo_free_buffer(fifo);
4815	}
4816}
4817
4818
4819static int
4820umidi_ioctl(struct usb_fifo *fifo, u_long cmd, void *data,
4821    int fflags)
4822{
4823	return (ENODEV);
4824}
4825
4826static void
4827umidi_init(device_t dev)
4828{
4829	struct uaudio_softc *sc = device_get_softc(dev);
4830	struct umidi_chan *chan = &sc->sc_midi_chan;
4831
4832	mtx_init(&chan->mtx, "umidi lock", NULL, MTX_DEF | MTX_RECURSE);
4833}
4834
4835static struct usb_fifo_methods umidi_fifo_methods = {
4836	.f_start_read = &umidi_start_read,
4837	.f_start_write = &umidi_start_write,
4838	.f_stop_read = &umidi_stop_read,
4839	.f_stop_write = &umidi_stop_write,
4840	.f_open = &umidi_open,
4841	.f_close = &umidi_close,
4842	.f_ioctl = &umidi_ioctl,
4843	.basename[0] = "umidi",
4844};
4845
4846static int
4847umidi_probe(device_t dev)
4848{
4849	struct uaudio_softc *sc = device_get_softc(dev);
4850	struct usb_attach_arg *uaa = device_get_ivars(dev);
4851	struct umidi_chan *chan = &sc->sc_midi_chan;
4852	struct umidi_sub_chan *sub;
4853	int unit = device_get_unit(dev);
4854	int error;
4855	uint32_t n;
4856
4857	if (usb_test_quirk(uaa, UQ_SINGLE_CMD_MIDI))
4858		chan->single_command = 1;
4859
4860	if (usbd_set_alt_interface_index(sc->sc_udev, chan->iface_index,
4861	    chan->iface_alt_index)) {
4862		DPRINTF("setting of alternate index failed!\n");
4863		goto detach;
4864	}
4865	usbd_set_parent_iface(sc->sc_udev, chan->iface_index,
4866	    sc->sc_mixer_iface_index);
4867
4868	error = usbd_transfer_setup(uaa->device, &chan->iface_index,
4869	    chan->xfer, umidi_config, UMIDI_N_TRANSFER,
4870	    chan, &chan->mtx);
4871	if (error) {
4872		DPRINTF("error=%s\n", usbd_errstr(error));
4873		goto detach;
4874	}
4875	if ((chan->max_cable > UMIDI_CABLES_MAX) ||
4876	    (chan->max_cable == 0)) {
4877		chan->max_cable = UMIDI_CABLES_MAX;
4878	}
4879
4880	for (n = 0; n < chan->max_cable; n++) {
4881
4882		sub = &chan->sub[n];
4883
4884		error = usb_fifo_attach(sc->sc_udev, chan, &chan->mtx,
4885		    &umidi_fifo_methods, &sub->fifo, unit, n,
4886		    chan->iface_index,
4887		    UID_ROOT, GID_OPERATOR, 0644);
4888		if (error) {
4889			goto detach;
4890		}
4891	}
4892
4893	mtx_lock(&chan->mtx);
4894
4895	/* clear stall first */
4896	usbd_xfer_set_stall(chan->xfer[UMIDI_RX_TRANSFER]);
4897
4898	/*
4899	 * NOTE: At least one device will not work properly unless the
4900	 * BULK IN pipe is open all the time. This might have to do
4901	 * about that the internal queues of the device overflow if we
4902	 * don't read them regularly.
4903	 */
4904	usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]);
4905
4906	mtx_unlock(&chan->mtx);
4907
4908	return (0);			/* success */
4909
4910detach:
4911	return (ENXIO);			/* failure */
4912}
4913
4914static int
4915umidi_detach(device_t dev)
4916{
4917	struct uaudio_softc *sc = device_get_softc(dev);
4918	struct umidi_chan *chan = &sc->sc_midi_chan;
4919	uint32_t n;
4920
4921	for (n = 0; n < UMIDI_CABLES_MAX; n++) {
4922		usb_fifo_detach(&chan->sub[n].fifo);
4923	}
4924
4925	mtx_lock(&chan->mtx);
4926
4927	usbd_transfer_stop(chan->xfer[UMIDI_RX_TRANSFER]);
4928
4929	mtx_unlock(&chan->mtx);
4930
4931	usbd_transfer_unsetup(chan->xfer, UMIDI_N_TRANSFER);
4932
4933	mtx_destroy(&chan->mtx);
4934
4935	return (0);
4936}
4937
4938DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, 0);
4939MODULE_DEPEND(uaudio, usb, 1, 1, 1);
4940MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
4941MODULE_VERSION(uaudio, 1);
4942