13a3f90c6SAndrew Thompson /* $NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $ */ 23a3f90c6SAndrew Thompson /* $FreeBSD$ */ 33a3f90c6SAndrew Thompson 43a3f90c6SAndrew Thompson /*- 53a3f90c6SAndrew Thompson * Copyright (c) 1999 The NetBSD Foundation, Inc. 63a3f90c6SAndrew Thompson * All rights reserved. 73a3f90c6SAndrew Thompson * 83a3f90c6SAndrew Thompson * This code is derived from software contributed to The NetBSD Foundation 93a3f90c6SAndrew Thompson * by Lennart Augustsson (lennart@augustsson.net) at 103a3f90c6SAndrew Thompson * Carlstedt Research & Technology. 113a3f90c6SAndrew Thompson * 123a3f90c6SAndrew Thompson * Redistribution and use in source and binary forms, with or without 133a3f90c6SAndrew Thompson * modification, are permitted provided that the following conditions 143a3f90c6SAndrew Thompson * are met: 153a3f90c6SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 163a3f90c6SAndrew Thompson * notice, this list of conditions and the following disclaimer. 173a3f90c6SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 183a3f90c6SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 193a3f90c6SAndrew Thompson * documentation and/or other materials provided with the distribution. 203a3f90c6SAndrew Thompson * 213a3f90c6SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 223a3f90c6SAndrew Thompson * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 233a3f90c6SAndrew Thompson * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 243a3f90c6SAndrew Thompson * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 253a3f90c6SAndrew Thompson * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 263a3f90c6SAndrew Thompson * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 273a3f90c6SAndrew Thompson * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 283a3f90c6SAndrew Thompson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 293a3f90c6SAndrew Thompson * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 303a3f90c6SAndrew Thompson * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 313a3f90c6SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE. 323a3f90c6SAndrew Thompson */ 333a3f90c6SAndrew Thompson 344b7ec270SMarius Strobl #include <sys/cdefs.h> 354b7ec270SMarius Strobl __FBSDID("$FreeBSD$"); 364b7ec270SMarius Strobl 373a3f90c6SAndrew Thompson /* 383a3f90c6SAndrew Thompson * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf 393a3f90c6SAndrew Thompson * http://www.usb.org/developers/devclass_docs/frmts10.pdf 403a3f90c6SAndrew Thompson * http://www.usb.org/developers/devclass_docs/termt10.pdf 413a3f90c6SAndrew Thompson */ 423a3f90c6SAndrew Thompson 433a3f90c6SAndrew Thompson /* 443a3f90c6SAndrew Thompson * Also merged: 453a3f90c6SAndrew Thompson * $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $ 463a3f90c6SAndrew Thompson * $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $ 473a3f90c6SAndrew Thompson * $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $ 483a3f90c6SAndrew Thompson * $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $ 493a3f90c6SAndrew Thompson */ 503a3f90c6SAndrew Thompson 51ed6d949aSAndrew Thompson #include <sys/stdint.h> 52ed6d949aSAndrew Thompson #include <sys/stddef.h> 53ed6d949aSAndrew Thompson #include <sys/param.h> 54ed6d949aSAndrew Thompson #include <sys/queue.h> 55ed6d949aSAndrew Thompson #include <sys/types.h> 56ed6d949aSAndrew Thompson #include <sys/systm.h> 57ed6d949aSAndrew Thompson #include <sys/kernel.h> 58ed6d949aSAndrew Thompson #include <sys/bus.h> 59ed6d949aSAndrew Thompson #include <sys/module.h> 60ed6d949aSAndrew Thompson #include <sys/lock.h> 61ed6d949aSAndrew Thompson #include <sys/mutex.h> 62ed6d949aSAndrew Thompson #include <sys/condvar.h> 63ed6d949aSAndrew Thompson #include <sys/sysctl.h> 64ed6d949aSAndrew Thompson #include <sys/sx.h> 65ed6d949aSAndrew Thompson #include <sys/unistd.h> 66ed6d949aSAndrew Thompson #include <sys/callout.h> 67ed6d949aSAndrew Thompson #include <sys/malloc.h> 68ed6d949aSAndrew Thompson #include <sys/priv.h> 69ed6d949aSAndrew Thompson 703a3f90c6SAndrew Thompson #include "usbdevs.h" 713a3f90c6SAndrew Thompson #include <dev/usb/usb.h> 72ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 73ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h> 743a3f90c6SAndrew Thompson 753a3f90c6SAndrew Thompson #define USB_DEBUG_VAR uaudio_debug 763a3f90c6SAndrew Thompson #include <dev/usb/usb_debug.h> 773a3f90c6SAndrew Thompson 783a3f90c6SAndrew Thompson #include <dev/usb/quirk/usb_quirk.h> 793a3f90c6SAndrew Thompson 803a3f90c6SAndrew Thompson #include <sys/reboot.h> /* for bootverbose */ 813a3f90c6SAndrew Thompson 8290da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 8390da2b28SAriff Abdullah #include "opt_snd.h" 8490da2b28SAriff Abdullah #endif 8590da2b28SAriff Abdullah 863a3f90c6SAndrew Thompson #include <dev/sound/pcm/sound.h> 873a3f90c6SAndrew Thompson #include <dev/sound/usb/uaudioreg.h> 883a3f90c6SAndrew Thompson #include <dev/sound/usb/uaudio.h> 893a3f90c6SAndrew Thompson #include <dev/sound/chip.h> 903a3f90c6SAndrew Thompson #include "feeder_if.h" 913a3f90c6SAndrew Thompson 92afbfddd9SAndrew Thompson static int uaudio_default_rate = 0; /* use rate list */ 933a3f90c6SAndrew Thompson static int uaudio_default_bits = 32; 94afbfddd9SAndrew Thompson static int uaudio_default_channels = 0; /* use default */ 953a3f90c6SAndrew Thompson 96b850ecc1SAndrew Thompson #ifdef USB_DEBUG 973a3f90c6SAndrew Thompson static int uaudio_debug = 0; 983a3f90c6SAndrew Thompson 996472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio"); 100afbfddd9SAndrew Thompson 101fadc970bSAndrew Thompson SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW, 1023a3f90c6SAndrew Thompson &uaudio_debug, 0, "uaudio debug level"); 103afbfddd9SAndrew Thompson 104afbfddd9SAndrew Thompson TUNABLE_INT("hw.usb.uaudio.default_rate", &uaudio_default_rate); 105fadc970bSAndrew Thompson SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_rate, CTLFLAG_RW, 1063a3f90c6SAndrew Thompson &uaudio_default_rate, 0, "uaudio default sample rate"); 107afbfddd9SAndrew Thompson 108afbfddd9SAndrew Thompson TUNABLE_INT("hw.usb.uaudio.default_bits", &uaudio_default_bits); 109fadc970bSAndrew Thompson SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_bits, CTLFLAG_RW, 1103a3f90c6SAndrew Thompson &uaudio_default_bits, 0, "uaudio default sample bits"); 111afbfddd9SAndrew Thompson 112afbfddd9SAndrew Thompson TUNABLE_INT("hw.usb.uaudio.default_channels", &uaudio_default_channels); 113fadc970bSAndrew Thompson SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW, 1143a3f90c6SAndrew Thompson &uaudio_default_channels, 0, "uaudio default sample channels"); 1153a3f90c6SAndrew Thompson #endif 1163a3f90c6SAndrew Thompson 117b029f6bbSAndrew Thompson #define UAUDIO_NFRAMES 64 /* must be factor of 8 due HS-USB */ 1183a3f90c6SAndrew Thompson #define UAUDIO_NCHANBUFS 2 /* number of outstanding request */ 119e2524b2eSHans Petter Selasky #define UAUDIO_RECURSE_LIMIT 255 /* rounds */ 1203a3f90c6SAndrew Thompson 1213a3f90c6SAndrew Thompson #define MAKE_WORD(h,l) (((h) << 8) | (l)) 1223a3f90c6SAndrew Thompson #define BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1) 123c737a632SAndrew Thompson #define UAUDIO_MAX_CHAN(x) (x) 1243a3f90c6SAndrew Thompson 125e2524b2eSHans Petter Selasky union uaudio_asid { 126e2524b2eSHans Petter Selasky const struct usb_audio_streaming_interface_descriptor *v1; 127e2524b2eSHans Petter Selasky const struct usb_audio20_streaming_interface_descriptor *v2; 128e2524b2eSHans Petter Selasky }; 129e2524b2eSHans Petter Selasky 130e2524b2eSHans Petter Selasky union uaudio_asf1d { 131e2524b2eSHans Petter Selasky const struct usb_audio_streaming_type1_descriptor *v1; 132e2524b2eSHans Petter Selasky const struct usb_audio20_streaming_type1_descriptor *v2; 133e2524b2eSHans Petter Selasky }; 134e2524b2eSHans Petter Selasky 135e2524b2eSHans Petter Selasky union uaudio_sed { 136e2524b2eSHans Petter Selasky const struct usb_audio_streaming_endpoint_descriptor *v1; 137e2524b2eSHans Petter Selasky const struct usb_audio20_streaming_endpoint_descriptor *v2; 138e2524b2eSHans Petter Selasky }; 139e2524b2eSHans Petter Selasky 1403a3f90c6SAndrew Thompson struct uaudio_mixer_node { 1413a3f90c6SAndrew Thompson int32_t minval; 1423a3f90c6SAndrew Thompson int32_t maxval; 1433a3f90c6SAndrew Thompson #define MIX_MAX_CHAN 8 1443a3f90c6SAndrew Thompson int32_t wValue[MIX_MAX_CHAN]; /* using nchan */ 1453a3f90c6SAndrew Thompson uint32_t mul; 1463a3f90c6SAndrew Thompson uint32_t ctl; 1473a3f90c6SAndrew Thompson 1483a3f90c6SAndrew Thompson uint16_t wData[MIX_MAX_CHAN]; /* using nchan */ 1493a3f90c6SAndrew Thompson uint16_t wIndex; 1503a3f90c6SAndrew Thompson 1513a3f90c6SAndrew Thompson uint8_t update[(MIX_MAX_CHAN + 7) / 8]; 1523a3f90c6SAndrew Thompson uint8_t nchan; 1533a3f90c6SAndrew Thompson uint8_t type; 1543a3f90c6SAndrew Thompson #define MIX_ON_OFF 1 1553a3f90c6SAndrew Thompson #define MIX_SIGNED_16 2 1563a3f90c6SAndrew Thompson #define MIX_UNSIGNED_16 3 1573a3f90c6SAndrew Thompson #define MIX_SIGNED_8 4 1583a3f90c6SAndrew Thompson #define MIX_SELECTOR 5 1593a3f90c6SAndrew Thompson #define MIX_UNKNOWN 6 1603a3f90c6SAndrew Thompson #define MIX_SIZE(n) ((((n) == MIX_SIGNED_16) || \ 1613a3f90c6SAndrew Thompson ((n) == MIX_UNSIGNED_16)) ? 2 : 1) 1623a3f90c6SAndrew Thompson #define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16) 1633a3f90c6SAndrew Thompson 1643a3f90c6SAndrew Thompson #define MAX_SELECTOR_INPUT_PIN 256 1653a3f90c6SAndrew Thompson uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN]; 1663a3f90c6SAndrew Thompson uint8_t class; 1673a3f90c6SAndrew Thompson 1683a3f90c6SAndrew Thompson struct uaudio_mixer_node *next; 1693a3f90c6SAndrew Thompson }; 1703a3f90c6SAndrew Thompson 1713a3f90c6SAndrew Thompson struct uaudio_chan { 1723a3f90c6SAndrew Thompson struct pcmchan_caps pcm_cap; /* capabilities */ 1733a3f90c6SAndrew Thompson 1743a3f90c6SAndrew Thompson struct snd_dbuf *pcm_buf; 1754c21be9bSRebecca Cran const struct usb_config *usb_cfg; 1763a3f90c6SAndrew Thompson struct mtx *pcm_mtx; /* lock protecting this structure */ 1773a3f90c6SAndrew Thompson struct uaudio_softc *priv_sc; 1783a3f90c6SAndrew Thompson struct pcm_channel *pcm_ch; 179760bc48eSAndrew Thompson struct usb_xfer *xfer[UAUDIO_NCHANBUFS]; 180e2524b2eSHans Petter Selasky union uaudio_asf1d p_asf1d; 181e2524b2eSHans Petter Selasky union uaudio_sed p_sed; 1824c21be9bSRebecca Cran const usb_endpoint_descriptor_audio_t *p_ed1; 1833a3f90c6SAndrew Thompson const struct uaudio_format *p_fmt; 1843a3f90c6SAndrew Thompson 1853a3f90c6SAndrew Thompson uint8_t *buf; /* pointer to buffer */ 1863a3f90c6SAndrew Thompson uint8_t *start; /* upper layer buffer start */ 1873a3f90c6SAndrew Thompson uint8_t *end; /* upper layer buffer end */ 1883a3f90c6SAndrew Thompson uint8_t *cur; /* current position in upper layer 1893a3f90c6SAndrew Thompson * buffer */ 1903a3f90c6SAndrew Thompson 1913a3f90c6SAndrew Thompson uint32_t intr_size; /* in bytes */ 192b029f6bbSAndrew Thompson uint32_t intr_frames; /* in units */ 1933a3f90c6SAndrew Thompson uint32_t sample_rate; 194afbfddd9SAndrew Thompson uint32_t frames_per_second; 195afbfddd9SAndrew Thompson uint32_t sample_rem; 196afbfddd9SAndrew Thompson uint32_t sample_curr; 197afbfddd9SAndrew Thompson 1983a3f90c6SAndrew Thompson uint32_t format; 1993a3f90c6SAndrew Thompson uint32_t pcm_format[2]; 2003a3f90c6SAndrew Thompson 201afbfddd9SAndrew Thompson uint16_t bytes_per_frame[2]; 202afbfddd9SAndrew Thompson 203afbfddd9SAndrew Thompson uint16_t sample_size; 2043a3f90c6SAndrew Thompson 2053a3f90c6SAndrew Thompson uint8_t valid; 2063a3f90c6SAndrew Thompson uint8_t iface_index; 2073a3f90c6SAndrew Thompson uint8_t iface_alt_index; 208e2524b2eSHans Petter Selasky uint8_t channels; 2093a3f90c6SAndrew Thompson }; 2103a3f90c6SAndrew Thompson 2113a3f90c6SAndrew Thompson #define UMIDI_CABLES_MAX 16 /* units */ 212e0b17a62SHans Petter Selasky #define UMIDI_TX_FRAMES 256 /* units */ 213910f1dcfSHans Petter Selasky #define UMIDI_TX_BUFFER (UMIDI_TX_FRAMES * 4) /* bytes */ 2143a3f90c6SAndrew Thompson 2156f068a43SHans Petter Selasky enum { 2166f068a43SHans Petter Selasky UMIDI_TX_TRANSFER, 2176f068a43SHans Petter Selasky UMIDI_RX_TRANSFER, 2186f068a43SHans Petter Selasky UMIDI_N_TRANSFER, 2196f068a43SHans Petter Selasky }; 2206f068a43SHans Petter Selasky 2213a3f90c6SAndrew Thompson struct umidi_sub_chan { 222760bc48eSAndrew Thompson struct usb_fifo_sc fifo; 2233a3f90c6SAndrew Thompson uint8_t *temp_cmd; 2243a3f90c6SAndrew Thompson uint8_t temp_0[4]; 2253a3f90c6SAndrew Thompson uint8_t temp_1[4]; 2263a3f90c6SAndrew Thompson uint8_t state; 2273a3f90c6SAndrew Thompson #define UMIDI_ST_UNKNOWN 0 /* scan for command */ 2283a3f90c6SAndrew Thompson #define UMIDI_ST_1PARAM 1 2293a3f90c6SAndrew Thompson #define UMIDI_ST_2PARAM_1 2 2303a3f90c6SAndrew Thompson #define UMIDI_ST_2PARAM_2 3 2313a3f90c6SAndrew Thompson #define UMIDI_ST_SYSEX_0 4 2323a3f90c6SAndrew Thompson #define UMIDI_ST_SYSEX_1 5 2333a3f90c6SAndrew Thompson #define UMIDI_ST_SYSEX_2 6 2343a3f90c6SAndrew Thompson 2353a3f90c6SAndrew Thompson uint8_t read_open:1; 2363a3f90c6SAndrew Thompson uint8_t write_open:1; 2373a3f90c6SAndrew Thompson uint8_t unused:6; 2383a3f90c6SAndrew Thompson }; 2393a3f90c6SAndrew Thompson 2403a3f90c6SAndrew Thompson struct umidi_chan { 2413a3f90c6SAndrew Thompson 2423a3f90c6SAndrew Thompson struct umidi_sub_chan sub[UMIDI_CABLES_MAX]; 2433a3f90c6SAndrew Thompson struct mtx mtx; 2443a3f90c6SAndrew Thompson 245760bc48eSAndrew Thompson struct usb_xfer *xfer[UMIDI_N_TRANSFER]; 2463a3f90c6SAndrew Thompson 2473a3f90c6SAndrew Thompson uint8_t iface_index; 2483a3f90c6SAndrew Thompson uint8_t iface_alt_index; 2493a3f90c6SAndrew Thompson 2503a3f90c6SAndrew Thompson uint8_t read_open_refcount; 2513a3f90c6SAndrew Thompson uint8_t write_open_refcount; 2523a3f90c6SAndrew Thompson 2533a3f90c6SAndrew Thompson uint8_t curr_cable; 2543a3f90c6SAndrew Thompson uint8_t max_cable; 2553a3f90c6SAndrew Thompson uint8_t valid; 2564944c3a8SHans Petter Selasky uint8_t single_command; 2573a3f90c6SAndrew Thompson }; 2583a3f90c6SAndrew Thompson 259e2524b2eSHans Petter Selasky struct uaudio_search_result { 260e2524b2eSHans Petter Selasky uint8_t bit_input[(256 + 7) / 8]; 261e2524b2eSHans Petter Selasky uint8_t bit_output[(256 + 7) / 8]; 262e2524b2eSHans Petter Selasky uint8_t recurse_level; 263e2524b2eSHans Petter Selasky uint8_t id_max; 264e2524b2eSHans Petter Selasky uint8_t is_input; 265e2524b2eSHans Petter Selasky }; 266e2524b2eSHans Petter Selasky 2673a3f90c6SAndrew Thompson struct uaudio_softc { 2683a3f90c6SAndrew Thompson struct sbuf sc_sndstat; 2693a3f90c6SAndrew Thompson struct sndcard_func sc_sndcard_func; 2703a3f90c6SAndrew Thompson struct uaudio_chan sc_rec_chan; 2713a3f90c6SAndrew Thompson struct uaudio_chan sc_play_chan; 2723a3f90c6SAndrew Thompson struct umidi_chan sc_midi_chan; 273e2524b2eSHans Petter Selasky struct uaudio_search_result sc_mixer_clocks; 2743a3f90c6SAndrew Thompson 275760bc48eSAndrew Thompson struct usb_device *sc_udev; 276760bc48eSAndrew Thompson struct usb_xfer *sc_mixer_xfer[1]; 2773a3f90c6SAndrew Thompson struct uaudio_mixer_node *sc_mixer_root; 2783a3f90c6SAndrew Thompson struct uaudio_mixer_node *sc_mixer_curr; 2793a3f90c6SAndrew Thompson 2803a3f90c6SAndrew Thompson uint32_t sc_mix_info; 2813a3f90c6SAndrew Thompson uint32_t sc_recsrc_info; 2823a3f90c6SAndrew Thompson 2833a3f90c6SAndrew Thompson uint16_t sc_audio_rev; 2843a3f90c6SAndrew Thompson uint16_t sc_mixer_count; 2853a3f90c6SAndrew Thompson 2863a3f90c6SAndrew Thompson uint8_t sc_sndstat_valid; 2873a3f90c6SAndrew Thompson uint8_t sc_mixer_iface_index; 2883a3f90c6SAndrew Thompson uint8_t sc_mixer_iface_no; 2893a3f90c6SAndrew Thompson uint8_t sc_mixer_chan; 2903a3f90c6SAndrew Thompson uint8_t sc_pcm_registered:1; 2913a3f90c6SAndrew Thompson uint8_t sc_mixer_init:1; 2923a3f90c6SAndrew Thompson uint8_t sc_uq_audio_swap_lr:1; 2933a3f90c6SAndrew Thompson uint8_t sc_uq_au_inp_async:1; 2943a3f90c6SAndrew Thompson uint8_t sc_uq_au_no_xu:1; 2953a3f90c6SAndrew Thompson uint8_t sc_uq_bad_adc:1; 29625b74dabSHans Petter Selasky uint8_t sc_uq_au_vendor_class:1; 2973a3f90c6SAndrew Thompson }; 2983a3f90c6SAndrew Thompson 2993a3f90c6SAndrew Thompson struct uaudio_terminal_node { 3003a3f90c6SAndrew Thompson union { 301760bc48eSAndrew Thompson const struct usb_descriptor *desc; 302e2524b2eSHans Petter Selasky const struct usb_audio_input_terminal *it_v1; 303e2524b2eSHans Petter Selasky const struct usb_audio_output_terminal *ot_v1; 304e2524b2eSHans Petter Selasky const struct usb_audio_mixer_unit_0 *mu_v1; 305e2524b2eSHans Petter Selasky const struct usb_audio_selector_unit *su_v1; 306e2524b2eSHans Petter Selasky const struct usb_audio_feature_unit *fu_v1; 307e2524b2eSHans Petter Selasky const struct usb_audio_processing_unit_0 *pu_v1; 308e2524b2eSHans Petter Selasky const struct usb_audio_extension_unit_0 *eu_v1; 309e2524b2eSHans Petter Selasky const struct usb_audio20_clock_source_unit *csrc_v2; 310e2524b2eSHans Petter Selasky const struct usb_audio20_clock_selector_unit_0 *csel_v2; 311e2524b2eSHans Petter Selasky const struct usb_audio20_clock_multiplier_unit *cmul_v2; 312e2524b2eSHans Petter Selasky const struct usb_audio20_input_terminal *it_v2; 313e2524b2eSHans Petter Selasky const struct usb_audio20_output_terminal *ot_v2; 314e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_0 *mu_v2; 315e2524b2eSHans Petter Selasky const struct usb_audio20_selector_unit *su_v2; 316e2524b2eSHans Petter Selasky const struct usb_audio20_feature_unit *fu_v2; 317e2524b2eSHans Petter Selasky const struct usb_audio20_sample_rate_unit *ru_v2; 318e2524b2eSHans Petter Selasky const struct usb_audio20_processing_unit_0 *pu_v2; 319e2524b2eSHans Petter Selasky const struct usb_audio20_extension_unit_0 *eu_v2; 320e2524b2eSHans Petter Selasky const struct usb_audio20_effect_unit *ef_v2; 3213a3f90c6SAndrew Thompson } u; 3223a3f90c6SAndrew Thompson struct uaudio_search_result usr; 3233a3f90c6SAndrew Thompson struct uaudio_terminal_node *root; 3243a3f90c6SAndrew Thompson }; 3253a3f90c6SAndrew Thompson 3263a3f90c6SAndrew Thompson struct uaudio_format { 3273a3f90c6SAndrew Thompson uint16_t wFormat; 3283a3f90c6SAndrew Thompson uint8_t bPrecision; 3293a3f90c6SAndrew Thompson uint32_t freebsd_fmt; 3303a3f90c6SAndrew Thompson const char *description; 3313a3f90c6SAndrew Thompson }; 3323a3f90c6SAndrew Thompson 333e2524b2eSHans Petter Selasky static const struct uaudio_format uaudio10_formats[] = { 3343a3f90c6SAndrew Thompson 3353a3f90c6SAndrew Thompson {UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"}, 3363a3f90c6SAndrew Thompson {UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"}, 3373a3f90c6SAndrew Thompson {UA_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"}, 3383a3f90c6SAndrew Thompson {UA_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"}, 3393a3f90c6SAndrew Thompson 3403a3f90c6SAndrew Thompson {UA_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"}, 3413a3f90c6SAndrew Thompson {UA_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"}, 3423a3f90c6SAndrew Thompson {UA_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"}, 3433a3f90c6SAndrew Thompson {UA_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"}, 3443a3f90c6SAndrew Thompson 3453a3f90c6SAndrew Thompson {UA_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"}, 3463a3f90c6SAndrew Thompson {UA_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"}, 3473a3f90c6SAndrew Thompson 3483a3f90c6SAndrew Thompson {0, 0, 0, NULL} 3493a3f90c6SAndrew Thompson }; 3503a3f90c6SAndrew Thompson 351e2524b2eSHans Petter Selasky static const struct uaudio_format uaudio20_formats[] = { 352e2524b2eSHans Petter Selasky 353e2524b2eSHans Petter Selasky {UA20_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"}, 354e2524b2eSHans Petter Selasky {UA20_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"}, 355e2524b2eSHans Petter Selasky {UA20_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"}, 356e2524b2eSHans Petter Selasky {UA20_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"}, 357e2524b2eSHans Petter Selasky 358e2524b2eSHans Petter Selasky {UA20_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"}, 359e2524b2eSHans Petter Selasky {UA20_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"}, 360e2524b2eSHans Petter Selasky {UA20_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"}, 361e2524b2eSHans Petter Selasky {UA20_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"}, 362e2524b2eSHans Petter Selasky 363e2524b2eSHans Petter Selasky {UA20_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"}, 364e2524b2eSHans Petter Selasky {UA20_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"}, 365e2524b2eSHans Petter Selasky 366e2524b2eSHans Petter Selasky {0, 0, 0, NULL} 367e2524b2eSHans Petter Selasky }; 368e2524b2eSHans Petter Selasky 3693a3f90c6SAndrew Thompson #define UAC_OUTPUT 0 3703a3f90c6SAndrew Thompson #define UAC_INPUT 1 3713a3f90c6SAndrew Thompson #define UAC_EQUAL 2 3723a3f90c6SAndrew Thompson #define UAC_RECORD 3 3733a3f90c6SAndrew Thompson #define UAC_NCLASSES 4 3743a3f90c6SAndrew Thompson 375b850ecc1SAndrew Thompson #ifdef USB_DEBUG 3763a3f90c6SAndrew Thompson static const char *uac_names[] = { 3773a3f90c6SAndrew Thompson "outputs", "inputs", "equalization", "record" 3783a3f90c6SAndrew Thompson }; 3793a3f90c6SAndrew Thompson 3803a3f90c6SAndrew Thompson #endif 3813a3f90c6SAndrew Thompson 3823a3f90c6SAndrew Thompson /* prototypes */ 3833a3f90c6SAndrew Thompson 3843a3f90c6SAndrew Thompson static device_probe_t uaudio_probe; 3853a3f90c6SAndrew Thompson static device_attach_t uaudio_attach; 3863a3f90c6SAndrew Thompson static device_detach_t uaudio_detach; 3873a3f90c6SAndrew Thompson 388e0a69b51SAndrew Thompson static usb_callback_t uaudio_chan_play_callback; 389e0a69b51SAndrew Thompson static usb_callback_t uaudio_chan_record_callback; 390e0a69b51SAndrew Thompson static usb_callback_t uaudio_mixer_write_cfg_callback; 391e0a69b51SAndrew Thompson static usb_callback_t umidi_bulk_read_callback; 392e0a69b51SAndrew Thompson static usb_callback_t umidi_bulk_write_callback; 3933a3f90c6SAndrew Thompson 394e2524b2eSHans Petter Selasky /* ==== USB audio v1.0 ==== */ 395e2524b2eSHans Petter Selasky 3963a3f90c6SAndrew Thompson static void uaudio_mixer_add_mixer(struct uaudio_softc *, 3973a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 3983a3f90c6SAndrew Thompson static void uaudio_mixer_add_selector(struct uaudio_softc *, 3993a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4003a3f90c6SAndrew Thompson static uint32_t uaudio_mixer_feature_get_bmaControls( 4014c21be9bSRebecca Cran const struct usb_audio_feature_unit *, uint8_t); 4023a3f90c6SAndrew Thompson static void uaudio_mixer_add_feature(struct uaudio_softc *, 4033a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4043a3f90c6SAndrew Thompson static void uaudio_mixer_add_processing_updown(struct uaudio_softc *, 4053a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4063a3f90c6SAndrew Thompson static void uaudio_mixer_add_processing(struct uaudio_softc *, 4073a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4083a3f90c6SAndrew Thompson static void uaudio_mixer_add_extension(struct uaudio_softc *, 4093a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4104c21be9bSRebecca Cran static struct usb_audio_cluster uaudio_mixer_get_cluster(uint8_t, 4113a3f90c6SAndrew Thompson const struct uaudio_terminal_node *); 4123a3f90c6SAndrew Thompson static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node *, 4133a3f90c6SAndrew Thompson struct uaudio_mixer_node *); 4143a3f90c6SAndrew Thompson static uint16_t uaudio_mixer_feature_name(const struct uaudio_terminal_node *, 4153a3f90c6SAndrew Thompson struct uaudio_mixer_node *); 416e2524b2eSHans Petter Selasky static void uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *, 417e2524b2eSHans Petter Selasky const uint8_t *, uint8_t, struct uaudio_search_result *); 418e2524b2eSHans Petter Selasky static const void *uaudio_mixer_verify_desc(const void *, uint32_t); 419e2524b2eSHans Petter Selasky static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t); 420e2524b2eSHans Petter Selasky static int uaudio_mixer_get(struct usb_device *, uint16_t, uint8_t, 421e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 422e2524b2eSHans Petter Selasky 423e2524b2eSHans Petter Selasky /* ==== USB audio v2.0 ==== */ 424e2524b2eSHans Petter Selasky 425e2524b2eSHans Petter Selasky static void uaudio20_mixer_add_mixer(struct uaudio_softc *, 426e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *, int); 427e2524b2eSHans Petter Selasky static void uaudio20_mixer_add_selector(struct uaudio_softc *, 428e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *, int); 429e2524b2eSHans Petter Selasky static void uaudio20_mixer_add_feature(struct uaudio_softc *, 430e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *, int); 431e2524b2eSHans Petter Selasky static struct usb_audio20_cluster uaudio20_mixer_get_cluster(uint8_t, 432e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *); 433e2524b2eSHans Petter Selasky static uint16_t uaudio20_mixer_determine_class(const struct uaudio_terminal_node *, 434e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 435e2524b2eSHans Petter Selasky static uint16_t uaudio20_mixer_feature_name(const struct uaudio_terminal_node *, 436e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 437e2524b2eSHans Petter Selasky static void uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *, 438e2524b2eSHans Petter Selasky const uint8_t *, uint8_t, struct uaudio_search_result *); 439e2524b2eSHans Petter Selasky static const void *uaudio20_mixer_verify_desc(const void *, uint32_t); 440e2524b2eSHans Petter Selasky static usb_error_t uaudio20_set_speed(struct usb_device *, uint8_t, 441e2524b2eSHans Petter Selasky uint8_t, uint32_t); 442e2524b2eSHans Petter Selasky 443e2524b2eSHans Petter Selasky /* USB audio v1.0 and v2.0 */ 444e2524b2eSHans Petter Selasky 445e2524b2eSHans Petter Selasky static void uaudio_chan_fill_info_sub(struct uaudio_softc *, 446e2524b2eSHans Petter Selasky struct usb_device *, uint32_t, uint8_t, uint8_t); 447e2524b2eSHans Petter Selasky static void uaudio_chan_fill_info(struct uaudio_softc *, 448e2524b2eSHans Petter Selasky struct usb_device *); 449e2524b2eSHans Petter Selasky static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *, 450e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 451e2524b2eSHans Petter Selasky static void uaudio_mixer_add_ctl(struct uaudio_softc *, 452e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 453e2524b2eSHans Petter Selasky static void uaudio_mixer_fill_info(struct uaudio_softc *, 454e2524b2eSHans Petter Selasky struct usb_device *, void *); 455e2524b2eSHans Petter Selasky static void uaudio_mixer_ctl_set(struct uaudio_softc *, 456e2524b2eSHans Petter Selasky struct uaudio_mixer_node *, uint8_t, int32_t val); 457e2524b2eSHans Petter Selasky static int uaudio_mixer_signext(uint8_t, int); 458e2524b2eSHans Petter Selasky static int uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val); 459e2524b2eSHans Petter Selasky static void uaudio_mixer_init(struct uaudio_softc *); 4603a3f90c6SAndrew Thompson static const struct uaudio_terminal_node *uaudio_mixer_get_input( 4613a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, uint8_t); 4623a3f90c6SAndrew Thompson static const struct uaudio_terminal_node *uaudio_mixer_get_output( 4633a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, uint8_t); 4643a3f90c6SAndrew Thompson static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *, 4653a3f90c6SAndrew Thompson uint8_t, uint8_t, struct uaudio_search_result *); 4663a3f90c6SAndrew Thompson static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t); 467760bc48eSAndrew Thompson static struct umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *); 468760bc48eSAndrew Thompson static void umidi_start_read(struct usb_fifo *); 469760bc48eSAndrew Thompson static void umidi_stop_read(struct usb_fifo *); 470760bc48eSAndrew Thompson static void umidi_start_write(struct usb_fifo *); 471760bc48eSAndrew Thompson static void umidi_stop_write(struct usb_fifo *); 472760bc48eSAndrew Thompson static int umidi_open(struct usb_fifo *, int); 473760bc48eSAndrew Thompson static int umidi_ioctl(struct usb_fifo *, u_long cmd, void *, int); 474760bc48eSAndrew Thompson static void umidi_close(struct usb_fifo *, int); 4753a3f90c6SAndrew Thompson static void umidi_init(device_t dev); 47625b74dabSHans Petter Selasky static int umidi_probe(device_t dev); 47725b74dabSHans Petter Selasky static int umidi_detach(device_t dev); 4783a3f90c6SAndrew Thompson 479b850ecc1SAndrew Thompson #ifdef USB_DEBUG 4803a3f90c6SAndrew Thompson static void uaudio_chan_dump_ep_desc( 4814c21be9bSRebecca Cran const usb_endpoint_descriptor_audio_t *); 4823a3f90c6SAndrew Thompson #endif 4833a3f90c6SAndrew Thompson 484760bc48eSAndrew Thompson static const struct usb_config 4853a3f90c6SAndrew Thompson uaudio_cfg_record[UAUDIO_NCHANBUFS] = { 4863a3f90c6SAndrew Thompson [0] = { 4873a3f90c6SAndrew Thompson .type = UE_ISOCHRONOUS, 4883a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 4893a3f90c6SAndrew Thompson .direction = UE_DIR_IN, 4904eae601eSAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 491b029f6bbSAndrew Thompson .frames = UAUDIO_NFRAMES, 4924eae601eSAndrew Thompson .flags = {.short_xfer_ok = 1,}, 4934eae601eSAndrew Thompson .callback = &uaudio_chan_record_callback, 4943a3f90c6SAndrew Thompson }, 4953a3f90c6SAndrew Thompson 4963a3f90c6SAndrew Thompson [1] = { 4973a3f90c6SAndrew Thompson .type = UE_ISOCHRONOUS, 4983a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 4993a3f90c6SAndrew Thompson .direction = UE_DIR_IN, 5004eae601eSAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 501b029f6bbSAndrew Thompson .frames = UAUDIO_NFRAMES, 5024eae601eSAndrew Thompson .flags = {.short_xfer_ok = 1,}, 5034eae601eSAndrew Thompson .callback = &uaudio_chan_record_callback, 5043a3f90c6SAndrew Thompson }, 5053a3f90c6SAndrew Thompson }; 5063a3f90c6SAndrew Thompson 507760bc48eSAndrew Thompson static const struct usb_config 5083a3f90c6SAndrew Thompson uaudio_cfg_play[UAUDIO_NCHANBUFS] = { 5093a3f90c6SAndrew Thompson [0] = { 5103a3f90c6SAndrew Thompson .type = UE_ISOCHRONOUS, 5113a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 5123a3f90c6SAndrew Thompson .direction = UE_DIR_OUT, 5134eae601eSAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 514b029f6bbSAndrew Thompson .frames = UAUDIO_NFRAMES, 5154eae601eSAndrew Thompson .flags = {.short_xfer_ok = 1,}, 5164eae601eSAndrew Thompson .callback = &uaudio_chan_play_callback, 5173a3f90c6SAndrew Thompson }, 5183a3f90c6SAndrew Thompson 5193a3f90c6SAndrew Thompson [1] = { 5203a3f90c6SAndrew Thompson .type = UE_ISOCHRONOUS, 5213a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 5223a3f90c6SAndrew Thompson .direction = UE_DIR_OUT, 5234eae601eSAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 524b029f6bbSAndrew Thompson .frames = UAUDIO_NFRAMES, 5254eae601eSAndrew Thompson .flags = {.short_xfer_ok = 1,}, 5264eae601eSAndrew Thompson .callback = &uaudio_chan_play_callback, 5273a3f90c6SAndrew Thompson }, 5283a3f90c6SAndrew Thompson }; 5293a3f90c6SAndrew Thompson 530760bc48eSAndrew Thompson static const struct usb_config 5313a3f90c6SAndrew Thompson uaudio_mixer_config[1] = { 5323a3f90c6SAndrew Thompson [0] = { 5333a3f90c6SAndrew Thompson .type = UE_CONTROL, 5343a3f90c6SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 5353a3f90c6SAndrew Thompson .direction = UE_DIR_ANY, 536760bc48eSAndrew Thompson .bufsize = (sizeof(struct usb_device_request) + 4), 5374eae601eSAndrew Thompson .callback = &uaudio_mixer_write_cfg_callback, 5384eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 5393a3f90c6SAndrew Thompson }, 5403a3f90c6SAndrew Thompson }; 5413a3f90c6SAndrew Thompson 5423a3f90c6SAndrew Thompson static const 5433a3f90c6SAndrew Thompson uint8_t umidi_cmd_to_len[16] = { 5443a3f90c6SAndrew Thompson [0x0] = 0, /* reserved */ 5453a3f90c6SAndrew Thompson [0x1] = 0, /* reserved */ 5463a3f90c6SAndrew Thompson [0x2] = 2, /* bytes */ 5473a3f90c6SAndrew Thompson [0x3] = 3, /* bytes */ 5483a3f90c6SAndrew Thompson [0x4] = 3, /* bytes */ 5493a3f90c6SAndrew Thompson [0x5] = 1, /* bytes */ 5503a3f90c6SAndrew Thompson [0x6] = 2, /* bytes */ 5513a3f90c6SAndrew Thompson [0x7] = 3, /* bytes */ 5523a3f90c6SAndrew Thompson [0x8] = 3, /* bytes */ 5533a3f90c6SAndrew Thompson [0x9] = 3, /* bytes */ 5543a3f90c6SAndrew Thompson [0xA] = 3, /* bytes */ 5553a3f90c6SAndrew Thompson [0xB] = 3, /* bytes */ 5563a3f90c6SAndrew Thompson [0xC] = 2, /* bytes */ 5573a3f90c6SAndrew Thompson [0xD] = 2, /* bytes */ 5583a3f90c6SAndrew Thompson [0xE] = 3, /* bytes */ 5593a3f90c6SAndrew Thompson [0xF] = 1, /* bytes */ 5603a3f90c6SAndrew Thompson }; 5613a3f90c6SAndrew Thompson 562760bc48eSAndrew Thompson static const struct usb_config 5633a3f90c6SAndrew Thompson umidi_config[UMIDI_N_TRANSFER] = { 5646f068a43SHans Petter Selasky [UMIDI_TX_TRANSFER] = { 5653a3f90c6SAndrew Thompson .type = UE_BULK, 5663a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 5673a3f90c6SAndrew Thompson .direction = UE_DIR_OUT, 568910f1dcfSHans Petter Selasky .bufsize = UMIDI_TX_BUFFER, 5694eae601eSAndrew Thompson .callback = &umidi_bulk_write_callback, 5703a3f90c6SAndrew Thompson }, 5713a3f90c6SAndrew Thompson 5726f068a43SHans Petter Selasky [UMIDI_RX_TRANSFER] = { 5733a3f90c6SAndrew Thompson .type = UE_BULK, 5743a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 5753a3f90c6SAndrew Thompson .direction = UE_DIR_IN, 5763851442cSAndrew Thompson .bufsize = 4, /* bytes */ 577910f1dcfSHans Petter Selasky .flags = {.short_xfer_ok = 1,.proxy_buffer = 1,}, 5784eae601eSAndrew Thompson .callback = &umidi_bulk_read_callback, 5793a3f90c6SAndrew Thompson }, 5803a3f90c6SAndrew Thompson }; 5813a3f90c6SAndrew Thompson 5823a3f90c6SAndrew Thompson static devclass_t uaudio_devclass; 5833a3f90c6SAndrew Thompson 5843a3f90c6SAndrew Thompson static device_method_t uaudio_methods[] = { 5853a3f90c6SAndrew Thompson DEVMETHOD(device_probe, uaudio_probe), 5863a3f90c6SAndrew Thompson DEVMETHOD(device_attach, uaudio_attach), 5873a3f90c6SAndrew Thompson DEVMETHOD(device_detach, uaudio_detach), 5883a3f90c6SAndrew Thompson DEVMETHOD(device_suspend, bus_generic_suspend), 5893a3f90c6SAndrew Thompson DEVMETHOD(device_resume, bus_generic_resume), 5903a3f90c6SAndrew Thompson DEVMETHOD(device_shutdown, bus_generic_shutdown), 5914b7ec270SMarius Strobl 5924b7ec270SMarius Strobl DEVMETHOD_END 5933a3f90c6SAndrew Thompson }; 5943a3f90c6SAndrew Thompson 5953a3f90c6SAndrew Thompson static driver_t uaudio_driver = { 5963a3f90c6SAndrew Thompson .name = "uaudio", 5973a3f90c6SAndrew Thompson .methods = uaudio_methods, 5983a3f90c6SAndrew Thompson .size = sizeof(struct uaudio_softc), 5993a3f90c6SAndrew Thompson }; 6003a3f90c6SAndrew Thompson 601f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID __used uaudio_devs[] = { 602f1a16106SHans Petter Selasky /* Generic USB audio class match */ 603f1a16106SHans Petter Selasky {USB_IFACE_CLASS(UICLASS_AUDIO), 604f1a16106SHans Petter Selasky USB_IFACE_SUBCLASS(UISUBCLASS_AUDIOCONTROL),}, 605f1a16106SHans Petter Selasky /* Generic USB MIDI class match */ 606f1a16106SHans Petter Selasky {USB_IFACE_CLASS(UICLASS_AUDIO), 607f1a16106SHans Petter Selasky USB_IFACE_SUBCLASS(UISUBCLASS_MIDISTREAM),}, 608f1a16106SHans Petter Selasky }; 609f1a16106SHans Petter Selasky 6103a3f90c6SAndrew Thompson static int 6113a3f90c6SAndrew Thompson uaudio_probe(device_t dev) 6123a3f90c6SAndrew Thompson { 613760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 6143a3f90c6SAndrew Thompson 615fadc970bSAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) 6163a3f90c6SAndrew Thompson return (ENXIO); 6173a3f90c6SAndrew Thompson 61825b74dabSHans Petter Selasky /* lookup non-standard device */ 6193a3f90c6SAndrew Thompson 62025b74dabSHans Petter Selasky if (uaa->info.bInterfaceClass != UICLASS_AUDIO) { 62125b74dabSHans Petter Selasky if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0) 62225b74dabSHans Petter Selasky return (ENXIO); 62325b74dabSHans Petter Selasky } 62425b74dabSHans Petter Selasky 62525b74dabSHans Petter Selasky /* check for AUDIO control interface */ 62625b74dabSHans Petter Selasky 62725b74dabSHans Petter Selasky if (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL) { 628a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_BAD_AUDIO)) 6293a3f90c6SAndrew Thompson return (ENXIO); 6303a3f90c6SAndrew Thompson else 631cd10bffaSAndriy Gapon return (BUS_PROBE_GENERIC); 6323a3f90c6SAndrew Thompson } 633dc694251SAndrew Thompson 634dc694251SAndrew Thompson /* check for MIDI stream */ 635dc694251SAndrew Thompson 63625b74dabSHans Petter Selasky if (uaa->info.bInterfaceSubClass == UISUBCLASS_MIDISTREAM) { 63725b74dabSHans Petter Selasky if (usb_test_quirk(uaa, UQ_BAD_MIDI)) 63825b74dabSHans Petter Selasky return (ENXIO); 63925b74dabSHans Petter Selasky else 640cd10bffaSAndriy Gapon return (BUS_PROBE_GENERIC); 641dc694251SAndrew Thompson } 6423a3f90c6SAndrew Thompson return (ENXIO); 6433a3f90c6SAndrew Thompson } 6443a3f90c6SAndrew Thompson 6453a3f90c6SAndrew Thompson static int 6463a3f90c6SAndrew Thompson uaudio_attach(device_t dev) 6473a3f90c6SAndrew Thompson { 648760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 6493a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 650760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 6513a3f90c6SAndrew Thompson device_t child; 6523a3f90c6SAndrew Thompson 6533a3f90c6SAndrew Thompson sc->sc_play_chan.priv_sc = sc; 6543a3f90c6SAndrew Thompson sc->sc_rec_chan.priv_sc = sc; 6553a3f90c6SAndrew Thompson sc->sc_udev = uaa->device; 656b029f6bbSAndrew Thompson sc->sc_mixer_iface_index = uaa->info.bIfaceIndex; 657b029f6bbSAndrew Thompson sc->sc_mixer_iface_no = uaa->info.bIfaceNum; 6583a3f90c6SAndrew Thompson 659a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR)) 6603a3f90c6SAndrew Thompson sc->sc_uq_audio_swap_lr = 1; 6613a3f90c6SAndrew Thompson 662a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_AU_INP_ASYNC)) 6633a3f90c6SAndrew Thompson sc->sc_uq_au_inp_async = 1; 6643a3f90c6SAndrew Thompson 665a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_AU_NO_XU)) 6663a3f90c6SAndrew Thompson sc->sc_uq_au_no_xu = 1; 6673a3f90c6SAndrew Thompson 668a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_BAD_ADC)) 6693a3f90c6SAndrew Thompson sc->sc_uq_bad_adc = 1; 6703a3f90c6SAndrew Thompson 67125b74dabSHans Petter Selasky if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS)) 67225b74dabSHans Petter Selasky sc->sc_uq_au_vendor_class = 1; 67325b74dabSHans Petter Selasky 6743a3f90c6SAndrew Thompson umidi_init(dev); 6753a3f90c6SAndrew Thompson 676a593f6b8SAndrew Thompson device_set_usb_desc(dev); 6773a3f90c6SAndrew Thompson 678a593f6b8SAndrew Thompson id = usbd_get_interface_descriptor(uaa->iface); 6793a3f90c6SAndrew Thompson 680e2524b2eSHans Petter Selasky /* must fill mixer info before channel info */ 6813a3f90c6SAndrew Thompson uaudio_mixer_fill_info(sc, uaa->device, id); 6823a3f90c6SAndrew Thompson 683e2524b2eSHans Petter Selasky /* fill channel info */ 684e2524b2eSHans Petter Selasky uaudio_chan_fill_info(sc, uaa->device); 685e2524b2eSHans Petter Selasky 6863a3f90c6SAndrew Thompson DPRINTF("audio rev %d.%02x\n", 6873a3f90c6SAndrew Thompson sc->sc_audio_rev >> 8, 6883a3f90c6SAndrew Thompson sc->sc_audio_rev & 0xff); 6893a3f90c6SAndrew Thompson 6903a3f90c6SAndrew Thompson DPRINTF("%d mixer controls\n", 6913a3f90c6SAndrew Thompson sc->sc_mixer_count); 6923a3f90c6SAndrew Thompson 6933a3f90c6SAndrew Thompson if (sc->sc_play_chan.valid) { 6949b5da816SHans Petter Selasky device_printf(dev, "Play: %d Hz, %d ch, %s format.\n", 6953a3f90c6SAndrew Thompson sc->sc_play_chan.sample_rate, 696e2524b2eSHans Petter Selasky sc->sc_play_chan.channels, 6973a3f90c6SAndrew Thompson sc->sc_play_chan.p_fmt->description); 6983a3f90c6SAndrew Thompson } else { 6999b5da816SHans Petter Selasky device_printf(dev, "No playback.\n"); 7003a3f90c6SAndrew Thompson } 7013a3f90c6SAndrew Thompson 7023a3f90c6SAndrew Thompson if (sc->sc_rec_chan.valid) { 7039b5da816SHans Petter Selasky device_printf(dev, "Record: %d Hz, %d ch, %s format.\n", 7043a3f90c6SAndrew Thompson sc->sc_rec_chan.sample_rate, 705e2524b2eSHans Petter Selasky sc->sc_play_chan.channels, 7063a3f90c6SAndrew Thompson sc->sc_rec_chan.p_fmt->description); 7073a3f90c6SAndrew Thompson } else { 7089b5da816SHans Petter Selasky device_printf(dev, "No recording.\n"); 7093a3f90c6SAndrew Thompson } 7103a3f90c6SAndrew Thompson 7113a3f90c6SAndrew Thompson if (sc->sc_midi_chan.valid) { 7123a3f90c6SAndrew Thompson 7133a3f90c6SAndrew Thompson if (umidi_probe(dev)) { 7143a3f90c6SAndrew Thompson goto detach; 7153a3f90c6SAndrew Thompson } 7169b5da816SHans Petter Selasky device_printf(dev, "MIDI sequencer.\n"); 7173a3f90c6SAndrew Thompson } else { 7189b5da816SHans Petter Selasky device_printf(dev, "No midi sequencer.\n"); 7193a3f90c6SAndrew Thompson } 7203a3f90c6SAndrew Thompson 7213a3f90c6SAndrew Thompson DPRINTF("doing child attach\n"); 7223a3f90c6SAndrew Thompson 7233a3f90c6SAndrew Thompson /* attach the children */ 7243a3f90c6SAndrew Thompson 7253a3f90c6SAndrew Thompson sc->sc_sndcard_func.func = SCF_PCM; 7263a3f90c6SAndrew Thompson 7279b5da816SHans Petter Selasky /* 7289b5da816SHans Petter Selasky * Only attach a PCM device if we have a playback, recording 7299b5da816SHans Petter Selasky * or mixer device present: 7309b5da816SHans Petter Selasky */ 7319b5da816SHans Petter Selasky if (sc->sc_play_chan.valid || 7329b5da816SHans Petter Selasky sc->sc_rec_chan.valid || 7339b5da816SHans Petter Selasky sc->sc_mix_info) { 7343a3f90c6SAndrew Thompson child = device_add_child(dev, "pcm", -1); 7353a3f90c6SAndrew Thompson 7363a3f90c6SAndrew Thompson if (child == NULL) { 7373a3f90c6SAndrew Thompson DPRINTF("out of memory\n"); 7383a3f90c6SAndrew Thompson goto detach; 7393a3f90c6SAndrew Thompson } 7403a3f90c6SAndrew Thompson device_set_ivars(child, &sc->sc_sndcard_func); 7419b5da816SHans Petter Selasky } 7423a3f90c6SAndrew Thompson 7433a3f90c6SAndrew Thompson if (bus_generic_attach(dev)) { 7443a3f90c6SAndrew Thompson DPRINTF("child attach failed\n"); 7453a3f90c6SAndrew Thompson goto detach; 7463a3f90c6SAndrew Thompson } 7473a3f90c6SAndrew Thompson return (0); /* success */ 7483a3f90c6SAndrew Thompson 7493a3f90c6SAndrew Thompson detach: 7503a3f90c6SAndrew Thompson uaudio_detach(dev); 7513a3f90c6SAndrew Thompson return (ENXIO); 7523a3f90c6SAndrew Thompson } 7533a3f90c6SAndrew Thompson 7543a3f90c6SAndrew Thompson static void 7553a3f90c6SAndrew Thompson uaudio_pcm_setflags(device_t dev, uint32_t flags) 7563a3f90c6SAndrew Thompson { 7573a3f90c6SAndrew Thompson pcm_setflags(dev, pcm_getflags(dev) | flags); 7583a3f90c6SAndrew Thompson } 7593a3f90c6SAndrew Thompson 7603a3f90c6SAndrew Thompson int 7613a3f90c6SAndrew Thompson uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class) 7623a3f90c6SAndrew Thompson { 7633a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(device_get_parent(dev)); 7643a3f90c6SAndrew Thompson char status[SND_STATUSLEN]; 7653a3f90c6SAndrew Thompson 7663a3f90c6SAndrew Thompson uaudio_mixer_init(sc); 7673a3f90c6SAndrew Thompson 7683a3f90c6SAndrew Thompson if (sc->sc_uq_audio_swap_lr) { 7693a3f90c6SAndrew Thompson DPRINTF("hardware has swapped left and right\n"); 77090da2b28SAriff Abdullah /* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */ 7713a3f90c6SAndrew Thompson } 7723a3f90c6SAndrew Thompson if (!(sc->sc_mix_info & SOUND_MASK_PCM)) { 7733a3f90c6SAndrew Thompson 7743a3f90c6SAndrew Thompson DPRINTF("emulating master volume\n"); 7753a3f90c6SAndrew Thompson 7763a3f90c6SAndrew Thompson /* 7773a3f90c6SAndrew Thompson * Emulate missing pcm mixer controller 7783a3f90c6SAndrew Thompson * through FEEDER_VOLUME 7793a3f90c6SAndrew Thompson */ 7803a3f90c6SAndrew Thompson uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL); 7813a3f90c6SAndrew Thompson } 7823a3f90c6SAndrew Thompson if (mixer_init(dev, mixer_class, sc)) { 7833a3f90c6SAndrew Thompson goto detach; 7843a3f90c6SAndrew Thompson } 7853a3f90c6SAndrew Thompson sc->sc_mixer_init = 1; 7863a3f90c6SAndrew Thompson 7873a3f90c6SAndrew Thompson snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio)); 7883a3f90c6SAndrew Thompson 7893a3f90c6SAndrew Thompson if (pcm_register(dev, sc, 7903a3f90c6SAndrew Thompson sc->sc_play_chan.valid ? 1 : 0, 7913a3f90c6SAndrew Thompson sc->sc_rec_chan.valid ? 1 : 0)) { 7923a3f90c6SAndrew Thompson goto detach; 7933a3f90c6SAndrew Thompson } 79490da2b28SAriff Abdullah 79590da2b28SAriff Abdullah uaudio_pcm_setflags(dev, SD_F_MPSAFE); 7963a3f90c6SAndrew Thompson sc->sc_pcm_registered = 1; 7973a3f90c6SAndrew Thompson 7983a3f90c6SAndrew Thompson if (sc->sc_play_chan.valid) { 7993a3f90c6SAndrew Thompson pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc); 8003a3f90c6SAndrew Thompson } 8013a3f90c6SAndrew Thompson if (sc->sc_rec_chan.valid) { 8023a3f90c6SAndrew Thompson pcm_addchan(dev, PCMDIR_REC, chan_class, sc); 8033a3f90c6SAndrew Thompson } 8043a3f90c6SAndrew Thompson pcm_setstatus(dev, status); 8053a3f90c6SAndrew Thompson 8063a3f90c6SAndrew Thompson return (0); /* success */ 8073a3f90c6SAndrew Thompson 8083a3f90c6SAndrew Thompson detach: 8093a3f90c6SAndrew Thompson uaudio_detach_sub(dev); 8103a3f90c6SAndrew Thompson return (ENXIO); 8113a3f90c6SAndrew Thompson } 8123a3f90c6SAndrew Thompson 8133a3f90c6SAndrew Thompson int 8143a3f90c6SAndrew Thompson uaudio_detach_sub(device_t dev) 8153a3f90c6SAndrew Thompson { 8163a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(device_get_parent(dev)); 8173a3f90c6SAndrew Thompson int error = 0; 8183a3f90c6SAndrew Thompson 8193a3f90c6SAndrew Thompson repeat: 8203a3f90c6SAndrew Thompson if (sc->sc_pcm_registered) { 8213a3f90c6SAndrew Thompson error = pcm_unregister(dev); 8223a3f90c6SAndrew Thompson } else { 8233a3f90c6SAndrew Thompson if (sc->sc_mixer_init) { 8243a3f90c6SAndrew Thompson error = mixer_uninit(dev); 8253a3f90c6SAndrew Thompson } 8263a3f90c6SAndrew Thompson } 8273a3f90c6SAndrew Thompson 8283a3f90c6SAndrew Thompson if (error) { 8293a3f90c6SAndrew Thompson device_printf(dev, "Waiting for sound application to exit!\n"); 830a593f6b8SAndrew Thompson usb_pause_mtx(NULL, 2 * hz); 8313a3f90c6SAndrew Thompson goto repeat; /* try again */ 8323a3f90c6SAndrew Thompson } 8333a3f90c6SAndrew Thompson return (0); /* success */ 8343a3f90c6SAndrew Thompson } 8353a3f90c6SAndrew Thompson 8363a3f90c6SAndrew Thompson static int 8373a3f90c6SAndrew Thompson uaudio_detach(device_t dev) 8383a3f90c6SAndrew Thompson { 8393a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 8403a3f90c6SAndrew Thompson 841a6ed4937SHans Petter Selasky /* 842a6ed4937SHans Petter Selasky * Stop USB transfers early so that any audio applications 843a6ed4937SHans Petter Selasky * will time out and close opened /dev/dspX.Y device(s), if 844a6ed4937SHans Petter Selasky * any. 845a6ed4937SHans Petter Selasky */ 84611b81cedSHans Petter Selasky if (sc->sc_play_chan.valid) 84711b81cedSHans Petter Selasky usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS); 84811b81cedSHans Petter Selasky if (sc->sc_rec_chan.valid) 84911b81cedSHans Petter Selasky usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS); 850a6ed4937SHans Petter Selasky 851a6ed4937SHans Petter Selasky if (bus_generic_detach(dev) != 0) { 8523a3f90c6SAndrew Thompson DPRINTF("detach failed!\n"); 8533a3f90c6SAndrew Thompson } 8543a3f90c6SAndrew Thompson sbuf_delete(&sc->sc_sndstat); 8553a3f90c6SAndrew Thompson sc->sc_sndstat_valid = 0; 8563a3f90c6SAndrew Thompson 8573a3f90c6SAndrew Thompson umidi_detach(dev); 8583a3f90c6SAndrew Thompson 8593a3f90c6SAndrew Thompson return (0); 8603a3f90c6SAndrew Thompson } 8613a3f90c6SAndrew Thompson 8623a3f90c6SAndrew Thompson /*========================================================================* 8633a3f90c6SAndrew Thompson * AS - Audio Stream - routines 8643a3f90c6SAndrew Thompson *========================================================================*/ 8653a3f90c6SAndrew Thompson 866b850ecc1SAndrew Thompson #ifdef USB_DEBUG 8673a3f90c6SAndrew Thompson static void 8684c21be9bSRebecca Cran uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed) 8693a3f90c6SAndrew Thompson { 8703a3f90c6SAndrew Thompson if (ed) { 8713a3f90c6SAndrew Thompson DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n" 8723a3f90c6SAndrew Thompson "bEndpointAddress=%d bmAttributes=0x%x \n" 8733a3f90c6SAndrew Thompson "wMaxPacketSize=%d bInterval=%d \n" 8743a3f90c6SAndrew Thompson "bRefresh=%d bSynchAddress=%d\n", 8753a3f90c6SAndrew Thompson ed, ed->bLength, ed->bDescriptorType, 8763a3f90c6SAndrew Thompson ed->bEndpointAddress, ed->bmAttributes, 8773a3f90c6SAndrew Thompson UGETW(ed->wMaxPacketSize), ed->bInterval, 878e3e05e50SAndrew Thompson UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0, 879e3e05e50SAndrew Thompson UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0); 8803a3f90c6SAndrew Thompson } 8813a3f90c6SAndrew Thompson } 8823a3f90c6SAndrew Thompson 8833a3f90c6SAndrew Thompson #endif 8843a3f90c6SAndrew Thompson 885f895cc0cSHans Petter Selasky /* 886f895cc0cSHans Petter Selasky * The following is a workaround for broken no-name USB audio devices 887f895cc0cSHans Petter Selasky * sold by dealextreme called "3D sound". The problem is that the 888f895cc0cSHans Petter Selasky * manufacturer computed wMaxPacketSize is too small to hold the 889f895cc0cSHans Petter Selasky * actual data sent. In other words the device sometimes sends more 890f895cc0cSHans Petter Selasky * data than it actually reports it can send in a single isochronous 891f895cc0cSHans Petter Selasky * packet. 892f895cc0cSHans Petter Selasky */ 893f895cc0cSHans Petter Selasky static void 894f895cc0cSHans Petter Selasky uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep, 895f895cc0cSHans Petter Selasky uint32_t xps, uint32_t add) 896f895cc0cSHans Petter Selasky { 897f895cc0cSHans Petter Selasky uint32_t mps; 898f895cc0cSHans Petter Selasky 899f895cc0cSHans Petter Selasky mps = UGETW(ep->wMaxPacketSize); 900f895cc0cSHans Petter Selasky 901f895cc0cSHans Petter Selasky /* 902f895cc0cSHans Petter Selasky * If the device indicates it can send more data than what the 903f895cc0cSHans Petter Selasky * sample rate indicates, we apply the workaround. 904f895cc0cSHans Petter Selasky */ 905f895cc0cSHans Petter Selasky if (mps > xps) { 906f895cc0cSHans Petter Selasky 907f895cc0cSHans Petter Selasky /* allow additional data */ 908f895cc0cSHans Petter Selasky xps += add; 909f895cc0cSHans Petter Selasky 910f895cc0cSHans Petter Selasky /* check against the maximum USB 1.x length */ 911f895cc0cSHans Petter Selasky if (xps > 1023) 912f895cc0cSHans Petter Selasky xps = 1023; 913f895cc0cSHans Petter Selasky 914f895cc0cSHans Petter Selasky /* check if we should do an update */ 915f895cc0cSHans Petter Selasky if (mps < xps) { 916f895cc0cSHans Petter Selasky /* simply update the wMaxPacketSize field */ 917f895cc0cSHans Petter Selasky USETW(ep->wMaxPacketSize, xps); 918f895cc0cSHans Petter Selasky DPRINTF("Workaround: Updated wMaxPacketSize " 919f895cc0cSHans Petter Selasky "from %d to %d bytes.\n", 920f895cc0cSHans Petter Selasky (int)mps, (int)xps); 921f895cc0cSHans Petter Selasky } 922f895cc0cSHans Petter Selasky } 923f895cc0cSHans Petter Selasky } 924f895cc0cSHans Petter Selasky 925e2524b2eSHans Petter Selasky static usb_error_t 926e2524b2eSHans Petter Selasky uaudio20_check_rate(struct usb_device *udev, uint8_t iface_no, 927e2524b2eSHans Petter Selasky uint8_t clockid, uint32_t rate) 928e2524b2eSHans Petter Selasky { 929e2524b2eSHans Petter Selasky struct usb_device_request req; 930e2524b2eSHans Petter Selasky usb_error_t error; 931e2524b2eSHans Petter Selasky uint8_t data[255]; 932e2524b2eSHans Petter Selasky uint16_t actlen; 933e2524b2eSHans Petter Selasky uint16_t rates; 934e2524b2eSHans Petter Selasky uint16_t x; 935e2524b2eSHans Petter Selasky 936e2524b2eSHans Petter Selasky DPRINTFN(6, "ifaceno=%d clockid=%d rate=%u\n", 937e2524b2eSHans Petter Selasky iface_no, clockid, rate); 938e2524b2eSHans Petter Selasky 939e2524b2eSHans Petter Selasky req.bmRequestType = UT_READ_CLASS_INTERFACE; 940e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_RANGE; 941e2524b2eSHans Petter Selasky USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0); 942e2524b2eSHans Petter Selasky USETW2(req.wIndex, clockid, iface_no); 943e2524b2eSHans Petter Selasky USETW(req.wLength, 255); 944e2524b2eSHans Petter Selasky 945e2524b2eSHans Petter Selasky error = usbd_do_request_flags(udev, NULL, &req, data, 946e2524b2eSHans Petter Selasky USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT); 947e2524b2eSHans Petter Selasky 948e2524b2eSHans Petter Selasky if (error != 0 || actlen < 2) 949e2524b2eSHans Petter Selasky return (USB_ERR_INVAL); 950e2524b2eSHans Petter Selasky 951e2524b2eSHans Petter Selasky rates = data[0] | (data[1] << 8); 952e2524b2eSHans Petter Selasky actlen = (actlen - 2) / 12; 953e2524b2eSHans Petter Selasky 954e2524b2eSHans Petter Selasky if (rates > actlen) { 955e2524b2eSHans Petter Selasky DPRINTF("Too many rates\n"); 956e2524b2eSHans Petter Selasky rates = actlen; 957e2524b2eSHans Petter Selasky } 958e2524b2eSHans Petter Selasky 959e2524b2eSHans Petter Selasky for (x = 0; x != rates; x++) { 960e2524b2eSHans Petter Selasky uint32_t min = UGETDW(data + 2 + (12 * x)); 961e2524b2eSHans Petter Selasky uint32_t max = UGETDW(data + 6 + (12 * x)); 962e2524b2eSHans Petter Selasky uint32_t res = UGETDW(data + 10 + (12 * x)); 963e2524b2eSHans Petter Selasky 964e2524b2eSHans Petter Selasky if (res == 0) { 965e2524b2eSHans Petter Selasky DPRINTF("Zero residue\n"); 966e2524b2eSHans Petter Selasky res = 1; 967e2524b2eSHans Petter Selasky } 968e2524b2eSHans Petter Selasky 969e2524b2eSHans Petter Selasky if (min > max) { 970e2524b2eSHans Petter Selasky DPRINTF("Swapped max and min\n"); 971e2524b2eSHans Petter Selasky uint32_t temp; 972e2524b2eSHans Petter Selasky temp = min; 973e2524b2eSHans Petter Selasky min = max; 974e2524b2eSHans Petter Selasky max = temp; 975e2524b2eSHans Petter Selasky } 976e2524b2eSHans Petter Selasky 977e2524b2eSHans Petter Selasky if (rate >= min && rate <= max && 978e2524b2eSHans Petter Selasky (((rate - min) % res) == 0)) { 979e2524b2eSHans Petter Selasky return (0); 980e2524b2eSHans Petter Selasky } 981e2524b2eSHans Petter Selasky } 982e2524b2eSHans Petter Selasky return (USB_ERR_INVAL); 983e2524b2eSHans Petter Selasky } 984e2524b2eSHans Petter Selasky 9853a3f90c6SAndrew Thompson static void 986760bc48eSAndrew Thompson uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, 987afbfddd9SAndrew Thompson uint32_t rate, uint8_t channels, uint8_t bit_resolution) 9883a3f90c6SAndrew Thompson { 989760bc48eSAndrew Thompson struct usb_descriptor *desc = NULL; 990e2524b2eSHans Petter Selasky union uaudio_asid asid = { NULL }; 991e2524b2eSHans Petter Selasky union uaudio_asf1d asf1d = { NULL }; 992e2524b2eSHans Petter Selasky union uaudio_sed sed = { NULL }; 993f895cc0cSHans Petter Selasky usb_endpoint_descriptor_audio_t *ed1 = NULL; 994e2524b2eSHans Petter Selasky const struct usb_audio_control_descriptor *acdp = NULL; 995a593f6b8SAndrew Thompson struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); 996760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 997e2524b2eSHans Petter Selasky const struct uaudio_format *p_fmt = NULL; 9983a3f90c6SAndrew Thompson struct uaudio_chan *chan; 9993a3f90c6SAndrew Thompson uint16_t curidx = 0xFFFF; 10003a3f90c6SAndrew Thompson uint16_t lastidx = 0xFFFF; 10013a3f90c6SAndrew Thompson uint16_t alt_index = 0; 1002e2524b2eSHans Petter Selasky uint16_t audio_rev = 0; 1003e2524b2eSHans Petter Selasky uint16_t x; 10043a3f90c6SAndrew Thompson uint8_t ep_dir; 10053a3f90c6SAndrew Thompson uint8_t bChannels; 10063a3f90c6SAndrew Thompson uint8_t bBitResolution; 10073a3f90c6SAndrew Thompson uint8_t audio_if = 0; 100825b74dabSHans Petter Selasky uint8_t uma_if_class; 10093a3f90c6SAndrew Thompson 1010a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) { 10113a3f90c6SAndrew Thompson 10123a3f90c6SAndrew Thompson if ((desc->bDescriptorType == UDESC_INTERFACE) && 10133a3f90c6SAndrew Thompson (desc->bLength >= sizeof(*id))) { 10143a3f90c6SAndrew Thompson 10153a3f90c6SAndrew Thompson id = (void *)desc; 10163a3f90c6SAndrew Thompson 10173a3f90c6SAndrew Thompson if (id->bInterfaceNumber != lastidx) { 10183a3f90c6SAndrew Thompson lastidx = id->bInterfaceNumber; 10193a3f90c6SAndrew Thompson curidx++; 10203a3f90c6SAndrew Thompson alt_index = 0; 10213a3f90c6SAndrew Thompson 10223a3f90c6SAndrew Thompson } else { 10233a3f90c6SAndrew Thompson alt_index++; 10243a3f90c6SAndrew Thompson } 10253a3f90c6SAndrew Thompson 102625b74dabSHans Petter Selasky uma_if_class = 102725b74dabSHans Petter Selasky ((id->bInterfaceClass == UICLASS_AUDIO) || 102825b74dabSHans Petter Selasky ((id->bInterfaceClass == UICLASS_VENDOR) && 102925b74dabSHans Petter Selasky (sc->sc_uq_au_vendor_class != 0))); 103025b74dabSHans Petter Selasky 103125b74dabSHans Petter Selasky if ((uma_if_class != 0) && (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) { 10323a3f90c6SAndrew Thompson audio_if = 1; 10333a3f90c6SAndrew Thompson } else { 10343a3f90c6SAndrew Thompson audio_if = 0; 10353a3f90c6SAndrew Thompson } 10363a3f90c6SAndrew Thompson 103725b74dabSHans Petter Selasky if ((uma_if_class != 0) && 10383a3f90c6SAndrew Thompson (id->bInterfaceSubClass == UISUBCLASS_MIDISTREAM)) { 10393a3f90c6SAndrew Thompson 10403a3f90c6SAndrew Thompson /* 10413a3f90c6SAndrew Thompson * XXX could allow multiple MIDI interfaces 10423a3f90c6SAndrew Thompson */ 10433a3f90c6SAndrew Thompson 10443a3f90c6SAndrew Thompson if ((sc->sc_midi_chan.valid == 0) && 1045a593f6b8SAndrew Thompson usbd_get_iface(udev, curidx)) { 10463a3f90c6SAndrew Thompson sc->sc_midi_chan.iface_index = curidx; 10473a3f90c6SAndrew Thompson sc->sc_midi_chan.iface_alt_index = alt_index; 10483a3f90c6SAndrew Thompson sc->sc_midi_chan.valid = 1; 10493a3f90c6SAndrew Thompson } 10503a3f90c6SAndrew Thompson } 1051e2524b2eSHans Petter Selasky asid.v1 = NULL; 1052e2524b2eSHans Petter Selasky asf1d.v1 = NULL; 10533a3f90c6SAndrew Thompson ed1 = NULL; 1054e2524b2eSHans Petter Selasky sed.v1 = NULL; 10553a3f90c6SAndrew Thompson } 1056e2524b2eSHans Petter Selasky 10571234097eSHans Petter Selasky if (audio_if == 0) { 1058e2524b2eSHans Petter Selasky if ((acdp == NULL) && 1059e2524b2eSHans Petter Selasky (desc->bDescriptorType == UDESC_CS_INTERFACE) && 1060e2524b2eSHans Petter Selasky (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) && 1061e2524b2eSHans Petter Selasky (desc->bLength >= sizeof(*acdp))) { 1062e2524b2eSHans Petter Selasky acdp = (void *)desc; 1063e2524b2eSHans Petter Selasky audio_rev = UGETW(acdp->bcdADC); 1064e2524b2eSHans Petter Selasky } 1065e2524b2eSHans Petter Selasky 10661234097eSHans Petter Selasky /* 10671234097eSHans Petter Selasky * Don't collect any USB audio descriptors if 10681234097eSHans Petter Selasky * this is not an USB audio stream interface. 10691234097eSHans Petter Selasky */ 10701234097eSHans Petter Selasky continue; 10711234097eSHans Petter Selasky } 10721234097eSHans Petter Selasky 1073e2524b2eSHans Petter Selasky if ((acdp != NULL) && 1074e2524b2eSHans Petter Selasky (desc->bDescriptorType == UDESC_CS_INTERFACE) && 1075e2524b2eSHans Petter Selasky (desc->bDescriptorSubtype == AS_GENERAL) && 1076e2524b2eSHans Petter Selasky (asid.v1 == NULL)) { 1077e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 1078e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 1079e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 1080e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*asid.v2)) { 1081e2524b2eSHans Petter Selasky asid.v2 = (void *)desc; 1082e2524b2eSHans Petter Selasky } 1083e2524b2eSHans Petter Selasky } else { 1084e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*asid.v1)) { 1085e2524b2eSHans Petter Selasky asid.v1 = (void *)desc; 10863a3f90c6SAndrew Thompson } 10873a3f90c6SAndrew Thompson } 1088e2524b2eSHans Petter Selasky } 1089e2524b2eSHans Petter Selasky if ((acdp != NULL) && 1090e2524b2eSHans Petter Selasky (desc->bDescriptorType == UDESC_CS_INTERFACE) && 10913a3f90c6SAndrew Thompson (desc->bDescriptorSubtype == FORMAT_TYPE) && 1092e2524b2eSHans Petter Selasky (asf1d.v1 == NULL)) { 1093e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 1094e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 1095e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 1096e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*asf1d.v2)) 1097e2524b2eSHans Petter Selasky asf1d.v2 = (void *)desc; 1098e2524b2eSHans Petter Selasky } else { 1099e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*asf1d.v1)) { 1100e2524b2eSHans Petter Selasky asf1d.v1 = (void *)desc; 1101e2524b2eSHans Petter Selasky 1102e2524b2eSHans Petter Selasky if (asf1d.v1->bFormatType != FORMAT_TYPE_I) { 11033a3f90c6SAndrew Thompson DPRINTFN(11, "ignored bFormatType = %d\n", 1104e2524b2eSHans Petter Selasky asf1d.v1->bFormatType); 1105e2524b2eSHans Petter Selasky asf1d.v1 = NULL; 11063a3f90c6SAndrew Thompson continue; 11073a3f90c6SAndrew Thompson } 1108e2524b2eSHans Petter Selasky if (desc->bLength < (sizeof(*asf1d.v1) + 1109e2524b2eSHans Petter Selasky ((asf1d.v1->bSamFreqType == 0) ? 6 : 1110e2524b2eSHans Petter Selasky (asf1d.v1->bSamFreqType * 3)))) { 1111e2524b2eSHans Petter Selasky DPRINTFN(11, "invalid descriptor, " 1112e2524b2eSHans Petter Selasky "too short\n"); 1113e2524b2eSHans Petter Selasky asf1d.v1 = NULL; 11143a3f90c6SAndrew Thompson continue; 11153a3f90c6SAndrew Thompson } 11163a3f90c6SAndrew Thompson } 11173a3f90c6SAndrew Thompson } 1118e2524b2eSHans Petter Selasky } 11193a3f90c6SAndrew Thompson if ((desc->bDescriptorType == UDESC_ENDPOINT) && 1120e2524b2eSHans Petter Selasky (desc->bLength >= UEP_MINSIZE) && 1121e2524b2eSHans Petter Selasky (ed1 == NULL)) { 11223a3f90c6SAndrew Thompson ed1 = (void *)desc; 11233a3f90c6SAndrew Thompson if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) { 11243a3f90c6SAndrew Thompson ed1 = NULL; 1125e2524b2eSHans Petter Selasky continue; 11263a3f90c6SAndrew Thompson } 11273a3f90c6SAndrew Thompson } 1128e2524b2eSHans Petter Selasky if ((acdp != NULL) && 1129e2524b2eSHans Petter Selasky (desc->bDescriptorType == UDESC_CS_ENDPOINT) && 11303a3f90c6SAndrew Thompson (desc->bDescriptorSubtype == AS_GENERAL) && 1131e2524b2eSHans Petter Selasky (sed.v1 == NULL)) { 1132e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 1133e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 1134e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 1135e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*sed.v2)) 1136e2524b2eSHans Petter Selasky sed.v2 = (void *)desc; 1137e2524b2eSHans Petter Selasky } else { 1138e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*sed.v1)) 1139e2524b2eSHans Petter Selasky sed.v1 = (void *)desc; 11403a3f90c6SAndrew Thompson } 11413a3f90c6SAndrew Thompson } 11421234097eSHans Petter Selasky if (asid.v1 == NULL || asf1d.v1 == NULL || 11431234097eSHans Petter Selasky ed1 == NULL || sed.v1 == NULL) { 1144e2524b2eSHans Petter Selasky /* need more descriptors */ 1145e2524b2eSHans Petter Selasky continue; 1146e2524b2eSHans Petter Selasky } 11473a3f90c6SAndrew Thompson 11483a3f90c6SAndrew Thompson ep_dir = UE_GET_DIR(ed1->bEndpointAddress); 11493a3f90c6SAndrew Thompson 1150e3e05e50SAndrew Thompson /* We ignore sync endpoint information until further. */ 11513a3f90c6SAndrew Thompson 1152e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 1153e2524b2eSHans Petter Selasky goto next_ep; 1154e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 11553a3f90c6SAndrew Thompson 1156e2524b2eSHans Petter Selasky uint32_t dwFormat; 1157e2524b2eSHans Petter Selasky uint8_t bSubslotSize; 11583a3f90c6SAndrew Thompson 1159e2524b2eSHans Petter Selasky dwFormat = UGETDW(asid.v2->bmFormats); 1160e2524b2eSHans Petter Selasky bChannels = asid.v2->bNrChannels; 1161e2524b2eSHans Petter Selasky bBitResolution = asf1d.v2->bBitResolution; 1162e2524b2eSHans Petter Selasky bSubslotSize = asf1d.v2->bSubslotSize; 1163e2524b2eSHans Petter Selasky 1164e2524b2eSHans Petter Selasky if (bBitResolution != (bSubslotSize * 8)) { 1165e2524b2eSHans Petter Selasky DPRINTF("Invalid bSubslotSize\n"); 1166e2524b2eSHans Petter Selasky goto next_ep; 1167e2524b2eSHans Petter Selasky } 1168e2524b2eSHans Petter Selasky 1169e2524b2eSHans Petter Selasky if ((bChannels != channels) || 1170e2524b2eSHans Petter Selasky (bBitResolution != bit_resolution)) { 1171e2524b2eSHans Petter Selasky DPRINTF("Wrong number of channels\n"); 1172e2524b2eSHans Petter Selasky goto next_ep; 1173e2524b2eSHans Petter Selasky } 1174e2524b2eSHans Petter Selasky 1175e2524b2eSHans Petter Selasky for (p_fmt = uaudio20_formats; 1176e2524b2eSHans Petter Selasky p_fmt->wFormat != 0; p_fmt++) { 1177e2524b2eSHans Petter Selasky if ((p_fmt->wFormat & dwFormat) && 1178e2524b2eSHans Petter Selasky (p_fmt->bPrecision == bBitResolution)) 1179e2524b2eSHans Petter Selasky break; 1180e2524b2eSHans Petter Selasky } 1181e2524b2eSHans Petter Selasky 1182e2524b2eSHans Petter Selasky if (p_fmt->wFormat == 0) { 1183e2524b2eSHans Petter Selasky DPRINTF("Unsupported audio format\n"); 1184e2524b2eSHans Petter Selasky goto next_ep; 1185e2524b2eSHans Petter Selasky } 1186e2524b2eSHans Petter Selasky 1187e2524b2eSHans Petter Selasky for (x = 0; x != 256; x++) { 1188e2524b2eSHans Petter Selasky if (ep_dir == UE_DIR_OUT) { 1189e2524b2eSHans Petter Selasky if (!(sc->sc_mixer_clocks.bit_output[x / 8] & 1190e2524b2eSHans Petter Selasky (1 << (x % 8)))) { 1191e2524b2eSHans Petter Selasky continue; 11923a3f90c6SAndrew Thompson } 11933a3f90c6SAndrew Thompson } else { 1194e2524b2eSHans Petter Selasky if (!(sc->sc_mixer_clocks.bit_input[x / 8] & 1195e2524b2eSHans Petter Selasky (1 << (x % 8)))) { 1196e2524b2eSHans Petter Selasky continue; 1197e2524b2eSHans Petter Selasky } 1198e2524b2eSHans Petter Selasky } 11993a3f90c6SAndrew Thompson 1200e2524b2eSHans Petter Selasky DPRINTF("Checking clock ID=%d\n", x); 1201e2524b2eSHans Petter Selasky 1202e2524b2eSHans Petter Selasky if (uaudio20_check_rate(udev, 1203e2524b2eSHans Petter Selasky sc->sc_mixer_iface_no, x, rate)) { 1204e2524b2eSHans Petter Selasky DPRINTF("Unsupported sampling " 1205e2524b2eSHans Petter Selasky "rate, id=%d\n", x); 1206e2524b2eSHans Petter Selasky goto next_ep; 1207e2524b2eSHans Petter Selasky } 1208e2524b2eSHans Petter Selasky } 1209e2524b2eSHans Petter Selasky } else { 1210e2524b2eSHans Petter Selasky uint16_t wFormat; 1211e2524b2eSHans Petter Selasky 1212e2524b2eSHans Petter Selasky wFormat = UGETW(asid.v1->wFormatTag); 1213e2524b2eSHans Petter Selasky bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels); 1214e2524b2eSHans Petter Selasky bBitResolution = asf1d.v1->bBitResolution; 1215e2524b2eSHans Petter Selasky 1216e2524b2eSHans Petter Selasky if (asf1d.v1->bSamFreqType == 0) { 1217e2524b2eSHans Petter Selasky DPRINTFN(16, "Sample rate: %d-%dHz\n", 1218e2524b2eSHans Petter Selasky UA_SAMP_LO(asf1d.v1), 1219e2524b2eSHans Petter Selasky UA_SAMP_HI(asf1d.v1)); 1220e2524b2eSHans Petter Selasky 1221e2524b2eSHans Petter Selasky if ((rate >= UA_SAMP_LO(asf1d.v1)) && 1222e2524b2eSHans Petter Selasky (rate <= UA_SAMP_HI(asf1d.v1))) 1223e2524b2eSHans Petter Selasky goto found_rate; 1224e2524b2eSHans Petter Selasky } else { 1225e2524b2eSHans Petter Selasky 1226e2524b2eSHans Petter Selasky for (x = 0; x < asf1d.v1->bSamFreqType; x++) { 12273a3f90c6SAndrew Thompson DPRINTFN(16, "Sample rate = %dHz\n", 1228e2524b2eSHans Petter Selasky UA_GETSAMP(asf1d.v1, x)); 12293a3f90c6SAndrew Thompson 1230e2524b2eSHans Petter Selasky if (rate == UA_GETSAMP(asf1d.v1, x)) 12313a3f90c6SAndrew Thompson goto found_rate; 12323a3f90c6SAndrew Thompson } 12333a3f90c6SAndrew Thompson } 1234e2524b2eSHans Petter Selasky goto next_ep; 12353a3f90c6SAndrew Thompson 12363a3f90c6SAndrew Thompson found_rate: 1237e2524b2eSHans Petter Selasky for (p_fmt = uaudio10_formats; 1238e2524b2eSHans Petter Selasky p_fmt->wFormat != 0; p_fmt++) { 12393a3f90c6SAndrew Thompson if ((p_fmt->wFormat == wFormat) && 1240e2524b2eSHans Petter Selasky (p_fmt->bPrecision == bBitResolution)) 1241e2524b2eSHans Petter Selasky break; 12423a3f90c6SAndrew Thompson } 1243e2524b2eSHans Petter Selasky if (p_fmt->wFormat == 0) { 1244e2524b2eSHans Petter Selasky DPRINTF("Unsupported audio format\n"); 1245e2524b2eSHans Petter Selasky goto next_ep; 12463a3f90c6SAndrew Thompson } 12473a3f90c6SAndrew Thompson 1248e2524b2eSHans Petter Selasky if ((bChannels != channels) || 1249e2524b2eSHans Petter Selasky (bBitResolution != bit_resolution)) { 1250e2524b2eSHans Petter Selasky DPRINTF("Wrong number of channels\n"); 1251e2524b2eSHans Petter Selasky goto next_ep; 1252e2524b2eSHans Petter Selasky } 1253e2524b2eSHans Petter Selasky } 12543a3f90c6SAndrew Thompson 12553a3f90c6SAndrew Thompson chan = (ep_dir == UE_DIR_IN) ? 1256e2524b2eSHans Petter Selasky &sc->sc_rec_chan : &sc->sc_play_chan; 12573a3f90c6SAndrew Thompson 1258e2524b2eSHans Petter Selasky if (chan->valid != 0 || 1259e2524b2eSHans Petter Selasky usbd_get_iface(udev, curidx) == NULL) { 1260e2524b2eSHans Petter Selasky DPRINTF("Channel already exists or " 1261e2524b2eSHans Petter Selasky "interface is not valid\n"); 1262e2524b2eSHans Petter Selasky goto next_ep; 1263e2524b2eSHans Petter Selasky } 12643a3f90c6SAndrew Thompson 12653a3f90c6SAndrew Thompson chan->valid = 1; 1266b850ecc1SAndrew Thompson #ifdef USB_DEBUG 12673a3f90c6SAndrew Thompson uaudio_chan_dump_ep_desc(ed1); 12683a3f90c6SAndrew Thompson #endif 12693a3f90c6SAndrew Thompson DPRINTF("Sample rate = %dHz, channels = %d, " 12703a3f90c6SAndrew Thompson "bits = %d, format = %s\n", rate, channels, 12713a3f90c6SAndrew Thompson bit_resolution, p_fmt->description); 12723a3f90c6SAndrew Thompson 12733a3f90c6SAndrew Thompson chan->sample_rate = rate; 12743a3f90c6SAndrew Thompson chan->p_asf1d = asf1d; 12753a3f90c6SAndrew Thompson chan->p_ed1 = ed1; 12763a3f90c6SAndrew Thompson chan->p_fmt = p_fmt; 12773a3f90c6SAndrew Thompson chan->p_sed = sed; 12783a3f90c6SAndrew Thompson chan->iface_index = curidx; 12793a3f90c6SAndrew Thompson chan->iface_alt_index = alt_index; 12803a3f90c6SAndrew Thompson 12813a3f90c6SAndrew Thompson if (ep_dir == UE_DIR_IN) 1282e2524b2eSHans Petter Selasky chan->usb_cfg = uaudio_cfg_record; 12833a3f90c6SAndrew Thompson else 1284e2524b2eSHans Petter Selasky chan->usb_cfg = uaudio_cfg_play; 12853a3f90c6SAndrew Thompson 1286e2524b2eSHans Petter Selasky chan->sample_size = (UAUDIO_MAX_CHAN(channels) * 1287e2524b2eSHans Petter Selasky p_fmt->bPrecision) / 8; 1288e2524b2eSHans Petter Selasky chan->channels = channels; 12893a3f90c6SAndrew Thompson 1290f895cc0cSHans Petter Selasky if (ep_dir == UE_DIR_IN && 1291f895cc0cSHans Petter Selasky usbd_get_speed(udev) == USB_SPEED_FULL) { 1292f895cc0cSHans Petter Selasky uaudio_record_fix_fs(ed1, 1293f895cc0cSHans Petter Selasky chan->sample_size * (rate / 1000), 1294f895cc0cSHans Petter Selasky chan->sample_size * (rate / 4000)); 1295f895cc0cSHans Petter Selasky } 1296f895cc0cSHans Petter Selasky 1297e2524b2eSHans Petter Selasky if (sc->sc_sndstat_valid != 0) { 12983a3f90c6SAndrew Thompson sbuf_printf(&sc->sc_sndstat, "\n\t" 1299e2524b2eSHans Petter Selasky "mode %d.%d:(%s) %dch, %dbit, %s, %dHz", 13003a3f90c6SAndrew Thompson curidx, alt_index, 13013a3f90c6SAndrew Thompson (ep_dir == UE_DIR_IN) ? "input" : "output", 1302e2524b2eSHans Petter Selasky channels, p_fmt->bPrecision, 13033a3f90c6SAndrew Thompson p_fmt->description, rate); 13043a3f90c6SAndrew Thompson } 1305e2524b2eSHans Petter Selasky 1306e2524b2eSHans Petter Selasky next_ep: 1307e2524b2eSHans Petter Selasky sed.v1 = NULL; 1308e2524b2eSHans Petter Selasky ed1 = NULL; 13093a3f90c6SAndrew Thompson } 13103a3f90c6SAndrew Thompson } 13113a3f90c6SAndrew Thompson 1312afbfddd9SAndrew Thompson /* This structure defines all the supported rates. */ 1313afbfddd9SAndrew Thompson 1314afbfddd9SAndrew Thompson static const uint32_t uaudio_rate_list[] = { 1315afbfddd9SAndrew Thompson 96000, 1316afbfddd9SAndrew Thompson 88000, 1317afbfddd9SAndrew Thompson 80000, 1318afbfddd9SAndrew Thompson 72000, 1319afbfddd9SAndrew Thompson 64000, 1320afbfddd9SAndrew Thompson 56000, 1321afbfddd9SAndrew Thompson 48000, 1322afbfddd9SAndrew Thompson 44100, 1323afbfddd9SAndrew Thompson 40000, 1324afbfddd9SAndrew Thompson 32000, 1325afbfddd9SAndrew Thompson 24000, 1326afbfddd9SAndrew Thompson 22050, 1327afbfddd9SAndrew Thompson 16000, 1328afbfddd9SAndrew Thompson 11025, 1329afbfddd9SAndrew Thompson 8000, 1330afbfddd9SAndrew Thompson 0 1331afbfddd9SAndrew Thompson }; 1332afbfddd9SAndrew Thompson 13333a3f90c6SAndrew Thompson static void 1334760bc48eSAndrew Thompson uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev) 13353a3f90c6SAndrew Thompson { 13363a3f90c6SAndrew Thompson uint32_t rate = uaudio_default_rate; 1337afbfddd9SAndrew Thompson uint8_t z; 13383a3f90c6SAndrew Thompson uint8_t bits = uaudio_default_bits; 13393a3f90c6SAndrew Thompson uint8_t y; 13403a3f90c6SAndrew Thompson uint8_t channels = uaudio_default_channels; 13413a3f90c6SAndrew Thompson uint8_t x; 13423a3f90c6SAndrew Thompson 13433a3f90c6SAndrew Thompson bits -= (bits % 8); 13443a3f90c6SAndrew Thompson if ((bits == 0) || (bits > 32)) { 13453a3f90c6SAndrew Thompson /* set a valid value */ 13463a3f90c6SAndrew Thompson bits = 32; 13473a3f90c6SAndrew Thompson } 1348afbfddd9SAndrew Thompson if (channels == 0) { 1349afbfddd9SAndrew Thompson switch (usbd_get_speed(udev)) { 1350afbfddd9SAndrew Thompson case USB_SPEED_LOW: 1351afbfddd9SAndrew Thompson case USB_SPEED_FULL: 1352afbfddd9SAndrew Thompson /* 1353afbfddd9SAndrew Thompson * Due to high bandwidth usage and problems 1354afbfddd9SAndrew Thompson * with HIGH-speed split transactions we 1355afbfddd9SAndrew Thompson * disable surround setups on FULL-speed USB 1356afbfddd9SAndrew Thompson * by default 1357afbfddd9SAndrew Thompson */ 13581234097eSHans Petter Selasky channels = 4; 1359afbfddd9SAndrew Thompson break; 1360afbfddd9SAndrew Thompson default: 1361afbfddd9SAndrew Thompson channels = 16; 1362afbfddd9SAndrew Thompson break; 1363afbfddd9SAndrew Thompson } 1364afbfddd9SAndrew Thompson } else if (channels > 16) { 1365afbfddd9SAndrew Thompson channels = 16; 13663a3f90c6SAndrew Thompson } 13673a3f90c6SAndrew Thompson if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) { 13683a3f90c6SAndrew Thompson sc->sc_sndstat_valid = 1; 13693a3f90c6SAndrew Thompson } 13703a3f90c6SAndrew Thompson /* try to search for a valid config */ 13713a3f90c6SAndrew Thompson 13723a3f90c6SAndrew Thompson for (x = channels; x; x--) { 13733a3f90c6SAndrew Thompson for (y = bits; y; y -= 8) { 1374afbfddd9SAndrew Thompson 1375afbfddd9SAndrew Thompson /* try user defined rate, if any */ 1376afbfddd9SAndrew Thompson if (rate != 0) 1377afbfddd9SAndrew Thompson uaudio_chan_fill_info_sub(sc, udev, rate, x, y); 1378afbfddd9SAndrew Thompson 1379afbfddd9SAndrew Thompson /* try find a matching rate, if any */ 1380afbfddd9SAndrew Thompson for (z = 0; uaudio_rate_list[z]; z++) { 1381afbfddd9SAndrew Thompson uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y); 13823a3f90c6SAndrew Thompson 13833a3f90c6SAndrew Thompson if (sc->sc_rec_chan.valid && 13843a3f90c6SAndrew Thompson sc->sc_play_chan.valid) { 13853a3f90c6SAndrew Thompson goto done; 13863a3f90c6SAndrew Thompson } 13873a3f90c6SAndrew Thompson } 13883a3f90c6SAndrew Thompson } 13893a3f90c6SAndrew Thompson } 13903a3f90c6SAndrew Thompson 13913a3f90c6SAndrew Thompson done: 13923a3f90c6SAndrew Thompson if (sc->sc_sndstat_valid) { 13933a3f90c6SAndrew Thompson sbuf_finish(&sc->sc_sndstat); 13943a3f90c6SAndrew Thompson } 13953a3f90c6SAndrew Thompson } 13963a3f90c6SAndrew Thompson 13973a3f90c6SAndrew Thompson static void 1398ed6d949aSAndrew Thompson uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error) 13993a3f90c6SAndrew Thompson { 1400ed6d949aSAndrew Thompson struct uaudio_chan *ch = usbd_xfer_softc(xfer); 1401ed6d949aSAndrew Thompson struct usb_page_cache *pc; 14023a3f90c6SAndrew Thompson uint32_t total; 14033a3f90c6SAndrew Thompson uint32_t blockcount; 14043a3f90c6SAndrew Thompson uint32_t n; 14053a3f90c6SAndrew Thompson uint32_t offset; 1406afbfddd9SAndrew Thompson int actlen; 1407afbfddd9SAndrew Thompson int sumlen; 1408ed6d949aSAndrew Thompson 1409ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 14103a3f90c6SAndrew Thompson 1411b029f6bbSAndrew Thompson if (ch->end == ch->start) { 1412b029f6bbSAndrew Thompson DPRINTF("no buffer!\n"); 1413b029f6bbSAndrew Thompson return; 1414b029f6bbSAndrew Thompson } 14153a3f90c6SAndrew Thompson 14163a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 14173a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 14183a3f90c6SAndrew Thompson tr_transferred: 1419ed6d949aSAndrew Thompson if (actlen < sumlen) { 14203a3f90c6SAndrew Thompson DPRINTF("short transfer, " 1421afbfddd9SAndrew Thompson "%d of %d bytes\n", actlen, sumlen); 14223a3f90c6SAndrew Thompson } 14233a3f90c6SAndrew Thompson chn_intr(ch->pcm_ch); 14243a3f90c6SAndrew Thompson 14253a3f90c6SAndrew Thompson case USB_ST_SETUP: 1426afbfddd9SAndrew Thompson if (ch->bytes_per_frame[1] > usbd_xfer_max_framelen(xfer)) { 14273a3f90c6SAndrew Thompson DPRINTF("bytes per transfer, %d, " 14283a3f90c6SAndrew Thompson "exceeds maximum, %d!\n", 1429afbfddd9SAndrew Thompson ch->bytes_per_frame[1], 1430ed6d949aSAndrew Thompson usbd_xfer_max_framelen(xfer)); 14313a3f90c6SAndrew Thompson break; 14323a3f90c6SAndrew Thompson } 1433afbfddd9SAndrew Thompson 1434afbfddd9SAndrew Thompson blockcount = ch->intr_frames; 1435afbfddd9SAndrew Thompson 1436afbfddd9SAndrew Thompson /* setup number of frames */ 1437ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, blockcount); 1438afbfddd9SAndrew Thompson 1439afbfddd9SAndrew Thompson /* reset total length */ 1440afbfddd9SAndrew Thompson total = 0; 1441afbfddd9SAndrew Thompson 1442afbfddd9SAndrew Thompson /* setup frame lengths */ 1443afbfddd9SAndrew Thompson for (n = 0; n != blockcount; n++) { 1444afbfddd9SAndrew Thompson ch->sample_curr += ch->sample_rem; 1445afbfddd9SAndrew Thompson if (ch->sample_curr >= ch->frames_per_second) { 1446afbfddd9SAndrew Thompson ch->sample_curr -= ch->frames_per_second; 1447afbfddd9SAndrew Thompson usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[1]); 1448afbfddd9SAndrew Thompson total += ch->bytes_per_frame[1]; 1449afbfddd9SAndrew Thompson } else { 1450afbfddd9SAndrew Thompson usbd_xfer_set_frame_len(xfer, n, ch->bytes_per_frame[0]); 1451afbfddd9SAndrew Thompson total += ch->bytes_per_frame[0]; 1452afbfddd9SAndrew Thompson } 1453afbfddd9SAndrew Thompson } 14543a3f90c6SAndrew Thompson 14553a3f90c6SAndrew Thompson DPRINTFN(6, "transfer %d bytes\n", total); 14563a3f90c6SAndrew Thompson 14573a3f90c6SAndrew Thompson offset = 0; 14583a3f90c6SAndrew Thompson 1459ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 14603a3f90c6SAndrew Thompson while (total > 0) { 14613a3f90c6SAndrew Thompson 14623a3f90c6SAndrew Thompson n = (ch->end - ch->cur); 14633a3f90c6SAndrew Thompson if (n > total) { 14643a3f90c6SAndrew Thompson n = total; 14653a3f90c6SAndrew Thompson } 1466ed6d949aSAndrew Thompson usbd_copy_in(pc, offset, ch->cur, n); 14673a3f90c6SAndrew Thompson 14683a3f90c6SAndrew Thompson total -= n; 14693a3f90c6SAndrew Thompson ch->cur += n; 14703a3f90c6SAndrew Thompson offset += n; 14713a3f90c6SAndrew Thompson 14723a3f90c6SAndrew Thompson if (ch->cur >= ch->end) { 14733a3f90c6SAndrew Thompson ch->cur = ch->start; 14743a3f90c6SAndrew Thompson } 14753a3f90c6SAndrew Thompson } 14763a3f90c6SAndrew Thompson 1477a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 14783a3f90c6SAndrew Thompson break; 14793a3f90c6SAndrew Thompson 14803a3f90c6SAndrew Thompson default: /* Error */ 1481ed6d949aSAndrew Thompson if (error == USB_ERR_CANCELLED) { 14823a3f90c6SAndrew Thompson break; 14833a3f90c6SAndrew Thompson } 14843a3f90c6SAndrew Thompson goto tr_transferred; 14853a3f90c6SAndrew Thompson } 14863a3f90c6SAndrew Thompson } 14873a3f90c6SAndrew Thompson 14883a3f90c6SAndrew Thompson static void 1489ed6d949aSAndrew Thompson uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error) 14903a3f90c6SAndrew Thompson { 1491ed6d949aSAndrew Thompson struct uaudio_chan *ch = usbd_xfer_softc(xfer); 1492ed6d949aSAndrew Thompson struct usb_page_cache *pc; 14933a3f90c6SAndrew Thompson uint32_t offset0; 14943a3f90c6SAndrew Thompson uint32_t offset1; 1495b029f6bbSAndrew Thompson uint32_t mfl; 14966d917491SHans Petter Selasky int m; 14976d917491SHans Petter Selasky int n; 1498ed6d949aSAndrew Thompson int len; 1499b029f6bbSAndrew Thompson int actlen; 1500b029f6bbSAndrew Thompson int nframes; 15016d917491SHans Petter Selasky int blockcount; 1502ed6d949aSAndrew Thompson 1503ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1504b029f6bbSAndrew Thompson mfl = usbd_xfer_max_framelen(xfer); 15053a3f90c6SAndrew Thompson 1506b029f6bbSAndrew Thompson if (ch->end == ch->start) { 1507b029f6bbSAndrew Thompson DPRINTF("no buffer!\n"); 1508b029f6bbSAndrew Thompson return; 1509b029f6bbSAndrew Thompson } 15103a3f90c6SAndrew Thompson 15113a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 15123a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 1513afbfddd9SAndrew Thompson 1514ed6d949aSAndrew Thompson DPRINTFN(6, "transferred %d bytes\n", actlen); 15153a3f90c6SAndrew Thompson 15163a3f90c6SAndrew Thompson offset0 = 0; 1517b029f6bbSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 15183a3f90c6SAndrew Thompson 1519ed6d949aSAndrew Thompson for (n = 0; n != nframes; n++) { 15203a3f90c6SAndrew Thompson 15213a3f90c6SAndrew Thompson offset1 = offset0; 15228f9e0ef9SAndrew Thompson len = usbd_xfer_frame_len(xfer, n); 15233a3f90c6SAndrew Thompson 1524ed6d949aSAndrew Thompson while (len > 0) { 15253a3f90c6SAndrew Thompson 15263a3f90c6SAndrew Thompson m = (ch->end - ch->cur); 15273a3f90c6SAndrew Thompson 15286d917491SHans Petter Selasky if (m > len) 1529ed6d949aSAndrew Thompson m = len; 15306d917491SHans Petter Selasky 1531ed6d949aSAndrew Thompson usbd_copy_out(pc, offset1, ch->cur, m); 15323a3f90c6SAndrew Thompson 1533ed6d949aSAndrew Thompson len -= m; 15343a3f90c6SAndrew Thompson offset1 += m; 15353a3f90c6SAndrew Thompson ch->cur += m; 15363a3f90c6SAndrew Thompson 15373a3f90c6SAndrew Thompson if (ch->cur >= ch->end) { 15383a3f90c6SAndrew Thompson ch->cur = ch->start; 15393a3f90c6SAndrew Thompson } 15403a3f90c6SAndrew Thompson } 15413a3f90c6SAndrew Thompson 1542b029f6bbSAndrew Thompson offset0 += mfl; 15433a3f90c6SAndrew Thompson } 15443a3f90c6SAndrew Thompson 15453a3f90c6SAndrew Thompson chn_intr(ch->pcm_ch); 15463a3f90c6SAndrew Thompson 15473a3f90c6SAndrew Thompson case USB_ST_SETUP: 1548b029f6bbSAndrew Thompson tr_setup: 1549afbfddd9SAndrew Thompson blockcount = ch->intr_frames; 1550afbfddd9SAndrew Thompson 1551ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, blockcount); 1552ed6d949aSAndrew Thompson for (n = 0; n < blockcount; n++) { 1553b029f6bbSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, mfl); 15543a3f90c6SAndrew Thompson } 15553a3f90c6SAndrew Thompson 1556a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 1557b029f6bbSAndrew Thompson break; 15583a3f90c6SAndrew Thompson 15593a3f90c6SAndrew Thompson default: /* Error */ 1560ed6d949aSAndrew Thompson if (error == USB_ERR_CANCELLED) { 1561b029f6bbSAndrew Thompson break; 15623a3f90c6SAndrew Thompson } 1563b029f6bbSAndrew Thompson goto tr_setup; 15643a3f90c6SAndrew Thompson } 15653a3f90c6SAndrew Thompson } 15663a3f90c6SAndrew Thompson 15673a3f90c6SAndrew Thompson void * 15683a3f90c6SAndrew Thompson uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, 15693a3f90c6SAndrew Thompson struct pcm_channel *c, int dir) 15703a3f90c6SAndrew Thompson { 15713a3f90c6SAndrew Thompson struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ? 15723a3f90c6SAndrew Thompson &sc->sc_play_chan : &sc->sc_rec_chan); 15733a3f90c6SAndrew Thompson uint32_t buf_size; 1574b029f6bbSAndrew Thompson uint32_t frames; 1575afbfddd9SAndrew Thompson uint32_t format; 1576afbfddd9SAndrew Thompson uint16_t fps; 15773a3f90c6SAndrew Thompson uint8_t endpoint; 1578b029f6bbSAndrew Thompson uint8_t blocks; 15793a3f90c6SAndrew Thompson uint8_t iface_index; 15803a3f90c6SAndrew Thompson uint8_t alt_index; 1581b029f6bbSAndrew Thompson uint8_t fps_shift; 1582e0a69b51SAndrew Thompson usb_error_t err; 15833a3f90c6SAndrew Thompson 1584afbfddd9SAndrew Thompson fps = usbd_get_isoc_fps(sc->sc_udev); 1585afbfddd9SAndrew Thompson 1586afbfddd9SAndrew Thompson if (fps < 8000) { 1587b029f6bbSAndrew Thompson /* FULL speed USB */ 1588b029f6bbSAndrew Thompson frames = 8; 1589b029f6bbSAndrew Thompson } else { 1590b029f6bbSAndrew Thompson /* HIGH speed USB */ 1591b029f6bbSAndrew Thompson frames = UAUDIO_NFRAMES; 1592b029f6bbSAndrew Thompson } 1593b029f6bbSAndrew Thompson 15943a3f90c6SAndrew Thompson /* setup play/record format */ 15953a3f90c6SAndrew Thompson 15963a3f90c6SAndrew Thompson ch->pcm_cap.fmtlist = ch->pcm_format; 15973a3f90c6SAndrew Thompson 15983a3f90c6SAndrew Thompson ch->pcm_format[0] = 0; 15993a3f90c6SAndrew Thompson ch->pcm_format[1] = 0; 16003a3f90c6SAndrew Thompson 16013a3f90c6SAndrew Thompson ch->pcm_cap.minspeed = ch->sample_rate; 16023a3f90c6SAndrew Thompson ch->pcm_cap.maxspeed = ch->sample_rate; 16033a3f90c6SAndrew Thompson 16047fb43570SAndrew Thompson /* setup mutex and PCM channel */ 16057fb43570SAndrew Thompson 16067fb43570SAndrew Thompson ch->pcm_ch = c; 16077fb43570SAndrew Thompson ch->pcm_mtx = c->lock; 16087fb43570SAndrew Thompson 1609afbfddd9SAndrew Thompson format = ch->p_fmt->freebsd_fmt; 16103a3f90c6SAndrew Thompson 1611e2524b2eSHans Petter Selasky switch (ch->channels) { 1612afbfddd9SAndrew Thompson case 2: 1613afbfddd9SAndrew Thompson /* stereo */ 1614afbfddd9SAndrew Thompson format = SND_FORMAT(format, 2, 0); 1615afbfddd9SAndrew Thompson break; 1616afbfddd9SAndrew Thompson case 1: 1617afbfddd9SAndrew Thompson /* mono */ 1618afbfddd9SAndrew Thompson format = SND_FORMAT(format, 1, 0); 1619afbfddd9SAndrew Thompson break; 1620afbfddd9SAndrew Thompson default: 1621afbfddd9SAndrew Thompson /* surround and more */ 1622afbfddd9SAndrew Thompson format = feeder_matrix_default_format( 1623e2524b2eSHans Petter Selasky SND_FORMAT(format, ch->channels, 0)); 1624afbfddd9SAndrew Thompson break; 1625afbfddd9SAndrew Thompson } 1626afbfddd9SAndrew Thompson 1627afbfddd9SAndrew Thompson ch->pcm_cap.fmtlist[0] = format; 16283a3f90c6SAndrew Thompson ch->pcm_cap.fmtlist[1] = 0; 16293a3f90c6SAndrew Thompson 1630afbfddd9SAndrew Thompson /* check if format is not supported */ 1631afbfddd9SAndrew Thompson 1632afbfddd9SAndrew Thompson if (format == 0) { 1633afbfddd9SAndrew Thompson DPRINTF("The selected audio format is not supported\n"); 1634afbfddd9SAndrew Thompson goto error; 1635afbfddd9SAndrew Thompson } 1636afbfddd9SAndrew Thompson 16373a3f90c6SAndrew Thompson /* set alternate interface corresponding to the mode */ 16383a3f90c6SAndrew Thompson 16393a3f90c6SAndrew Thompson endpoint = ch->p_ed1->bEndpointAddress; 16403a3f90c6SAndrew Thompson iface_index = ch->iface_index; 16413a3f90c6SAndrew Thompson alt_index = ch->iface_alt_index; 16423a3f90c6SAndrew Thompson 16433a3f90c6SAndrew Thompson DPRINTF("endpoint=0x%02x, speed=%d, iface=%d alt=%d\n", 16443a3f90c6SAndrew Thompson endpoint, ch->sample_rate, iface_index, alt_index); 16453a3f90c6SAndrew Thompson 1646a593f6b8SAndrew Thompson err = usbd_set_alt_interface_index(sc->sc_udev, iface_index, alt_index); 16473a3f90c6SAndrew Thompson if (err) { 16483a3f90c6SAndrew Thompson DPRINTF("setting of alternate index failed: %s!\n", 1649a593f6b8SAndrew Thompson usbd_errstr(err)); 16503a3f90c6SAndrew Thompson goto error; 16513a3f90c6SAndrew Thompson } 165225b74dabSHans Petter Selasky usbd_set_parent_iface(sc->sc_udev, iface_index, 165325b74dabSHans Petter Selasky sc->sc_mixer_iface_index); 16543a3f90c6SAndrew Thompson 16553a3f90c6SAndrew Thompson /* 1656aca2249dSHans Petter Selasky * Only set the sample rate if the channel reports that it 1657aca2249dSHans Petter Selasky * supports the frequency control. 16583a3f90c6SAndrew Thompson */ 1659e2524b2eSHans Petter Selasky 1660e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { 1661e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 1662e2524b2eSHans Petter Selasky } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { 1663e2524b2eSHans Petter Selasky unsigned int x; 1664e2524b2eSHans Petter Selasky 1665e2524b2eSHans Petter Selasky for (x = 0; x != 256; x++) { 1666e2524b2eSHans Petter Selasky if (dir == PCMDIR_PLAY) { 1667e2524b2eSHans Petter Selasky if (!(sc->sc_mixer_clocks.bit_output[x / 8] & 1668e2524b2eSHans Petter Selasky (1 << (x % 8)))) { 1669e2524b2eSHans Petter Selasky continue; 1670e2524b2eSHans Petter Selasky } 1671e2524b2eSHans Petter Selasky } else { 1672e2524b2eSHans Petter Selasky if (!(sc->sc_mixer_clocks.bit_input[x / 8] & 1673e2524b2eSHans Petter Selasky (1 << (x % 8)))) { 1674e2524b2eSHans Petter Selasky continue; 1675e2524b2eSHans Petter Selasky } 1676e2524b2eSHans Petter Selasky } 1677e2524b2eSHans Petter Selasky 1678e2524b2eSHans Petter Selasky if (uaudio20_set_speed(sc->sc_udev, 1679e2524b2eSHans Petter Selasky sc->sc_mixer_iface_no, x, ch->sample_rate)) { 1680e2524b2eSHans Petter Selasky /* 1681e2524b2eSHans Petter Selasky * If the endpoint is adaptive setting 1682e2524b2eSHans Petter Selasky * the speed may fail. 1683e2524b2eSHans Petter Selasky */ 1684e2524b2eSHans Petter Selasky DPRINTF("setting of sample rate failed! " 1685e2524b2eSHans Petter Selasky "(continuing anyway)\n"); 1686e2524b2eSHans Petter Selasky } 1687e2524b2eSHans Petter Selasky } 1688e2524b2eSHans Petter Selasky } else if (ch->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) { 16893a3f90c6SAndrew Thompson if (uaudio_set_speed(sc->sc_udev, endpoint, ch->sample_rate)) { 16903a3f90c6SAndrew Thompson /* 1691e2524b2eSHans Petter Selasky * If the endpoint is adaptive setting the 1692e2524b2eSHans Petter Selasky * speed may fail. 16933a3f90c6SAndrew Thompson */ 1694e2524b2eSHans Petter Selasky DPRINTF("setting of sample rate failed! " 1695e2524b2eSHans Petter Selasky "(continuing anyway)\n"); 16963a3f90c6SAndrew Thompson } 16973a3f90c6SAndrew Thompson } 1698a593f6b8SAndrew Thompson if (usbd_transfer_setup(sc->sc_udev, &iface_index, ch->xfer, 16994c21be9bSRebecca Cran ch->usb_cfg, UAUDIO_NCHANBUFS, ch, ch->pcm_mtx)) { 17003a3f90c6SAndrew Thompson DPRINTF("could not allocate USB transfers!\n"); 17013a3f90c6SAndrew Thompson goto error; 17023a3f90c6SAndrew Thompson } 1703b029f6bbSAndrew Thompson 1704b029f6bbSAndrew Thompson fps_shift = usbd_xfer_get_fps_shift(ch->xfer[0]); 1705b029f6bbSAndrew Thompson 1706afbfddd9SAndrew Thompson /* down shift number of frames per second, if any */ 1707afbfddd9SAndrew Thompson fps >>= fps_shift; 1708afbfddd9SAndrew Thompson frames >>= fps_shift; 1709afbfddd9SAndrew Thompson 1710afbfddd9SAndrew Thompson /* bytes per frame should not be zero */ 1711afbfddd9SAndrew Thompson ch->bytes_per_frame[0] = ((ch->sample_rate / fps) * ch->sample_size); 1712afbfddd9SAndrew Thompson ch->bytes_per_frame[1] = (((ch->sample_rate + fps - 1) / fps) * ch->sample_size); 1713afbfddd9SAndrew Thompson 1714afbfddd9SAndrew Thompson /* setup data rate dithering, if any */ 1715afbfddd9SAndrew Thompson ch->frames_per_second = fps; 1716afbfddd9SAndrew Thompson ch->sample_rem = ch->sample_rate % fps; 1717afbfddd9SAndrew Thompson ch->sample_curr = 0; 1718afbfddd9SAndrew Thompson ch->frames_per_second = fps; 1719afbfddd9SAndrew Thompson 1720afbfddd9SAndrew Thompson /* compute required buffer size */ 1721afbfddd9SAndrew Thompson buf_size = (ch->bytes_per_frame[1] * frames); 1722afbfddd9SAndrew Thompson 1723b029f6bbSAndrew Thompson ch->intr_size = buf_size; 1724afbfddd9SAndrew Thompson ch->intr_frames = frames; 1725afbfddd9SAndrew Thompson 1726afbfddd9SAndrew Thompson DPRINTF("fps=%d sample_rem=%d\n", fps, ch->sample_rem); 1727b029f6bbSAndrew Thompson 1728b029f6bbSAndrew Thompson if (ch->intr_frames == 0) { 1729b029f6bbSAndrew Thompson DPRINTF("frame shift is too high!\n"); 1730b029f6bbSAndrew Thompson goto error; 1731b029f6bbSAndrew Thompson } 1732b029f6bbSAndrew Thompson 1733b029f6bbSAndrew Thompson /* setup double buffering */ 1734b029f6bbSAndrew Thompson buf_size *= 2; 1735b029f6bbSAndrew Thompson blocks = 2; 1736b029f6bbSAndrew Thompson 1737b029f6bbSAndrew Thompson ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO); 1738b029f6bbSAndrew Thompson if (ch->buf == NULL) 1739b029f6bbSAndrew Thompson goto error; 1740b029f6bbSAndrew Thompson if (sndbuf_setup(b, ch->buf, buf_size) != 0) 1741b029f6bbSAndrew Thompson goto error; 1742b029f6bbSAndrew Thompson if (sndbuf_resize(b, blocks, ch->intr_size)) 1743b029f6bbSAndrew Thompson goto error; 1744b029f6bbSAndrew Thompson 1745b029f6bbSAndrew Thompson ch->start = ch->buf; 1746b029f6bbSAndrew Thompson ch->end = ch->buf + buf_size; 1747b029f6bbSAndrew Thompson ch->cur = ch->buf; 1748b029f6bbSAndrew Thompson ch->pcm_buf = b; 1749b029f6bbSAndrew Thompson 1750b029f6bbSAndrew Thompson if (ch->pcm_mtx == NULL) { 1751b029f6bbSAndrew Thompson DPRINTF("ERROR: PCM channels does not have a mutex!\n"); 1752b029f6bbSAndrew Thompson goto error; 1753b029f6bbSAndrew Thompson } 1754b029f6bbSAndrew Thompson 17553a3f90c6SAndrew Thompson return (ch); 17563a3f90c6SAndrew Thompson 17573a3f90c6SAndrew Thompson error: 17583a3f90c6SAndrew Thompson uaudio_chan_free(ch); 17593a3f90c6SAndrew Thompson return (NULL); 17603a3f90c6SAndrew Thompson } 17613a3f90c6SAndrew Thompson 17623a3f90c6SAndrew Thompson int 17633a3f90c6SAndrew Thompson uaudio_chan_free(struct uaudio_chan *ch) 17643a3f90c6SAndrew Thompson { 17653a3f90c6SAndrew Thompson if (ch->buf != NULL) { 17663a3f90c6SAndrew Thompson free(ch->buf, M_DEVBUF); 17673a3f90c6SAndrew Thompson ch->buf = NULL; 17683a3f90c6SAndrew Thompson } 1769a593f6b8SAndrew Thompson usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS); 17703a3f90c6SAndrew Thompson 17713a3f90c6SAndrew Thompson ch->valid = 0; 17723a3f90c6SAndrew Thompson 17733a3f90c6SAndrew Thompson return (0); 17743a3f90c6SAndrew Thompson } 17753a3f90c6SAndrew Thompson 17763a3f90c6SAndrew Thompson int 17773a3f90c6SAndrew Thompson uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize) 17783a3f90c6SAndrew Thompson { 1779b029f6bbSAndrew Thompson return (ch->intr_size); 17803a3f90c6SAndrew Thompson } 17813a3f90c6SAndrew Thompson 17823a3f90c6SAndrew Thompson int 17833a3f90c6SAndrew Thompson uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize, 17843a3f90c6SAndrew Thompson uint32_t blockcount) 17853a3f90c6SAndrew Thompson { 17863a3f90c6SAndrew Thompson return (1); 17873a3f90c6SAndrew Thompson } 17883a3f90c6SAndrew Thompson 17893a3f90c6SAndrew Thompson int 17903a3f90c6SAndrew Thompson uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed) 17913a3f90c6SAndrew Thompson { 17923a3f90c6SAndrew Thompson if (speed != ch->sample_rate) { 17933a3f90c6SAndrew Thompson DPRINTF("rate conversion required\n"); 17943a3f90c6SAndrew Thompson } 17953a3f90c6SAndrew Thompson return (ch->sample_rate); 17963a3f90c6SAndrew Thompson } 17973a3f90c6SAndrew Thompson 17983a3f90c6SAndrew Thompson int 17993a3f90c6SAndrew Thompson uaudio_chan_getptr(struct uaudio_chan *ch) 18003a3f90c6SAndrew Thompson { 18013a3f90c6SAndrew Thompson return (ch->cur - ch->start); 18023a3f90c6SAndrew Thompson } 18033a3f90c6SAndrew Thompson 18043a3f90c6SAndrew Thompson struct pcmchan_caps * 18053a3f90c6SAndrew Thompson uaudio_chan_getcaps(struct uaudio_chan *ch) 18063a3f90c6SAndrew Thompson { 18073a3f90c6SAndrew Thompson return (&ch->pcm_cap); 18083a3f90c6SAndrew Thompson } 18093a3f90c6SAndrew Thompson 181090da2b28SAriff Abdullah static struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = { 181190da2b28SAriff Abdullah .id = SND_CHN_MATRIX_DRV, 181290da2b28SAriff Abdullah .channels = 2, 181390da2b28SAriff Abdullah .ext = 0, 181490da2b28SAriff Abdullah .map = { 181590da2b28SAriff Abdullah /* Right */ 181690da2b28SAriff Abdullah [0] = { 181790da2b28SAriff Abdullah .type = SND_CHN_T_FR, 181890da2b28SAriff Abdullah .members = 181990da2b28SAriff Abdullah SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | 182090da2b28SAriff Abdullah SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | 182190da2b28SAriff Abdullah SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR 182290da2b28SAriff Abdullah }, 182390da2b28SAriff Abdullah /* Left */ 182490da2b28SAriff Abdullah [1] = { 182590da2b28SAriff Abdullah .type = SND_CHN_T_FL, 182690da2b28SAriff Abdullah .members = 182790da2b28SAriff Abdullah SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | 182890da2b28SAriff Abdullah SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | 182990da2b28SAriff Abdullah SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL 183090da2b28SAriff Abdullah }, 183190da2b28SAriff Abdullah [2] = { 183290da2b28SAriff Abdullah .type = SND_CHN_T_MAX, 183390da2b28SAriff Abdullah .members = 0 183490da2b28SAriff Abdullah } 183590da2b28SAriff Abdullah }, 183690da2b28SAriff Abdullah .mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL, 183790da2b28SAriff Abdullah .offset = { 1, 0, -1, -1, -1, -1, -1, -1, -1, 183890da2b28SAriff Abdullah -1, -1, -1, -1, -1, -1, -1, -1, -1 } 183990da2b28SAriff Abdullah }; 184090da2b28SAriff Abdullah 184190da2b28SAriff Abdullah struct pcmchan_matrix * 184290da2b28SAriff Abdullah uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format) 184390da2b28SAriff Abdullah { 184490da2b28SAriff Abdullah struct uaudio_softc *sc; 184590da2b28SAriff Abdullah 184690da2b28SAriff Abdullah sc = ch->priv_sc; 184790da2b28SAriff Abdullah 184890da2b28SAriff Abdullah if (sc != NULL && sc->sc_uq_audio_swap_lr != 0 && 184990da2b28SAriff Abdullah AFMT_CHANNEL(format) == 2) 185090da2b28SAriff Abdullah return (&uaudio_chan_matrix_swap_2_0); 185190da2b28SAriff Abdullah 185290da2b28SAriff Abdullah return (feeder_matrix_format_map(format)); 185390da2b28SAriff Abdullah } 185490da2b28SAriff Abdullah 18553a3f90c6SAndrew Thompson int 18563a3f90c6SAndrew Thompson uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format) 18573a3f90c6SAndrew Thompson { 18583a3f90c6SAndrew Thompson ch->format = format; 18593a3f90c6SAndrew Thompson return (0); 18603a3f90c6SAndrew Thompson } 18613a3f90c6SAndrew Thompson 18623a3f90c6SAndrew Thompson int 18633a3f90c6SAndrew Thompson uaudio_chan_start(struct uaudio_chan *ch) 18643a3f90c6SAndrew Thompson { 18653a3f90c6SAndrew Thompson ch->cur = ch->start; 18663a3f90c6SAndrew Thompson 18673a3f90c6SAndrew Thompson #if (UAUDIO_NCHANBUFS != 2) 18683a3f90c6SAndrew Thompson #error "please update code" 18693a3f90c6SAndrew Thompson #endif 18703a3f90c6SAndrew Thompson if (ch->xfer[0]) { 1871a593f6b8SAndrew Thompson usbd_transfer_start(ch->xfer[0]); 18723a3f90c6SAndrew Thompson } 18733a3f90c6SAndrew Thompson if (ch->xfer[1]) { 1874a593f6b8SAndrew Thompson usbd_transfer_start(ch->xfer[1]); 18753a3f90c6SAndrew Thompson } 18763a3f90c6SAndrew Thompson return (0); 18773a3f90c6SAndrew Thompson } 18783a3f90c6SAndrew Thompson 18793a3f90c6SAndrew Thompson int 18803a3f90c6SAndrew Thompson uaudio_chan_stop(struct uaudio_chan *ch) 18813a3f90c6SAndrew Thompson { 18823a3f90c6SAndrew Thompson #if (UAUDIO_NCHANBUFS != 2) 18833a3f90c6SAndrew Thompson #error "please update code" 18843a3f90c6SAndrew Thompson #endif 1885a593f6b8SAndrew Thompson usbd_transfer_stop(ch->xfer[0]); 1886a593f6b8SAndrew Thompson usbd_transfer_stop(ch->xfer[1]); 18873a3f90c6SAndrew Thompson return (0); 18883a3f90c6SAndrew Thompson } 18893a3f90c6SAndrew Thompson 18903a3f90c6SAndrew Thompson /*========================================================================* 18913a3f90c6SAndrew Thompson * AC - Audio Controller - routines 18923a3f90c6SAndrew Thompson *========================================================================*/ 18933a3f90c6SAndrew Thompson 18943a3f90c6SAndrew Thompson static void 18953a3f90c6SAndrew Thompson uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) 18963a3f90c6SAndrew Thompson { 18973a3f90c6SAndrew Thompson struct uaudio_mixer_node *p_mc_new = 18983a3f90c6SAndrew Thompson malloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK); 18993a3f90c6SAndrew Thompson 19006f068a43SHans Petter Selasky if (p_mc_new != NULL) { 19016f068a43SHans Petter Selasky memcpy(p_mc_new, mc, sizeof(*p_mc_new)); 19023a3f90c6SAndrew Thompson p_mc_new->next = sc->sc_mixer_root; 19033a3f90c6SAndrew Thompson sc->sc_mixer_root = p_mc_new; 19043a3f90c6SAndrew Thompson sc->sc_mixer_count++; 19053a3f90c6SAndrew Thompson } else { 19063a3f90c6SAndrew Thompson DPRINTF("out of memory\n"); 19073a3f90c6SAndrew Thompson } 19083a3f90c6SAndrew Thompson } 19093a3f90c6SAndrew Thompson 19103a3f90c6SAndrew Thompson static void 19113a3f90c6SAndrew Thompson uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) 19123a3f90c6SAndrew Thompson { 19133a3f90c6SAndrew Thompson int32_t res; 19143a3f90c6SAndrew Thompson 19153a3f90c6SAndrew Thompson if (mc->class < UAC_NCLASSES) { 19163a3f90c6SAndrew Thompson DPRINTF("adding %s.%d\n", 19173a3f90c6SAndrew Thompson uac_names[mc->class], mc->ctl); 19183a3f90c6SAndrew Thompson } else { 19193a3f90c6SAndrew Thompson DPRINTF("adding %d\n", mc->ctl); 19203a3f90c6SAndrew Thompson } 19213a3f90c6SAndrew Thompson 19223a3f90c6SAndrew Thompson if (mc->type == MIX_ON_OFF) { 19233a3f90c6SAndrew Thompson mc->minval = 0; 19243a3f90c6SAndrew Thompson mc->maxval = 1; 19253a3f90c6SAndrew Thompson } else if (mc->type == MIX_SELECTOR) { 19263a3f90c6SAndrew Thompson } else { 19273a3f90c6SAndrew Thompson 19283a3f90c6SAndrew Thompson /* determine min and max values */ 19293a3f90c6SAndrew Thompson 1930e2524b2eSHans Petter Selasky mc->minval = uaudio_mixer_get(sc->sc_udev, 1931e2524b2eSHans Petter Selasky sc->sc_audio_rev, GET_MIN, mc); 1932e2524b2eSHans Petter Selasky mc->maxval = uaudio_mixer_get(sc->sc_udev, 1933e2524b2eSHans Petter Selasky sc->sc_audio_rev, GET_MAX, mc); 19343a3f90c6SAndrew Thompson 1935b029f6bbSAndrew Thompson /* check if max and min was swapped */ 19363a3f90c6SAndrew Thompson 19373a3f90c6SAndrew Thompson if (mc->maxval < mc->minval) { 1938b029f6bbSAndrew Thompson res = mc->maxval; 19393a3f90c6SAndrew Thompson mc->maxval = mc->minval; 1940b029f6bbSAndrew Thompson mc->minval = res; 19413a3f90c6SAndrew Thompson } 1942b029f6bbSAndrew Thompson 1943b029f6bbSAndrew Thompson /* compute value range */ 1944b029f6bbSAndrew Thompson mc->mul = mc->maxval - mc->minval; 1945b029f6bbSAndrew Thompson if (mc->mul == 0) 1946b029f6bbSAndrew Thompson mc->mul = 1; 1947b029f6bbSAndrew Thompson 1948b029f6bbSAndrew Thompson /* compute value alignment */ 1949e2524b2eSHans Petter Selasky res = uaudio_mixer_get(sc->sc_udev, 1950e2524b2eSHans Petter Selasky sc->sc_audio_rev, GET_RES, mc); 19517fb43570SAndrew Thompson 19527fb43570SAndrew Thompson DPRINTF("Resolution = %d\n", (int)res); 1953b029f6bbSAndrew Thompson } 1954b029f6bbSAndrew Thompson 19553a3f90c6SAndrew Thompson uaudio_mixer_add_ctl_sub(sc, mc); 19563a3f90c6SAndrew Thompson 1957b850ecc1SAndrew Thompson #ifdef USB_DEBUG 19583a3f90c6SAndrew Thompson if (uaudio_debug > 2) { 19593a3f90c6SAndrew Thompson uint8_t i; 19603a3f90c6SAndrew Thompson 19613a3f90c6SAndrew Thompson for (i = 0; i < mc->nchan; i++) { 19623a3f90c6SAndrew Thompson DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]); 19633a3f90c6SAndrew Thompson } 19643a3f90c6SAndrew Thompson DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' " 19653a3f90c6SAndrew Thompson "min=%d max=%d\n", 19663a3f90c6SAndrew Thompson mc->wIndex, mc->type, mc->ctl, 19673a3f90c6SAndrew Thompson mc->minval, mc->maxval); 19683a3f90c6SAndrew Thompson } 19693a3f90c6SAndrew Thompson #endif 19703a3f90c6SAndrew Thompson } 19713a3f90c6SAndrew Thompson 19723a3f90c6SAndrew Thompson static void 19733a3f90c6SAndrew Thompson uaudio_mixer_add_mixer(struct uaudio_softc *sc, 19743a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 19753a3f90c6SAndrew Thompson { 19763a3f90c6SAndrew Thompson struct uaudio_mixer_node mix; 19773a3f90c6SAndrew Thompson 1978e2524b2eSHans Petter Selasky const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu_v1; 19794c21be9bSRebecca Cran const struct usb_audio_mixer_unit_1 *d1; 19803a3f90c6SAndrew Thompson 19813a3f90c6SAndrew Thompson uint32_t bno; /* bit number */ 19823a3f90c6SAndrew Thompson uint32_t p; /* bit number accumulator */ 19833a3f90c6SAndrew Thompson uint32_t mo; /* matching outputs */ 19843a3f90c6SAndrew Thompson uint32_t mc; /* matching channels */ 19853a3f90c6SAndrew Thompson uint32_t ichs; /* input channels */ 19863a3f90c6SAndrew Thompson uint32_t ochs; /* output channels */ 19873a3f90c6SAndrew Thompson uint32_t c; 19883a3f90c6SAndrew Thompson uint32_t chs; /* channels */ 19893a3f90c6SAndrew Thompson uint32_t i; 19903a3f90c6SAndrew Thompson uint32_t o; 19913a3f90c6SAndrew Thompson 19923a3f90c6SAndrew Thompson DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 19933a3f90c6SAndrew Thompson d0->bUnitId, d0->bNrInPins); 19943a3f90c6SAndrew Thompson 19953a3f90c6SAndrew Thompson /* compute the number of input channels */ 19963a3f90c6SAndrew Thompson 19973a3f90c6SAndrew Thompson ichs = 0; 19983a3f90c6SAndrew Thompson for (i = 0; i < d0->bNrInPins; i++) { 1999e2524b2eSHans Petter Selasky ichs += uaudio_mixer_get_cluster( 2000e2524b2eSHans Petter Selasky d0->baSourceId[i], iot).bNrChannels; 20013a3f90c6SAndrew Thompson } 20023a3f90c6SAndrew Thompson 20033a3f90c6SAndrew Thompson d1 = (const void *)(d0->baSourceId + d0->bNrInPins); 20043a3f90c6SAndrew Thompson 20053a3f90c6SAndrew Thompson /* and the number of output channels */ 20063a3f90c6SAndrew Thompson 20073a3f90c6SAndrew Thompson ochs = d1->bNrChannels; 20083a3f90c6SAndrew Thompson 20093a3f90c6SAndrew Thompson DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs); 20103a3f90c6SAndrew Thompson 20116f068a43SHans Petter Selasky memset(&mix, 0, sizeof(mix)); 20123a3f90c6SAndrew Thompson 20133a3f90c6SAndrew Thompson mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 20143a3f90c6SAndrew Thompson uaudio_mixer_determine_class(&iot[id], &mix); 20153a3f90c6SAndrew Thompson mix.type = MIX_SIGNED_16; 20163a3f90c6SAndrew Thompson 2017e2524b2eSHans Petter Selasky if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) 20183a3f90c6SAndrew Thompson return; 2019e2524b2eSHans Petter Selasky 20203a3f90c6SAndrew Thompson for (p = i = 0; i < d0->bNrInPins; i++) { 2021e2524b2eSHans Petter Selasky chs = uaudio_mixer_get_cluster( 2022e2524b2eSHans Petter Selasky d0->baSourceId[i], iot).bNrChannels; 20233a3f90c6SAndrew Thompson mc = 0; 20243a3f90c6SAndrew Thompson for (c = 0; c < chs; c++) { 20253a3f90c6SAndrew Thompson mo = 0; 20263a3f90c6SAndrew Thompson for (o = 0; o < ochs; o++) { 20273a3f90c6SAndrew Thompson bno = ((p + c) * ochs) + o; 2028e2524b2eSHans Petter Selasky if (BIT_TEST(d1->bmControls, bno)) 20293a3f90c6SAndrew Thompson mo++; 20303a3f90c6SAndrew Thompson } 2031e2524b2eSHans Petter Selasky if (mo == 1) 20323a3f90c6SAndrew Thompson mc++; 20333a3f90c6SAndrew Thompson } 20343a3f90c6SAndrew Thompson if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { 20353a3f90c6SAndrew Thompson 20363a3f90c6SAndrew Thompson /* repeat bit-scan */ 20373a3f90c6SAndrew Thompson 20383a3f90c6SAndrew Thompson mc = 0; 20393a3f90c6SAndrew Thompson for (c = 0; c < chs; c++) { 20403a3f90c6SAndrew Thompson for (o = 0; o < ochs; o++) { 20413a3f90c6SAndrew Thompson bno = ((p + c) * ochs) + o; 2042e2524b2eSHans Petter Selasky if (BIT_TEST(d1->bmControls, bno)) 20433a3f90c6SAndrew Thompson mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); 20443a3f90c6SAndrew Thompson } 20453a3f90c6SAndrew Thompson } 20463a3f90c6SAndrew Thompson mix.nchan = chs; 20473a3f90c6SAndrew Thompson uaudio_mixer_add_ctl(sc, &mix); 2048e2524b2eSHans Petter Selasky } 2049e2524b2eSHans Petter Selasky p += chs; 2050e2524b2eSHans Petter Selasky } 2051e2524b2eSHans Petter Selasky } 2052e2524b2eSHans Petter Selasky 2053e2524b2eSHans Petter Selasky static void 2054e2524b2eSHans Petter Selasky uaudio20_mixer_add_mixer(struct uaudio_softc *sc, 2055e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *iot, int id) 2056e2524b2eSHans Petter Selasky { 2057e2524b2eSHans Petter Selasky struct uaudio_mixer_node mix; 2058e2524b2eSHans Petter Selasky 2059e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_0 *d0 = iot[id].u.mu_v2; 2060e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_1 *d1; 2061e2524b2eSHans Petter Selasky 2062e2524b2eSHans Petter Selasky uint32_t bno; /* bit number */ 2063e2524b2eSHans Petter Selasky uint32_t p; /* bit number accumulator */ 2064e2524b2eSHans Petter Selasky uint32_t mo; /* matching outputs */ 2065e2524b2eSHans Petter Selasky uint32_t mc; /* matching channels */ 2066e2524b2eSHans Petter Selasky uint32_t ichs; /* input channels */ 2067e2524b2eSHans Petter Selasky uint32_t ochs; /* output channels */ 2068e2524b2eSHans Petter Selasky uint32_t c; 2069e2524b2eSHans Petter Selasky uint32_t chs; /* channels */ 2070e2524b2eSHans Petter Selasky uint32_t i; 2071e2524b2eSHans Petter Selasky uint32_t o; 2072e2524b2eSHans Petter Selasky 2073e2524b2eSHans Petter Selasky DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 2074e2524b2eSHans Petter Selasky d0->bUnitId, d0->bNrInPins); 2075e2524b2eSHans Petter Selasky 2076e2524b2eSHans Petter Selasky /* compute the number of input channels */ 2077e2524b2eSHans Petter Selasky 2078e2524b2eSHans Petter Selasky ichs = 0; 2079e2524b2eSHans Petter Selasky for (i = 0; i < d0->bNrInPins; i++) { 2080e2524b2eSHans Petter Selasky ichs += uaudio20_mixer_get_cluster( 2081e2524b2eSHans Petter Selasky d0->baSourceId[i], iot).bNrChannels; 2082e2524b2eSHans Petter Selasky } 2083e2524b2eSHans Petter Selasky 2084e2524b2eSHans Petter Selasky d1 = (const void *)(d0->baSourceId + d0->bNrInPins); 2085e2524b2eSHans Petter Selasky 2086e2524b2eSHans Petter Selasky /* and the number of output channels */ 2087e2524b2eSHans Petter Selasky 2088e2524b2eSHans Petter Selasky ochs = d1->bNrChannels; 2089e2524b2eSHans Petter Selasky 2090e2524b2eSHans Petter Selasky DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs); 2091e2524b2eSHans Petter Selasky 2092e2524b2eSHans Petter Selasky memset(&mix, 0, sizeof(mix)); 2093e2524b2eSHans Petter Selasky 2094e2524b2eSHans Petter Selasky mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 2095e2524b2eSHans Petter Selasky uaudio20_mixer_determine_class(&iot[id], &mix); 2096e2524b2eSHans Petter Selasky mix.type = MIX_SIGNED_16; 2097e2524b2eSHans Petter Selasky 2098e2524b2eSHans Petter Selasky if (uaudio20_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) 2099e2524b2eSHans Petter Selasky return; 2100e2524b2eSHans Petter Selasky 2101e2524b2eSHans Petter Selasky for (p = i = 0; i < d0->bNrInPins; i++) { 2102e2524b2eSHans Petter Selasky chs = uaudio20_mixer_get_cluster( 2103e2524b2eSHans Petter Selasky d0->baSourceId[i], iot).bNrChannels; 2104e2524b2eSHans Petter Selasky mc = 0; 2105e2524b2eSHans Petter Selasky for (c = 0; c < chs; c++) { 2106e2524b2eSHans Petter Selasky mo = 0; 2107e2524b2eSHans Petter Selasky for (o = 0; o < ochs; o++) { 2108e2524b2eSHans Petter Selasky bno = ((p + c) * ochs) + o; 2109e2524b2eSHans Petter Selasky if (BIT_TEST(d1->bmControls, bno)) 2110e2524b2eSHans Petter Selasky mo++; 2111e2524b2eSHans Petter Selasky } 2112e2524b2eSHans Petter Selasky if (mo == 1) 2113e2524b2eSHans Petter Selasky mc++; 2114e2524b2eSHans Petter Selasky } 2115e2524b2eSHans Petter Selasky if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { 2116e2524b2eSHans Petter Selasky 2117e2524b2eSHans Petter Selasky /* repeat bit-scan */ 2118e2524b2eSHans Petter Selasky 2119e2524b2eSHans Petter Selasky mc = 0; 2120e2524b2eSHans Petter Selasky for (c = 0; c < chs; c++) { 2121e2524b2eSHans Petter Selasky for (o = 0; o < ochs; o++) { 2122e2524b2eSHans Petter Selasky bno = ((p + c) * ochs) + o; 2123e2524b2eSHans Petter Selasky if (BIT_TEST(d1->bmControls, bno)) 2124e2524b2eSHans Petter Selasky mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); 2125e2524b2eSHans Petter Selasky } 2126e2524b2eSHans Petter Selasky } 2127e2524b2eSHans Petter Selasky mix.nchan = chs; 2128e2524b2eSHans Petter Selasky uaudio_mixer_add_ctl(sc, &mix); 21293a3f90c6SAndrew Thompson } 21303a3f90c6SAndrew Thompson p += chs; 21313a3f90c6SAndrew Thompson } 21323a3f90c6SAndrew Thompson } 21333a3f90c6SAndrew Thompson 21343a3f90c6SAndrew Thompson static void 21353a3f90c6SAndrew Thompson uaudio_mixer_add_selector(struct uaudio_softc *sc, 21363a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 21373a3f90c6SAndrew Thompson { 2138e2524b2eSHans Petter Selasky const struct usb_audio_selector_unit *d = iot[id].u.su_v1; 21393a3f90c6SAndrew Thompson struct uaudio_mixer_node mix; 21403a3f90c6SAndrew Thompson uint16_t i; 21413a3f90c6SAndrew Thompson 21423a3f90c6SAndrew Thompson DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 21433a3f90c6SAndrew Thompson d->bUnitId, d->bNrInPins); 21443a3f90c6SAndrew Thompson 21453a3f90c6SAndrew Thompson if (d->bNrInPins == 0) { 21463a3f90c6SAndrew Thompson return; 21473a3f90c6SAndrew Thompson } 21486f068a43SHans Petter Selasky memset(&mix, 0, sizeof(mix)); 21493a3f90c6SAndrew Thompson 21503a3f90c6SAndrew Thompson mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); 21513a3f90c6SAndrew Thompson mix.wValue[0] = MAKE_WORD(0, 0); 21523a3f90c6SAndrew Thompson uaudio_mixer_determine_class(&iot[id], &mix); 21533a3f90c6SAndrew Thompson mix.nchan = 1; 21543a3f90c6SAndrew Thompson mix.type = MIX_SELECTOR; 21553a3f90c6SAndrew Thompson 21563a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_NRDEVICES; 21573a3f90c6SAndrew Thompson mix.minval = 1; 21583a3f90c6SAndrew Thompson mix.maxval = d->bNrInPins; 21593a3f90c6SAndrew Thompson 21603a3f90c6SAndrew Thompson if (mix.maxval > MAX_SELECTOR_INPUT_PIN) { 21613a3f90c6SAndrew Thompson mix.maxval = MAX_SELECTOR_INPUT_PIN; 21623a3f90c6SAndrew Thompson } 21633a3f90c6SAndrew Thompson mix.mul = (mix.maxval - mix.minval); 21643a3f90c6SAndrew Thompson for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) { 21653a3f90c6SAndrew Thompson mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; 21663a3f90c6SAndrew Thompson } 21673a3f90c6SAndrew Thompson 21683a3f90c6SAndrew Thompson for (i = 0; i < mix.maxval; i++) { 2169e2524b2eSHans Petter Selasky mix.slctrtype[i] = uaudio_mixer_feature_name( 2170e2524b2eSHans Petter Selasky &iot[d->baSourceId[i]], &mix); 2171e2524b2eSHans Petter Selasky } 2172e2524b2eSHans Petter Selasky 2173e2524b2eSHans Petter Selasky mix.class = 0; /* not used */ 2174e2524b2eSHans Petter Selasky 2175e2524b2eSHans Petter Selasky uaudio_mixer_add_ctl(sc, &mix); 2176e2524b2eSHans Petter Selasky } 2177e2524b2eSHans Petter Selasky 2178e2524b2eSHans Petter Selasky static void 2179e2524b2eSHans Petter Selasky uaudio20_mixer_add_selector(struct uaudio_softc *sc, 2180e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *iot, int id) 2181e2524b2eSHans Petter Selasky { 2182e2524b2eSHans Petter Selasky const struct usb_audio20_selector_unit *d = iot[id].u.su_v2; 2183e2524b2eSHans Petter Selasky struct uaudio_mixer_node mix; 2184e2524b2eSHans Petter Selasky uint16_t i; 2185e2524b2eSHans Petter Selasky 2186e2524b2eSHans Petter Selasky DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 2187e2524b2eSHans Petter Selasky d->bUnitId, d->bNrInPins); 2188e2524b2eSHans Petter Selasky 2189e2524b2eSHans Petter Selasky if (d->bNrInPins == 0) 2190e2524b2eSHans Petter Selasky return; 2191e2524b2eSHans Petter Selasky 2192e2524b2eSHans Petter Selasky memset(&mix, 0, sizeof(mix)); 2193e2524b2eSHans Petter Selasky 2194e2524b2eSHans Petter Selasky mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); 2195e2524b2eSHans Petter Selasky mix.wValue[0] = MAKE_WORD(0, 0); 2196e2524b2eSHans Petter Selasky uaudio20_mixer_determine_class(&iot[id], &mix); 2197e2524b2eSHans Petter Selasky mix.nchan = 1; 2198e2524b2eSHans Petter Selasky mix.type = MIX_SELECTOR; 2199e2524b2eSHans Petter Selasky 2200e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_NRDEVICES; 2201e2524b2eSHans Petter Selasky mix.minval = 1; 2202e2524b2eSHans Petter Selasky mix.maxval = d->bNrInPins; 2203e2524b2eSHans Petter Selasky 2204e2524b2eSHans Petter Selasky if (mix.maxval > MAX_SELECTOR_INPUT_PIN) 2205e2524b2eSHans Petter Selasky mix.maxval = MAX_SELECTOR_INPUT_PIN; 2206e2524b2eSHans Petter Selasky 2207e2524b2eSHans Petter Selasky mix.mul = (mix.maxval - mix.minval); 2208e2524b2eSHans Petter Selasky for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) 2209e2524b2eSHans Petter Selasky mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; 2210e2524b2eSHans Petter Selasky 2211e2524b2eSHans Petter Selasky for (i = 0; i < mix.maxval; i++) { 2212e2524b2eSHans Petter Selasky mix.slctrtype[i] = uaudio20_mixer_feature_name( 2213e2524b2eSHans Petter Selasky &iot[d->baSourceId[i]], &mix); 22143a3f90c6SAndrew Thompson } 22153a3f90c6SAndrew Thompson 22163a3f90c6SAndrew Thompson mix.class = 0; /* not used */ 22173a3f90c6SAndrew Thompson 22183a3f90c6SAndrew Thompson uaudio_mixer_add_ctl(sc, &mix); 22193a3f90c6SAndrew Thompson } 22203a3f90c6SAndrew Thompson 22213a3f90c6SAndrew Thompson static uint32_t 22224c21be9bSRebecca Cran uaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit *d, 22236d917491SHans Petter Selasky uint8_t i) 22243a3f90c6SAndrew Thompson { 22253a3f90c6SAndrew Thompson uint32_t temp = 0; 22266d917491SHans Petter Selasky uint32_t offset = (i * d->bControlSize); 22273a3f90c6SAndrew Thompson 22283a3f90c6SAndrew Thompson if (d->bControlSize > 0) { 22293a3f90c6SAndrew Thompson temp |= d->bmaControls[offset]; 22303a3f90c6SAndrew Thompson if (d->bControlSize > 1) { 22313a3f90c6SAndrew Thompson temp |= d->bmaControls[offset + 1] << 8; 22323a3f90c6SAndrew Thompson if (d->bControlSize > 2) { 22333a3f90c6SAndrew Thompson temp |= d->bmaControls[offset + 2] << 16; 22343a3f90c6SAndrew Thompson if (d->bControlSize > 3) { 22353a3f90c6SAndrew Thompson temp |= d->bmaControls[offset + 3] << 24; 22363a3f90c6SAndrew Thompson } 22373a3f90c6SAndrew Thompson } 22383a3f90c6SAndrew Thompson } 22393a3f90c6SAndrew Thompson } 22403a3f90c6SAndrew Thompson return (temp); 22413a3f90c6SAndrew Thompson } 22423a3f90c6SAndrew Thompson 22433a3f90c6SAndrew Thompson static void 22443a3f90c6SAndrew Thompson uaudio_mixer_add_feature(struct uaudio_softc *sc, 22453a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 22463a3f90c6SAndrew Thompson { 2247e2524b2eSHans Petter Selasky const struct usb_audio_feature_unit *d = iot[id].u.fu_v1; 22483a3f90c6SAndrew Thompson struct uaudio_mixer_node mix; 22493a3f90c6SAndrew Thompson uint32_t fumask; 22503a3f90c6SAndrew Thompson uint32_t mmask; 22513a3f90c6SAndrew Thompson uint32_t cmask; 22523a3f90c6SAndrew Thompson uint16_t mixernumber; 22533a3f90c6SAndrew Thompson uint8_t nchan; 22543a3f90c6SAndrew Thompson uint8_t chan; 22553a3f90c6SAndrew Thompson uint8_t ctl; 22563a3f90c6SAndrew Thompson uint8_t i; 22573a3f90c6SAndrew Thompson 22583a3f90c6SAndrew Thompson if (d->bControlSize == 0) { 22593a3f90c6SAndrew Thompson return; 22603a3f90c6SAndrew Thompson } 22616f068a43SHans Petter Selasky memset(&mix, 0, sizeof(mix)); 22623a3f90c6SAndrew Thompson 22633a3f90c6SAndrew Thompson nchan = (d->bLength - 7) / d->bControlSize; 22643a3f90c6SAndrew Thompson mmask = uaudio_mixer_feature_get_bmaControls(d, 0); 22653a3f90c6SAndrew Thompson cmask = 0; 22663a3f90c6SAndrew Thompson 22673a3f90c6SAndrew Thompson if (nchan == 0) { 22683a3f90c6SAndrew Thompson return; 22693a3f90c6SAndrew Thompson } 22703a3f90c6SAndrew Thompson /* figure out what we can control */ 22713a3f90c6SAndrew Thompson 22723a3f90c6SAndrew Thompson for (chan = 1; chan < nchan; chan++) { 22733a3f90c6SAndrew Thompson DPRINTFN(10, "chan=%d mask=%x\n", 22743a3f90c6SAndrew Thompson chan, uaudio_mixer_feature_get_bmaControls(d, chan)); 22753a3f90c6SAndrew Thompson 22763a3f90c6SAndrew Thompson cmask |= uaudio_mixer_feature_get_bmaControls(d, chan); 22773a3f90c6SAndrew Thompson } 22783a3f90c6SAndrew Thompson 22793a3f90c6SAndrew Thompson if (nchan > MIX_MAX_CHAN) { 22803a3f90c6SAndrew Thompson nchan = MIX_MAX_CHAN; 22813a3f90c6SAndrew Thompson } 22823a3f90c6SAndrew Thompson mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); 22833a3f90c6SAndrew Thompson 22843a3f90c6SAndrew Thompson for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) { 22853a3f90c6SAndrew Thompson 22863a3f90c6SAndrew Thompson fumask = FU_MASK(ctl); 22873a3f90c6SAndrew Thompson 22883a3f90c6SAndrew Thompson DPRINTFN(5, "ctl=%d fumask=0x%04x\n", 22893a3f90c6SAndrew Thompson ctl, fumask); 22903a3f90c6SAndrew Thompson 22913a3f90c6SAndrew Thompson if (mmask & fumask) { 22923a3f90c6SAndrew Thompson mix.nchan = 1; 22933a3f90c6SAndrew Thompson mix.wValue[0] = MAKE_WORD(ctl, 0); 22943a3f90c6SAndrew Thompson } else if (cmask & fumask) { 22953a3f90c6SAndrew Thompson mix.nchan = nchan - 1; 22963a3f90c6SAndrew Thompson for (i = 1; i < nchan; i++) { 22973a3f90c6SAndrew Thompson if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask) 22983a3f90c6SAndrew Thompson mix.wValue[i - 1] = MAKE_WORD(ctl, i); 22993a3f90c6SAndrew Thompson else 23003a3f90c6SAndrew Thompson mix.wValue[i - 1] = -1; 23013a3f90c6SAndrew Thompson } 23023a3f90c6SAndrew Thompson } else { 23033a3f90c6SAndrew Thompson continue; 23043a3f90c6SAndrew Thompson } 23053a3f90c6SAndrew Thompson 23063a3f90c6SAndrew Thompson mixernumber = uaudio_mixer_feature_name(&iot[id], &mix); 23073a3f90c6SAndrew Thompson 23083a3f90c6SAndrew Thompson switch (ctl) { 23093a3f90c6SAndrew Thompson case MUTE_CONTROL: 23103a3f90c6SAndrew Thompson mix.type = MIX_ON_OFF; 23113a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_NRDEVICES; 23123a3f90c6SAndrew Thompson break; 23133a3f90c6SAndrew Thompson 23143a3f90c6SAndrew Thompson case VOLUME_CONTROL: 23153a3f90c6SAndrew Thompson mix.type = MIX_SIGNED_16; 23163a3f90c6SAndrew Thompson mix.ctl = mixernumber; 23173a3f90c6SAndrew Thompson break; 23183a3f90c6SAndrew Thompson 23193a3f90c6SAndrew Thompson case BASS_CONTROL: 23203a3f90c6SAndrew Thompson mix.type = MIX_SIGNED_8; 23213a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_BASS; 23223a3f90c6SAndrew Thompson break; 23233a3f90c6SAndrew Thompson 23243a3f90c6SAndrew Thompson case MID_CONTROL: 23253a3f90c6SAndrew Thompson mix.type = MIX_SIGNED_8; 23263a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 23273a3f90c6SAndrew Thompson break; 23283a3f90c6SAndrew Thompson 23293a3f90c6SAndrew Thompson case TREBLE_CONTROL: 23303a3f90c6SAndrew Thompson mix.type = MIX_SIGNED_8; 23313a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_TREBLE; 23323a3f90c6SAndrew Thompson break; 23333a3f90c6SAndrew Thompson 23343a3f90c6SAndrew Thompson case GRAPHIC_EQUALIZER_CONTROL: 23353a3f90c6SAndrew Thompson continue; /* XXX don't add anything */ 23363a3f90c6SAndrew Thompson break; 23373a3f90c6SAndrew Thompson 23383a3f90c6SAndrew Thompson case AGC_CONTROL: 23393a3f90c6SAndrew Thompson mix.type = MIX_ON_OFF; 23403a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 23413a3f90c6SAndrew Thompson break; 23423a3f90c6SAndrew Thompson 23433a3f90c6SAndrew Thompson case DELAY_CONTROL: 23443a3f90c6SAndrew Thompson mix.type = MIX_UNSIGNED_16; 23453a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 23463a3f90c6SAndrew Thompson break; 23473a3f90c6SAndrew Thompson 23483a3f90c6SAndrew Thompson case BASS_BOOST_CONTROL: 23493a3f90c6SAndrew Thompson mix.type = MIX_ON_OFF; 23503a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 23513a3f90c6SAndrew Thompson break; 23523a3f90c6SAndrew Thompson 23533a3f90c6SAndrew Thompson case LOUDNESS_CONTROL: 23543a3f90c6SAndrew Thompson mix.type = MIX_ON_OFF; 23553a3f90c6SAndrew Thompson mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ 23563a3f90c6SAndrew Thompson break; 23573a3f90c6SAndrew Thompson 23583a3f90c6SAndrew Thompson default: 23593a3f90c6SAndrew Thompson mix.type = MIX_UNKNOWN; 23603a3f90c6SAndrew Thompson break; 23613a3f90c6SAndrew Thompson } 23623a3f90c6SAndrew Thompson 2363e2524b2eSHans Petter Selasky if (mix.type != MIX_UNKNOWN) 23643a3f90c6SAndrew Thompson uaudio_mixer_add_ctl(sc, &mix); 23653a3f90c6SAndrew Thompson } 23663a3f90c6SAndrew Thompson } 2367e2524b2eSHans Petter Selasky 2368e2524b2eSHans Petter Selasky static void 2369e2524b2eSHans Petter Selasky uaudio20_mixer_add_feature(struct uaudio_softc *sc, 2370e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *iot, int id) 2371e2524b2eSHans Petter Selasky { 2372e2524b2eSHans Petter Selasky const struct usb_audio20_feature_unit *d = iot[id].u.fu_v2; 2373e2524b2eSHans Petter Selasky struct uaudio_mixer_node mix; 2374e2524b2eSHans Petter Selasky uint32_t ctl; 2375e2524b2eSHans Petter Selasky uint32_t mmask; 2376e2524b2eSHans Petter Selasky uint32_t cmask; 2377e2524b2eSHans Petter Selasky uint16_t mixernumber; 2378e2524b2eSHans Petter Selasky uint8_t nchan; 2379e2524b2eSHans Petter Selasky uint8_t chan; 2380e2524b2eSHans Petter Selasky uint8_t i; 2381e2524b2eSHans Petter Selasky uint8_t what; 2382e2524b2eSHans Petter Selasky 2383e2524b2eSHans Petter Selasky if (UGETDW(d->bmaControls[0]) == 0) 2384e2524b2eSHans Petter Selasky return; 2385e2524b2eSHans Petter Selasky 2386e2524b2eSHans Petter Selasky memset(&mix, 0, sizeof(mix)); 2387e2524b2eSHans Petter Selasky 2388e2524b2eSHans Petter Selasky nchan = (d->bLength - 6) / 4; 2389e2524b2eSHans Petter Selasky mmask = UGETDW(d->bmaControls[0]); 2390e2524b2eSHans Petter Selasky cmask = 0; 2391e2524b2eSHans Petter Selasky 2392e2524b2eSHans Petter Selasky if (nchan == 0) 2393e2524b2eSHans Petter Selasky return; 2394e2524b2eSHans Petter Selasky 2395e2524b2eSHans Petter Selasky /* figure out what we can control */ 2396e2524b2eSHans Petter Selasky 2397e2524b2eSHans Petter Selasky for (chan = 1; chan < nchan; chan++) 2398e2524b2eSHans Petter Selasky cmask |= UGETDW(d->bmaControls[chan]); 2399e2524b2eSHans Petter Selasky 2400e2524b2eSHans Petter Selasky if (nchan > MIX_MAX_CHAN) 2401e2524b2eSHans Petter Selasky nchan = MIX_MAX_CHAN; 2402e2524b2eSHans Petter Selasky 2403e2524b2eSHans Petter Selasky mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); 2404e2524b2eSHans Petter Selasky 2405e2524b2eSHans Petter Selasky for (ctl = 3; ctl != 0; ctl <<= 2) { 2406e2524b2eSHans Petter Selasky 2407e2524b2eSHans Petter Selasky mixernumber = uaudio20_mixer_feature_name(&iot[id], &mix); 2408e2524b2eSHans Petter Selasky 2409e2524b2eSHans Petter Selasky switch (ctl) { 2410e2524b2eSHans Petter Selasky case (3 << 0): 2411e2524b2eSHans Petter Selasky mix.type = MIX_ON_OFF; 2412e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_NRDEVICES; 2413e2524b2eSHans Petter Selasky what = MUTE_CONTROL; 2414e2524b2eSHans Petter Selasky break; 2415e2524b2eSHans Petter Selasky case (3 << 2): 2416e2524b2eSHans Petter Selasky mix.type = MIX_SIGNED_16; 2417e2524b2eSHans Petter Selasky mix.ctl = mixernumber; 2418e2524b2eSHans Petter Selasky what = VOLUME_CONTROL; 2419e2524b2eSHans Petter Selasky break; 2420e2524b2eSHans Petter Selasky case (3 << 4): 2421e2524b2eSHans Petter Selasky mix.type = MIX_SIGNED_8; 2422e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_BASS; 2423e2524b2eSHans Petter Selasky what = BASS_CONTROL; 2424e2524b2eSHans Petter Selasky break; 2425e2524b2eSHans Petter Selasky case (3 << 6): 2426e2524b2eSHans Petter Selasky mix.type = MIX_SIGNED_8; 2427e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 2428e2524b2eSHans Petter Selasky what = MID_CONTROL; 2429e2524b2eSHans Petter Selasky break; 2430e2524b2eSHans Petter Selasky case (3 << 8): 2431e2524b2eSHans Petter Selasky mix.type = MIX_SIGNED_8; 2432e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_TREBLE; 2433e2524b2eSHans Petter Selasky what = TREBLE_CONTROL; 2434e2524b2eSHans Petter Selasky break; 2435e2524b2eSHans Petter Selasky case (3 << 12): 2436e2524b2eSHans Petter Selasky mix.type = MIX_ON_OFF; 2437e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 2438e2524b2eSHans Petter Selasky what = AGC_CONTROL; 2439e2524b2eSHans Petter Selasky break; 2440e2524b2eSHans Petter Selasky case (3 << 14): 2441e2524b2eSHans Petter Selasky mix.type = MIX_UNSIGNED_16; 2442e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 2443e2524b2eSHans Petter Selasky what = DELAY_CONTROL; 2444e2524b2eSHans Petter Selasky break; 2445e2524b2eSHans Petter Selasky case (3 << 16): 2446e2524b2eSHans Petter Selasky mix.type = MIX_ON_OFF; 2447e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 2448e2524b2eSHans Petter Selasky what = BASS_BOOST_CONTROL; 2449e2524b2eSHans Petter Selasky break; 2450e2524b2eSHans Petter Selasky case (3 << 18): 2451e2524b2eSHans Petter Selasky mix.type = MIX_ON_OFF; 2452e2524b2eSHans Petter Selasky mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ 2453e2524b2eSHans Petter Selasky what = LOUDNESS_CONTROL; 2454e2524b2eSHans Petter Selasky break; 2455e2524b2eSHans Petter Selasky case (3 << 20): 2456e2524b2eSHans Petter Selasky mix.type = MIX_SIGNED_16; 2457e2524b2eSHans Petter Selasky mix.ctl = mixernumber; 2458e2524b2eSHans Petter Selasky what = INPUT_GAIN_CONTROL; 2459e2524b2eSHans Petter Selasky break; 2460e2524b2eSHans Petter Selasky case (3 << 22): 2461e2524b2eSHans Petter Selasky mix.type = MIX_SIGNED_16; 2462e2524b2eSHans Petter Selasky mix.ctl = mixernumber; 2463e2524b2eSHans Petter Selasky what = INPUT_GAIN_PAD_CONTROL; 2464e2524b2eSHans Petter Selasky break; 2465e2524b2eSHans Petter Selasky default: 2466e2524b2eSHans Petter Selasky continue; 2467e2524b2eSHans Petter Selasky } 2468e2524b2eSHans Petter Selasky 2469e2524b2eSHans Petter Selasky if ((mmask & ctl) == ctl) { 2470e2524b2eSHans Petter Selasky mix.nchan = 1; 2471e2524b2eSHans Petter Selasky mix.wValue[0] = MAKE_WORD(what, 0); 2472e2524b2eSHans Petter Selasky } else if ((cmask & ctl) == ctl) { 2473e2524b2eSHans Petter Selasky mix.nchan = nchan - 1; 2474e2524b2eSHans Petter Selasky for (i = 1; i < nchan; i++) { 2475e2524b2eSHans Petter Selasky if ((UGETDW(d->bmaControls[i]) & ctl) == ctl) 2476e2524b2eSHans Petter Selasky mix.wValue[i - 1] = MAKE_WORD(what, i); 2477e2524b2eSHans Petter Selasky else 2478e2524b2eSHans Petter Selasky mix.wValue[i - 1] = -1; 2479e2524b2eSHans Petter Selasky } 2480e2524b2eSHans Petter Selasky } else { 2481e2524b2eSHans Petter Selasky continue; 2482e2524b2eSHans Petter Selasky } 2483e2524b2eSHans Petter Selasky 2484e2524b2eSHans Petter Selasky if (mix.type != MIX_UNKNOWN) 2485e2524b2eSHans Petter Selasky uaudio_mixer_add_ctl(sc, &mix); 2486e2524b2eSHans Petter Selasky } 24873a3f90c6SAndrew Thompson } 24883a3f90c6SAndrew Thompson 24893a3f90c6SAndrew Thompson static void 24903a3f90c6SAndrew Thompson uaudio_mixer_add_processing_updown(struct uaudio_softc *sc, 24913a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 24923a3f90c6SAndrew Thompson { 2493e2524b2eSHans Petter Selasky const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1; 24944c21be9bSRebecca Cran const struct usb_audio_processing_unit_1 *d1 = 24953a3f90c6SAndrew Thompson (const void *)(d0->baSourceId + d0->bNrInPins); 24964c21be9bSRebecca Cran const struct usb_audio_processing_unit_updown *ud = 24973a3f90c6SAndrew Thompson (const void *)(d1->bmControls + d1->bControlSize); 24983a3f90c6SAndrew Thompson struct uaudio_mixer_node mix; 24993a3f90c6SAndrew Thompson uint8_t i; 25003a3f90c6SAndrew Thompson 25013a3f90c6SAndrew Thompson if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) { 25023a3f90c6SAndrew Thompson return; 25033a3f90c6SAndrew Thompson } 25043a3f90c6SAndrew Thompson if (uaudio_mixer_verify_desc(d0, sizeof(*ud) + (2 * ud->bNrModes)) 25053a3f90c6SAndrew Thompson == NULL) { 25063a3f90c6SAndrew Thompson return; 25073a3f90c6SAndrew Thompson } 25083a3f90c6SAndrew Thompson DPRINTFN(3, "bUnitId=%d bNrModes=%d\n", 25093a3f90c6SAndrew Thompson d0->bUnitId, ud->bNrModes); 25103a3f90c6SAndrew Thompson 25113a3f90c6SAndrew Thompson if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) { 25123a3f90c6SAndrew Thompson DPRINTF("no mode select\n"); 25133a3f90c6SAndrew Thompson return; 25143a3f90c6SAndrew Thompson } 25156f068a43SHans Petter Selasky memset(&mix, 0, sizeof(mix)); 25163a3f90c6SAndrew Thompson 25173a3f90c6SAndrew Thompson mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 25183a3f90c6SAndrew Thompson mix.nchan = 1; 25193a3f90c6SAndrew Thompson mix.wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0); 25203a3f90c6SAndrew Thompson uaudio_mixer_determine_class(&iot[id], &mix); 25213a3f90c6SAndrew Thompson mix.type = MIX_ON_OFF; /* XXX */ 25223a3f90c6SAndrew Thompson 25233a3f90c6SAndrew Thompson for (i = 0; i < ud->bNrModes; i++) { 25243a3f90c6SAndrew Thompson DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i])); 25253a3f90c6SAndrew Thompson /* XXX */ 25263a3f90c6SAndrew Thompson } 25273a3f90c6SAndrew Thompson 25283a3f90c6SAndrew Thompson uaudio_mixer_add_ctl(sc, &mix); 25293a3f90c6SAndrew Thompson } 25303a3f90c6SAndrew Thompson 25313a3f90c6SAndrew Thompson static void 25323a3f90c6SAndrew Thompson uaudio_mixer_add_processing(struct uaudio_softc *sc, 25333a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 25343a3f90c6SAndrew Thompson { 2535e2524b2eSHans Petter Selasky const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1; 25364c21be9bSRebecca Cran const struct usb_audio_processing_unit_1 *d1 = 25373a3f90c6SAndrew Thompson (const void *)(d0->baSourceId + d0->bNrInPins); 25383a3f90c6SAndrew Thompson struct uaudio_mixer_node mix; 25393a3f90c6SAndrew Thompson uint16_t ptype; 25403a3f90c6SAndrew Thompson 25416f068a43SHans Petter Selasky memset(&mix, 0, sizeof(mix)); 25423a3f90c6SAndrew Thompson 25433a3f90c6SAndrew Thompson ptype = UGETW(d0->wProcessType); 25443a3f90c6SAndrew Thompson 25453a3f90c6SAndrew Thompson DPRINTFN(3, "wProcessType=%d bUnitId=%d " 25463a3f90c6SAndrew Thompson "bNrInPins=%d\n", ptype, d0->bUnitId, d0->bNrInPins); 25473a3f90c6SAndrew Thompson 25483a3f90c6SAndrew Thompson if (d1->bControlSize == 0) { 25493a3f90c6SAndrew Thompson return; 25503a3f90c6SAndrew Thompson } 25513a3f90c6SAndrew Thompson if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) { 25523a3f90c6SAndrew Thompson mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 25533a3f90c6SAndrew Thompson mix.nchan = 1; 25543a3f90c6SAndrew Thompson mix.wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0); 25553a3f90c6SAndrew Thompson uaudio_mixer_determine_class(&iot[id], &mix); 25563a3f90c6SAndrew Thompson mix.type = MIX_ON_OFF; 25573a3f90c6SAndrew Thompson uaudio_mixer_add_ctl(sc, &mix); 25583a3f90c6SAndrew Thompson } 25593a3f90c6SAndrew Thompson switch (ptype) { 25603a3f90c6SAndrew Thompson case UPDOWNMIX_PROCESS: 25613a3f90c6SAndrew Thompson uaudio_mixer_add_processing_updown(sc, iot, id); 25623a3f90c6SAndrew Thompson break; 25633a3f90c6SAndrew Thompson 25643a3f90c6SAndrew Thompson case DOLBY_PROLOGIC_PROCESS: 25653a3f90c6SAndrew Thompson case P3D_STEREO_EXTENDER_PROCESS: 25663a3f90c6SAndrew Thompson case REVERBATION_PROCESS: 25673a3f90c6SAndrew Thompson case CHORUS_PROCESS: 25683a3f90c6SAndrew Thompson case DYN_RANGE_COMP_PROCESS: 25693a3f90c6SAndrew Thompson default: 25703a3f90c6SAndrew Thompson DPRINTF("unit %d, type=%d is not implemented\n", 25713a3f90c6SAndrew Thompson d0->bUnitId, ptype); 25723a3f90c6SAndrew Thompson break; 25733a3f90c6SAndrew Thompson } 25743a3f90c6SAndrew Thompson } 25753a3f90c6SAndrew Thompson 25763a3f90c6SAndrew Thompson static void 25773a3f90c6SAndrew Thompson uaudio_mixer_add_extension(struct uaudio_softc *sc, 25783a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 25793a3f90c6SAndrew Thompson { 2580e2524b2eSHans Petter Selasky const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu_v1; 25814c21be9bSRebecca Cran const struct usb_audio_extension_unit_1 *d1 = 25823a3f90c6SAndrew Thompson (const void *)(d0->baSourceId + d0->bNrInPins); 25833a3f90c6SAndrew Thompson struct uaudio_mixer_node mix; 25843a3f90c6SAndrew Thompson 25853a3f90c6SAndrew Thompson DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 25863a3f90c6SAndrew Thompson d0->bUnitId, d0->bNrInPins); 25873a3f90c6SAndrew Thompson 25883a3f90c6SAndrew Thompson if (sc->sc_uq_au_no_xu) { 25893a3f90c6SAndrew Thompson return; 25903a3f90c6SAndrew Thompson } 25913a3f90c6SAndrew Thompson if (d1->bControlSize == 0) { 25923a3f90c6SAndrew Thompson return; 25933a3f90c6SAndrew Thompson } 25943a3f90c6SAndrew Thompson if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) { 25953a3f90c6SAndrew Thompson 25966f068a43SHans Petter Selasky memset(&mix, 0, sizeof(mix)); 25973a3f90c6SAndrew Thompson 25983a3f90c6SAndrew Thompson mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 25993a3f90c6SAndrew Thompson mix.nchan = 1; 26003a3f90c6SAndrew Thompson mix.wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0); 26013a3f90c6SAndrew Thompson uaudio_mixer_determine_class(&iot[id], &mix); 26023a3f90c6SAndrew Thompson mix.type = MIX_ON_OFF; 26033a3f90c6SAndrew Thompson 26043a3f90c6SAndrew Thompson uaudio_mixer_add_ctl(sc, &mix); 26053a3f90c6SAndrew Thompson } 26063a3f90c6SAndrew Thompson } 26073a3f90c6SAndrew Thompson 26083a3f90c6SAndrew Thompson static const void * 26093a3f90c6SAndrew Thompson uaudio_mixer_verify_desc(const void *arg, uint32_t len) 26103a3f90c6SAndrew Thompson { 26114c21be9bSRebecca Cran const struct usb_audio_mixer_unit_1 *d1; 26124c21be9bSRebecca Cran const struct usb_audio_extension_unit_1 *e1; 26134c21be9bSRebecca Cran const struct usb_audio_processing_unit_1 *u1; 26143a3f90c6SAndrew Thompson 26153a3f90c6SAndrew Thompson union { 2616760bc48eSAndrew Thompson const struct usb_descriptor *desc; 26174c21be9bSRebecca Cran const struct usb_audio_input_terminal *it; 26184c21be9bSRebecca Cran const struct usb_audio_output_terminal *ot; 26194c21be9bSRebecca Cran const struct usb_audio_mixer_unit_0 *mu; 26204c21be9bSRebecca Cran const struct usb_audio_selector_unit *su; 26214c21be9bSRebecca Cran const struct usb_audio_feature_unit *fu; 26224c21be9bSRebecca Cran const struct usb_audio_processing_unit_0 *pu; 26234c21be9bSRebecca Cran const struct usb_audio_extension_unit_0 *eu; 26243a3f90c6SAndrew Thompson } u; 26253a3f90c6SAndrew Thompson 26263a3f90c6SAndrew Thompson u.desc = arg; 26273a3f90c6SAndrew Thompson 26283a3f90c6SAndrew Thompson if (u.desc == NULL) { 26293a3f90c6SAndrew Thompson goto error; 26303a3f90c6SAndrew Thompson } 26313a3f90c6SAndrew Thompson if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) { 26323a3f90c6SAndrew Thompson goto error; 26333a3f90c6SAndrew Thompson } 26343a3f90c6SAndrew Thompson switch (u.desc->bDescriptorSubtype) { 26353a3f90c6SAndrew Thompson case UDESCSUB_AC_INPUT: 26363a3f90c6SAndrew Thompson len += sizeof(*u.it); 26373a3f90c6SAndrew Thompson break; 26383a3f90c6SAndrew Thompson 26393a3f90c6SAndrew Thompson case UDESCSUB_AC_OUTPUT: 26403a3f90c6SAndrew Thompson len += sizeof(*u.ot); 26413a3f90c6SAndrew Thompson break; 26423a3f90c6SAndrew Thompson 26433a3f90c6SAndrew Thompson case UDESCSUB_AC_MIXER: 26443a3f90c6SAndrew Thompson len += sizeof(*u.mu); 26453a3f90c6SAndrew Thompson 26463a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 26473a3f90c6SAndrew Thompson goto error; 26483a3f90c6SAndrew Thompson } 26493a3f90c6SAndrew Thompson len += u.mu->bNrInPins; 26503a3f90c6SAndrew Thompson 26513a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 26523a3f90c6SAndrew Thompson goto error; 26533a3f90c6SAndrew Thompson } 26543a3f90c6SAndrew Thompson d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins); 26553a3f90c6SAndrew Thompson 26563a3f90c6SAndrew Thompson len += sizeof(*d1); 26573a3f90c6SAndrew Thompson break; 26583a3f90c6SAndrew Thompson 26593a3f90c6SAndrew Thompson case UDESCSUB_AC_SELECTOR: 26603a3f90c6SAndrew Thompson len += sizeof(*u.su); 26613a3f90c6SAndrew Thompson 26623a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 26633a3f90c6SAndrew Thompson goto error; 26643a3f90c6SAndrew Thompson } 26653a3f90c6SAndrew Thompson len += u.su->bNrInPins; 26663a3f90c6SAndrew Thompson break; 26673a3f90c6SAndrew Thompson 26683a3f90c6SAndrew Thompson case UDESCSUB_AC_FEATURE: 26693a3f90c6SAndrew Thompson len += (sizeof(*u.fu) + 1); 26703a3f90c6SAndrew Thompson break; 26713a3f90c6SAndrew Thompson 26723a3f90c6SAndrew Thompson case UDESCSUB_AC_PROCESSING: 26733a3f90c6SAndrew Thompson len += sizeof(*u.pu); 26743a3f90c6SAndrew Thompson 26753a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 26763a3f90c6SAndrew Thompson goto error; 26773a3f90c6SAndrew Thompson } 26783a3f90c6SAndrew Thompson len += u.pu->bNrInPins; 26793a3f90c6SAndrew Thompson 26803a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 26813a3f90c6SAndrew Thompson goto error; 26823a3f90c6SAndrew Thompson } 26833a3f90c6SAndrew Thompson u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins); 26843a3f90c6SAndrew Thompson 26853a3f90c6SAndrew Thompson len += sizeof(*u1); 26863a3f90c6SAndrew Thompson 26873a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 26883a3f90c6SAndrew Thompson goto error; 26893a3f90c6SAndrew Thompson } 26903a3f90c6SAndrew Thompson len += u1->bControlSize; 26913a3f90c6SAndrew Thompson 26923a3f90c6SAndrew Thompson break; 26933a3f90c6SAndrew Thompson 26943a3f90c6SAndrew Thompson case UDESCSUB_AC_EXTENSION: 26953a3f90c6SAndrew Thompson len += sizeof(*u.eu); 26963a3f90c6SAndrew Thompson 26973a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 26983a3f90c6SAndrew Thompson goto error; 26993a3f90c6SAndrew Thompson } 27003a3f90c6SAndrew Thompson len += u.eu->bNrInPins; 27013a3f90c6SAndrew Thompson 27023a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 27033a3f90c6SAndrew Thompson goto error; 27043a3f90c6SAndrew Thompson } 27053a3f90c6SAndrew Thompson e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins); 27063a3f90c6SAndrew Thompson 27073a3f90c6SAndrew Thompson len += sizeof(*e1); 27083a3f90c6SAndrew Thompson 27093a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 27103a3f90c6SAndrew Thompson goto error; 27113a3f90c6SAndrew Thompson } 27123a3f90c6SAndrew Thompson len += e1->bControlSize; 27133a3f90c6SAndrew Thompson break; 27143a3f90c6SAndrew Thompson 27153a3f90c6SAndrew Thompson default: 27163a3f90c6SAndrew Thompson goto error; 27173a3f90c6SAndrew Thompson } 27183a3f90c6SAndrew Thompson 27193a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 27203a3f90c6SAndrew Thompson goto error; 27213a3f90c6SAndrew Thompson } 27223a3f90c6SAndrew Thompson return (u.desc); 27233a3f90c6SAndrew Thompson 27243a3f90c6SAndrew Thompson error: 27253a3f90c6SAndrew Thompson if (u.desc) { 27263a3f90c6SAndrew Thompson DPRINTF("invalid descriptor, type=%d, " 27273a3f90c6SAndrew Thompson "sub_type=%d, len=%d of %d bytes\n", 27283a3f90c6SAndrew Thompson u.desc->bDescriptorType, 27293a3f90c6SAndrew Thompson u.desc->bDescriptorSubtype, 27303a3f90c6SAndrew Thompson u.desc->bLength, len); 27313a3f90c6SAndrew Thompson } 27323a3f90c6SAndrew Thompson return (NULL); 27333a3f90c6SAndrew Thompson } 27343a3f90c6SAndrew Thompson 2735e2524b2eSHans Petter Selasky static const void * 2736e2524b2eSHans Petter Selasky uaudio20_mixer_verify_desc(const void *arg, uint32_t len) 27373a3f90c6SAndrew Thompson { 2738e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_1 *d1; 2739e2524b2eSHans Petter Selasky const struct usb_audio20_extension_unit_1 *e1; 2740e2524b2eSHans Petter Selasky const struct usb_audio20_processing_unit_1 *u1; 2741e2524b2eSHans Petter Selasky const struct usb_audio20_clock_selector_unit_1 *c1; 27423a3f90c6SAndrew Thompson 2743e2524b2eSHans Petter Selasky union { 2744e2524b2eSHans Petter Selasky const struct usb_descriptor *desc; 2745e2524b2eSHans Petter Selasky const struct usb_audio20_clock_source_unit *csrc; 2746e2524b2eSHans Petter Selasky const struct usb_audio20_clock_selector_unit_0 *csel; 2747e2524b2eSHans Petter Selasky const struct usb_audio20_clock_multiplier_unit *cmul; 2748e2524b2eSHans Petter Selasky const struct usb_audio20_input_terminal *it; 2749e2524b2eSHans Petter Selasky const struct usb_audio20_output_terminal *ot; 2750e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_0 *mu; 2751e2524b2eSHans Petter Selasky const struct usb_audio20_selector_unit *su; 2752e2524b2eSHans Petter Selasky const struct usb_audio20_feature_unit *fu; 2753e2524b2eSHans Petter Selasky const struct usb_audio20_sample_rate_unit *ru; 2754e2524b2eSHans Petter Selasky const struct usb_audio20_processing_unit_0 *pu; 2755e2524b2eSHans Petter Selasky const struct usb_audio20_extension_unit_0 *eu; 2756e2524b2eSHans Petter Selasky const struct usb_audio20_effect_unit *ef; 2757e2524b2eSHans Petter Selasky } u; 27583a3f90c6SAndrew Thompson 2759e2524b2eSHans Petter Selasky u.desc = arg; 27603a3f90c6SAndrew Thompson 2761e2524b2eSHans Petter Selasky if (u.desc == NULL) 2762e2524b2eSHans Petter Selasky goto error; 2763e2524b2eSHans Petter Selasky 2764e2524b2eSHans Petter Selasky if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) 2765e2524b2eSHans Petter Selasky goto error; 2766e2524b2eSHans Petter Selasky 2767e2524b2eSHans Petter Selasky switch (u.desc->bDescriptorSubtype) { 2768e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 2769e2524b2eSHans Petter Selasky len += sizeof(*u.it); 2770e2524b2eSHans Petter Selasky break; 2771e2524b2eSHans Petter Selasky 2772e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 2773e2524b2eSHans Petter Selasky len += sizeof(*u.ot); 2774e2524b2eSHans Petter Selasky break; 2775e2524b2eSHans Petter Selasky 2776e2524b2eSHans Petter Selasky case UDESCSUB_AC_MIXER: 2777e2524b2eSHans Petter Selasky len += sizeof(*u.mu); 2778e2524b2eSHans Petter Selasky 2779e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2780e2524b2eSHans Petter Selasky goto error; 2781e2524b2eSHans Petter Selasky len += u.mu->bNrInPins; 2782e2524b2eSHans Petter Selasky 2783e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2784e2524b2eSHans Petter Selasky goto error; 2785e2524b2eSHans Petter Selasky 2786e2524b2eSHans Petter Selasky d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins); 2787e2524b2eSHans Petter Selasky 2788e2524b2eSHans Petter Selasky len += sizeof(*d1) + d1->bNrChannels; 2789e2524b2eSHans Petter Selasky break; 2790e2524b2eSHans Petter Selasky 2791e2524b2eSHans Petter Selasky case UDESCSUB_AC_SELECTOR: 2792e2524b2eSHans Petter Selasky len += sizeof(*u.su); 2793e2524b2eSHans Petter Selasky 2794e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2795e2524b2eSHans Petter Selasky goto error; 2796e2524b2eSHans Petter Selasky 2797e2524b2eSHans Petter Selasky len += u.su->bNrInPins; 2798e2524b2eSHans Petter Selasky break; 2799e2524b2eSHans Petter Selasky 2800e2524b2eSHans Petter Selasky case UDESCSUB_AC_FEATURE: 2801e2524b2eSHans Petter Selasky len += sizeof(*u.fu) + 1; 2802e2524b2eSHans Petter Selasky 2803e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2804e2524b2eSHans Petter Selasky goto error; 2805e2524b2eSHans Petter Selasky break; 2806e2524b2eSHans Petter Selasky 2807e2524b2eSHans Petter Selasky case UDESCSUB_AC_EFFECT: 2808e2524b2eSHans Petter Selasky len += sizeof(*u.ef) + 4; 2809e2524b2eSHans Petter Selasky break; 2810e2524b2eSHans Petter Selasky 2811e2524b2eSHans Petter Selasky case UDESCSUB_AC_PROCESSING_V2: 2812e2524b2eSHans Petter Selasky len += sizeof(*u.pu); 2813e2524b2eSHans Petter Selasky 2814e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2815e2524b2eSHans Petter Selasky goto error; 2816e2524b2eSHans Petter Selasky 2817e2524b2eSHans Petter Selasky len += u.pu->bNrInPins; 2818e2524b2eSHans Petter Selasky 2819e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2820e2524b2eSHans Petter Selasky goto error; 2821e2524b2eSHans Petter Selasky 2822e2524b2eSHans Petter Selasky u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins); 2823e2524b2eSHans Petter Selasky 2824e2524b2eSHans Petter Selasky len += sizeof(*u1); 2825e2524b2eSHans Petter Selasky break; 2826e2524b2eSHans Petter Selasky 2827e2524b2eSHans Petter Selasky case UDESCSUB_AC_EXTENSION_V2: 2828e2524b2eSHans Petter Selasky len += sizeof(*u.eu); 2829e2524b2eSHans Petter Selasky 2830e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2831e2524b2eSHans Petter Selasky goto error; 2832e2524b2eSHans Petter Selasky 2833e2524b2eSHans Petter Selasky len += u.eu->bNrInPins; 2834e2524b2eSHans Petter Selasky 2835e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2836e2524b2eSHans Petter Selasky goto error; 2837e2524b2eSHans Petter Selasky 2838e2524b2eSHans Petter Selasky e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins); 2839e2524b2eSHans Petter Selasky 2840e2524b2eSHans Petter Selasky len += sizeof(*e1); 2841e2524b2eSHans Petter Selasky break; 2842e2524b2eSHans Petter Selasky 2843e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SRC: 2844e2524b2eSHans Petter Selasky len += sizeof(*u.csrc); 2845e2524b2eSHans Petter Selasky break; 2846e2524b2eSHans Petter Selasky 2847e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SEL: 2848e2524b2eSHans Petter Selasky len += sizeof(*u.csel); 2849e2524b2eSHans Petter Selasky 2850e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2851e2524b2eSHans Petter Selasky goto error; 2852e2524b2eSHans Petter Selasky 2853e2524b2eSHans Petter Selasky len += u.csel->bNrInPins; 2854e2524b2eSHans Petter Selasky 2855e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2856e2524b2eSHans Petter Selasky goto error; 2857e2524b2eSHans Petter Selasky 2858e2524b2eSHans Petter Selasky c1 = (const void *)(u.csel->baCSourceId + u.csel->bNrInPins); 2859e2524b2eSHans Petter Selasky 2860e2524b2eSHans Petter Selasky len += sizeof(*c1); 2861e2524b2eSHans Petter Selasky break; 2862e2524b2eSHans Petter Selasky 2863e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_MUL: 2864e2524b2eSHans Petter Selasky len += sizeof(*u.cmul); 2865e2524b2eSHans Petter Selasky break; 2866e2524b2eSHans Petter Selasky 2867e2524b2eSHans Petter Selasky case UDESCSUB_AC_SAMPLE_RT: 2868e2524b2eSHans Petter Selasky len += sizeof(*u.ru); 2869e2524b2eSHans Petter Selasky break; 2870e2524b2eSHans Petter Selasky 2871e2524b2eSHans Petter Selasky default: 2872e2524b2eSHans Petter Selasky goto error; 28733a3f90c6SAndrew Thompson } 28743a3f90c6SAndrew Thompson 2875e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 2876e2524b2eSHans Petter Selasky goto error; 2877e2524b2eSHans Petter Selasky 2878e2524b2eSHans Petter Selasky return (u.desc); 2879e2524b2eSHans Petter Selasky 2880e2524b2eSHans Petter Selasky error: 2881e2524b2eSHans Petter Selasky if (u.desc) { 2882e2524b2eSHans Petter Selasky DPRINTF("invalid descriptor, type=%d, " 2883e2524b2eSHans Petter Selasky "sub_type=%d, len=%d of %d bytes\n", 2884e2524b2eSHans Petter Selasky u.desc->bDescriptorType, 2885e2524b2eSHans Petter Selasky u.desc->bDescriptorSubtype, 2886e2524b2eSHans Petter Selasky u.desc->bLength, len); 2887e2524b2eSHans Petter Selasky } 2888e2524b2eSHans Petter Selasky return (NULL); 2889e2524b2eSHans Petter Selasky } 28903a3f90c6SAndrew Thompson 28914c21be9bSRebecca Cran static struct usb_audio_cluster 28923a3f90c6SAndrew Thompson uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) 28933a3f90c6SAndrew Thompson { 28944c21be9bSRebecca Cran struct usb_audio_cluster r; 2895760bc48eSAndrew Thompson const struct usb_descriptor *dp; 28963a3f90c6SAndrew Thompson uint8_t i; 28973a3f90c6SAndrew Thompson 28983a3f90c6SAndrew Thompson for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) { /* avoid infinite loops */ 28993a3f90c6SAndrew Thompson dp = iot[id].u.desc; 29003a3f90c6SAndrew Thompson if (dp == NULL) { 29013a3f90c6SAndrew Thompson goto error; 29023a3f90c6SAndrew Thompson } 29033a3f90c6SAndrew Thompson switch (dp->bDescriptorSubtype) { 29043a3f90c6SAndrew Thompson case UDESCSUB_AC_INPUT: 2905e2524b2eSHans Petter Selasky r.bNrChannels = iot[id].u.it_v1->bNrChannels; 2906e2524b2eSHans Petter Selasky r.wChannelConfig[0] = iot[id].u.it_v1->wChannelConfig[0]; 2907e2524b2eSHans Petter Selasky r.wChannelConfig[1] = iot[id].u.it_v1->wChannelConfig[1]; 2908e2524b2eSHans Petter Selasky r.iChannelNames = iot[id].u.it_v1->iChannelNames; 29093a3f90c6SAndrew Thompson goto done; 29103a3f90c6SAndrew Thompson 29113a3f90c6SAndrew Thompson case UDESCSUB_AC_OUTPUT: 2912e2524b2eSHans Petter Selasky id = iot[id].u.ot_v1->bSourceId; 29133a3f90c6SAndrew Thompson break; 29143a3f90c6SAndrew Thompson 29153a3f90c6SAndrew Thompson case UDESCSUB_AC_MIXER: 29164c21be9bSRebecca Cran r = *(const struct usb_audio_cluster *) 2917e2524b2eSHans Petter Selasky &iot[id].u.mu_v1->baSourceId[ 2918e2524b2eSHans Petter Selasky iot[id].u.mu_v1->bNrInPins]; 29193a3f90c6SAndrew Thompson goto done; 29203a3f90c6SAndrew Thompson 29213a3f90c6SAndrew Thompson case UDESCSUB_AC_SELECTOR: 2922e2524b2eSHans Petter Selasky if (iot[id].u.su_v1->bNrInPins > 0) { 29233a3f90c6SAndrew Thompson /* XXX This is not really right */ 2924e2524b2eSHans Petter Selasky id = iot[id].u.su_v1->baSourceId[0]; 29253a3f90c6SAndrew Thompson } 29263a3f90c6SAndrew Thompson break; 29273a3f90c6SAndrew Thompson 29283a3f90c6SAndrew Thompson case UDESCSUB_AC_FEATURE: 2929e2524b2eSHans Petter Selasky id = iot[id].u.fu_v1->bSourceId; 29303a3f90c6SAndrew Thompson break; 29313a3f90c6SAndrew Thompson 29323a3f90c6SAndrew Thompson case UDESCSUB_AC_PROCESSING: 29334c21be9bSRebecca Cran r = *((const struct usb_audio_cluster *) 2934e2524b2eSHans Petter Selasky &iot[id].u.pu_v1->baSourceId[ 2935e2524b2eSHans Petter Selasky iot[id].u.pu_v1->bNrInPins]); 29363a3f90c6SAndrew Thompson goto done; 29373a3f90c6SAndrew Thompson 29383a3f90c6SAndrew Thompson case UDESCSUB_AC_EXTENSION: 29394c21be9bSRebecca Cran r = *((const struct usb_audio_cluster *) 2940e2524b2eSHans Petter Selasky &iot[id].u.eu_v1->baSourceId[ 2941e2524b2eSHans Petter Selasky iot[id].u.eu_v1->bNrInPins]); 29423a3f90c6SAndrew Thompson goto done; 29433a3f90c6SAndrew Thompson 29443a3f90c6SAndrew Thompson default: 29453a3f90c6SAndrew Thompson goto error; 29463a3f90c6SAndrew Thompson } 29473a3f90c6SAndrew Thompson } 29483a3f90c6SAndrew Thompson error: 29493a3f90c6SAndrew Thompson DPRINTF("bad data\n"); 29506f068a43SHans Petter Selasky memset(&r, 0, sizeof(r)); 29513a3f90c6SAndrew Thompson done: 29523a3f90c6SAndrew Thompson return (r); 29533a3f90c6SAndrew Thompson } 29543a3f90c6SAndrew Thompson 2955e2524b2eSHans Petter Selasky static struct usb_audio20_cluster 2956e2524b2eSHans Petter Selasky uaudio20_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) 29573a3f90c6SAndrew Thompson { 2958e2524b2eSHans Petter Selasky struct usb_audio20_cluster r; 2959e2524b2eSHans Petter Selasky const struct usb_descriptor *dp; 2960e2524b2eSHans Petter Selasky uint8_t i; 29613a3f90c6SAndrew Thompson 2962e2524b2eSHans Petter Selasky for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) { /* avoid infinite loops */ 2963e2524b2eSHans Petter Selasky dp = iot[id].u.desc; 2964e2524b2eSHans Petter Selasky if (dp == NULL) 2965e2524b2eSHans Petter Selasky goto error; 2966e2524b2eSHans Petter Selasky 2967e2524b2eSHans Petter Selasky switch (dp->bDescriptorSubtype) { 2968e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 2969e2524b2eSHans Petter Selasky r.bNrChannels = iot[id].u.it_v2->bNrChannels; 2970e2524b2eSHans Petter Selasky r.bmChannelConfig[0] = iot[id].u.it_v2->bmChannelConfig[0]; 2971e2524b2eSHans Petter Selasky r.bmChannelConfig[1] = iot[id].u.it_v2->bmChannelConfig[1]; 2972e2524b2eSHans Petter Selasky r.bmChannelConfig[2] = iot[id].u.it_v2->bmChannelConfig[2]; 2973e2524b2eSHans Petter Selasky r.bmChannelConfig[3] = iot[id].u.it_v2->bmChannelConfig[3]; 2974e2524b2eSHans Petter Selasky r.iChannelNames = iot[id].u.it_v2->iTerminal; 2975e2524b2eSHans Petter Selasky goto done; 2976e2524b2eSHans Petter Selasky 2977e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 2978e2524b2eSHans Petter Selasky id = iot[id].u.ot_v2->bSourceId; 29793a3f90c6SAndrew Thompson break; 29803a3f90c6SAndrew Thompson 2981e2524b2eSHans Petter Selasky case UDESCSUB_AC_MIXER: 2982e2524b2eSHans Petter Selasky r = *(const struct usb_audio20_cluster *) 2983e2524b2eSHans Petter Selasky &iot[id].u.mu_v2->baSourceId[ 2984e2524b2eSHans Petter Selasky iot[id].u.mu_v2->bNrInPins]; 2985e2524b2eSHans Petter Selasky goto done; 2986e2524b2eSHans Petter Selasky 2987e2524b2eSHans Petter Selasky case UDESCSUB_AC_SELECTOR: 2988e2524b2eSHans Petter Selasky if (iot[id].u.su_v2->bNrInPins > 0) { 2989e2524b2eSHans Petter Selasky /* XXX This is not really right */ 2990e2524b2eSHans Petter Selasky id = iot[id].u.su_v2->baSourceId[0]; 2991e2524b2eSHans Petter Selasky } 2992e2524b2eSHans Petter Selasky break; 2993e2524b2eSHans Petter Selasky 2994e2524b2eSHans Petter Selasky case UDESCSUB_AC_SAMPLE_RT: 2995e2524b2eSHans Petter Selasky id = iot[id].u.ru_v2->bSourceId; 2996e2524b2eSHans Petter Selasky break; 2997e2524b2eSHans Petter Selasky 2998e2524b2eSHans Petter Selasky case UDESCSUB_AC_EFFECT: 2999e2524b2eSHans Petter Selasky id = iot[id].u.ef_v2->bSourceId; 3000e2524b2eSHans Petter Selasky break; 3001e2524b2eSHans Petter Selasky 3002e2524b2eSHans Petter Selasky case UDESCSUB_AC_FEATURE: 3003e2524b2eSHans Petter Selasky id = iot[id].u.fu_v2->bSourceId; 3004e2524b2eSHans Petter Selasky break; 3005e2524b2eSHans Petter Selasky 3006e2524b2eSHans Petter Selasky case UDESCSUB_AC_PROCESSING_V2: 3007e2524b2eSHans Petter Selasky r = *((const struct usb_audio20_cluster *) 3008e2524b2eSHans Petter Selasky &iot[id].u.pu_v2->baSourceId[ 3009e2524b2eSHans Petter Selasky iot[id].u.pu_v2->bNrInPins]); 3010e2524b2eSHans Petter Selasky goto done; 3011e2524b2eSHans Petter Selasky 3012e2524b2eSHans Petter Selasky case UDESCSUB_AC_EXTENSION_V2: 3013e2524b2eSHans Petter Selasky r = *((const struct usb_audio20_cluster *) 3014e2524b2eSHans Petter Selasky &iot[id].u.eu_v2->baSourceId[ 3015e2524b2eSHans Petter Selasky iot[id].u.eu_v2->bNrInPins]); 3016e2524b2eSHans Petter Selasky goto done; 3017e2524b2eSHans Petter Selasky 3018e2524b2eSHans Petter Selasky default: 3019e2524b2eSHans Petter Selasky goto error; 3020e2524b2eSHans Petter Selasky } 3021e2524b2eSHans Petter Selasky } 3022e2524b2eSHans Petter Selasky error: 3023e2524b2eSHans Petter Selasky DPRINTF("Bad data!\n"); 3024e2524b2eSHans Petter Selasky memset(&r, 0, sizeof(r)); 3025e2524b2eSHans Petter Selasky done: 3026e2524b2eSHans Petter Selasky return (r); 3027e2524b2eSHans Petter Selasky } 30283a3f90c6SAndrew Thompson 30293a3f90c6SAndrew Thompson static uint16_t 30303a3f90c6SAndrew Thompson uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, 30313a3f90c6SAndrew Thompson struct uaudio_mixer_node *mix) 30323a3f90c6SAndrew Thompson { 30333a3f90c6SAndrew Thompson uint16_t terminal_type = 0x0000; 30343a3f90c6SAndrew Thompson const struct uaudio_terminal_node *input[2]; 30353a3f90c6SAndrew Thompson const struct uaudio_terminal_node *output[2]; 30363a3f90c6SAndrew Thompson 30373a3f90c6SAndrew Thompson input[0] = uaudio_mixer_get_input(iot, 0); 30383a3f90c6SAndrew Thompson input[1] = uaudio_mixer_get_input(iot, 1); 30393a3f90c6SAndrew Thompson 30403a3f90c6SAndrew Thompson output[0] = uaudio_mixer_get_output(iot, 0); 30413a3f90c6SAndrew Thompson output[1] = uaudio_mixer_get_output(iot, 1); 30423a3f90c6SAndrew Thompson 30433a3f90c6SAndrew Thompson /* 30443a3f90c6SAndrew Thompson * check if there is only 30453a3f90c6SAndrew Thompson * one output terminal: 30463a3f90c6SAndrew Thompson */ 30473a3f90c6SAndrew Thompson if (output[0] && (!output[1])) { 3048e2524b2eSHans Petter Selasky terminal_type = 3049e2524b2eSHans Petter Selasky UGETW(output[0]->u.ot_v1->wTerminalType); 30503a3f90c6SAndrew Thompson } 30513a3f90c6SAndrew Thompson /* 30523a3f90c6SAndrew Thompson * If the only output terminal is USB, 30533a3f90c6SAndrew Thompson * the class is UAC_RECORD. 30543a3f90c6SAndrew Thompson */ 30553a3f90c6SAndrew Thompson if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { 30563a3f90c6SAndrew Thompson 30573a3f90c6SAndrew Thompson mix->class = UAC_RECORD; 30583a3f90c6SAndrew Thompson if (input[0] && (!input[1])) { 3059e2524b2eSHans Petter Selasky terminal_type = 3060e2524b2eSHans Petter Selasky UGETW(input[0]->u.it_v1->wTerminalType); 30613a3f90c6SAndrew Thompson } else { 30623a3f90c6SAndrew Thompson terminal_type = 0; 30633a3f90c6SAndrew Thompson } 30643a3f90c6SAndrew Thompson goto done; 30653a3f90c6SAndrew Thompson } 30663a3f90c6SAndrew Thompson /* 30673a3f90c6SAndrew Thompson * if the unit is connected to just 30683a3f90c6SAndrew Thompson * one input terminal, the 30693a3f90c6SAndrew Thompson * class is UAC_INPUT: 30703a3f90c6SAndrew Thompson */ 30713a3f90c6SAndrew Thompson if (input[0] && (!input[1])) { 30723a3f90c6SAndrew Thompson mix->class = UAC_INPUT; 3073e2524b2eSHans Petter Selasky terminal_type = 3074e2524b2eSHans Petter Selasky UGETW(input[0]->u.it_v1->wTerminalType); 3075e2524b2eSHans Petter Selasky goto done; 3076e2524b2eSHans Petter Selasky } 3077e2524b2eSHans Petter Selasky /* 3078e2524b2eSHans Petter Selasky * Otherwise, the class is UAC_OUTPUT. 3079e2524b2eSHans Petter Selasky */ 3080e2524b2eSHans Petter Selasky mix->class = UAC_OUTPUT; 3081e2524b2eSHans Petter Selasky done: 3082e2524b2eSHans Petter Selasky return (terminal_type); 3083e2524b2eSHans Petter Selasky } 3084e2524b2eSHans Petter Selasky 3085e2524b2eSHans Petter Selasky static uint16_t 3086e2524b2eSHans Petter Selasky uaudio20_mixer_determine_class(const struct uaudio_terminal_node *iot, 3087e2524b2eSHans Petter Selasky struct uaudio_mixer_node *mix) 3088e2524b2eSHans Petter Selasky { 3089e2524b2eSHans Petter Selasky uint16_t terminal_type = 0x0000; 3090e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *input[2]; 3091e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *output[2]; 3092e2524b2eSHans Petter Selasky 3093e2524b2eSHans Petter Selasky input[0] = uaudio_mixer_get_input(iot, 0); 3094e2524b2eSHans Petter Selasky input[1] = uaudio_mixer_get_input(iot, 1); 3095e2524b2eSHans Petter Selasky 3096e2524b2eSHans Petter Selasky output[0] = uaudio_mixer_get_output(iot, 0); 3097e2524b2eSHans Petter Selasky output[1] = uaudio_mixer_get_output(iot, 1); 3098e2524b2eSHans Petter Selasky 3099e2524b2eSHans Petter Selasky /* 3100e2524b2eSHans Petter Selasky * check if there is only 3101e2524b2eSHans Petter Selasky * one output terminal: 3102e2524b2eSHans Petter Selasky */ 3103e2524b2eSHans Petter Selasky if (output[0] && (!output[1])) 3104e2524b2eSHans Petter Selasky terminal_type = UGETW(output[0]->u.ot_v2->wTerminalType); 3105e2524b2eSHans Petter Selasky /* 3106e2524b2eSHans Petter Selasky * If the only output terminal is USB, 3107e2524b2eSHans Petter Selasky * the class is UAC_RECORD. 3108e2524b2eSHans Petter Selasky */ 3109e2524b2eSHans Petter Selasky if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { 3110e2524b2eSHans Petter Selasky 3111e2524b2eSHans Petter Selasky mix->class = UAC_RECORD; 3112e2524b2eSHans Petter Selasky if (input[0] && (!input[1])) { 3113e2524b2eSHans Petter Selasky terminal_type = 3114e2524b2eSHans Petter Selasky UGETW(input[0]->u.it_v2->wTerminalType); 3115e2524b2eSHans Petter Selasky } else { 3116e2524b2eSHans Petter Selasky terminal_type = 0; 3117e2524b2eSHans Petter Selasky } 3118e2524b2eSHans Petter Selasky goto done; 3119e2524b2eSHans Petter Selasky } 3120e2524b2eSHans Petter Selasky /* 3121e2524b2eSHans Petter Selasky * if the unit is connected to just 3122e2524b2eSHans Petter Selasky * one input terminal, the 3123e2524b2eSHans Petter Selasky * class is UAC_INPUT: 3124e2524b2eSHans Petter Selasky */ 3125e2524b2eSHans Petter Selasky if (input[0] && (!input[1])) { 3126e2524b2eSHans Petter Selasky mix->class = UAC_INPUT; 3127e2524b2eSHans Petter Selasky terminal_type = 3128e2524b2eSHans Petter Selasky UGETW(input[0]->u.it_v2->wTerminalType); 31293a3f90c6SAndrew Thompson goto done; 31303a3f90c6SAndrew Thompson } 31313a3f90c6SAndrew Thompson /* 31323a3f90c6SAndrew Thompson * Otherwise, the class is UAC_OUTPUT. 31333a3f90c6SAndrew Thompson */ 31343a3f90c6SAndrew Thompson mix->class = UAC_OUTPUT; 31353a3f90c6SAndrew Thompson done: 31363a3f90c6SAndrew Thompson return (terminal_type); 31373a3f90c6SAndrew Thompson } 31383a3f90c6SAndrew Thompson 31393a3f90c6SAndrew Thompson struct uaudio_tt_to_feature { 31403a3f90c6SAndrew Thompson uint16_t terminal_type; 31413a3f90c6SAndrew Thompson uint16_t feature; 31423a3f90c6SAndrew Thompson }; 31433a3f90c6SAndrew Thompson 31443a3f90c6SAndrew Thompson static const struct uaudio_tt_to_feature uaudio_tt_to_feature[] = { 31453a3f90c6SAndrew Thompson 31463a3f90c6SAndrew Thompson {UAT_STREAM, SOUND_MIXER_PCM}, 31473a3f90c6SAndrew Thompson 31483a3f90c6SAndrew Thompson {UATI_MICROPHONE, SOUND_MIXER_MIC}, 31493a3f90c6SAndrew Thompson {UATI_DESKMICROPHONE, SOUND_MIXER_MIC}, 31503a3f90c6SAndrew Thompson {UATI_PERSONALMICROPHONE, SOUND_MIXER_MIC}, 31513a3f90c6SAndrew Thompson {UATI_OMNIMICROPHONE, SOUND_MIXER_MIC}, 31523a3f90c6SAndrew Thompson {UATI_MICROPHONEARRAY, SOUND_MIXER_MIC}, 31533a3f90c6SAndrew Thompson {UATI_PROCMICROPHONEARR, SOUND_MIXER_MIC}, 31543a3f90c6SAndrew Thompson 31553a3f90c6SAndrew Thompson {UATO_SPEAKER, SOUND_MIXER_SPEAKER}, 31563a3f90c6SAndrew Thompson {UATO_DESKTOPSPEAKER, SOUND_MIXER_SPEAKER}, 31573a3f90c6SAndrew Thompson {UATO_ROOMSPEAKER, SOUND_MIXER_SPEAKER}, 31583a3f90c6SAndrew Thompson {UATO_COMMSPEAKER, SOUND_MIXER_SPEAKER}, 31593a3f90c6SAndrew Thompson 31603a3f90c6SAndrew Thompson {UATE_ANALOGCONN, SOUND_MIXER_LINE}, 31613a3f90c6SAndrew Thompson {UATE_LINECONN, SOUND_MIXER_LINE}, 31623a3f90c6SAndrew Thompson {UATE_LEGACYCONN, SOUND_MIXER_LINE}, 31633a3f90c6SAndrew Thompson 31643a3f90c6SAndrew Thompson {UATE_DIGITALAUIFC, SOUND_MIXER_ALTPCM}, 31653a3f90c6SAndrew Thompson {UATE_SPDIF, SOUND_MIXER_ALTPCM}, 31663a3f90c6SAndrew Thompson {UATE_1394DA, SOUND_MIXER_ALTPCM}, 31673a3f90c6SAndrew Thompson {UATE_1394DV, SOUND_MIXER_ALTPCM}, 31683a3f90c6SAndrew Thompson 31693a3f90c6SAndrew Thompson {UATF_CDPLAYER, SOUND_MIXER_CD}, 31703a3f90c6SAndrew Thompson 31713a3f90c6SAndrew Thompson {UATF_SYNTHESIZER, SOUND_MIXER_SYNTH}, 31723a3f90c6SAndrew Thompson 31733a3f90c6SAndrew Thompson {UATF_VIDEODISCAUDIO, SOUND_MIXER_VIDEO}, 31743a3f90c6SAndrew Thompson {UATF_DVDAUDIO, SOUND_MIXER_VIDEO}, 31753a3f90c6SAndrew Thompson {UATF_TVTUNERAUDIO, SOUND_MIXER_VIDEO}, 31763a3f90c6SAndrew Thompson 31773a3f90c6SAndrew Thompson /* telephony terminal types */ 31783a3f90c6SAndrew Thompson {UATT_UNDEFINED, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ 31793a3f90c6SAndrew Thompson {UATT_PHONELINE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ 31803a3f90c6SAndrew Thompson {UATT_TELEPHONE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ 31813a3f90c6SAndrew Thompson {UATT_DOWNLINEPHONE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ 31823a3f90c6SAndrew Thompson 31833a3f90c6SAndrew Thompson {UATF_RADIORECV, SOUND_MIXER_RADIO}, 31843a3f90c6SAndrew Thompson {UATF_RADIOXMIT, SOUND_MIXER_RADIO}, 31853a3f90c6SAndrew Thompson 31863a3f90c6SAndrew Thompson {UAT_UNDEFINED, SOUND_MIXER_VOLUME}, 31873a3f90c6SAndrew Thompson {UAT_VENDOR, SOUND_MIXER_VOLUME}, 31883a3f90c6SAndrew Thompson {UATI_UNDEFINED, SOUND_MIXER_VOLUME}, 31893a3f90c6SAndrew Thompson 31903a3f90c6SAndrew Thompson /* output terminal types */ 31913a3f90c6SAndrew Thompson {UATO_UNDEFINED, SOUND_MIXER_VOLUME}, 31923a3f90c6SAndrew Thompson {UATO_DISPLAYAUDIO, SOUND_MIXER_VOLUME}, 31933a3f90c6SAndrew Thompson {UATO_SUBWOOFER, SOUND_MIXER_VOLUME}, 31943a3f90c6SAndrew Thompson {UATO_HEADPHONES, SOUND_MIXER_VOLUME}, 31953a3f90c6SAndrew Thompson 31963a3f90c6SAndrew Thompson /* bidir terminal types */ 31973a3f90c6SAndrew Thompson {UATB_UNDEFINED, SOUND_MIXER_VOLUME}, 31983a3f90c6SAndrew Thompson {UATB_HANDSET, SOUND_MIXER_VOLUME}, 31993a3f90c6SAndrew Thompson {UATB_HEADSET, SOUND_MIXER_VOLUME}, 32003a3f90c6SAndrew Thompson {UATB_SPEAKERPHONE, SOUND_MIXER_VOLUME}, 32013a3f90c6SAndrew Thompson {UATB_SPEAKERPHONEESUP, SOUND_MIXER_VOLUME}, 32023a3f90c6SAndrew Thompson {UATB_SPEAKERPHONEECANC, SOUND_MIXER_VOLUME}, 32033a3f90c6SAndrew Thompson 32043a3f90c6SAndrew Thompson /* external terminal types */ 32053a3f90c6SAndrew Thompson {UATE_UNDEFINED, SOUND_MIXER_VOLUME}, 32063a3f90c6SAndrew Thompson 32073a3f90c6SAndrew Thompson /* embedded function terminal types */ 32083a3f90c6SAndrew Thompson {UATF_UNDEFINED, SOUND_MIXER_VOLUME}, 32093a3f90c6SAndrew Thompson {UATF_CALIBNOISE, SOUND_MIXER_VOLUME}, 32103a3f90c6SAndrew Thompson {UATF_EQUNOISE, SOUND_MIXER_VOLUME}, 32113a3f90c6SAndrew Thompson {UATF_DAT, SOUND_MIXER_VOLUME}, 32123a3f90c6SAndrew Thompson {UATF_DCC, SOUND_MIXER_VOLUME}, 32133a3f90c6SAndrew Thompson {UATF_MINIDISK, SOUND_MIXER_VOLUME}, 32143a3f90c6SAndrew Thompson {UATF_ANALOGTAPE, SOUND_MIXER_VOLUME}, 32153a3f90c6SAndrew Thompson {UATF_PHONOGRAPH, SOUND_MIXER_VOLUME}, 32163a3f90c6SAndrew Thompson {UATF_VCRAUDIO, SOUND_MIXER_VOLUME}, 32173a3f90c6SAndrew Thompson {UATF_SATELLITE, SOUND_MIXER_VOLUME}, 32183a3f90c6SAndrew Thompson {UATF_CABLETUNER, SOUND_MIXER_VOLUME}, 32193a3f90c6SAndrew Thompson {UATF_DSS, SOUND_MIXER_VOLUME}, 32203a3f90c6SAndrew Thompson {UATF_MULTITRACK, SOUND_MIXER_VOLUME}, 32213a3f90c6SAndrew Thompson {0xffff, SOUND_MIXER_VOLUME}, 32223a3f90c6SAndrew Thompson 32233a3f90c6SAndrew Thompson /* default */ 32243a3f90c6SAndrew Thompson {0x0000, SOUND_MIXER_VOLUME}, 32253a3f90c6SAndrew Thompson }; 32263a3f90c6SAndrew Thompson 32273a3f90c6SAndrew Thompson static uint16_t 32283a3f90c6SAndrew Thompson uaudio_mixer_feature_name(const struct uaudio_terminal_node *iot, 32293a3f90c6SAndrew Thompson struct uaudio_mixer_node *mix) 32303a3f90c6SAndrew Thompson { 32313a3f90c6SAndrew Thompson const struct uaudio_tt_to_feature *uat = uaudio_tt_to_feature; 32323a3f90c6SAndrew Thompson uint16_t terminal_type = uaudio_mixer_determine_class(iot, mix); 32333a3f90c6SAndrew Thompson 32343a3f90c6SAndrew Thompson if ((mix->class == UAC_RECORD) && (terminal_type == 0)) { 32353a3f90c6SAndrew Thompson return (SOUND_MIXER_IMIX); 32363a3f90c6SAndrew Thompson } 32373a3f90c6SAndrew Thompson while (uat->terminal_type) { 32383a3f90c6SAndrew Thompson if (uat->terminal_type == terminal_type) { 32393a3f90c6SAndrew Thompson break; 32403a3f90c6SAndrew Thompson } 32413a3f90c6SAndrew Thompson uat++; 32423a3f90c6SAndrew Thompson } 32433a3f90c6SAndrew Thompson 3244e2524b2eSHans Petter Selasky DPRINTF("terminal_type=0x%04x -> %d\n", 3245e2524b2eSHans Petter Selasky terminal_type, uat->feature); 3246e2524b2eSHans Petter Selasky 3247e2524b2eSHans Petter Selasky return (uat->feature); 3248e2524b2eSHans Petter Selasky } 3249e2524b2eSHans Petter Selasky 3250e2524b2eSHans Petter Selasky static uint16_t 3251e2524b2eSHans Petter Selasky uaudio20_mixer_feature_name(const struct uaudio_terminal_node *iot, 3252e2524b2eSHans Petter Selasky struct uaudio_mixer_node *mix) 3253e2524b2eSHans Petter Selasky { 3254e2524b2eSHans Petter Selasky const struct uaudio_tt_to_feature *uat; 3255e2524b2eSHans Petter Selasky uint16_t terminal_type = uaudio20_mixer_determine_class(iot, mix); 3256e2524b2eSHans Petter Selasky 3257e2524b2eSHans Petter Selasky if ((mix->class == UAC_RECORD) && (terminal_type == 0)) 3258e2524b2eSHans Petter Selasky return (SOUND_MIXER_IMIX); 3259e2524b2eSHans Petter Selasky 3260e2524b2eSHans Petter Selasky for (uat = uaudio_tt_to_feature; uat->terminal_type != 0; uat++) { 3261e2524b2eSHans Petter Selasky if (uat->terminal_type == terminal_type) 3262e2524b2eSHans Petter Selasky break; 3263e2524b2eSHans Petter Selasky } 3264e2524b2eSHans Petter Selasky 3265e2524b2eSHans Petter Selasky DPRINTF("terminal_type=0x%04x -> %d\n", 32663a3f90c6SAndrew Thompson terminal_type, uat->feature); 32673a3f90c6SAndrew Thompson 32683a3f90c6SAndrew Thompson return (uat->feature); 32693a3f90c6SAndrew Thompson } 32703a3f90c6SAndrew Thompson 32716d917491SHans Petter Selasky static const struct uaudio_terminal_node * 32726d917491SHans Petter Selasky uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t i) 32733a3f90c6SAndrew Thompson { 32743a3f90c6SAndrew Thompson struct uaudio_terminal_node *root = iot->root; 32753a3f90c6SAndrew Thompson uint8_t n; 32763a3f90c6SAndrew Thompson 32773a3f90c6SAndrew Thompson n = iot->usr.id_max; 32783a3f90c6SAndrew Thompson do { 32793a3f90c6SAndrew Thompson if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) { 32806d917491SHans Petter Selasky if (!i--) 32813a3f90c6SAndrew Thompson return (root + n); 32823a3f90c6SAndrew Thompson } 32833a3f90c6SAndrew Thompson } while (n--); 32843a3f90c6SAndrew Thompson 32853a3f90c6SAndrew Thompson return (NULL); 32863a3f90c6SAndrew Thompson } 32873a3f90c6SAndrew Thompson 32886d917491SHans Petter Selasky static const struct uaudio_terminal_node * 32896d917491SHans Petter Selasky uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t i) 32903a3f90c6SAndrew Thompson { 32913a3f90c6SAndrew Thompson struct uaudio_terminal_node *root = iot->root; 32923a3f90c6SAndrew Thompson uint8_t n; 32933a3f90c6SAndrew Thompson 32943a3f90c6SAndrew Thompson n = iot->usr.id_max; 32953a3f90c6SAndrew Thompson do { 32963a3f90c6SAndrew Thompson if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) { 32976d917491SHans Petter Selasky if (!i--) 32983a3f90c6SAndrew Thompson return (root + n); 32993a3f90c6SAndrew Thompson } 33003a3f90c6SAndrew Thompson } while (n--); 33013a3f90c6SAndrew Thompson 33023a3f90c6SAndrew Thompson return (NULL); 33033a3f90c6SAndrew Thompson } 33043a3f90c6SAndrew Thompson 33053a3f90c6SAndrew Thompson static void 33063a3f90c6SAndrew Thompson uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root, 33073a3f90c6SAndrew Thompson const uint8_t *p_id, uint8_t n_id, 33083a3f90c6SAndrew Thompson struct uaudio_search_result *info) 33093a3f90c6SAndrew Thompson { 33103a3f90c6SAndrew Thompson struct uaudio_terminal_node *iot; 33113a3f90c6SAndrew Thompson uint8_t n; 33123a3f90c6SAndrew Thompson uint8_t i; 3313e2524b2eSHans Petter Selasky uint8_t is_last; 33143a3f90c6SAndrew Thompson 3315e2524b2eSHans Petter Selasky top: 33163a3f90c6SAndrew Thompson for (n = 0; n < n_id; n++) { 33173a3f90c6SAndrew Thompson 33183a3f90c6SAndrew Thompson i = p_id[n]; 33193a3f90c6SAndrew Thompson 3320e2524b2eSHans Petter Selasky if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { 33213a3f90c6SAndrew Thompson DPRINTF("avoided going into a circle at id=%d!\n", i); 3322e2524b2eSHans Petter Selasky return; 33233a3f90c6SAndrew Thompson } 33243a3f90c6SAndrew Thompson 3325e2524b2eSHans Petter Selasky info->recurse_level++; 3326e2524b2eSHans Petter Selasky 33273a3f90c6SAndrew Thompson iot = (root + i); 33283a3f90c6SAndrew Thompson 3329e2524b2eSHans Petter Selasky if (iot->u.desc == NULL) 33303a3f90c6SAndrew Thompson continue; 3331e2524b2eSHans Petter Selasky 3332e2524b2eSHans Petter Selasky is_last = ((n + 1) == n_id); 3333e2524b2eSHans Petter Selasky 33343a3f90c6SAndrew Thompson switch (iot->u.desc->bDescriptorSubtype) { 33353a3f90c6SAndrew Thompson case UDESCSUB_AC_INPUT: 33363a3f90c6SAndrew Thompson info->bit_input[i / 8] |= (1 << (i % 8)); 33373a3f90c6SAndrew Thompson break; 33383a3f90c6SAndrew Thompson 33393a3f90c6SAndrew Thompson case UDESCSUB_AC_FEATURE: 3340e2524b2eSHans Petter Selasky if (is_last) { 3341e2524b2eSHans Petter Selasky p_id = &iot->u.fu_v1->bSourceId; 3342e2524b2eSHans Petter Selasky n_id = 1; 3343e2524b2eSHans Petter Selasky goto top; 3344e2524b2eSHans Petter Selasky } 3345e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 3346e2524b2eSHans Petter Selasky root, &iot->u.fu_v1->bSourceId, 1, info); 33473a3f90c6SAndrew Thompson break; 33483a3f90c6SAndrew Thompson 33493a3f90c6SAndrew Thompson case UDESCSUB_AC_OUTPUT: 3350e2524b2eSHans Petter Selasky if (is_last) { 3351e2524b2eSHans Petter Selasky p_id = &iot->u.ot_v1->bSourceId; 3352e2524b2eSHans Petter Selasky n_id = 1; 3353e2524b2eSHans Petter Selasky goto top; 3354e2524b2eSHans Petter Selasky } 3355e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 3356e2524b2eSHans Petter Selasky root, &iot->u.ot_v1->bSourceId, 1, info); 33573a3f90c6SAndrew Thompson break; 33583a3f90c6SAndrew Thompson 33593a3f90c6SAndrew Thompson case UDESCSUB_AC_MIXER: 3360e2524b2eSHans Petter Selasky if (is_last) { 3361e2524b2eSHans Petter Selasky p_id = iot->u.mu_v1->baSourceId; 3362e2524b2eSHans Petter Selasky n_id = iot->u.mu_v1->bNrInPins; 3363e2524b2eSHans Petter Selasky goto top; 3364e2524b2eSHans Petter Selasky } 3365e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 3366e2524b2eSHans Petter Selasky root, iot->u.mu_v1->baSourceId, 3367e2524b2eSHans Petter Selasky iot->u.mu_v1->bNrInPins, info); 33683a3f90c6SAndrew Thompson break; 33693a3f90c6SAndrew Thompson 33703a3f90c6SAndrew Thompson case UDESCSUB_AC_SELECTOR: 3371e2524b2eSHans Petter Selasky if (is_last) { 3372e2524b2eSHans Petter Selasky p_id = iot->u.su_v1->baSourceId; 3373e2524b2eSHans Petter Selasky n_id = iot->u.su_v1->bNrInPins; 3374e2524b2eSHans Petter Selasky goto top; 3375e2524b2eSHans Petter Selasky } 3376e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 3377e2524b2eSHans Petter Selasky root, iot->u.su_v1->baSourceId, 3378e2524b2eSHans Petter Selasky iot->u.su_v1->bNrInPins, info); 33793a3f90c6SAndrew Thompson break; 33803a3f90c6SAndrew Thompson 33813a3f90c6SAndrew Thompson case UDESCSUB_AC_PROCESSING: 3382e2524b2eSHans Petter Selasky if (is_last) { 3383e2524b2eSHans Petter Selasky p_id = iot->u.pu_v1->baSourceId; 3384e2524b2eSHans Petter Selasky n_id = iot->u.pu_v1->bNrInPins; 3385e2524b2eSHans Petter Selasky goto top; 3386e2524b2eSHans Petter Selasky } 3387e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 3388e2524b2eSHans Petter Selasky root, iot->u.pu_v1->baSourceId, 3389e2524b2eSHans Petter Selasky iot->u.pu_v1->bNrInPins, info); 33903a3f90c6SAndrew Thompson break; 33913a3f90c6SAndrew Thompson 33923a3f90c6SAndrew Thompson case UDESCSUB_AC_EXTENSION: 3393e2524b2eSHans Petter Selasky if (is_last) { 3394e2524b2eSHans Petter Selasky p_id = iot->u.eu_v1->baSourceId; 3395e2524b2eSHans Petter Selasky n_id = iot->u.eu_v1->bNrInPins; 3396e2524b2eSHans Petter Selasky goto top; 3397e2524b2eSHans Petter Selasky } 3398e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 3399e2524b2eSHans Petter Selasky root, iot->u.eu_v1->baSourceId, 3400e2524b2eSHans Petter Selasky iot->u.eu_v1->bNrInPins, info); 34013a3f90c6SAndrew Thompson break; 34023a3f90c6SAndrew Thompson 34033a3f90c6SAndrew Thompson default: 34043a3f90c6SAndrew Thompson break; 34053a3f90c6SAndrew Thompson } 34063a3f90c6SAndrew Thompson } 3407e2524b2eSHans Petter Selasky } 3408e2524b2eSHans Petter Selasky 3409e2524b2eSHans Petter Selasky static void 3410e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *root, 3411e2524b2eSHans Petter Selasky const uint8_t *p_id, uint8_t n_id, 3412e2524b2eSHans Petter Selasky struct uaudio_search_result *info) 3413e2524b2eSHans Petter Selasky { 3414e2524b2eSHans Petter Selasky struct uaudio_terminal_node *iot; 3415e2524b2eSHans Petter Selasky uint8_t n; 3416e2524b2eSHans Petter Selasky uint8_t i; 3417e2524b2eSHans Petter Selasky uint8_t is_last; 3418e2524b2eSHans Petter Selasky 3419e2524b2eSHans Petter Selasky top: 3420e2524b2eSHans Petter Selasky for (n = 0; n < n_id; n++) { 3421e2524b2eSHans Petter Selasky 3422e2524b2eSHans Petter Selasky i = p_id[n]; 3423e2524b2eSHans Petter Selasky 3424e2524b2eSHans Petter Selasky if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { 3425e2524b2eSHans Petter Selasky DPRINTF("avoided going into a circle at id=%d!\n", i); 3426e2524b2eSHans Petter Selasky return; 3427e2524b2eSHans Petter Selasky } 3428e2524b2eSHans Petter Selasky 3429e2524b2eSHans Petter Selasky info->recurse_level++; 3430e2524b2eSHans Petter Selasky 3431e2524b2eSHans Petter Selasky iot = (root + i); 3432e2524b2eSHans Petter Selasky 3433e2524b2eSHans Petter Selasky if (iot->u.desc == NULL) 3434e2524b2eSHans Petter Selasky continue; 3435e2524b2eSHans Petter Selasky 3436e2524b2eSHans Petter Selasky is_last = ((n + 1) == n_id); 3437e2524b2eSHans Petter Selasky 3438e2524b2eSHans Petter Selasky switch (iot->u.desc->bDescriptorSubtype) { 3439e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 3440e2524b2eSHans Petter Selasky info->bit_input[i / 8] |= (1 << (i % 8)); 3441e2524b2eSHans Petter Selasky break; 3442e2524b2eSHans Petter Selasky 3443e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 3444e2524b2eSHans Petter Selasky if (is_last) { 3445e2524b2eSHans Petter Selasky p_id = &iot->u.ot_v2->bSourceId; 3446e2524b2eSHans Petter Selasky n_id = 1; 3447e2524b2eSHans Petter Selasky goto top; 3448e2524b2eSHans Petter Selasky } 3449e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 3450e2524b2eSHans Petter Selasky root, &iot->u.ot_v2->bSourceId, 1, info); 3451e2524b2eSHans Petter Selasky break; 3452e2524b2eSHans Petter Selasky 3453e2524b2eSHans Petter Selasky case UDESCSUB_AC_MIXER: 3454e2524b2eSHans Petter Selasky if (is_last) { 3455e2524b2eSHans Petter Selasky p_id = iot->u.mu_v2->baSourceId; 3456e2524b2eSHans Petter Selasky n_id = iot->u.mu_v2->bNrInPins; 3457e2524b2eSHans Petter Selasky goto top; 3458e2524b2eSHans Petter Selasky } 3459e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 3460e2524b2eSHans Petter Selasky root, iot->u.mu_v2->baSourceId, 3461e2524b2eSHans Petter Selasky iot->u.mu_v2->bNrInPins, info); 3462e2524b2eSHans Petter Selasky break; 3463e2524b2eSHans Petter Selasky 3464e2524b2eSHans Petter Selasky case UDESCSUB_AC_SELECTOR: 3465e2524b2eSHans Petter Selasky if (is_last) { 3466e2524b2eSHans Petter Selasky p_id = iot->u.su_v2->baSourceId; 3467e2524b2eSHans Petter Selasky n_id = iot->u.su_v2->bNrInPins; 3468e2524b2eSHans Petter Selasky goto top; 3469e2524b2eSHans Petter Selasky } 3470e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 3471e2524b2eSHans Petter Selasky root, iot->u.su_v2->baSourceId, 3472e2524b2eSHans Petter Selasky iot->u.su_v2->bNrInPins, info); 3473e2524b2eSHans Petter Selasky break; 3474e2524b2eSHans Petter Selasky 3475e2524b2eSHans Petter Selasky case UDESCSUB_AC_SAMPLE_RT: 3476e2524b2eSHans Petter Selasky if (is_last) { 3477e2524b2eSHans Petter Selasky p_id = &iot->u.ru_v2->bSourceId; 3478e2524b2eSHans Petter Selasky n_id = 1; 3479e2524b2eSHans Petter Selasky goto top; 3480e2524b2eSHans Petter Selasky } 3481e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 3482e2524b2eSHans Petter Selasky root, &iot->u.ru_v2->bSourceId, 3483e2524b2eSHans Petter Selasky 1, info); 3484e2524b2eSHans Petter Selasky break; 3485e2524b2eSHans Petter Selasky 3486e2524b2eSHans Petter Selasky case UDESCSUB_AC_EFFECT: 3487e2524b2eSHans Petter Selasky if (is_last) { 3488e2524b2eSHans Petter Selasky p_id = &iot->u.ef_v2->bSourceId; 3489e2524b2eSHans Petter Selasky n_id = 1; 3490e2524b2eSHans Petter Selasky goto top; 3491e2524b2eSHans Petter Selasky } 3492e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 3493e2524b2eSHans Petter Selasky root, &iot->u.ef_v2->bSourceId, 3494e2524b2eSHans Petter Selasky 1, info); 3495e2524b2eSHans Petter Selasky break; 3496e2524b2eSHans Petter Selasky 3497e2524b2eSHans Petter Selasky case UDESCSUB_AC_FEATURE: 3498e2524b2eSHans Petter Selasky if (is_last) { 3499e2524b2eSHans Petter Selasky p_id = &iot->u.fu_v2->bSourceId; 3500e2524b2eSHans Petter Selasky n_id = 1; 3501e2524b2eSHans Petter Selasky goto top; 3502e2524b2eSHans Petter Selasky } 3503e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 3504e2524b2eSHans Petter Selasky root, &iot->u.fu_v2->bSourceId, 1, info); 3505e2524b2eSHans Petter Selasky break; 3506e2524b2eSHans Petter Selasky 3507e2524b2eSHans Petter Selasky case UDESCSUB_AC_PROCESSING_V2: 3508e2524b2eSHans Petter Selasky if (is_last) { 3509e2524b2eSHans Petter Selasky p_id = iot->u.pu_v2->baSourceId; 3510e2524b2eSHans Petter Selasky n_id = iot->u.pu_v2->bNrInPins; 3511e2524b2eSHans Petter Selasky goto top; 3512e2524b2eSHans Petter Selasky } 3513e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 3514e2524b2eSHans Petter Selasky root, iot->u.pu_v2->baSourceId, 3515e2524b2eSHans Petter Selasky iot->u.pu_v2->bNrInPins, info); 3516e2524b2eSHans Petter Selasky break; 3517e2524b2eSHans Petter Selasky 3518e2524b2eSHans Petter Selasky case UDESCSUB_AC_EXTENSION_V2: 3519e2524b2eSHans Petter Selasky if (is_last) { 3520e2524b2eSHans Petter Selasky p_id = iot->u.eu_v2->baSourceId; 3521e2524b2eSHans Petter Selasky n_id = iot->u.eu_v2->bNrInPins; 3522e2524b2eSHans Petter Selasky goto top; 3523e2524b2eSHans Petter Selasky } 3524e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 3525e2524b2eSHans Petter Selasky root, iot->u.eu_v2->baSourceId, 3526e2524b2eSHans Petter Selasky iot->u.eu_v2->bNrInPins, info); 3527e2524b2eSHans Petter Selasky break; 3528e2524b2eSHans Petter Selasky default: 3529e2524b2eSHans Petter Selasky break; 3530e2524b2eSHans Petter Selasky } 3531e2524b2eSHans Petter Selasky } 3532e2524b2eSHans Petter Selasky } 3533e2524b2eSHans Petter Selasky 3534e2524b2eSHans Petter Selasky static void 3535e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(struct uaudio_terminal_node *root, 3536e2524b2eSHans Petter Selasky const uint8_t *p_id, uint8_t n_id, 3537e2524b2eSHans Petter Selasky struct uaudio_search_result *info) 3538e2524b2eSHans Petter Selasky { 3539e2524b2eSHans Petter Selasky struct uaudio_terminal_node *iot; 3540e2524b2eSHans Petter Selasky uint8_t n; 3541e2524b2eSHans Petter Selasky uint8_t i; 3542e2524b2eSHans Petter Selasky uint8_t is_last; 3543e2524b2eSHans Petter Selasky uint8_t id; 3544e2524b2eSHans Petter Selasky 3545e2524b2eSHans Petter Selasky top: 3546e2524b2eSHans Petter Selasky for (n = 0; n < n_id; n++) { 3547e2524b2eSHans Petter Selasky 3548e2524b2eSHans Petter Selasky i = p_id[n]; 3549e2524b2eSHans Petter Selasky 3550e2524b2eSHans Petter Selasky if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { 3551e2524b2eSHans Petter Selasky DPRINTF("avoided going into a circle at id=%d!\n", i); 3552e2524b2eSHans Petter Selasky return; 3553e2524b2eSHans Petter Selasky } 3554e2524b2eSHans Petter Selasky 3555e2524b2eSHans Petter Selasky info->recurse_level++; 3556e2524b2eSHans Petter Selasky 3557e2524b2eSHans Petter Selasky iot = (root + i); 3558e2524b2eSHans Petter Selasky 3559e2524b2eSHans Petter Selasky if (iot->u.desc == NULL) 3560e2524b2eSHans Petter Selasky continue; 3561e2524b2eSHans Petter Selasky 3562e2524b2eSHans Petter Selasky is_last = ((n + 1) == n_id); 3563e2524b2eSHans Petter Selasky 3564e2524b2eSHans Petter Selasky switch (iot->u.desc->bDescriptorSubtype) { 3565e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 3566e2524b2eSHans Petter Selasky info->is_input = 1; 3567e2524b2eSHans Petter Selasky if (is_last) { 3568e2524b2eSHans Petter Selasky p_id = &iot->u.it_v2->bCSourceId; 3569e2524b2eSHans Petter Selasky n_id = 1; 3570e2524b2eSHans Petter Selasky goto top; 3571e2524b2eSHans Petter Selasky } 3572e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(root, 3573e2524b2eSHans Petter Selasky &iot->u.it_v2->bCSourceId, 1, info); 3574e2524b2eSHans Petter Selasky break; 3575e2524b2eSHans Petter Selasky 3576e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 3577e2524b2eSHans Petter Selasky info->is_input = 0; 3578e2524b2eSHans Petter Selasky if (is_last) { 3579e2524b2eSHans Petter Selasky p_id = &iot->u.ot_v2->bCSourceId; 3580e2524b2eSHans Petter Selasky n_id = 1; 3581e2524b2eSHans Petter Selasky goto top; 3582e2524b2eSHans Petter Selasky } 3583e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(root, 3584e2524b2eSHans Petter Selasky &iot->u.ot_v2->bCSourceId, 1, info); 3585e2524b2eSHans Petter Selasky break; 3586e2524b2eSHans Petter Selasky 3587e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SEL: 3588e2524b2eSHans Petter Selasky if (is_last) { 3589e2524b2eSHans Petter Selasky p_id = iot->u.csel_v2->baCSourceId; 3590e2524b2eSHans Petter Selasky n_id = iot->u.csel_v2->bNrInPins; 3591e2524b2eSHans Petter Selasky goto top; 3592e2524b2eSHans Petter Selasky } 3593e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(root, 3594e2524b2eSHans Petter Selasky iot->u.csel_v2->baCSourceId, 3595e2524b2eSHans Petter Selasky iot->u.csel_v2->bNrInPins, info); 3596e2524b2eSHans Petter Selasky break; 3597e2524b2eSHans Petter Selasky 3598e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_MUL: 3599e2524b2eSHans Petter Selasky if (is_last) { 3600e2524b2eSHans Petter Selasky p_id = &iot->u.cmul_v2->bCSourceId; 3601e2524b2eSHans Petter Selasky n_id = 1; 3602e2524b2eSHans Petter Selasky goto top; 3603e2524b2eSHans Petter Selasky } 3604e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(root, 3605e2524b2eSHans Petter Selasky &iot->u.cmul_v2->bCSourceId, 3606e2524b2eSHans Petter Selasky 1, info); 3607e2524b2eSHans Petter Selasky break; 3608e2524b2eSHans Petter Selasky 3609e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SRC: 3610e2524b2eSHans Petter Selasky 3611e2524b2eSHans Petter Selasky id = iot->u.csrc_v2->bClockId; 3612e2524b2eSHans Petter Selasky 3613e2524b2eSHans Petter Selasky switch (info->is_input) { 3614e2524b2eSHans Petter Selasky case 0: 3615e2524b2eSHans Petter Selasky info->bit_output[id / 8] |= (1 << (id % 8)); 3616e2524b2eSHans Petter Selasky break; 3617e2524b2eSHans Petter Selasky case 1: 3618e2524b2eSHans Petter Selasky info->bit_input[id / 8] |= (1 << (id % 8)); 3619e2524b2eSHans Petter Selasky break; 3620e2524b2eSHans Petter Selasky default: 3621e2524b2eSHans Petter Selasky break; 3622e2524b2eSHans Petter Selasky } 3623e2524b2eSHans Petter Selasky break; 3624e2524b2eSHans Petter Selasky 3625e2524b2eSHans Petter Selasky default: 3626e2524b2eSHans Petter Selasky break; 3627e2524b2eSHans Petter Selasky } 3628e2524b2eSHans Petter Selasky } 36293a3f90c6SAndrew Thompson } 36303a3f90c6SAndrew Thompson 36313a3f90c6SAndrew Thompson static void 36323a3f90c6SAndrew Thompson uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id, 36333a3f90c6SAndrew Thompson uint8_t n_id, struct uaudio_search_result *info) 36343a3f90c6SAndrew Thompson { 36353a3f90c6SAndrew Thompson struct uaudio_terminal_node *iot = (root + id); 36363a3f90c6SAndrew Thompson uint8_t j; 36373a3f90c6SAndrew Thompson 36383a3f90c6SAndrew Thompson j = n_id; 36393a3f90c6SAndrew Thompson do { 36403a3f90c6SAndrew Thompson if ((j != id) && ((root + j)->u.desc) && 36413a3f90c6SAndrew Thompson ((root + j)->u.desc->bDescriptorSubtype == UDESCSUB_AC_OUTPUT)) { 36423a3f90c6SAndrew Thompson 36433a3f90c6SAndrew Thompson /* 36443a3f90c6SAndrew Thompson * "j" (output) <--- virtual wire <--- "id" (input) 36453a3f90c6SAndrew Thompson * 36463a3f90c6SAndrew Thompson * if "j" has "id" on the input, then "id" have "j" on 36473a3f90c6SAndrew Thompson * the output, because they are connected: 36483a3f90c6SAndrew Thompson */ 36493a3f90c6SAndrew Thompson if ((root + j)->usr.bit_input[id / 8] & (1 << (id % 8))) { 36503a3f90c6SAndrew Thompson iot->usr.bit_output[j / 8] |= (1 << (j % 8)); 36513a3f90c6SAndrew Thompson } 36523a3f90c6SAndrew Thompson } 36533a3f90c6SAndrew Thompson } while (j--); 36543a3f90c6SAndrew Thompson } 36553a3f90c6SAndrew Thompson 36563a3f90c6SAndrew Thompson static void 3657e2524b2eSHans Petter Selasky uaudio_mixer_fill_info(struct uaudio_softc *sc, 3658e2524b2eSHans Petter Selasky struct usb_device *udev, void *desc) 36593a3f90c6SAndrew Thompson { 36604c21be9bSRebecca Cran const struct usb_audio_control_descriptor *acdp; 3661a593f6b8SAndrew Thompson struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); 3662760bc48eSAndrew Thompson const struct usb_descriptor *dp; 36634c21be9bSRebecca Cran const struct usb_audio_unit *au; 36643a3f90c6SAndrew Thompson struct uaudio_terminal_node *iot = NULL; 36653a3f90c6SAndrew Thompson uint16_t wTotalLen; 36663a3f90c6SAndrew Thompson uint8_t ID_max = 0; /* inclusive */ 36673a3f90c6SAndrew Thompson uint8_t i; 36683a3f90c6SAndrew Thompson 3669a593f6b8SAndrew Thompson desc = usb_desc_foreach(cd, desc); 36703a3f90c6SAndrew Thompson 36713a3f90c6SAndrew Thompson if (desc == NULL) { 36723a3f90c6SAndrew Thompson DPRINTF("no Audio Control header\n"); 36733a3f90c6SAndrew Thompson goto done; 36743a3f90c6SAndrew Thompson } 36753a3f90c6SAndrew Thompson acdp = desc; 36763a3f90c6SAndrew Thompson 36773a3f90c6SAndrew Thompson if ((acdp->bLength < sizeof(*acdp)) || 36783a3f90c6SAndrew Thompson (acdp->bDescriptorType != UDESC_CS_INTERFACE) || 36793a3f90c6SAndrew Thompson (acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)) { 36803a3f90c6SAndrew Thompson DPRINTF("invalid Audio Control header\n"); 36813a3f90c6SAndrew Thompson goto done; 36823a3f90c6SAndrew Thompson } 36833a3f90c6SAndrew Thompson /* "wTotalLen" is allowed to be corrupt */ 36843a3f90c6SAndrew Thompson wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength; 36853a3f90c6SAndrew Thompson 36863a3f90c6SAndrew Thompson /* get USB audio revision */ 36873a3f90c6SAndrew Thompson sc->sc_audio_rev = UGETW(acdp->bcdADC); 36883a3f90c6SAndrew Thompson 36893a3f90c6SAndrew Thompson DPRINTFN(3, "found AC header, vers=%03x, len=%d\n", 36903a3f90c6SAndrew Thompson sc->sc_audio_rev, wTotalLen); 36913a3f90c6SAndrew Thompson 36923a3f90c6SAndrew Thompson iot = malloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP, 36933a3f90c6SAndrew Thompson M_WAITOK | M_ZERO); 36943a3f90c6SAndrew Thompson 36953a3f90c6SAndrew Thompson if (iot == NULL) { 36963a3f90c6SAndrew Thompson DPRINTF("no memory!\n"); 36973a3f90c6SAndrew Thompson goto done; 36983a3f90c6SAndrew Thompson } 3699a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) { 37003a3f90c6SAndrew Thompson 37013a3f90c6SAndrew Thompson dp = desc; 37023a3f90c6SAndrew Thompson 37033a3f90c6SAndrew Thompson if (dp->bLength > wTotalLen) { 37043a3f90c6SAndrew Thompson break; 37053a3f90c6SAndrew Thompson } else { 37063a3f90c6SAndrew Thompson wTotalLen -= dp->bLength; 37073a3f90c6SAndrew Thompson } 37083a3f90c6SAndrew Thompson 3709e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) 3710e2524b2eSHans Petter Selasky au = NULL; 3711e2524b2eSHans Petter Selasky else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) 3712e2524b2eSHans Petter Selasky au = uaudio20_mixer_verify_desc(dp, 0); 3713e2524b2eSHans Petter Selasky else 37143a3f90c6SAndrew Thompson au = uaudio_mixer_verify_desc(dp, 0); 37153a3f90c6SAndrew Thompson 37163a3f90c6SAndrew Thompson if (au) { 37173a3f90c6SAndrew Thompson iot[au->bUnitId].u.desc = (const void *)au; 3718e2524b2eSHans Petter Selasky if (au->bUnitId > ID_max) 37193a3f90c6SAndrew Thompson ID_max = au->bUnitId; 37203a3f90c6SAndrew Thompson } 37213a3f90c6SAndrew Thompson } 37223a3f90c6SAndrew Thompson 37233a3f90c6SAndrew Thompson DPRINTF("Maximum ID=%d\n", ID_max); 37243a3f90c6SAndrew Thompson 37253a3f90c6SAndrew Thompson /* 37263a3f90c6SAndrew Thompson * determine sourcing inputs for 37273a3f90c6SAndrew Thompson * all nodes in the tree: 37283a3f90c6SAndrew Thompson */ 37293a3f90c6SAndrew Thompson i = ID_max; 37303a3f90c6SAndrew Thompson do { 3731e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { 3732e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 3733e2524b2eSHans Petter Selasky } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { 3734e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub(iot, 3735e2524b2eSHans Petter Selasky &i, 1, &((iot + i)->usr)); 3736e2524b2eSHans Petter Selasky 3737e2524b2eSHans Petter Selasky sc->sc_mixer_clocks.is_input = 255; 3738e2524b2eSHans Petter Selasky sc->sc_mixer_clocks.recurse_level = 0; 3739e2524b2eSHans Petter Selasky 3740e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(iot, 3741e2524b2eSHans Petter Selasky &i, 1, &sc->sc_mixer_clocks); 3742e2524b2eSHans Petter Selasky } else { 3743e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub(iot, 3744e2524b2eSHans Petter Selasky &i, 1, &((iot + i)->usr)); 3745e2524b2eSHans Petter Selasky } 37463a3f90c6SAndrew Thompson } while (i--); 37473a3f90c6SAndrew Thompson 37483a3f90c6SAndrew Thompson /* 37493a3f90c6SAndrew Thompson * determine outputs for 37503a3f90c6SAndrew Thompson * all nodes in the tree: 37513a3f90c6SAndrew Thompson */ 37523a3f90c6SAndrew Thompson i = ID_max; 37533a3f90c6SAndrew Thompson do { 3754e2524b2eSHans Petter Selasky uaudio_mixer_find_outputs_sub(iot, 3755e2524b2eSHans Petter Selasky i, ID_max, &((iot + i)->usr)); 37563a3f90c6SAndrew Thompson } while (i--); 37573a3f90c6SAndrew Thompson 37583a3f90c6SAndrew Thompson /* set "id_max" and "root" */ 37593a3f90c6SAndrew Thompson 37603a3f90c6SAndrew Thompson i = ID_max; 37613a3f90c6SAndrew Thompson do { 37623a3f90c6SAndrew Thompson (iot + i)->usr.id_max = ID_max; 37633a3f90c6SAndrew Thompson (iot + i)->root = iot; 37643a3f90c6SAndrew Thompson } while (i--); 37653a3f90c6SAndrew Thompson 37663a3f90c6SAndrew Thompson /* 3767e2524b2eSHans Petter Selasky * Scan the config to create a linked list of "mixer" nodes: 37683a3f90c6SAndrew Thompson */ 37693a3f90c6SAndrew Thompson 37703a3f90c6SAndrew Thompson i = ID_max; 37713a3f90c6SAndrew Thompson do { 37723a3f90c6SAndrew Thompson dp = iot[i].u.desc; 37733a3f90c6SAndrew Thompson 3774e2524b2eSHans Petter Selasky if (dp == NULL) 37753a3f90c6SAndrew Thompson continue; 3776e2524b2eSHans Petter Selasky 37773a3f90c6SAndrew Thompson DPRINTFN(11, "id=%d subtype=%d\n", 37783a3f90c6SAndrew Thompson i, dp->bDescriptorSubtype); 37793a3f90c6SAndrew Thompson 3780e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { 3781e2524b2eSHans Petter Selasky continue; 3782e2524b2eSHans Petter Selasky } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { 3783e2524b2eSHans Petter Selasky 37843a3f90c6SAndrew Thompson switch (dp->bDescriptorSubtype) { 37853a3f90c6SAndrew Thompson case UDESCSUB_AC_HEADER: 37863a3f90c6SAndrew Thompson DPRINTF("unexpected AC header\n"); 37873a3f90c6SAndrew Thompson break; 37883a3f90c6SAndrew Thompson 37893a3f90c6SAndrew Thompson case UDESCSUB_AC_INPUT: 3790e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 3791e2524b2eSHans Petter Selasky case UDESCSUB_AC_PROCESSING_V2: 3792e2524b2eSHans Petter Selasky case UDESCSUB_AC_EXTENSION_V2: 3793e2524b2eSHans Petter Selasky case UDESCSUB_AC_EFFECT: 3794e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SRC: 3795e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SEL: 3796e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_MUL: 3797e2524b2eSHans Petter Selasky case UDESCSUB_AC_SAMPLE_RT: 37983a3f90c6SAndrew Thompson break; 37993a3f90c6SAndrew Thompson 3800e2524b2eSHans Petter Selasky case UDESCSUB_AC_MIXER: 3801e2524b2eSHans Petter Selasky uaudio20_mixer_add_mixer(sc, iot, i); 3802e2524b2eSHans Petter Selasky break; 3803e2524b2eSHans Petter Selasky 3804e2524b2eSHans Petter Selasky case UDESCSUB_AC_SELECTOR: 3805e2524b2eSHans Petter Selasky uaudio20_mixer_add_selector(sc, iot, i); 3806e2524b2eSHans Petter Selasky break; 3807e2524b2eSHans Petter Selasky 3808e2524b2eSHans Petter Selasky case UDESCSUB_AC_FEATURE: 3809e2524b2eSHans Petter Selasky uaudio20_mixer_add_feature(sc, iot, i); 3810e2524b2eSHans Petter Selasky break; 3811e2524b2eSHans Petter Selasky 3812e2524b2eSHans Petter Selasky default: 3813e2524b2eSHans Petter Selasky DPRINTF("bad AC desc subtype=0x%02x\n", 3814e2524b2eSHans Petter Selasky dp->bDescriptorSubtype); 3815e2524b2eSHans Petter Selasky break; 3816e2524b2eSHans Petter Selasky } 3817e2524b2eSHans Petter Selasky continue; 3818e2524b2eSHans Petter Selasky } 3819e2524b2eSHans Petter Selasky 3820e2524b2eSHans Petter Selasky switch (dp->bDescriptorSubtype) { 3821e2524b2eSHans Petter Selasky case UDESCSUB_AC_HEADER: 3822e2524b2eSHans Petter Selasky DPRINTF("unexpected AC header\n"); 3823e2524b2eSHans Petter Selasky break; 3824e2524b2eSHans Petter Selasky 3825e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 38263a3f90c6SAndrew Thompson case UDESCSUB_AC_OUTPUT: 38273a3f90c6SAndrew Thompson break; 38283a3f90c6SAndrew Thompson 38293a3f90c6SAndrew Thompson case UDESCSUB_AC_MIXER: 38303a3f90c6SAndrew Thompson uaudio_mixer_add_mixer(sc, iot, i); 38313a3f90c6SAndrew Thompson break; 38323a3f90c6SAndrew Thompson 38333a3f90c6SAndrew Thompson case UDESCSUB_AC_SELECTOR: 38343a3f90c6SAndrew Thompson uaudio_mixer_add_selector(sc, iot, i); 38353a3f90c6SAndrew Thompson break; 38363a3f90c6SAndrew Thompson 38373a3f90c6SAndrew Thompson case UDESCSUB_AC_FEATURE: 38383a3f90c6SAndrew Thompson uaudio_mixer_add_feature(sc, iot, i); 38393a3f90c6SAndrew Thompson break; 38403a3f90c6SAndrew Thompson 38413a3f90c6SAndrew Thompson case UDESCSUB_AC_PROCESSING: 38423a3f90c6SAndrew Thompson uaudio_mixer_add_processing(sc, iot, i); 38433a3f90c6SAndrew Thompson break; 38443a3f90c6SAndrew Thompson 38453a3f90c6SAndrew Thompson case UDESCSUB_AC_EXTENSION: 38463a3f90c6SAndrew Thompson uaudio_mixer_add_extension(sc, iot, i); 38473a3f90c6SAndrew Thompson break; 38483a3f90c6SAndrew Thompson 38493a3f90c6SAndrew Thompson default: 38503a3f90c6SAndrew Thompson DPRINTF("bad AC desc subtype=0x%02x\n", 38513a3f90c6SAndrew Thompson dp->bDescriptorSubtype); 38523a3f90c6SAndrew Thompson break; 38533a3f90c6SAndrew Thompson } 38543a3f90c6SAndrew Thompson 38553a3f90c6SAndrew Thompson } while (i--); 38563a3f90c6SAndrew Thompson 38573a3f90c6SAndrew Thompson done: 38583a3f90c6SAndrew Thompson free(iot, M_TEMP); 38593a3f90c6SAndrew Thompson } 38603a3f90c6SAndrew Thompson 3861e2524b2eSHans Petter Selasky static int 3862e2524b2eSHans Petter Selasky uaudio_mixer_get(struct usb_device *udev, uint16_t audio_rev, 3863e2524b2eSHans Petter Selasky uint8_t what, struct uaudio_mixer_node *mc) 38643a3f90c6SAndrew Thompson { 3865760bc48eSAndrew Thompson struct usb_device_request req; 3866e2524b2eSHans Petter Selasky int val; 3867e2524b2eSHans Petter Selasky uint8_t data[2 + (2 * 3)]; 3868e0a69b51SAndrew Thompson usb_error_t err; 38693a3f90c6SAndrew Thompson 3870e2524b2eSHans Petter Selasky if (mc->wValue[0] == -1) 38713a3f90c6SAndrew Thompson return (0); 3872e2524b2eSHans Petter Selasky 3873e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) 3874e2524b2eSHans Petter Selasky return (0); 3875e2524b2eSHans Petter Selasky else if (audio_rev >= UAUDIO_VERSION_20) { 3876e2524b2eSHans Petter Selasky if (what == GET_CUR) { 3877e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_CUR; 3878e2524b2eSHans Petter Selasky USETW(req.wLength, 2); 3879e2524b2eSHans Petter Selasky } else { 3880e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_RANGE; 3881e2524b2eSHans Petter Selasky USETW(req.wLength, 8); 38823a3f90c6SAndrew Thompson } 3883e2524b2eSHans Petter Selasky } else { 3884e2524b2eSHans Petter Selasky uint16_t len = MIX_SIZE(mc->type); 3885e2524b2eSHans Petter Selasky 38863a3f90c6SAndrew Thompson req.bRequest = what; 3887e2524b2eSHans Petter Selasky USETW(req.wLength, len); 3888e2524b2eSHans Petter Selasky } 3889e2524b2eSHans Petter Selasky 3890e2524b2eSHans Petter Selasky req.bmRequestType = UT_READ_CLASS_INTERFACE; 38913a3f90c6SAndrew Thompson USETW(req.wValue, mc->wValue[0]); 38923a3f90c6SAndrew Thompson USETW(req.wIndex, mc->wIndex); 3893e2524b2eSHans Petter Selasky 3894e2524b2eSHans Petter Selasky memset(data, 0, sizeof(data)); 38953a3f90c6SAndrew Thompson 3896f24b6817SAlfred Perlstein err = usbd_do_request(udev, NULL, &req, data); 38973a3f90c6SAndrew Thompson if (err) { 3898a593f6b8SAndrew Thompson DPRINTF("err=%s\n", usbd_errstr(err)); 38993a3f90c6SAndrew Thompson return (0); 39003a3f90c6SAndrew Thompson } 3901e2524b2eSHans Petter Selasky 3902e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 3903e2524b2eSHans Petter Selasky val = 0; 3904e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 3905e2524b2eSHans Petter Selasky switch (what) { 3906e2524b2eSHans Petter Selasky case GET_CUR: 39073a3f90c6SAndrew Thompson val = (data[0] | (data[1] << 8)); 3908e2524b2eSHans Petter Selasky break; 3909e2524b2eSHans Petter Selasky case GET_MIN: 3910e2524b2eSHans Petter Selasky val = (data[2] | (data[3] << 8)); 3911e2524b2eSHans Petter Selasky break; 3912e2524b2eSHans Petter Selasky case GET_MAX: 3913e2524b2eSHans Petter Selasky val = (data[4] | (data[5] << 8)); 3914e2524b2eSHans Petter Selasky break; 3915e2524b2eSHans Petter Selasky case GET_RES: 3916e2524b2eSHans Petter Selasky val = (data[6] | (data[7] << 8)); 3917e2524b2eSHans Petter Selasky break; 3918e2524b2eSHans Petter Selasky default: 3919e2524b2eSHans Petter Selasky val = 0; 3920e2524b2eSHans Petter Selasky break; 3921e2524b2eSHans Petter Selasky } 3922e2524b2eSHans Petter Selasky } else { 3923e2524b2eSHans Petter Selasky val = (data[0] | (data[1] << 8)); 3924e2524b2eSHans Petter Selasky } 3925e2524b2eSHans Petter Selasky 3926e2524b2eSHans Petter Selasky if (what == GET_CUR || what == GET_MIN || what == GET_MAX) 3927e2524b2eSHans Petter Selasky val = uaudio_mixer_signext(mc->type, val); 39283a3f90c6SAndrew Thompson 39293a3f90c6SAndrew Thompson DPRINTFN(3, "val=%d\n", val); 39303a3f90c6SAndrew Thompson 39313a3f90c6SAndrew Thompson return (val); 39323a3f90c6SAndrew Thompson } 39333a3f90c6SAndrew Thompson 39343a3f90c6SAndrew Thompson static void 3935ed6d949aSAndrew Thompson uaudio_mixer_write_cfg_callback(struct usb_xfer *xfer, usb_error_t error) 39363a3f90c6SAndrew Thompson { 3937760bc48eSAndrew Thompson struct usb_device_request req; 3938ed6d949aSAndrew Thompson struct uaudio_softc *sc = usbd_xfer_softc(xfer); 39393a3f90c6SAndrew Thompson struct uaudio_mixer_node *mc = sc->sc_mixer_curr; 3940ed6d949aSAndrew Thompson struct usb_page_cache *pc; 39413a3f90c6SAndrew Thompson uint16_t len; 39423a3f90c6SAndrew Thompson uint8_t repeat = 1; 39433a3f90c6SAndrew Thompson uint8_t update; 39443a3f90c6SAndrew Thompson uint8_t chan; 39453a3f90c6SAndrew Thompson uint8_t buf[2]; 39463a3f90c6SAndrew Thompson 39473a3f90c6SAndrew Thompson DPRINTF("\n"); 39483a3f90c6SAndrew Thompson 39493a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 39503a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 39513a3f90c6SAndrew Thompson tr_transferred: 39523a3f90c6SAndrew Thompson case USB_ST_SETUP: 39533a3f90c6SAndrew Thompson tr_setup: 39543a3f90c6SAndrew Thompson 39553a3f90c6SAndrew Thompson if (mc == NULL) { 39563a3f90c6SAndrew Thompson mc = sc->sc_mixer_root; 39573a3f90c6SAndrew Thompson sc->sc_mixer_curr = mc; 39583a3f90c6SAndrew Thompson sc->sc_mixer_chan = 0; 39593a3f90c6SAndrew Thompson repeat = 0; 39603a3f90c6SAndrew Thompson } 39613a3f90c6SAndrew Thompson while (mc) { 39623a3f90c6SAndrew Thompson while (sc->sc_mixer_chan < mc->nchan) { 39633a3f90c6SAndrew Thompson 39643a3f90c6SAndrew Thompson chan = sc->sc_mixer_chan; 39653a3f90c6SAndrew Thompson 39663a3f90c6SAndrew Thompson sc->sc_mixer_chan++; 39673a3f90c6SAndrew Thompson 39683a3f90c6SAndrew Thompson update = ((mc->update[chan / 8] & (1 << (chan % 8))) && 39693a3f90c6SAndrew Thompson (mc->wValue[chan] != -1)); 39703a3f90c6SAndrew Thompson 39713a3f90c6SAndrew Thompson mc->update[chan / 8] &= ~(1 << (chan % 8)); 39723a3f90c6SAndrew Thompson 39733a3f90c6SAndrew Thompson if (update) { 39743a3f90c6SAndrew Thompson 39753a3f90c6SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 39763a3f90c6SAndrew Thompson USETW(req.wValue, mc->wValue[chan]); 39773a3f90c6SAndrew Thompson USETW(req.wIndex, mc->wIndex); 39783a3f90c6SAndrew Thompson 3979e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { 3980e2524b2eSHans Petter Selasky return; 3981e2524b2eSHans Petter Selasky } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { 3982e2524b2eSHans Petter Selasky len = 2; 3983e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_CUR; 3984e2524b2eSHans Petter Selasky USETW(req.wLength, len); 3985e2524b2eSHans Petter Selasky } else { 3986e2524b2eSHans Petter Selasky len = MIX_SIZE(mc->type); 3987e2524b2eSHans Petter Selasky req.bRequest = SET_CUR; 3988e2524b2eSHans Petter Selasky USETW(req.wLength, len); 3989e2524b2eSHans Petter Selasky } 3990e2524b2eSHans Petter Selasky 39913a3f90c6SAndrew Thompson buf[0] = (mc->wData[chan] & 0xFF); 39923a3f90c6SAndrew Thompson buf[1] = (mc->wData[chan] >> 8) & 0xFF; 3993e2524b2eSHans Petter Selasky 3994ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 3995ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, &req, sizeof(req)); 3996ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 1); 3997ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, buf, len); 39983a3f90c6SAndrew Thompson 3999ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 4000ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, len); 4001ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, len ? 2 : 1); 4002a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 40033a3f90c6SAndrew Thompson return; 40043a3f90c6SAndrew Thompson } 40053a3f90c6SAndrew Thompson } 40063a3f90c6SAndrew Thompson 40073a3f90c6SAndrew Thompson mc = mc->next; 40083a3f90c6SAndrew Thompson sc->sc_mixer_curr = mc; 40093a3f90c6SAndrew Thompson sc->sc_mixer_chan = 0; 40103a3f90c6SAndrew Thompson } 40113a3f90c6SAndrew Thompson 40123a3f90c6SAndrew Thompson if (repeat) { 40133a3f90c6SAndrew Thompson goto tr_setup; 40143a3f90c6SAndrew Thompson } 40153a3f90c6SAndrew Thompson break; 40163a3f90c6SAndrew Thompson 40173a3f90c6SAndrew Thompson default: /* Error */ 4018ed6d949aSAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 4019ed6d949aSAndrew Thompson if (error == USB_ERR_CANCELLED) { 40203a3f90c6SAndrew Thompson /* do nothing - we are detaching */ 40213a3f90c6SAndrew Thompson break; 40223a3f90c6SAndrew Thompson } 40233a3f90c6SAndrew Thompson goto tr_transferred; 40243a3f90c6SAndrew Thompson } 40253a3f90c6SAndrew Thompson } 40263a3f90c6SAndrew Thompson 4027e0a69b51SAndrew Thompson static usb_error_t 4028760bc48eSAndrew Thompson uaudio_set_speed(struct usb_device *udev, uint8_t endpt, uint32_t speed) 40293a3f90c6SAndrew Thompson { 4030760bc48eSAndrew Thompson struct usb_device_request req; 40313a3f90c6SAndrew Thompson uint8_t data[3]; 40323a3f90c6SAndrew Thompson 40333a3f90c6SAndrew Thompson DPRINTFN(6, "endpt=%d speed=%u\n", endpt, speed); 40343a3f90c6SAndrew Thompson 40353a3f90c6SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_ENDPOINT; 40363a3f90c6SAndrew Thompson req.bRequest = SET_CUR; 40373a3f90c6SAndrew Thompson USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0); 40383a3f90c6SAndrew Thompson USETW(req.wIndex, endpt); 40393a3f90c6SAndrew Thompson USETW(req.wLength, 3); 40403a3f90c6SAndrew Thompson data[0] = speed; 40413a3f90c6SAndrew Thompson data[1] = speed >> 8; 40423a3f90c6SAndrew Thompson data[2] = speed >> 16; 40433a3f90c6SAndrew Thompson 4044f24b6817SAlfred Perlstein return (usbd_do_request(udev, NULL, &req, data)); 40453a3f90c6SAndrew Thompson } 40463a3f90c6SAndrew Thompson 4047e2524b2eSHans Petter Selasky static usb_error_t 4048e2524b2eSHans Petter Selasky uaudio20_set_speed(struct usb_device *udev, uint8_t iface_no, 4049e2524b2eSHans Petter Selasky uint8_t clockid, uint32_t speed) 4050e2524b2eSHans Petter Selasky { 4051e2524b2eSHans Petter Selasky struct usb_device_request req; 4052e2524b2eSHans Petter Selasky uint8_t data[4]; 4053e2524b2eSHans Petter Selasky 4054e2524b2eSHans Petter Selasky DPRINTFN(6, "ifaceno=%d clockid=%d speed=%u\n", 4055e2524b2eSHans Petter Selasky iface_no, clockid, speed); 4056e2524b2eSHans Petter Selasky 4057e2524b2eSHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 4058e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_CUR; 4059e2524b2eSHans Petter Selasky USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0); 4060e2524b2eSHans Petter Selasky USETW2(req.wIndex, clockid, iface_no); 4061e2524b2eSHans Petter Selasky USETW(req.wLength, 4); 4062e2524b2eSHans Petter Selasky data[0] = speed; 4063e2524b2eSHans Petter Selasky data[1] = speed >> 8; 4064e2524b2eSHans Petter Selasky data[2] = speed >> 16; 4065e2524b2eSHans Petter Selasky data[3] = speed >> 24; 4066e2524b2eSHans Petter Selasky 4067e2524b2eSHans Petter Selasky return (usbd_do_request(udev, NULL, &req, data)); 4068e2524b2eSHans Petter Selasky } 4069e2524b2eSHans Petter Selasky 40703a3f90c6SAndrew Thompson static int 40713a3f90c6SAndrew Thompson uaudio_mixer_signext(uint8_t type, int val) 40723a3f90c6SAndrew Thompson { 40733a3f90c6SAndrew Thompson if (!MIX_UNSIGNED(type)) { 40743a3f90c6SAndrew Thompson if (MIX_SIZE(type) == 2) { 40753a3f90c6SAndrew Thompson val = (int16_t)val; 40763a3f90c6SAndrew Thompson } else { 40773a3f90c6SAndrew Thompson val = (int8_t)val; 40783a3f90c6SAndrew Thompson } 40793a3f90c6SAndrew Thompson } 40803a3f90c6SAndrew Thompson return (val); 40813a3f90c6SAndrew Thompson } 40823a3f90c6SAndrew Thompson 40833a3f90c6SAndrew Thompson static int 40843a3f90c6SAndrew Thompson uaudio_mixer_bsd2value(struct uaudio_mixer_node *mc, int32_t val) 40853a3f90c6SAndrew Thompson { 40863a3f90c6SAndrew Thompson if (mc->type == MIX_ON_OFF) { 40873a3f90c6SAndrew Thompson val = (val != 0); 40883a3f90c6SAndrew Thompson } else if (mc->type == MIX_SELECTOR) { 40893a3f90c6SAndrew Thompson if ((val < mc->minval) || 40903a3f90c6SAndrew Thompson (val > mc->maxval)) { 40913a3f90c6SAndrew Thompson val = mc->minval; 40923a3f90c6SAndrew Thompson } 40933a3f90c6SAndrew Thompson } else { 4094b029f6bbSAndrew Thompson 4095b029f6bbSAndrew Thompson /* compute actual volume */ 4096b029f6bbSAndrew Thompson val = (val * mc->mul) / 255; 4097b029f6bbSAndrew Thompson 4098b029f6bbSAndrew Thompson /* add lower offset */ 4099b029f6bbSAndrew Thompson val = val + mc->minval; 4100b029f6bbSAndrew Thompson 4101b029f6bbSAndrew Thompson /* make sure we don't write a value out of range */ 4102b029f6bbSAndrew Thompson if (val > mc->maxval) 4103b029f6bbSAndrew Thompson val = mc->maxval; 4104b029f6bbSAndrew Thompson else if (val < mc->minval) 4105b029f6bbSAndrew Thompson val = mc->minval; 41063a3f90c6SAndrew Thompson } 41073a3f90c6SAndrew Thompson 41083a3f90c6SAndrew Thompson DPRINTFN(6, "type=0x%03x val=%d min=%d max=%d val=%d\n", 41093a3f90c6SAndrew Thompson mc->type, val, mc->minval, mc->maxval, val); 41103a3f90c6SAndrew Thompson return (val); 41113a3f90c6SAndrew Thompson } 41123a3f90c6SAndrew Thompson 41133a3f90c6SAndrew Thompson static void 41143a3f90c6SAndrew Thompson uaudio_mixer_ctl_set(struct uaudio_softc *sc, struct uaudio_mixer_node *mc, 41153a3f90c6SAndrew Thompson uint8_t chan, int32_t val) 41163a3f90c6SAndrew Thompson { 41173a3f90c6SAndrew Thompson val = uaudio_mixer_bsd2value(mc, val); 41183a3f90c6SAndrew Thompson 41193a3f90c6SAndrew Thompson mc->update[chan / 8] |= (1 << (chan % 8)); 41203a3f90c6SAndrew Thompson mc->wData[chan] = val; 41213a3f90c6SAndrew Thompson 41223a3f90c6SAndrew Thompson /* start the transfer, if not already started */ 41233a3f90c6SAndrew Thompson 4124a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_mixer_xfer[0]); 41253a3f90c6SAndrew Thompson } 41263a3f90c6SAndrew Thompson 41273a3f90c6SAndrew Thompson static void 41283a3f90c6SAndrew Thompson uaudio_mixer_init(struct uaudio_softc *sc) 41293a3f90c6SAndrew Thompson { 41303a3f90c6SAndrew Thompson struct uaudio_mixer_node *mc; 41313a3f90c6SAndrew Thompson int32_t i; 41323a3f90c6SAndrew Thompson 41333a3f90c6SAndrew Thompson for (mc = sc->sc_mixer_root; mc; 41343a3f90c6SAndrew Thompson mc = mc->next) { 41353a3f90c6SAndrew Thompson 41363a3f90c6SAndrew Thompson if (mc->ctl != SOUND_MIXER_NRDEVICES) { 41373a3f90c6SAndrew Thompson /* 41383a3f90c6SAndrew Thompson * Set device mask bits. See 41393a3f90c6SAndrew Thompson * /usr/include/machine/soundcard.h 41403a3f90c6SAndrew Thompson */ 41413a3f90c6SAndrew Thompson sc->sc_mix_info |= (1 << mc->ctl); 41423a3f90c6SAndrew Thompson } 41433a3f90c6SAndrew Thompson if ((mc->ctl == SOUND_MIXER_NRDEVICES) && 41443a3f90c6SAndrew Thompson (mc->type == MIX_SELECTOR)) { 41453a3f90c6SAndrew Thompson 41463a3f90c6SAndrew Thompson for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { 41473a3f90c6SAndrew Thompson if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) { 41483a3f90c6SAndrew Thompson continue; 41493a3f90c6SAndrew Thompson } 41503a3f90c6SAndrew Thompson sc->sc_recsrc_info |= 1 << mc->slctrtype[i - 1]; 41513a3f90c6SAndrew Thompson } 41523a3f90c6SAndrew Thompson } 41533a3f90c6SAndrew Thompson } 41543a3f90c6SAndrew Thompson } 41553a3f90c6SAndrew Thompson 41563a3f90c6SAndrew Thompson int 41573a3f90c6SAndrew Thompson uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m) 41583a3f90c6SAndrew Thompson { 41593a3f90c6SAndrew Thompson DPRINTF("\n"); 41603a3f90c6SAndrew Thompson 4161a593f6b8SAndrew Thompson if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index, 41623a3f90c6SAndrew Thompson sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc, 41633a3f90c6SAndrew Thompson mixer_get_lock(m))) { 41643a3f90c6SAndrew Thompson DPRINTFN(0, "could not allocate USB " 41653a3f90c6SAndrew Thompson "transfer for audio mixer!\n"); 41663a3f90c6SAndrew Thompson return (ENOMEM); 41673a3f90c6SAndrew Thompson } 41683a3f90c6SAndrew Thompson if (!(sc->sc_mix_info & SOUND_MASK_VOLUME)) { 41693a3f90c6SAndrew Thompson mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM); 41703a3f90c6SAndrew Thompson mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); 41713a3f90c6SAndrew Thompson } 41723a3f90c6SAndrew Thompson mix_setdevs(m, sc->sc_mix_info); 41733a3f90c6SAndrew Thompson mix_setrecdevs(m, sc->sc_recsrc_info); 41743a3f90c6SAndrew Thompson return (0); 41753a3f90c6SAndrew Thompson } 41763a3f90c6SAndrew Thompson 41773a3f90c6SAndrew Thompson int 41783a3f90c6SAndrew Thompson uaudio_mixer_uninit_sub(struct uaudio_softc *sc) 41793a3f90c6SAndrew Thompson { 41803a3f90c6SAndrew Thompson DPRINTF("\n"); 41813a3f90c6SAndrew Thompson 4182a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_mixer_xfer, 1); 41833a3f90c6SAndrew Thompson 41843a3f90c6SAndrew Thompson return (0); 41853a3f90c6SAndrew Thompson } 41863a3f90c6SAndrew Thompson 41873a3f90c6SAndrew Thompson void 41883a3f90c6SAndrew Thompson uaudio_mixer_set(struct uaudio_softc *sc, unsigned type, 41893a3f90c6SAndrew Thompson unsigned left, unsigned right) 41903a3f90c6SAndrew Thompson { 41913a3f90c6SAndrew Thompson struct uaudio_mixer_node *mc; 41923a3f90c6SAndrew Thompson 41933a3f90c6SAndrew Thompson for (mc = sc->sc_mixer_root; mc; 41943a3f90c6SAndrew Thompson mc = mc->next) { 41953a3f90c6SAndrew Thompson 41963a3f90c6SAndrew Thompson if (mc->ctl == type) { 41973a3f90c6SAndrew Thompson if (mc->nchan == 2) { 41983a3f90c6SAndrew Thompson /* set Right */ 41993a3f90c6SAndrew Thompson uaudio_mixer_ctl_set(sc, mc, 1, (int)(right * 255) / 100); 42003a3f90c6SAndrew Thompson } 42013a3f90c6SAndrew Thompson /* set Left or Mono */ 42023a3f90c6SAndrew Thompson uaudio_mixer_ctl_set(sc, mc, 0, (int)(left * 255) / 100); 42033a3f90c6SAndrew Thompson } 42043a3f90c6SAndrew Thompson } 42053a3f90c6SAndrew Thompson } 42063a3f90c6SAndrew Thompson 42073a3f90c6SAndrew Thompson uint32_t 42083a3f90c6SAndrew Thompson uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src) 42093a3f90c6SAndrew Thompson { 42103a3f90c6SAndrew Thompson struct uaudio_mixer_node *mc; 42113a3f90c6SAndrew Thompson uint32_t mask; 42123a3f90c6SAndrew Thompson uint32_t temp; 42133a3f90c6SAndrew Thompson int32_t i; 42143a3f90c6SAndrew Thompson 42153a3f90c6SAndrew Thompson for (mc = sc->sc_mixer_root; mc; 42163a3f90c6SAndrew Thompson mc = mc->next) { 42173a3f90c6SAndrew Thompson 42183a3f90c6SAndrew Thompson if ((mc->ctl == SOUND_MIXER_NRDEVICES) && 42193a3f90c6SAndrew Thompson (mc->type == MIX_SELECTOR)) { 42203a3f90c6SAndrew Thompson 42213a3f90c6SAndrew Thompson /* compute selector mask */ 42223a3f90c6SAndrew Thompson 42233a3f90c6SAndrew Thompson mask = 0; 42243a3f90c6SAndrew Thompson for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { 42253a3f90c6SAndrew Thompson mask |= (1 << mc->slctrtype[i - 1]); 42263a3f90c6SAndrew Thompson } 42273a3f90c6SAndrew Thompson 42283a3f90c6SAndrew Thompson temp = mask & src; 42293a3f90c6SAndrew Thompson if (temp == 0) { 42303a3f90c6SAndrew Thompson continue; 42313a3f90c6SAndrew Thompson } 42323a3f90c6SAndrew Thompson /* find the first set bit */ 42333a3f90c6SAndrew Thompson temp = (-temp) & temp; 42343a3f90c6SAndrew Thompson 42353a3f90c6SAndrew Thompson /* update "src" */ 42363a3f90c6SAndrew Thompson src &= ~mask; 42373a3f90c6SAndrew Thompson src |= temp; 42383a3f90c6SAndrew Thompson 42393a3f90c6SAndrew Thompson for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { 42403a3f90c6SAndrew Thompson if (temp != (1 << mc->slctrtype[i - 1])) { 42413a3f90c6SAndrew Thompson continue; 42423a3f90c6SAndrew Thompson } 42433a3f90c6SAndrew Thompson uaudio_mixer_ctl_set(sc, mc, 0, i); 42443a3f90c6SAndrew Thompson break; 42453a3f90c6SAndrew Thompson } 42463a3f90c6SAndrew Thompson } 42473a3f90c6SAndrew Thompson } 42483a3f90c6SAndrew Thompson return (src); 42493a3f90c6SAndrew Thompson } 42503a3f90c6SAndrew Thompson 42513a3f90c6SAndrew Thompson /*========================================================================* 42523a3f90c6SAndrew Thompson * MIDI support routines 42533a3f90c6SAndrew Thompson *========================================================================*/ 42543a3f90c6SAndrew Thompson 42553a3f90c6SAndrew Thompson static void 4256ed6d949aSAndrew Thompson umidi_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 42573a3f90c6SAndrew Thompson { 4258ed6d949aSAndrew Thompson struct umidi_chan *chan = usbd_xfer_softc(xfer); 42593a3f90c6SAndrew Thompson struct umidi_sub_chan *sub; 4260ed6d949aSAndrew Thompson struct usb_page_cache *pc; 42616f068a43SHans Petter Selasky uint8_t buf[4]; 42623a3f90c6SAndrew Thompson uint8_t cmd_len; 42633a3f90c6SAndrew Thompson uint8_t cn; 42643a3f90c6SAndrew Thompson uint16_t pos; 4265ed6d949aSAndrew Thompson int actlen; 4266ed6d949aSAndrew Thompson 4267ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 42683a3f90c6SAndrew Thompson 42693a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 42703a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 42713a3f90c6SAndrew Thompson 4272ed6d949aSAndrew Thompson DPRINTF("actlen=%d bytes\n", actlen); 42733a3f90c6SAndrew Thompson 42743a3f90c6SAndrew Thompson pos = 0; 4275ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 42763a3f90c6SAndrew Thompson 4277ed6d949aSAndrew Thompson while (actlen >= 4) { 42783a3f90c6SAndrew Thompson 42796f068a43SHans Petter Selasky /* copy out the MIDI data */ 42806f068a43SHans Petter Selasky usbd_copy_out(pc, pos, buf, 4); 42816f068a43SHans Petter Selasky /* command length */ 42826f068a43SHans Petter Selasky cmd_len = umidi_cmd_to_len[buf[0] & 0xF]; 42836f068a43SHans Petter Selasky /* cable number */ 42846f068a43SHans Petter Selasky cn = buf[0] >> 4; 42856f068a43SHans Petter Selasky /* 42866f068a43SHans Petter Selasky * Lookup sub-channel. The index is range 42876f068a43SHans Petter Selasky * checked below. 42886f068a43SHans Petter Selasky */ 42893a3f90c6SAndrew Thompson sub = &chan->sub[cn]; 42903a3f90c6SAndrew Thompson 42916f068a43SHans Petter Selasky if ((cmd_len != 0) && 42926f068a43SHans Petter Selasky (cn < chan->max_cable) && 42936f068a43SHans Petter Selasky (sub->read_open != 0)) { 42943a3f90c6SAndrew Thompson 42956f068a43SHans Petter Selasky /* Send data to the application */ 42966f068a43SHans Petter Selasky usb_fifo_put_data_linear( 42976f068a43SHans Petter Selasky sub->fifo.fp[USB_FIFO_RX], 42986f068a43SHans Petter Selasky buf + 1, cmd_len, 1); 42996f068a43SHans Petter Selasky } 4300ed6d949aSAndrew Thompson actlen -= 4; 43013a3f90c6SAndrew Thompson pos += 4; 43023a3f90c6SAndrew Thompson } 43033a3f90c6SAndrew Thompson 43043a3f90c6SAndrew Thompson case USB_ST_SETUP: 43053a3f90c6SAndrew Thompson DPRINTF("start\n"); 43066f068a43SHans Petter Selasky tr_setup: 4307ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 4308a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 43096f068a43SHans Petter Selasky break; 43103a3f90c6SAndrew Thompson 43113a3f90c6SAndrew Thompson default: 4312ed6d949aSAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 43133a3f90c6SAndrew Thompson 4314ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 43153a3f90c6SAndrew Thompson /* try to clear stall first */ 43166f068a43SHans Petter Selasky usbd_xfer_set_stall(xfer); 43176f068a43SHans Petter Selasky goto tr_setup; 43183a3f90c6SAndrew Thompson } 43196f068a43SHans Petter Selasky break; 43203a3f90c6SAndrew Thompson } 43213a3f90c6SAndrew Thompson } 43223a3f90c6SAndrew Thompson 43233a3f90c6SAndrew Thompson /* 43243a3f90c6SAndrew Thompson * The following statemachine, that converts MIDI commands to 43253a3f90c6SAndrew Thompson * USB MIDI packets, derives from Linux's usbmidi.c, which 43263a3f90c6SAndrew Thompson * was written by "Clemens Ladisch": 43273a3f90c6SAndrew Thompson * 43283a3f90c6SAndrew Thompson * Returns: 43293a3f90c6SAndrew Thompson * 0: No command 43303a3f90c6SAndrew Thompson * Else: Command is complete 43313a3f90c6SAndrew Thompson */ 43323a3f90c6SAndrew Thompson static uint8_t 43333a3f90c6SAndrew Thompson umidi_convert_to_usb(struct umidi_sub_chan *sub, uint8_t cn, uint8_t b) 43343a3f90c6SAndrew Thompson { 43353a3f90c6SAndrew Thompson uint8_t p0 = (cn << 4); 43363a3f90c6SAndrew Thompson 43373a3f90c6SAndrew Thompson if (b >= 0xf8) { 43383a3f90c6SAndrew Thompson sub->temp_0[0] = p0 | 0x0f; 43393a3f90c6SAndrew Thompson sub->temp_0[1] = b; 43403a3f90c6SAndrew Thompson sub->temp_0[2] = 0; 43413a3f90c6SAndrew Thompson sub->temp_0[3] = 0; 43423a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_0; 43433a3f90c6SAndrew Thompson return (1); 43443a3f90c6SAndrew Thompson 43453a3f90c6SAndrew Thompson } else if (b >= 0xf0) { 43463a3f90c6SAndrew Thompson switch (b) { 43473a3f90c6SAndrew Thompson case 0xf0: /* system exclusive begin */ 43483a3f90c6SAndrew Thompson sub->temp_1[1] = b; 43493a3f90c6SAndrew Thompson sub->state = UMIDI_ST_SYSEX_1; 43503a3f90c6SAndrew Thompson break; 43513a3f90c6SAndrew Thompson case 0xf1: /* MIDI time code */ 43523a3f90c6SAndrew Thompson case 0xf3: /* song select */ 43533a3f90c6SAndrew Thompson sub->temp_1[1] = b; 43543a3f90c6SAndrew Thompson sub->state = UMIDI_ST_1PARAM; 43553a3f90c6SAndrew Thompson break; 43563a3f90c6SAndrew Thompson case 0xf2: /* song position pointer */ 43573a3f90c6SAndrew Thompson sub->temp_1[1] = b; 43583a3f90c6SAndrew Thompson sub->state = UMIDI_ST_2PARAM_1; 43593a3f90c6SAndrew Thompson break; 43603a3f90c6SAndrew Thompson case 0xf4: /* unknown */ 43613a3f90c6SAndrew Thompson case 0xf5: /* unknown */ 43623a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 43633a3f90c6SAndrew Thompson break; 43643a3f90c6SAndrew Thompson case 0xf6: /* tune request */ 43653a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x05; 43663a3f90c6SAndrew Thompson sub->temp_1[1] = 0xf6; 43673a3f90c6SAndrew Thompson sub->temp_1[2] = 0; 43683a3f90c6SAndrew Thompson sub->temp_1[3] = 0; 43693a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 43703a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 43713a3f90c6SAndrew Thompson return (1); 43723a3f90c6SAndrew Thompson 43733a3f90c6SAndrew Thompson case 0xf7: /* system exclusive end */ 43743a3f90c6SAndrew Thompson switch (sub->state) { 43753a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_0: 43763a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x05; 43773a3f90c6SAndrew Thompson sub->temp_1[1] = 0xf7; 43783a3f90c6SAndrew Thompson sub->temp_1[2] = 0; 43793a3f90c6SAndrew Thompson sub->temp_1[3] = 0; 43803a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 43813a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 43823a3f90c6SAndrew Thompson return (1); 43833a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_1: 43843a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x06; 43853a3f90c6SAndrew Thompson sub->temp_1[2] = 0xf7; 43863a3f90c6SAndrew Thompson sub->temp_1[3] = 0; 43873a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 43883a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 43893a3f90c6SAndrew Thompson return (1); 43903a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_2: 43913a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x07; 43923a3f90c6SAndrew Thompson sub->temp_1[3] = 0xf7; 43933a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 43943a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 43953a3f90c6SAndrew Thompson return (1); 43963a3f90c6SAndrew Thompson } 43973a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 43983a3f90c6SAndrew Thompson break; 43993a3f90c6SAndrew Thompson } 44003a3f90c6SAndrew Thompson } else if (b >= 0x80) { 44013a3f90c6SAndrew Thompson sub->temp_1[1] = b; 44023a3f90c6SAndrew Thompson if ((b >= 0xc0) && (b <= 0xdf)) { 44033a3f90c6SAndrew Thompson sub->state = UMIDI_ST_1PARAM; 44043a3f90c6SAndrew Thompson } else { 44053a3f90c6SAndrew Thompson sub->state = UMIDI_ST_2PARAM_1; 44063a3f90c6SAndrew Thompson } 44073a3f90c6SAndrew Thompson } else { /* b < 0x80 */ 44083a3f90c6SAndrew Thompson switch (sub->state) { 44093a3f90c6SAndrew Thompson case UMIDI_ST_1PARAM: 44103a3f90c6SAndrew Thompson if (sub->temp_1[1] < 0xf0) { 44113a3f90c6SAndrew Thompson p0 |= sub->temp_1[1] >> 4; 44123a3f90c6SAndrew Thompson } else { 44133a3f90c6SAndrew Thompson p0 |= 0x02; 44143a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 44153a3f90c6SAndrew Thompson } 44163a3f90c6SAndrew Thompson sub->temp_1[0] = p0; 44173a3f90c6SAndrew Thompson sub->temp_1[2] = b; 44183a3f90c6SAndrew Thompson sub->temp_1[3] = 0; 44193a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 44203a3f90c6SAndrew Thompson return (1); 44213a3f90c6SAndrew Thompson case UMIDI_ST_2PARAM_1: 44223a3f90c6SAndrew Thompson sub->temp_1[2] = b; 44233a3f90c6SAndrew Thompson sub->state = UMIDI_ST_2PARAM_2; 44243a3f90c6SAndrew Thompson break; 44253a3f90c6SAndrew Thompson case UMIDI_ST_2PARAM_2: 44263a3f90c6SAndrew Thompson if (sub->temp_1[1] < 0xf0) { 44273a3f90c6SAndrew Thompson p0 |= sub->temp_1[1] >> 4; 44283a3f90c6SAndrew Thompson sub->state = UMIDI_ST_2PARAM_1; 44293a3f90c6SAndrew Thompson } else { 44303a3f90c6SAndrew Thompson p0 |= 0x03; 44313a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 44323a3f90c6SAndrew Thompson } 44333a3f90c6SAndrew Thompson sub->temp_1[0] = p0; 44343a3f90c6SAndrew Thompson sub->temp_1[3] = b; 44353a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 44363a3f90c6SAndrew Thompson return (1); 44373a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_0: 44383a3f90c6SAndrew Thompson sub->temp_1[1] = b; 44393a3f90c6SAndrew Thompson sub->state = UMIDI_ST_SYSEX_1; 44403a3f90c6SAndrew Thompson break; 44413a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_1: 44423a3f90c6SAndrew Thompson sub->temp_1[2] = b; 44433a3f90c6SAndrew Thompson sub->state = UMIDI_ST_SYSEX_2; 44443a3f90c6SAndrew Thompson break; 44453a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_2: 44463a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x04; 44473a3f90c6SAndrew Thompson sub->temp_1[3] = b; 44483a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 44493a3f90c6SAndrew Thompson sub->state = UMIDI_ST_SYSEX_0; 44503a3f90c6SAndrew Thompson return (1); 44516f068a43SHans Petter Selasky default: 44526f068a43SHans Petter Selasky break; 44533a3f90c6SAndrew Thompson } 44543a3f90c6SAndrew Thompson } 44553a3f90c6SAndrew Thompson return (0); 44563a3f90c6SAndrew Thompson } 44573a3f90c6SAndrew Thompson 44583a3f90c6SAndrew Thompson static void 4459ed6d949aSAndrew Thompson umidi_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 44603a3f90c6SAndrew Thompson { 4461ed6d949aSAndrew Thompson struct umidi_chan *chan = usbd_xfer_softc(xfer); 44623a3f90c6SAndrew Thompson struct umidi_sub_chan *sub; 4463ed6d949aSAndrew Thompson struct usb_page_cache *pc; 44643a3f90c6SAndrew Thompson uint32_t actlen; 4465910f1dcfSHans Petter Selasky uint16_t nframes; 44663a3f90c6SAndrew Thompson uint8_t buf; 44673a3f90c6SAndrew Thompson uint8_t start_cable; 44683a3f90c6SAndrew Thompson uint8_t tr_any; 4469ed6d949aSAndrew Thompson int len; 4470ed6d949aSAndrew Thompson 4471ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 44723a3f90c6SAndrew Thompson 4473910f1dcfSHans Petter Selasky /* 4474910f1dcfSHans Petter Selasky * NOTE: Some MIDI devices only accept 4 bytes of data per 4475910f1dcfSHans Petter Selasky * short terminated USB transfer. 4476910f1dcfSHans Petter Selasky */ 44773a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 44783a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 4479ed6d949aSAndrew Thompson DPRINTF("actlen=%d bytes\n", len); 44803a3f90c6SAndrew Thompson 44813a3f90c6SAndrew Thompson case USB_ST_SETUP: 44826f068a43SHans Petter Selasky tr_setup: 44833a3f90c6SAndrew Thompson DPRINTF("start\n"); 44843a3f90c6SAndrew Thompson 4485910f1dcfSHans Petter Selasky nframes = 0; /* reset */ 44863a3f90c6SAndrew Thompson start_cable = chan->curr_cable; 44873a3f90c6SAndrew Thompson tr_any = 0; 44884944c3a8SHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 0); 44893a3f90c6SAndrew Thompson 44903a3f90c6SAndrew Thompson while (1) { 44913a3f90c6SAndrew Thompson 44923a3f90c6SAndrew Thompson /* round robin de-queueing */ 44933a3f90c6SAndrew Thompson 44943a3f90c6SAndrew Thompson sub = &chan->sub[chan->curr_cable]; 44953a3f90c6SAndrew Thompson 44963a3f90c6SAndrew Thompson if (sub->write_open) { 4497910f1dcfSHans Petter Selasky usb_fifo_get_data_linear(sub->fifo.fp[USB_FIFO_TX], 4498910f1dcfSHans Petter Selasky &buf, 1, &actlen, 0); 44993a3f90c6SAndrew Thompson } else { 45003a3f90c6SAndrew Thompson actlen = 0; 45013a3f90c6SAndrew Thompson } 45023a3f90c6SAndrew Thompson 45033a3f90c6SAndrew Thompson if (actlen) { 45043a3f90c6SAndrew Thompson 45053a3f90c6SAndrew Thompson tr_any = 1; 45063a3f90c6SAndrew Thompson 4507910f1dcfSHans Petter Selasky DPRINTF("byte=0x%02x from FIFO %u\n", buf, 4508910f1dcfSHans Petter Selasky (unsigned int)chan->curr_cable); 45093a3f90c6SAndrew Thompson 45103a3f90c6SAndrew Thompson if (umidi_convert_to_usb(sub, chan->curr_cable, buf)) { 45113a3f90c6SAndrew Thompson 4512910f1dcfSHans Petter Selasky DPRINTF("sub=0x%02x 0x%02x 0x%02x 0x%02x\n", 45133a3f90c6SAndrew Thompson sub->temp_cmd[0], sub->temp_cmd[1], 45143a3f90c6SAndrew Thompson sub->temp_cmd[2], sub->temp_cmd[3]); 45153a3f90c6SAndrew Thompson 45164944c3a8SHans Petter Selasky usbd_copy_in(pc, nframes * 4, sub->temp_cmd, 4); 4517910f1dcfSHans Petter Selasky 4518910f1dcfSHans Petter Selasky nframes++; 45194944c3a8SHans Petter Selasky 45204944c3a8SHans Petter Selasky if ((nframes >= UMIDI_TX_FRAMES) || (chan->single_command != 0)) 45213a3f90c6SAndrew Thompson break; 45223a3f90c6SAndrew Thompson } else { 45233a3f90c6SAndrew Thompson continue; 45243a3f90c6SAndrew Thompson } 45253a3f90c6SAndrew Thompson } 4526910f1dcfSHans Petter Selasky 45273a3f90c6SAndrew Thompson chan->curr_cable++; 4528910f1dcfSHans Petter Selasky if (chan->curr_cable >= chan->max_cable) 45293a3f90c6SAndrew Thompson chan->curr_cable = 0; 4530910f1dcfSHans Petter Selasky 45313a3f90c6SAndrew Thompson if (chan->curr_cable == start_cable) { 4532910f1dcfSHans Petter Selasky if (tr_any == 0) 45333a3f90c6SAndrew Thompson break; 45343a3f90c6SAndrew Thompson tr_any = 0; 45353a3f90c6SAndrew Thompson } 45363a3f90c6SAndrew Thompson } 45373a3f90c6SAndrew Thompson 45384944c3a8SHans Petter Selasky if (nframes != 0) { 4539910f1dcfSHans Petter Selasky DPRINTF("Transferring %d frames\n", (int)nframes); 45404944c3a8SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, 4 * nframes); 4541a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 45423a3f90c6SAndrew Thompson } 45436f068a43SHans Petter Selasky break; 45443a3f90c6SAndrew Thompson 45453a3f90c6SAndrew Thompson default: /* Error */ 45463a3f90c6SAndrew Thompson 4547ed6d949aSAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 45483a3f90c6SAndrew Thompson 4549ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 45503a3f90c6SAndrew Thompson /* try to clear stall first */ 45516f068a43SHans Petter Selasky usbd_xfer_set_stall(xfer); 45526f068a43SHans Petter Selasky goto tr_setup; 45533a3f90c6SAndrew Thompson } 45546f068a43SHans Petter Selasky break; 45553a3f90c6SAndrew Thompson } 45563a3f90c6SAndrew Thompson } 45573a3f90c6SAndrew Thompson 45583a3f90c6SAndrew Thompson static struct umidi_sub_chan * 4559760bc48eSAndrew Thompson umidi_sub_by_fifo(struct usb_fifo *fifo) 45603a3f90c6SAndrew Thompson { 4561ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 45623a3f90c6SAndrew Thompson struct umidi_sub_chan *sub; 45633a3f90c6SAndrew Thompson uint32_t n; 45643a3f90c6SAndrew Thompson 45653a3f90c6SAndrew Thompson for (n = 0; n < UMIDI_CABLES_MAX; n++) { 45663a3f90c6SAndrew Thompson sub = &chan->sub[n]; 45673a3f90c6SAndrew Thompson if ((sub->fifo.fp[USB_FIFO_RX] == fifo) || 45683a3f90c6SAndrew Thompson (sub->fifo.fp[USB_FIFO_TX] == fifo)) { 45693a3f90c6SAndrew Thompson return (sub); 45703a3f90c6SAndrew Thompson } 45713a3f90c6SAndrew Thompson } 45723a3f90c6SAndrew Thompson 4573760bc48eSAndrew Thompson panic("%s:%d cannot find usb_fifo!\n", 45743a3f90c6SAndrew Thompson __FILE__, __LINE__); 45753a3f90c6SAndrew Thompson 45763a3f90c6SAndrew Thompson return (NULL); 45773a3f90c6SAndrew Thompson } 45783a3f90c6SAndrew Thompson 45793a3f90c6SAndrew Thompson static void 4580760bc48eSAndrew Thompson umidi_start_read(struct usb_fifo *fifo) 45813a3f90c6SAndrew Thompson { 4582ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 45833a3f90c6SAndrew Thompson 45846f068a43SHans Petter Selasky usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]); 45853a3f90c6SAndrew Thompson } 45863a3f90c6SAndrew Thompson 45873a3f90c6SAndrew Thompson static void 4588760bc48eSAndrew Thompson umidi_stop_read(struct usb_fifo *fifo) 45893a3f90c6SAndrew Thompson { 4590ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 45913a3f90c6SAndrew Thompson struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); 45923a3f90c6SAndrew Thompson 45933a3f90c6SAndrew Thompson DPRINTF("\n"); 45943a3f90c6SAndrew Thompson 45953a3f90c6SAndrew Thompson sub->read_open = 0; 45963a3f90c6SAndrew Thompson 45973a3f90c6SAndrew Thompson if (--(chan->read_open_refcount) == 0) { 45983a3f90c6SAndrew Thompson /* 45993a3f90c6SAndrew Thompson * XXX don't stop the read transfer here, hence that causes 46003a3f90c6SAndrew Thompson * problems with some MIDI adapters 46013a3f90c6SAndrew Thompson */ 46023a3f90c6SAndrew Thompson DPRINTF("(stopping read transfer)\n"); 46033a3f90c6SAndrew Thompson } 46043a3f90c6SAndrew Thompson } 46053a3f90c6SAndrew Thompson 46063a3f90c6SAndrew Thompson static void 4607760bc48eSAndrew Thompson umidi_start_write(struct usb_fifo *fifo) 46083a3f90c6SAndrew Thompson { 4609ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 46103a3f90c6SAndrew Thompson 46116f068a43SHans Petter Selasky usbd_transfer_start(chan->xfer[UMIDI_TX_TRANSFER]); 46123a3f90c6SAndrew Thompson } 46133a3f90c6SAndrew Thompson 46143a3f90c6SAndrew Thompson static void 4615760bc48eSAndrew Thompson umidi_stop_write(struct usb_fifo *fifo) 46163a3f90c6SAndrew Thompson { 4617ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 46183a3f90c6SAndrew Thompson struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); 46193a3f90c6SAndrew Thompson 46203a3f90c6SAndrew Thompson DPRINTF("\n"); 46213a3f90c6SAndrew Thompson 46223a3f90c6SAndrew Thompson sub->write_open = 0; 46233a3f90c6SAndrew Thompson 46243a3f90c6SAndrew Thompson if (--(chan->write_open_refcount) == 0) { 46253a3f90c6SAndrew Thompson DPRINTF("(stopping write transfer)\n"); 46266f068a43SHans Petter Selasky usbd_transfer_stop(chan->xfer[UMIDI_TX_TRANSFER]); 46273a3f90c6SAndrew Thompson } 46283a3f90c6SAndrew Thompson } 46293a3f90c6SAndrew Thompson 46303a3f90c6SAndrew Thompson static int 4631760bc48eSAndrew Thompson umidi_open(struct usb_fifo *fifo, int fflags) 46323a3f90c6SAndrew Thompson { 4633ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 46343a3f90c6SAndrew Thompson struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); 46353a3f90c6SAndrew Thompson 46363a3f90c6SAndrew Thompson if (fflags & FREAD) { 4637a593f6b8SAndrew Thompson if (usb_fifo_alloc_buffer(fifo, 4, (1024 / 4))) { 46383a3f90c6SAndrew Thompson return (ENOMEM); 46393a3f90c6SAndrew Thompson } 46407e6e6b67SAndrew Thompson mtx_lock(&chan->mtx); 46413a3f90c6SAndrew Thompson chan->read_open_refcount++; 46423a3f90c6SAndrew Thompson sub->read_open = 1; 46437e6e6b67SAndrew Thompson mtx_unlock(&chan->mtx); 46443a3f90c6SAndrew Thompson } 46453a3f90c6SAndrew Thompson if (fflags & FWRITE) { 4646a593f6b8SAndrew Thompson if (usb_fifo_alloc_buffer(fifo, 32, (1024 / 32))) { 46473a3f90c6SAndrew Thompson return (ENOMEM); 46483a3f90c6SAndrew Thompson } 46493a3f90c6SAndrew Thompson /* clear stall first */ 46507e6e6b67SAndrew Thompson mtx_lock(&chan->mtx); 46516f068a43SHans Petter Selasky usbd_xfer_set_stall(chan->xfer[UMIDI_TX_TRANSFER]); 46523a3f90c6SAndrew Thompson chan->write_open_refcount++; 46533a3f90c6SAndrew Thompson sub->write_open = 1; 46543a3f90c6SAndrew Thompson 46553a3f90c6SAndrew Thompson /* reset */ 46563a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 46577e6e6b67SAndrew Thompson mtx_unlock(&chan->mtx); 46583a3f90c6SAndrew Thompson } 46593a3f90c6SAndrew Thompson return (0); /* success */ 46603a3f90c6SAndrew Thompson } 46613a3f90c6SAndrew Thompson 46623a3f90c6SAndrew Thompson static void 4663760bc48eSAndrew Thompson umidi_close(struct usb_fifo *fifo, int fflags) 46643a3f90c6SAndrew Thompson { 46653a3f90c6SAndrew Thompson if (fflags & FREAD) { 4666a593f6b8SAndrew Thompson usb_fifo_free_buffer(fifo); 46673a3f90c6SAndrew Thompson } 46683a3f90c6SAndrew Thompson if (fflags & FWRITE) { 4669a593f6b8SAndrew Thompson usb_fifo_free_buffer(fifo); 46703a3f90c6SAndrew Thompson } 46713a3f90c6SAndrew Thompson } 46723a3f90c6SAndrew Thompson 46733a3f90c6SAndrew Thompson 46743a3f90c6SAndrew Thompson static int 4675760bc48eSAndrew Thompson umidi_ioctl(struct usb_fifo *fifo, u_long cmd, void *data, 4676ee3e3ff5SAndrew Thompson int fflags) 46773a3f90c6SAndrew Thompson { 46783a3f90c6SAndrew Thompson return (ENODEV); 46793a3f90c6SAndrew Thompson } 46803a3f90c6SAndrew Thompson 46813a3f90c6SAndrew Thompson static void 46823a3f90c6SAndrew Thompson umidi_init(device_t dev) 46833a3f90c6SAndrew Thompson { 46843a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 46853a3f90c6SAndrew Thompson struct umidi_chan *chan = &sc->sc_midi_chan; 46863a3f90c6SAndrew Thompson 46873a3f90c6SAndrew Thompson mtx_init(&chan->mtx, "umidi lock", NULL, MTX_DEF | MTX_RECURSE); 46883a3f90c6SAndrew Thompson } 46893a3f90c6SAndrew Thompson 4690760bc48eSAndrew Thompson static struct usb_fifo_methods umidi_fifo_methods = { 46913a3f90c6SAndrew Thompson .f_start_read = &umidi_start_read, 46923a3f90c6SAndrew Thompson .f_start_write = &umidi_start_write, 46933a3f90c6SAndrew Thompson .f_stop_read = &umidi_stop_read, 46943a3f90c6SAndrew Thompson .f_stop_write = &umidi_stop_write, 46953a3f90c6SAndrew Thompson .f_open = &umidi_open, 46963a3f90c6SAndrew Thompson .f_close = &umidi_close, 46973a3f90c6SAndrew Thompson .f_ioctl = &umidi_ioctl, 46983a3f90c6SAndrew Thompson .basename[0] = "umidi", 46993a3f90c6SAndrew Thompson }; 47003a3f90c6SAndrew Thompson 470125b74dabSHans Petter Selasky static int 47023a3f90c6SAndrew Thompson umidi_probe(device_t dev) 47033a3f90c6SAndrew Thompson { 47043a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 4705760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 47063a3f90c6SAndrew Thompson struct umidi_chan *chan = &sc->sc_midi_chan; 47073a3f90c6SAndrew Thompson struct umidi_sub_chan *sub; 47083a3f90c6SAndrew Thompson int unit = device_get_unit(dev); 47093a3f90c6SAndrew Thompson int error; 47103a3f90c6SAndrew Thompson uint32_t n; 47113a3f90c6SAndrew Thompson 47124944c3a8SHans Petter Selasky if (usb_test_quirk(uaa, UQ_SINGLE_CMD_MIDI)) 47134944c3a8SHans Petter Selasky chan->single_command = 1; 47144944c3a8SHans Petter Selasky 4715a593f6b8SAndrew Thompson if (usbd_set_alt_interface_index(sc->sc_udev, chan->iface_index, 47163a3f90c6SAndrew Thompson chan->iface_alt_index)) { 47173a3f90c6SAndrew Thompson DPRINTF("setting of alternate index failed!\n"); 47183a3f90c6SAndrew Thompson goto detach; 47193a3f90c6SAndrew Thompson } 47206f068a43SHans Petter Selasky usbd_set_parent_iface(sc->sc_udev, chan->iface_index, 47216f068a43SHans Petter Selasky sc->sc_mixer_iface_index); 47223a3f90c6SAndrew Thompson 4723a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, &chan->iface_index, 47243a3f90c6SAndrew Thompson chan->xfer, umidi_config, UMIDI_N_TRANSFER, 47253a3f90c6SAndrew Thompson chan, &chan->mtx); 47263a3f90c6SAndrew Thompson if (error) { 4727a593f6b8SAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 47283a3f90c6SAndrew Thompson goto detach; 47293a3f90c6SAndrew Thompson } 47303a3f90c6SAndrew Thompson if ((chan->max_cable > UMIDI_CABLES_MAX) || 47313a3f90c6SAndrew Thompson (chan->max_cable == 0)) { 47323a3f90c6SAndrew Thompson chan->max_cable = UMIDI_CABLES_MAX; 47333a3f90c6SAndrew Thompson } 47343a3f90c6SAndrew Thompson 47353a3f90c6SAndrew Thompson for (n = 0; n < chan->max_cable; n++) { 47363a3f90c6SAndrew Thompson 47373a3f90c6SAndrew Thompson sub = &chan->sub[n]; 47383a3f90c6SAndrew Thompson 4739a593f6b8SAndrew Thompson error = usb_fifo_attach(sc->sc_udev, chan, &chan->mtx, 47403a3f90c6SAndrew Thompson &umidi_fifo_methods, &sub->fifo, unit, n, 4741ee3e3ff5SAndrew Thompson chan->iface_index, 4742ee3e3ff5SAndrew Thompson UID_ROOT, GID_OPERATOR, 0644); 47433a3f90c6SAndrew Thompson if (error) { 47443a3f90c6SAndrew Thompson goto detach; 47453a3f90c6SAndrew Thompson } 47463a3f90c6SAndrew Thompson } 47473a3f90c6SAndrew Thompson 47483a3f90c6SAndrew Thompson mtx_lock(&chan->mtx); 47493a3f90c6SAndrew Thompson 47503a3f90c6SAndrew Thompson /* clear stall first */ 47516f068a43SHans Petter Selasky usbd_xfer_set_stall(chan->xfer[UMIDI_RX_TRANSFER]); 47523a3f90c6SAndrew Thompson 47533a3f90c6SAndrew Thompson /* 47546f068a43SHans Petter Selasky * NOTE: At least one device will not work properly unless the 47556f068a43SHans Petter Selasky * BULK IN pipe is open all the time. This might have to do 47566f068a43SHans Petter Selasky * about that the internal queues of the device overflow if we 47576f068a43SHans Petter Selasky * don't read them regularly. 47583a3f90c6SAndrew Thompson */ 47596f068a43SHans Petter Selasky usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]); 47603a3f90c6SAndrew Thompson 47613a3f90c6SAndrew Thompson mtx_unlock(&chan->mtx); 47623a3f90c6SAndrew Thompson 47633a3f90c6SAndrew Thompson return (0); /* success */ 47643a3f90c6SAndrew Thompson 47653a3f90c6SAndrew Thompson detach: 47663a3f90c6SAndrew Thompson return (ENXIO); /* failure */ 47673a3f90c6SAndrew Thompson } 47683a3f90c6SAndrew Thompson 476925b74dabSHans Petter Selasky static int 47703a3f90c6SAndrew Thompson umidi_detach(device_t dev) 47713a3f90c6SAndrew Thompson { 47723a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 47733a3f90c6SAndrew Thompson struct umidi_chan *chan = &sc->sc_midi_chan; 47743a3f90c6SAndrew Thompson uint32_t n; 47753a3f90c6SAndrew Thompson 47763a3f90c6SAndrew Thompson for (n = 0; n < UMIDI_CABLES_MAX; n++) { 4777a593f6b8SAndrew Thompson usb_fifo_detach(&chan->sub[n].fifo); 47783a3f90c6SAndrew Thompson } 47793a3f90c6SAndrew Thompson 47803a3f90c6SAndrew Thompson mtx_lock(&chan->mtx); 47813a3f90c6SAndrew Thompson 47826f068a43SHans Petter Selasky usbd_transfer_stop(chan->xfer[UMIDI_RX_TRANSFER]); 47833a3f90c6SAndrew Thompson 47843a3f90c6SAndrew Thompson mtx_unlock(&chan->mtx); 47853a3f90c6SAndrew Thompson 4786a593f6b8SAndrew Thompson usbd_transfer_unsetup(chan->xfer, UMIDI_N_TRANSFER); 47873a3f90c6SAndrew Thompson 47883a3f90c6SAndrew Thompson mtx_destroy(&chan->mtx); 47893a3f90c6SAndrew Thompson 47903a3f90c6SAndrew Thompson return (0); 47913a3f90c6SAndrew Thompson } 47923a3f90c6SAndrew Thompson 47939aef556dSAndrew Thompson DRIVER_MODULE(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, 0); 47943a3f90c6SAndrew Thompson MODULE_DEPEND(uaudio, usb, 1, 1, 1); 47953a3f90c6SAndrew Thompson MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 47963a3f90c6SAndrew Thompson MODULE_VERSION(uaudio, 1); 4797