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