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> 7476b71212SHans Petter Selasky #include <dev/usb/usbhid.h> 75902514f6SHans Petter Selasky #include <dev/usb/usb_request.h> 76455a367fSHans Petter Selasky #include <dev/usb/usb_process.h> 773a3f90c6SAndrew Thompson 783a3f90c6SAndrew Thompson #define USB_DEBUG_VAR uaudio_debug 793a3f90c6SAndrew Thompson #include <dev/usb/usb_debug.h> 803a3f90c6SAndrew Thompson 813a3f90c6SAndrew Thompson #include <dev/usb/quirk/usb_quirk.h> 823a3f90c6SAndrew Thompson 833a3f90c6SAndrew Thompson #include <sys/reboot.h> /* for bootverbose */ 843a3f90c6SAndrew Thompson 8590da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 8690da2b28SAriff Abdullah #include "opt_snd.h" 8790da2b28SAriff Abdullah #endif 8890da2b28SAriff Abdullah 893a3f90c6SAndrew Thompson #include <dev/sound/pcm/sound.h> 903a3f90c6SAndrew Thompson #include <dev/sound/usb/uaudioreg.h> 913a3f90c6SAndrew Thompson #include <dev/sound/usb/uaudio.h> 923a3f90c6SAndrew Thompson #include <dev/sound/chip.h> 933a3f90c6SAndrew Thompson #include "feeder_if.h" 943a3f90c6SAndrew Thompson 95afbfddd9SAndrew Thompson static int uaudio_default_rate = 0; /* use rate list */ 963a3f90c6SAndrew Thompson static int uaudio_default_bits = 32; 97afbfddd9SAndrew Thompson static int uaudio_default_channels = 0; /* use default */ 983a3f90c6SAndrew Thompson 99b850ecc1SAndrew Thompson #ifdef USB_DEBUG 1003a3f90c6SAndrew Thompson static int uaudio_debug = 0; 1013a3f90c6SAndrew Thompson 1026472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio"); 103afbfddd9SAndrew Thompson 104fadc970bSAndrew Thompson SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW, 1053a3f90c6SAndrew Thompson &uaudio_debug, 0, "uaudio debug level"); 106afbfddd9SAndrew Thompson 107afbfddd9SAndrew Thompson TUNABLE_INT("hw.usb.uaudio.default_rate", &uaudio_default_rate); 108fadc970bSAndrew Thompson SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_rate, CTLFLAG_RW, 1093a3f90c6SAndrew Thompson &uaudio_default_rate, 0, "uaudio default sample rate"); 110afbfddd9SAndrew Thompson 111afbfddd9SAndrew Thompson TUNABLE_INT("hw.usb.uaudio.default_bits", &uaudio_default_bits); 112fadc970bSAndrew Thompson SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_bits, CTLFLAG_RW, 1133a3f90c6SAndrew Thompson &uaudio_default_bits, 0, "uaudio default sample bits"); 114afbfddd9SAndrew Thompson 115afbfddd9SAndrew Thompson TUNABLE_INT("hw.usb.uaudio.default_channels", &uaudio_default_channels); 116fadc970bSAndrew Thompson SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, default_channels, CTLFLAG_RW, 1173a3f90c6SAndrew Thompson &uaudio_default_channels, 0, "uaudio default sample channels"); 1183a3f90c6SAndrew Thompson #endif 1193a3f90c6SAndrew Thompson 120b029f6bbSAndrew Thompson #define UAUDIO_NFRAMES 64 /* must be factor of 8 due HS-USB */ 1213a3f90c6SAndrew Thompson #define UAUDIO_NCHANBUFS 2 /* number of outstanding request */ 122e2524b2eSHans Petter Selasky #define UAUDIO_RECURSE_LIMIT 255 /* rounds */ 1233a3f90c6SAndrew Thompson 1243a3f90c6SAndrew Thompson #define MAKE_WORD(h,l) (((h) << 8) | (l)) 1253a3f90c6SAndrew Thompson #define BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1) 126c737a632SAndrew Thompson #define UAUDIO_MAX_CHAN(x) (x) 127f7e62ad0SHans Petter Selasky #define MIX(sc) ((sc)->sc_mixer_node) 1283a3f90c6SAndrew Thompson 129e2524b2eSHans Petter Selasky union uaudio_asid { 130e2524b2eSHans Petter Selasky const struct usb_audio_streaming_interface_descriptor *v1; 131e2524b2eSHans Petter Selasky const struct usb_audio20_streaming_interface_descriptor *v2; 132e2524b2eSHans Petter Selasky }; 133e2524b2eSHans Petter Selasky 134e2524b2eSHans Petter Selasky union uaudio_asf1d { 135e2524b2eSHans Petter Selasky const struct usb_audio_streaming_type1_descriptor *v1; 136e2524b2eSHans Petter Selasky const struct usb_audio20_streaming_type1_descriptor *v2; 137e2524b2eSHans Petter Selasky }; 138e2524b2eSHans Petter Selasky 139e2524b2eSHans Petter Selasky union uaudio_sed { 140e2524b2eSHans Petter Selasky const struct usb_audio_streaming_endpoint_descriptor *v1; 141e2524b2eSHans Petter Selasky const struct usb_audio20_streaming_endpoint_descriptor *v2; 142e2524b2eSHans Petter Selasky }; 143e2524b2eSHans Petter Selasky 1443a3f90c6SAndrew Thompson struct uaudio_mixer_node { 145902514f6SHans Petter Selasky const char *name; 146902514f6SHans Petter Selasky 1473a3f90c6SAndrew Thompson int32_t minval; 1483a3f90c6SAndrew Thompson int32_t maxval; 149902514f6SHans Petter Selasky #define MIX_MAX_CHAN 16 1503a3f90c6SAndrew Thompson int32_t wValue[MIX_MAX_CHAN]; /* using nchan */ 1513a3f90c6SAndrew Thompson uint32_t mul; 1523a3f90c6SAndrew Thompson uint32_t ctl; 1533a3f90c6SAndrew Thompson 154902514f6SHans Petter Selasky int wData[MIX_MAX_CHAN]; /* using nchan */ 1553a3f90c6SAndrew Thompson uint16_t wIndex; 1563a3f90c6SAndrew Thompson 1573a3f90c6SAndrew Thompson uint8_t update[(MIX_MAX_CHAN + 7) / 8]; 1583a3f90c6SAndrew Thompson uint8_t nchan; 1593a3f90c6SAndrew Thompson uint8_t type; 1603a3f90c6SAndrew Thompson #define MIX_ON_OFF 1 1613a3f90c6SAndrew Thompson #define MIX_SIGNED_16 2 1623a3f90c6SAndrew Thompson #define MIX_UNSIGNED_16 3 1633a3f90c6SAndrew Thompson #define MIX_SIGNED_8 4 1643a3f90c6SAndrew Thompson #define MIX_SELECTOR 5 1653a3f90c6SAndrew Thompson #define MIX_UNKNOWN 6 1663a3f90c6SAndrew Thompson #define MIX_SIZE(n) ((((n) == MIX_SIGNED_16) || \ 1673a3f90c6SAndrew Thompson ((n) == MIX_UNSIGNED_16)) ? 2 : 1) 1683a3f90c6SAndrew Thompson #define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16) 1693a3f90c6SAndrew Thompson 1703a3f90c6SAndrew Thompson #define MAX_SELECTOR_INPUT_PIN 256 1713a3f90c6SAndrew Thompson uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN]; 1723a3f90c6SAndrew Thompson uint8_t class; 173ff4d5953SHans Petter Selasky uint8_t val_default; 1743a3f90c6SAndrew Thompson 175902514f6SHans Petter Selasky uint8_t desc[64]; 176902514f6SHans Petter Selasky 1773a3f90c6SAndrew Thompson struct uaudio_mixer_node *next; 1783a3f90c6SAndrew Thompson }; 1793a3f90c6SAndrew Thompson 180455a367fSHans Petter Selasky struct uaudio_configure_msg { 181455a367fSHans Petter Selasky struct usb_proc_msg hdr; 182455a367fSHans Petter Selasky struct uaudio_softc *sc; 183455a367fSHans Petter Selasky }; 1843a3f90c6SAndrew Thompson 1852c2752d3SHans Petter Selasky #define CHAN_MAX_ALT 24 186455a367fSHans Petter Selasky 187455a367fSHans Petter Selasky struct uaudio_chan_alt { 188e2524b2eSHans Petter Selasky union uaudio_asf1d p_asf1d; 189e2524b2eSHans Petter Selasky union uaudio_sed p_sed; 1904c21be9bSRebecca Cran const usb_endpoint_descriptor_audio_t *p_ed1; 1913a3f90c6SAndrew Thompson const struct uaudio_format *p_fmt; 192455a367fSHans Petter Selasky const struct usb_config *usb_cfg; 193455a367fSHans Petter Selasky uint32_t sample_rate; /* in Hz */ 194455a367fSHans Petter Selasky uint16_t sample_size; 195455a367fSHans Petter Selasky uint8_t iface_index; 196455a367fSHans Petter Selasky uint8_t iface_alt_index; 197455a367fSHans Petter Selasky uint8_t channels; 198455a367fSHans Petter Selasky }; 199455a367fSHans Petter Selasky 200455a367fSHans Petter Selasky struct uaudio_chan { 201455a367fSHans Petter Selasky struct pcmchan_caps pcm_cap; /* capabilities */ 202455a367fSHans Petter Selasky struct uaudio_chan_alt usb_alt[CHAN_MAX_ALT]; 203455a367fSHans Petter Selasky struct snd_dbuf *pcm_buf; 204455a367fSHans Petter Selasky struct mtx *pcm_mtx; /* lock protecting this structure */ 205455a367fSHans Petter Selasky struct uaudio_softc *priv_sc; 206455a367fSHans Petter Selasky struct pcm_channel *pcm_ch; 207455a367fSHans Petter Selasky struct usb_xfer *xfer[UAUDIO_NCHANBUFS + 1]; 2083a3f90c6SAndrew Thompson 2093a3f90c6SAndrew Thompson uint8_t *buf; /* pointer to buffer */ 2103a3f90c6SAndrew Thompson uint8_t *start; /* upper layer buffer start */ 2113a3f90c6SAndrew Thompson uint8_t *end; /* upper layer buffer end */ 2123a3f90c6SAndrew Thompson uint8_t *cur; /* current position in upper layer 2133a3f90c6SAndrew Thompson * buffer */ 2143a3f90c6SAndrew Thompson 215b029f6bbSAndrew Thompson uint32_t intr_frames; /* in units */ 216afbfddd9SAndrew Thompson uint32_t frames_per_second; 217afbfddd9SAndrew Thompson uint32_t sample_rem; 218afbfddd9SAndrew Thompson uint32_t sample_curr; 219455a367fSHans Petter Selasky uint32_t max_buf; 220afbfddd9SAndrew Thompson 2213a3f90c6SAndrew Thompson uint32_t pcm_format[2]; 2223a3f90c6SAndrew Thompson 223afbfddd9SAndrew Thompson uint16_t bytes_per_frame[2]; 224afbfddd9SAndrew Thompson 225455a367fSHans Petter Selasky uint8_t num_alt; 226455a367fSHans Petter Selasky uint8_t cur_alt; 227455a367fSHans Petter Selasky uint8_t set_alt; 228455a367fSHans Petter Selasky uint8_t operation; 229455a367fSHans Petter Selasky #define CHAN_OP_NONE 0 230455a367fSHans Petter Selasky #define CHAN_OP_START 1 231455a367fSHans Petter Selasky #define CHAN_OP_STOP 2 232455a367fSHans Petter Selasky #define CHAN_OP_DRAIN 3 233b4380da7SHans Petter Selasky 234b4380da7SHans Petter Selasky uint8_t last_sync_time; 235b4380da7SHans Petter Selasky uint8_t last_sync_state; 236b4380da7SHans Petter Selasky #define UAUDIO_SYNC_NONE 0 237b4380da7SHans Petter Selasky #define UAUDIO_SYNC_MORE 1 238b4380da7SHans Petter Selasky #define UAUDIO_SYNC_LESS 2 2393a3f90c6SAndrew Thompson }; 2403a3f90c6SAndrew Thompson 2413a3f90c6SAndrew Thompson #define UMIDI_CABLES_MAX 16 /* units */ 242e0b17a62SHans Petter Selasky #define UMIDI_TX_FRAMES 256 /* units */ 243910f1dcfSHans Petter Selasky #define UMIDI_TX_BUFFER (UMIDI_TX_FRAMES * 4) /* bytes */ 2443a3f90c6SAndrew Thompson 2456f068a43SHans Petter Selasky enum { 2466f068a43SHans Petter Selasky UMIDI_TX_TRANSFER, 2476f068a43SHans Petter Selasky UMIDI_RX_TRANSFER, 2486f068a43SHans Petter Selasky UMIDI_N_TRANSFER, 2496f068a43SHans Petter Selasky }; 2506f068a43SHans Petter Selasky 2513a3f90c6SAndrew Thompson struct umidi_sub_chan { 252760bc48eSAndrew Thompson struct usb_fifo_sc fifo; 2533a3f90c6SAndrew Thompson uint8_t *temp_cmd; 2543a3f90c6SAndrew Thompson uint8_t temp_0[4]; 2553a3f90c6SAndrew Thompson uint8_t temp_1[4]; 2563a3f90c6SAndrew Thompson uint8_t state; 2573a3f90c6SAndrew Thompson #define UMIDI_ST_UNKNOWN 0 /* scan for command */ 2583a3f90c6SAndrew Thompson #define UMIDI_ST_1PARAM 1 2593a3f90c6SAndrew Thompson #define UMIDI_ST_2PARAM_1 2 2603a3f90c6SAndrew Thompson #define UMIDI_ST_2PARAM_2 3 2613a3f90c6SAndrew Thompson #define UMIDI_ST_SYSEX_0 4 2623a3f90c6SAndrew Thompson #define UMIDI_ST_SYSEX_1 5 2633a3f90c6SAndrew Thompson #define UMIDI_ST_SYSEX_2 6 2643a3f90c6SAndrew Thompson 2653a3f90c6SAndrew Thompson uint8_t read_open:1; 2663a3f90c6SAndrew Thompson uint8_t write_open:1; 2673a3f90c6SAndrew Thompson uint8_t unused:6; 2683a3f90c6SAndrew Thompson }; 2693a3f90c6SAndrew Thompson 2703a3f90c6SAndrew Thompson struct umidi_chan { 2713a3f90c6SAndrew Thompson 2723a3f90c6SAndrew Thompson struct umidi_sub_chan sub[UMIDI_CABLES_MAX]; 2733a3f90c6SAndrew Thompson struct mtx mtx; 2743a3f90c6SAndrew Thompson 275760bc48eSAndrew Thompson struct usb_xfer *xfer[UMIDI_N_TRANSFER]; 2763a3f90c6SAndrew Thompson 2773a3f90c6SAndrew Thompson uint8_t iface_index; 2783a3f90c6SAndrew Thompson uint8_t iface_alt_index; 2793a3f90c6SAndrew Thompson 2803a3f90c6SAndrew Thompson uint8_t read_open_refcount; 2813a3f90c6SAndrew Thompson uint8_t write_open_refcount; 2823a3f90c6SAndrew Thompson 2833a3f90c6SAndrew Thompson uint8_t curr_cable; 2843a3f90c6SAndrew Thompson uint8_t max_cable; 2853a3f90c6SAndrew Thompson uint8_t valid; 2864944c3a8SHans Petter Selasky uint8_t single_command; 2873a3f90c6SAndrew Thompson }; 2883a3f90c6SAndrew Thompson 289e2524b2eSHans Petter Selasky struct uaudio_search_result { 290e2524b2eSHans Petter Selasky uint8_t bit_input[(256 + 7) / 8]; 291e2524b2eSHans Petter Selasky uint8_t bit_output[(256 + 7) / 8]; 292e2524b2eSHans Petter Selasky uint8_t recurse_level; 293e2524b2eSHans Petter Selasky uint8_t id_max; 294e2524b2eSHans Petter Selasky uint8_t is_input; 295e2524b2eSHans Petter Selasky }; 296e2524b2eSHans Petter Selasky 29776b71212SHans Petter Selasky enum { 29876b71212SHans Petter Selasky UAUDIO_HID_RX_TRANSFER, 29976b71212SHans Petter Selasky UAUDIO_HID_N_TRANSFER, 30076b71212SHans Petter Selasky }; 30176b71212SHans Petter Selasky 30276b71212SHans Petter Selasky struct uaudio_hid { 30376b71212SHans Petter Selasky struct usb_xfer *xfer[UAUDIO_HID_N_TRANSFER]; 30476b71212SHans Petter Selasky struct hid_location volume_up_loc; 30576b71212SHans Petter Selasky struct hid_location volume_down_loc; 3062ba0f361SHans Petter Selasky struct hid_location mute_loc; 30776b71212SHans Petter Selasky uint32_t flags; 30876b71212SHans Petter Selasky #define UAUDIO_HID_VALID 0x0001 30976b71212SHans Petter Selasky #define UAUDIO_HID_HAS_ID 0x0002 31076b71212SHans Petter Selasky #define UAUDIO_HID_HAS_VOLUME_UP 0x0004 31176b71212SHans Petter Selasky #define UAUDIO_HID_HAS_VOLUME_DOWN 0x0008 3122ba0f361SHans Petter Selasky #define UAUDIO_HID_HAS_MUTE 0x0010 31376b71212SHans Petter Selasky uint8_t iface_index; 31476b71212SHans Petter Selasky uint8_t volume_up_id; 31576b71212SHans Petter Selasky uint8_t volume_down_id; 3162ba0f361SHans Petter Selasky uint8_t mute_id; 31776b71212SHans Petter Selasky }; 31876b71212SHans Petter Selasky 3193a3f90c6SAndrew Thompson struct uaudio_softc { 3203a3f90c6SAndrew Thompson struct sbuf sc_sndstat; 3213a3f90c6SAndrew Thompson struct sndcard_func sc_sndcard_func; 3223a3f90c6SAndrew Thompson struct uaudio_chan sc_rec_chan; 3233a3f90c6SAndrew Thompson struct uaudio_chan sc_play_chan; 3243a3f90c6SAndrew Thompson struct umidi_chan sc_midi_chan; 32576b71212SHans Petter Selasky struct uaudio_hid sc_hid; 326e2524b2eSHans Petter Selasky struct uaudio_search_result sc_mixer_clocks; 327f7e62ad0SHans Petter Selasky struct uaudio_mixer_node sc_mixer_node; 328455a367fSHans Petter Selasky struct uaudio_configure_msg sc_config_msg[2]; 3293a3f90c6SAndrew Thompson 330902514f6SHans Petter Selasky struct mtx *sc_mixer_lock; 33176b71212SHans Petter Selasky struct snd_mixer *sc_mixer_dev; 332760bc48eSAndrew Thompson struct usb_device *sc_udev; 333760bc48eSAndrew Thompson struct usb_xfer *sc_mixer_xfer[1]; 3343a3f90c6SAndrew Thompson struct uaudio_mixer_node *sc_mixer_root; 3353a3f90c6SAndrew Thompson struct uaudio_mixer_node *sc_mixer_curr; 3363a3f90c6SAndrew Thompson 3373a3f90c6SAndrew Thompson uint32_t sc_mix_info; 3383a3f90c6SAndrew Thompson uint32_t sc_recsrc_info; 3393a3f90c6SAndrew Thompson 3403a3f90c6SAndrew Thompson uint16_t sc_audio_rev; 3413a3f90c6SAndrew Thompson uint16_t sc_mixer_count; 3423a3f90c6SAndrew Thompson 3433a3f90c6SAndrew Thompson uint8_t sc_sndstat_valid; 3443a3f90c6SAndrew Thompson uint8_t sc_mixer_iface_index; 3453a3f90c6SAndrew Thompson uint8_t sc_mixer_iface_no; 3463a3f90c6SAndrew Thompson uint8_t sc_mixer_chan; 3473a3f90c6SAndrew Thompson uint8_t sc_pcm_registered:1; 3483a3f90c6SAndrew Thompson uint8_t sc_mixer_init:1; 3493a3f90c6SAndrew Thompson uint8_t sc_uq_audio_swap_lr:1; 3503a3f90c6SAndrew Thompson uint8_t sc_uq_au_inp_async:1; 3513a3f90c6SAndrew Thompson uint8_t sc_uq_au_no_xu:1; 3523a3f90c6SAndrew Thompson uint8_t sc_uq_bad_adc:1; 35325b74dabSHans Petter Selasky uint8_t sc_uq_au_vendor_class:1; 3543a3f90c6SAndrew Thompson }; 3553a3f90c6SAndrew Thompson 3563a3f90c6SAndrew Thompson struct uaudio_terminal_node { 3573a3f90c6SAndrew Thompson union { 358760bc48eSAndrew Thompson const struct usb_descriptor *desc; 359e2524b2eSHans Petter Selasky const struct usb_audio_input_terminal *it_v1; 360e2524b2eSHans Petter Selasky const struct usb_audio_output_terminal *ot_v1; 361e2524b2eSHans Petter Selasky const struct usb_audio_mixer_unit_0 *mu_v1; 362e2524b2eSHans Petter Selasky const struct usb_audio_selector_unit *su_v1; 363e2524b2eSHans Petter Selasky const struct usb_audio_feature_unit *fu_v1; 364e2524b2eSHans Petter Selasky const struct usb_audio_processing_unit_0 *pu_v1; 365e2524b2eSHans Petter Selasky const struct usb_audio_extension_unit_0 *eu_v1; 366e2524b2eSHans Petter Selasky const struct usb_audio20_clock_source_unit *csrc_v2; 367e2524b2eSHans Petter Selasky const struct usb_audio20_clock_selector_unit_0 *csel_v2; 368e2524b2eSHans Petter Selasky const struct usb_audio20_clock_multiplier_unit *cmul_v2; 369e2524b2eSHans Petter Selasky const struct usb_audio20_input_terminal *it_v2; 370e2524b2eSHans Petter Selasky const struct usb_audio20_output_terminal *ot_v2; 371e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_0 *mu_v2; 372e2524b2eSHans Petter Selasky const struct usb_audio20_selector_unit *su_v2; 373e2524b2eSHans Petter Selasky const struct usb_audio20_feature_unit *fu_v2; 374e2524b2eSHans Petter Selasky const struct usb_audio20_sample_rate_unit *ru_v2; 375e2524b2eSHans Petter Selasky const struct usb_audio20_processing_unit_0 *pu_v2; 376e2524b2eSHans Petter Selasky const struct usb_audio20_extension_unit_0 *eu_v2; 377e2524b2eSHans Petter Selasky const struct usb_audio20_effect_unit *ef_v2; 3783a3f90c6SAndrew Thompson } u; 3793a3f90c6SAndrew Thompson struct uaudio_search_result usr; 3803a3f90c6SAndrew Thompson struct uaudio_terminal_node *root; 3813a3f90c6SAndrew Thompson }; 3823a3f90c6SAndrew Thompson 3833a3f90c6SAndrew Thompson struct uaudio_format { 3843a3f90c6SAndrew Thompson uint16_t wFormat; 3853a3f90c6SAndrew Thompson uint8_t bPrecision; 3863a3f90c6SAndrew Thompson uint32_t freebsd_fmt; 3873a3f90c6SAndrew Thompson const char *description; 3883a3f90c6SAndrew Thompson }; 3893a3f90c6SAndrew Thompson 390e2524b2eSHans Petter Selasky static const struct uaudio_format uaudio10_formats[] = { 3913a3f90c6SAndrew Thompson 3923a3f90c6SAndrew Thompson {UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"}, 3933a3f90c6SAndrew Thompson {UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"}, 3943a3f90c6SAndrew Thompson {UA_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"}, 3953a3f90c6SAndrew Thompson {UA_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"}, 3963a3f90c6SAndrew Thompson 3973a3f90c6SAndrew Thompson {UA_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"}, 3983a3f90c6SAndrew Thompson {UA_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"}, 3993a3f90c6SAndrew Thompson {UA_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"}, 4003a3f90c6SAndrew Thompson {UA_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"}, 4013a3f90c6SAndrew Thompson 4023a3f90c6SAndrew Thompson {UA_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"}, 4033a3f90c6SAndrew Thompson {UA_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"}, 4043a3f90c6SAndrew Thompson 4053a3f90c6SAndrew Thompson {0, 0, 0, NULL} 4063a3f90c6SAndrew Thompson }; 4073a3f90c6SAndrew Thompson 408e2524b2eSHans Petter Selasky static const struct uaudio_format uaudio20_formats[] = { 409e2524b2eSHans Petter Selasky 410e2524b2eSHans Petter Selasky {UA20_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"}, 411e2524b2eSHans Petter Selasky {UA20_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"}, 412e2524b2eSHans Petter Selasky {UA20_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"}, 413e2524b2eSHans Petter Selasky {UA20_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"}, 414e2524b2eSHans Petter Selasky 415e2524b2eSHans Petter Selasky {UA20_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"}, 416e2524b2eSHans Petter Selasky {UA20_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"}, 417e2524b2eSHans Petter Selasky {UA20_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"}, 418e2524b2eSHans Petter Selasky {UA20_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"}, 419e2524b2eSHans Petter Selasky 420e2524b2eSHans Petter Selasky {UA20_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"}, 421e2524b2eSHans Petter Selasky {UA20_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"}, 422e2524b2eSHans Petter Selasky 423e2524b2eSHans Petter Selasky {0, 0, 0, NULL} 424e2524b2eSHans Petter Selasky }; 425e2524b2eSHans Petter Selasky 4263a3f90c6SAndrew Thompson #define UAC_OUTPUT 0 4273a3f90c6SAndrew Thompson #define UAC_INPUT 1 4283a3f90c6SAndrew Thompson #define UAC_EQUAL 2 4293a3f90c6SAndrew Thompson #define UAC_RECORD 3 4303a3f90c6SAndrew Thompson #define UAC_NCLASSES 4 4313a3f90c6SAndrew Thompson 432b850ecc1SAndrew Thompson #ifdef USB_DEBUG 4333a3f90c6SAndrew Thompson static const char *uac_names[] = { 4343a3f90c6SAndrew Thompson "outputs", "inputs", "equalization", "record" 4353a3f90c6SAndrew Thompson }; 4363a3f90c6SAndrew Thompson 4373a3f90c6SAndrew Thompson #endif 4383a3f90c6SAndrew Thompson 4393a3f90c6SAndrew Thompson /* prototypes */ 4403a3f90c6SAndrew Thompson 4413a3f90c6SAndrew Thompson static device_probe_t uaudio_probe; 4423a3f90c6SAndrew Thompson static device_attach_t uaudio_attach; 4433a3f90c6SAndrew Thompson static device_detach_t uaudio_detach; 4443a3f90c6SAndrew Thompson 445e0a69b51SAndrew Thompson static usb_callback_t uaudio_chan_play_callback; 446b4380da7SHans Petter Selasky static usb_callback_t uaudio_chan_play_sync_callback; 447e0a69b51SAndrew Thompson static usb_callback_t uaudio_chan_record_callback; 448b4380da7SHans Petter Selasky static usb_callback_t uaudio_chan_record_sync_callback; 449e0a69b51SAndrew Thompson static usb_callback_t uaudio_mixer_write_cfg_callback; 450e0a69b51SAndrew Thompson static usb_callback_t umidi_bulk_read_callback; 451e0a69b51SAndrew Thompson static usb_callback_t umidi_bulk_write_callback; 45276b71212SHans Petter Selasky static usb_callback_t uaudio_hid_rx_callback; 4533a3f90c6SAndrew Thompson 454455a367fSHans Petter Selasky static usb_proc_callback_t uaudio_configure_msg; 455455a367fSHans Petter Selasky 456902514f6SHans Petter Selasky /* ==== USB mixer ==== */ 457902514f6SHans Petter Selasky 458902514f6SHans Petter Selasky static int uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS); 459902514f6SHans Petter Selasky static void uaudio_mixer_ctl_free(struct uaudio_softc *); 460902514f6SHans Petter Selasky static void uaudio_mixer_register_sysctl(struct uaudio_softc *, device_t); 461902514f6SHans Petter Selasky static void uaudio_mixer_reload_all(struct uaudio_softc *); 462ff4d5953SHans Petter Selasky static void uaudio_mixer_controls_create_ftu(struct uaudio_softc *); 463902514f6SHans Petter Selasky 464e2524b2eSHans Petter Selasky /* ==== USB audio v1.0 ==== */ 465e2524b2eSHans Petter Selasky 4663a3f90c6SAndrew Thompson static void uaudio_mixer_add_mixer(struct uaudio_softc *, 4673a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4683a3f90c6SAndrew Thompson static void uaudio_mixer_add_selector(struct uaudio_softc *, 4693a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4703a3f90c6SAndrew Thompson static uint32_t uaudio_mixer_feature_get_bmaControls( 4714c21be9bSRebecca Cran const struct usb_audio_feature_unit *, uint8_t); 4723a3f90c6SAndrew Thompson static void uaudio_mixer_add_feature(struct uaudio_softc *, 4733a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4743a3f90c6SAndrew Thompson static void uaudio_mixer_add_processing_updown(struct uaudio_softc *, 4753a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4763a3f90c6SAndrew Thompson static void uaudio_mixer_add_processing(struct uaudio_softc *, 4773a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4783a3f90c6SAndrew Thompson static void uaudio_mixer_add_extension(struct uaudio_softc *, 4793a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, int); 4804c21be9bSRebecca Cran static struct usb_audio_cluster uaudio_mixer_get_cluster(uint8_t, 4813a3f90c6SAndrew Thompson const struct uaudio_terminal_node *); 4823a3f90c6SAndrew Thompson static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node *, 4833a3f90c6SAndrew Thompson struct uaudio_mixer_node *); 4843a3f90c6SAndrew Thompson static uint16_t uaudio_mixer_feature_name(const struct uaudio_terminal_node *, 4853a3f90c6SAndrew Thompson struct uaudio_mixer_node *); 486e2524b2eSHans Petter Selasky static void uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *, 487e2524b2eSHans Petter Selasky const uint8_t *, uint8_t, struct uaudio_search_result *); 488e2524b2eSHans Petter Selasky static const void *uaudio_mixer_verify_desc(const void *, uint32_t); 489e2524b2eSHans Petter Selasky static usb_error_t uaudio_set_speed(struct usb_device *, uint8_t, uint32_t); 490e2524b2eSHans Petter Selasky static int uaudio_mixer_get(struct usb_device *, uint16_t, uint8_t, 491e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 492e2524b2eSHans Petter Selasky 493e2524b2eSHans Petter Selasky /* ==== USB audio v2.0 ==== */ 494e2524b2eSHans Petter Selasky 495e2524b2eSHans Petter Selasky static void uaudio20_mixer_add_mixer(struct uaudio_softc *, 496e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *, int); 497e2524b2eSHans Petter Selasky static void uaudio20_mixer_add_selector(struct uaudio_softc *, 498e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *, int); 499e2524b2eSHans Petter Selasky static void uaudio20_mixer_add_feature(struct uaudio_softc *, 500e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *, int); 501e2524b2eSHans Petter Selasky static struct usb_audio20_cluster uaudio20_mixer_get_cluster(uint8_t, 502e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *); 503e2524b2eSHans Petter Selasky static uint16_t uaudio20_mixer_determine_class(const struct uaudio_terminal_node *, 504e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 505e2524b2eSHans Petter Selasky static uint16_t uaudio20_mixer_feature_name(const struct uaudio_terminal_node *, 506e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 507e2524b2eSHans Petter Selasky static void uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *, 508e2524b2eSHans Petter Selasky const uint8_t *, uint8_t, struct uaudio_search_result *); 509e2524b2eSHans Petter Selasky static const void *uaudio20_mixer_verify_desc(const void *, uint32_t); 510e2524b2eSHans Petter Selasky static usb_error_t uaudio20_set_speed(struct usb_device *, uint8_t, 511e2524b2eSHans Petter Selasky uint8_t, uint32_t); 512e2524b2eSHans Petter Selasky 513e2524b2eSHans Petter Selasky /* USB audio v1.0 and v2.0 */ 514e2524b2eSHans Petter Selasky 515e2524b2eSHans Petter Selasky static void uaudio_chan_fill_info_sub(struct uaudio_softc *, 516e2524b2eSHans Petter Selasky struct usb_device *, uint32_t, uint8_t, uint8_t); 517e2524b2eSHans Petter Selasky static void uaudio_chan_fill_info(struct uaudio_softc *, 518e2524b2eSHans Petter Selasky struct usb_device *); 519e2524b2eSHans Petter Selasky static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *, 520e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 521e2524b2eSHans Petter Selasky static void uaudio_mixer_add_ctl(struct uaudio_softc *, 522e2524b2eSHans Petter Selasky struct uaudio_mixer_node *); 523e2524b2eSHans Petter Selasky static void uaudio_mixer_fill_info(struct uaudio_softc *, 524e2524b2eSHans Petter Selasky struct usb_device *, void *); 525e2524b2eSHans Petter Selasky static void uaudio_mixer_ctl_set(struct uaudio_softc *, 526e2524b2eSHans Petter Selasky struct uaudio_mixer_node *, uint8_t, int32_t val); 527e2524b2eSHans Petter Selasky static int uaudio_mixer_signext(uint8_t, int); 528e2524b2eSHans Petter Selasky static int uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val); 529e2524b2eSHans Petter Selasky static void uaudio_mixer_init(struct uaudio_softc *); 5303a3f90c6SAndrew Thompson static const struct uaudio_terminal_node *uaudio_mixer_get_input( 5313a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, uint8_t); 5323a3f90c6SAndrew Thompson static const struct uaudio_terminal_node *uaudio_mixer_get_output( 5333a3f90c6SAndrew Thompson const struct uaudio_terminal_node *, uint8_t); 5343a3f90c6SAndrew Thompson static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *, 5353a3f90c6SAndrew Thompson uint8_t, uint8_t, struct uaudio_search_result *); 5363a3f90c6SAndrew Thompson static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t); 537760bc48eSAndrew Thompson static struct umidi_sub_chan *umidi_sub_by_fifo(struct usb_fifo *); 538760bc48eSAndrew Thompson static void umidi_start_read(struct usb_fifo *); 539760bc48eSAndrew Thompson static void umidi_stop_read(struct usb_fifo *); 540760bc48eSAndrew Thompson static void umidi_start_write(struct usb_fifo *); 541760bc48eSAndrew Thompson static void umidi_stop_write(struct usb_fifo *); 542760bc48eSAndrew Thompson static int umidi_open(struct usb_fifo *, int); 543760bc48eSAndrew Thompson static int umidi_ioctl(struct usb_fifo *, u_long cmd, void *, int); 544760bc48eSAndrew Thompson static void umidi_close(struct usb_fifo *, int); 5453a3f90c6SAndrew Thompson static void umidi_init(device_t dev); 54625b74dabSHans Petter Selasky static int umidi_probe(device_t dev); 54725b74dabSHans Petter Selasky static int umidi_detach(device_t dev); 54876b71212SHans Petter Selasky static int uaudio_hid_probe(struct uaudio_softc *sc, 54976b71212SHans Petter Selasky struct usb_attach_arg *uaa); 55076b71212SHans Petter Selasky static void uaudio_hid_detach(struct uaudio_softc *sc); 5513a3f90c6SAndrew Thompson 552b850ecc1SAndrew Thompson #ifdef USB_DEBUG 5533a3f90c6SAndrew Thompson static void uaudio_chan_dump_ep_desc( 5544c21be9bSRebecca Cran const usb_endpoint_descriptor_audio_t *); 5553a3f90c6SAndrew Thompson #endif 5563a3f90c6SAndrew Thompson 557760bc48eSAndrew Thompson static const struct usb_config 558b4380da7SHans Petter Selasky uaudio_cfg_record[UAUDIO_NCHANBUFS + 1] = { 5593a3f90c6SAndrew Thompson [0] = { 5603a3f90c6SAndrew Thompson .type = UE_ISOCHRONOUS, 5613a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 5623a3f90c6SAndrew Thompson .direction = UE_DIR_IN, 5634eae601eSAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 564b029f6bbSAndrew Thompson .frames = UAUDIO_NFRAMES, 5654eae601eSAndrew Thompson .flags = {.short_xfer_ok = 1,}, 5664eae601eSAndrew Thompson .callback = &uaudio_chan_record_callback, 5673a3f90c6SAndrew Thompson }, 5683a3f90c6SAndrew Thompson 5693a3f90c6SAndrew Thompson [1] = { 5703a3f90c6SAndrew Thompson .type = UE_ISOCHRONOUS, 5713a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 5723a3f90c6SAndrew Thompson .direction = UE_DIR_IN, 5734eae601eSAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 574b029f6bbSAndrew Thompson .frames = UAUDIO_NFRAMES, 5754eae601eSAndrew Thompson .flags = {.short_xfer_ok = 1,}, 5764eae601eSAndrew Thompson .callback = &uaudio_chan_record_callback, 5773a3f90c6SAndrew Thompson }, 578b4380da7SHans Petter Selasky 579b4380da7SHans Petter Selasky [2] = { 580b4380da7SHans Petter Selasky .type = UE_ISOCHRONOUS, 581b4380da7SHans Petter Selasky .endpoint = UE_ADDR_ANY, 582b4380da7SHans Petter Selasky .direction = UE_DIR_OUT, 583b4380da7SHans Petter Selasky .bufsize = 0, /* use "wMaxPacketSize * frames" */ 584b4380da7SHans Petter Selasky .frames = 1, 585b4380da7SHans Petter Selasky .flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,}, 586b4380da7SHans Petter Selasky .callback = &uaudio_chan_record_sync_callback, 587b4380da7SHans Petter Selasky }, 5883a3f90c6SAndrew Thompson }; 5893a3f90c6SAndrew Thompson 590760bc48eSAndrew Thompson static const struct usb_config 591b4380da7SHans Petter Selasky uaudio_cfg_play[UAUDIO_NCHANBUFS + 1] = { 5923a3f90c6SAndrew Thompson [0] = { 5933a3f90c6SAndrew Thompson .type = UE_ISOCHRONOUS, 5943a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 5953a3f90c6SAndrew Thompson .direction = UE_DIR_OUT, 5964eae601eSAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 597b029f6bbSAndrew Thompson .frames = UAUDIO_NFRAMES, 5984eae601eSAndrew Thompson .flags = {.short_xfer_ok = 1,}, 5994eae601eSAndrew Thompson .callback = &uaudio_chan_play_callback, 6003a3f90c6SAndrew Thompson }, 6013a3f90c6SAndrew Thompson 6023a3f90c6SAndrew Thompson [1] = { 6033a3f90c6SAndrew Thompson .type = UE_ISOCHRONOUS, 6043a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 6053a3f90c6SAndrew Thompson .direction = UE_DIR_OUT, 6064eae601eSAndrew Thompson .bufsize = 0, /* use "wMaxPacketSize * frames" */ 607b029f6bbSAndrew Thompson .frames = UAUDIO_NFRAMES, 6084eae601eSAndrew Thompson .flags = {.short_xfer_ok = 1,}, 6094eae601eSAndrew Thompson .callback = &uaudio_chan_play_callback, 6103a3f90c6SAndrew Thompson }, 611b4380da7SHans Petter Selasky 612b4380da7SHans Petter Selasky [2] = { 613b4380da7SHans Petter Selasky .type = UE_ISOCHRONOUS, 614b4380da7SHans Petter Selasky .endpoint = UE_ADDR_ANY, 615b4380da7SHans Petter Selasky .direction = UE_DIR_IN, 616b4380da7SHans Petter Selasky .bufsize = 0, /* use "wMaxPacketSize * frames" */ 617b4380da7SHans Petter Selasky .frames = 1, 618b4380da7SHans Petter Selasky .flags = {.no_pipe_ok = 1,.short_xfer_ok = 1,}, 619b4380da7SHans Petter Selasky .callback = &uaudio_chan_play_sync_callback, 620b4380da7SHans Petter Selasky }, 6213a3f90c6SAndrew Thompson }; 6223a3f90c6SAndrew Thompson 623760bc48eSAndrew Thompson static const struct usb_config 6243a3f90c6SAndrew Thompson uaudio_mixer_config[1] = { 6253a3f90c6SAndrew Thompson [0] = { 6263a3f90c6SAndrew Thompson .type = UE_CONTROL, 6273a3f90c6SAndrew Thompson .endpoint = 0x00, /* Control pipe */ 6283a3f90c6SAndrew Thompson .direction = UE_DIR_ANY, 629760bc48eSAndrew Thompson .bufsize = (sizeof(struct usb_device_request) + 4), 6304eae601eSAndrew Thompson .callback = &uaudio_mixer_write_cfg_callback, 6314eae601eSAndrew Thompson .timeout = 1000, /* 1 second */ 6323a3f90c6SAndrew Thompson }, 6333a3f90c6SAndrew Thompson }; 6343a3f90c6SAndrew Thompson 6353a3f90c6SAndrew Thompson static const 6363a3f90c6SAndrew Thompson uint8_t umidi_cmd_to_len[16] = { 6373a3f90c6SAndrew Thompson [0x0] = 0, /* reserved */ 6383a3f90c6SAndrew Thompson [0x1] = 0, /* reserved */ 6393a3f90c6SAndrew Thompson [0x2] = 2, /* bytes */ 6403a3f90c6SAndrew Thompson [0x3] = 3, /* bytes */ 6413a3f90c6SAndrew Thompson [0x4] = 3, /* bytes */ 6423a3f90c6SAndrew Thompson [0x5] = 1, /* bytes */ 6433a3f90c6SAndrew Thompson [0x6] = 2, /* bytes */ 6443a3f90c6SAndrew Thompson [0x7] = 3, /* bytes */ 6453a3f90c6SAndrew Thompson [0x8] = 3, /* bytes */ 6463a3f90c6SAndrew Thompson [0x9] = 3, /* bytes */ 6473a3f90c6SAndrew Thompson [0xA] = 3, /* bytes */ 6483a3f90c6SAndrew Thompson [0xB] = 3, /* bytes */ 6493a3f90c6SAndrew Thompson [0xC] = 2, /* bytes */ 6503a3f90c6SAndrew Thompson [0xD] = 2, /* bytes */ 6513a3f90c6SAndrew Thompson [0xE] = 3, /* bytes */ 6523a3f90c6SAndrew Thompson [0xF] = 1, /* bytes */ 6533a3f90c6SAndrew Thompson }; 6543a3f90c6SAndrew Thompson 655760bc48eSAndrew Thompson static const struct usb_config 6563a3f90c6SAndrew Thompson umidi_config[UMIDI_N_TRANSFER] = { 6576f068a43SHans Petter Selasky [UMIDI_TX_TRANSFER] = { 6583a3f90c6SAndrew Thompson .type = UE_BULK, 6593a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 6603a3f90c6SAndrew Thompson .direction = UE_DIR_OUT, 661910f1dcfSHans Petter Selasky .bufsize = UMIDI_TX_BUFFER, 6624eae601eSAndrew Thompson .callback = &umidi_bulk_write_callback, 6633a3f90c6SAndrew Thompson }, 6643a3f90c6SAndrew Thompson 6656f068a43SHans Petter Selasky [UMIDI_RX_TRANSFER] = { 6663a3f90c6SAndrew Thompson .type = UE_BULK, 6673a3f90c6SAndrew Thompson .endpoint = UE_ADDR_ANY, 6683a3f90c6SAndrew Thompson .direction = UE_DIR_IN, 6693851442cSAndrew Thompson .bufsize = 4, /* bytes */ 670910f1dcfSHans Petter Selasky .flags = {.short_xfer_ok = 1,.proxy_buffer = 1,}, 6714eae601eSAndrew Thompson .callback = &umidi_bulk_read_callback, 6723a3f90c6SAndrew Thompson }, 6733a3f90c6SAndrew Thompson }; 6743a3f90c6SAndrew Thompson 67576b71212SHans Petter Selasky static const struct usb_config 67676b71212SHans Petter Selasky uaudio_hid_config[UAUDIO_HID_N_TRANSFER] = { 67776b71212SHans Petter Selasky [UAUDIO_HID_RX_TRANSFER] = { 67876b71212SHans Petter Selasky .type = UE_INTERRUPT, 67976b71212SHans Petter Selasky .endpoint = UE_ADDR_ANY, 68076b71212SHans Petter Selasky .direction = UE_DIR_IN, 68176b71212SHans Petter Selasky .bufsize = 0, /* use wMaxPacketSize */ 68276b71212SHans Petter Selasky .flags = {.short_xfer_ok = 1,}, 68376b71212SHans Petter Selasky .callback = &uaudio_hid_rx_callback, 68476b71212SHans Petter Selasky }, 68576b71212SHans Petter Selasky }; 68676b71212SHans Petter Selasky 6873a3f90c6SAndrew Thompson static devclass_t uaudio_devclass; 6883a3f90c6SAndrew Thompson 6893a3f90c6SAndrew Thompson static device_method_t uaudio_methods[] = { 6903a3f90c6SAndrew Thompson DEVMETHOD(device_probe, uaudio_probe), 6913a3f90c6SAndrew Thompson DEVMETHOD(device_attach, uaudio_attach), 6923a3f90c6SAndrew Thompson DEVMETHOD(device_detach, uaudio_detach), 6933a3f90c6SAndrew Thompson DEVMETHOD(device_suspend, bus_generic_suspend), 6943a3f90c6SAndrew Thompson DEVMETHOD(device_resume, bus_generic_resume), 6953a3f90c6SAndrew Thompson DEVMETHOD(device_shutdown, bus_generic_shutdown), 6964b7ec270SMarius Strobl 6974b7ec270SMarius Strobl DEVMETHOD_END 6983a3f90c6SAndrew Thompson }; 6993a3f90c6SAndrew Thompson 7003a3f90c6SAndrew Thompson static driver_t uaudio_driver = { 7013a3f90c6SAndrew Thompson .name = "uaudio", 7023a3f90c6SAndrew Thompson .methods = uaudio_methods, 7033a3f90c6SAndrew Thompson .size = sizeof(struct uaudio_softc), 7043a3f90c6SAndrew Thompson }; 7053a3f90c6SAndrew Thompson 70646f0b27aSHans Petter Selasky /* The following table is derived from Linux's quirks-table.h */ 70746f0b27aSHans Petter Selasky static const STRUCT_USB_HOST_ID uaudio_vendor_midi[] = { 70846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1000, 0) }, /* UX256 */ 70946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1001, 0) }, /* MU1000 */ 71046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1002, 0) }, /* MU2000 */ 71146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1003, 0) }, /* MU500 */ 71246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1004, 3) }, /* UW500 */ 71346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1005, 0) }, /* MOTIF6 */ 71446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1006, 0) }, /* MOTIF7 */ 71546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1007, 0) }, /* MOTIF8 */ 71646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1008, 0) }, /* UX96 */ 71746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1009, 0) }, /* UX16 */ 71846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x100a, 3) }, /* EOS BX */ 71946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x100c, 0) }, /* UC-MX */ 72046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x100d, 0) }, /* UC-KX */ 72146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x100e, 0) }, /* S08 */ 72246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x100f, 0) }, /* CLP-150 */ 72346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1010, 0) }, /* CLP-170 */ 72446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1011, 0) }, /* P-250 */ 72546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1012, 0) }, /* TYROS */ 72646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1013, 0) }, /* PF-500 */ 72746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1014, 0) }, /* S90 */ 72846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1015, 0) }, /* MOTIF-R */ 72946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1016, 0) }, /* MDP-5 */ 73046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1017, 0) }, /* CVP-204 */ 73146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1018, 0) }, /* CVP-206 */ 73246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1019, 0) }, /* CVP-208 */ 73346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x101a, 0) }, /* CVP-210 */ 73446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x101b, 0) }, /* PSR-1100 */ 73546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x101c, 0) }, /* PSR-2100 */ 73646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x101d, 0) }, /* CLP-175 */ 73746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x101e, 0) }, /* PSR-K1 */ 73846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x101f, 0) }, /* EZ-J24 */ 73946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1020, 0) }, /* EZ-250i */ 74046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1021, 0) }, /* MOTIF ES 6 */ 74146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1022, 0) }, /* MOTIF ES 7 */ 74246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1023, 0) }, /* MOTIF ES 8 */ 74346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1024, 0) }, /* CVP-301 */ 74446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1025, 0) }, /* CVP-303 */ 74546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1026, 0) }, /* CVP-305 */ 74646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1027, 0) }, /* CVP-307 */ 74746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1028, 0) }, /* CVP-309 */ 74846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1029, 0) }, /* CVP-309GP */ 74946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x102a, 0) }, /* PSR-1500 */ 75046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x102b, 0) }, /* PSR-3000 */ 75146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x102e, 0) }, /* ELS-01/01C */ 75246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1030, 0) }, /* PSR-295/293 */ 75346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1031, 0) }, /* DGX-205/203 */ 75446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1032, 0) }, /* DGX-305 */ 75546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1033, 0) }, /* DGX-505 */ 75646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1034, 0) }, /* NULL */ 75746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1035, 0) }, /* NULL */ 75846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1036, 0) }, /* NULL */ 75946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1037, 0) }, /* NULL */ 76046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1038, 0) }, /* NULL */ 76146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1039, 0) }, /* NULL */ 76246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x103a, 0) }, /* NULL */ 76346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x103b, 0) }, /* NULL */ 76446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x103c, 0) }, /* NULL */ 76546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x103d, 0) }, /* NULL */ 76646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x103e, 0) }, /* NULL */ 76746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x103f, 0) }, /* NULL */ 76846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1040, 0) }, /* NULL */ 76946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1041, 0) }, /* NULL */ 77046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1042, 0) }, /* NULL */ 77146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1043, 0) }, /* NULL */ 77246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1044, 0) }, /* NULL */ 77346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1045, 0) }, /* NULL */ 77446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x104e, 0) }, /* NULL */ 77546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x104f, 0) }, /* NULL */ 77646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1050, 0) }, /* NULL */ 77746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1051, 0) }, /* NULL */ 77846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1052, 0) }, /* NULL */ 77946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1053, 0) }, /* NULL */ 78046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1054, 0) }, /* NULL */ 78146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1055, 0) }, /* NULL */ 78246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1056, 0) }, /* NULL */ 78346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1057, 0) }, /* NULL */ 78446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1058, 0) }, /* NULL */ 78546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1059, 0) }, /* NULL */ 78646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x105a, 0) }, /* NULL */ 78746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x105b, 0) }, /* NULL */ 78846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x105c, 0) }, /* NULL */ 78946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x105d, 0) }, /* NULL */ 79046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x1503, 3) }, /* MOX6/MOX8 */ 79146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x2000, 0) }, /* DGP-7 */ 79246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x2001, 0) }, /* DGP-5 */ 79346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x2002, 0) }, /* NULL */ 79446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x2003, 0) }, /* NULL */ 79546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5000, 0) }, /* CS1D */ 79646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5001, 0) }, /* DSP1D */ 79746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5002, 0) }, /* DME32 */ 79846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5003, 0) }, /* DM2000 */ 79946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5004, 0) }, /* 02R96 */ 80046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5005, 0) }, /* ACU16-C */ 80146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5006, 0) }, /* NHB32-C */ 80246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5007, 0) }, /* DM1000 */ 80346f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5008, 0) }, /* 01V96 */ 80446f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x5009, 0) }, /* SPX2000 */ 80546f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x500a, 0) }, /* PM5D */ 80646f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x500b, 0) }, /* DME64N */ 80746f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x500c, 0) }, /* DME24N */ 80846f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x500d, 0) }, /* NULL */ 80946f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x500e, 0) }, /* NULL */ 81046f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x500f, 0) }, /* NULL */ 81146f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x7000, 0) }, /* DTX */ 81246f0b27aSHans Petter Selasky { USB_VPI(USB_VENDOR_YAMAHA, 0x7010, 0) }, /* UB99 */ 81346f0b27aSHans Petter Selasky }; 81446f0b27aSHans Petter Selasky 815f1a16106SHans Petter Selasky static const STRUCT_USB_HOST_ID __used uaudio_devs[] = { 816f1a16106SHans Petter Selasky /* Generic USB audio class match */ 817f1a16106SHans Petter Selasky {USB_IFACE_CLASS(UICLASS_AUDIO), 818f1a16106SHans Petter Selasky USB_IFACE_SUBCLASS(UISUBCLASS_AUDIOCONTROL),}, 819f1a16106SHans Petter Selasky /* Generic USB MIDI class match */ 820f1a16106SHans Petter Selasky {USB_IFACE_CLASS(UICLASS_AUDIO), 821f1a16106SHans Petter Selasky USB_IFACE_SUBCLASS(UISUBCLASS_MIDISTREAM),}, 822f1a16106SHans Petter Selasky }; 823f1a16106SHans Petter Selasky 8243a3f90c6SAndrew Thompson static int 8253a3f90c6SAndrew Thompson uaudio_probe(device_t dev) 8263a3f90c6SAndrew Thompson { 827760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 8283a3f90c6SAndrew Thompson 829fadc970bSAndrew Thompson if (uaa->usb_mode != USB_MODE_HOST) 8303a3f90c6SAndrew Thompson return (ENXIO); 8313a3f90c6SAndrew Thompson 83246f0b27aSHans Petter Selasky /* lookup non-standard device(s) */ 83346f0b27aSHans Petter Selasky 83446f0b27aSHans Petter Selasky if (usbd_lookup_id_by_uaa(uaudio_vendor_midi, 83546f0b27aSHans Petter Selasky sizeof(uaudio_vendor_midi), uaa) == 0) { 83646f0b27aSHans Petter Selasky return (BUS_PROBE_SPECIFIC); 83746f0b27aSHans Petter Selasky } 8383a3f90c6SAndrew Thompson 83925b74dabSHans Petter Selasky if (uaa->info.bInterfaceClass != UICLASS_AUDIO) { 84076eaf537SHans Petter Selasky if (uaa->info.bInterfaceClass != UICLASS_VENDOR || 84176eaf537SHans Petter Selasky usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS) == 0) 84225b74dabSHans Petter Selasky return (ENXIO); 84325b74dabSHans Petter Selasky } 84425b74dabSHans Petter Selasky 84525b74dabSHans Petter Selasky /* check for AUDIO control interface */ 84625b74dabSHans Petter Selasky 84725b74dabSHans Petter Selasky if (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL) { 848a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_BAD_AUDIO)) 8493a3f90c6SAndrew Thompson return (ENXIO); 8503a3f90c6SAndrew Thompson else 851cd10bffaSAndriy Gapon return (BUS_PROBE_GENERIC); 8523a3f90c6SAndrew Thompson } 853dc694251SAndrew Thompson 854dc694251SAndrew Thompson /* check for MIDI stream */ 855dc694251SAndrew Thompson 85625b74dabSHans Petter Selasky if (uaa->info.bInterfaceSubClass == UISUBCLASS_MIDISTREAM) { 85725b74dabSHans Petter Selasky if (usb_test_quirk(uaa, UQ_BAD_MIDI)) 85825b74dabSHans Petter Selasky return (ENXIO); 85925b74dabSHans Petter Selasky else 860cd10bffaSAndriy Gapon return (BUS_PROBE_GENERIC); 861dc694251SAndrew Thompson } 8623a3f90c6SAndrew Thompson return (ENXIO); 8633a3f90c6SAndrew Thompson } 8643a3f90c6SAndrew Thompson 8653a3f90c6SAndrew Thompson static int 8663a3f90c6SAndrew Thompson uaudio_attach(device_t dev) 8673a3f90c6SAndrew Thompson { 868760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 8693a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 870760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 87133da3daaSHans Petter Selasky usb_error_t err; 8723a3f90c6SAndrew Thompson device_t child; 8733a3f90c6SAndrew Thompson 8743a3f90c6SAndrew Thompson sc->sc_play_chan.priv_sc = sc; 8753a3f90c6SAndrew Thompson sc->sc_rec_chan.priv_sc = sc; 8763a3f90c6SAndrew Thompson sc->sc_udev = uaa->device; 877b029f6bbSAndrew Thompson sc->sc_mixer_iface_index = uaa->info.bIfaceIndex; 878b029f6bbSAndrew Thompson sc->sc_mixer_iface_no = uaa->info.bIfaceNum; 879455a367fSHans Petter Selasky sc->sc_config_msg[0].hdr.pm_callback = &uaudio_configure_msg; 880455a367fSHans Petter Selasky sc->sc_config_msg[0].sc = sc; 881455a367fSHans Petter Selasky sc->sc_config_msg[1].hdr.pm_callback = &uaudio_configure_msg; 882455a367fSHans Petter Selasky sc->sc_config_msg[1].sc = sc; 8833a3f90c6SAndrew Thompson 884a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_AUDIO_SWAP_LR)) 8853a3f90c6SAndrew Thompson sc->sc_uq_audio_swap_lr = 1; 8863a3f90c6SAndrew Thompson 887a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_AU_INP_ASYNC)) 8883a3f90c6SAndrew Thompson sc->sc_uq_au_inp_async = 1; 8893a3f90c6SAndrew Thompson 890a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_AU_NO_XU)) 8913a3f90c6SAndrew Thompson sc->sc_uq_au_no_xu = 1; 8923a3f90c6SAndrew Thompson 893a593f6b8SAndrew Thompson if (usb_test_quirk(uaa, UQ_BAD_ADC)) 8943a3f90c6SAndrew Thompson sc->sc_uq_bad_adc = 1; 8953a3f90c6SAndrew Thompson 89625b74dabSHans Petter Selasky if (usb_test_quirk(uaa, UQ_AU_VENDOR_CLASS)) 89725b74dabSHans Petter Selasky sc->sc_uq_au_vendor_class = 1; 89825b74dabSHans Petter Selasky 8993a3f90c6SAndrew Thompson umidi_init(dev); 9003a3f90c6SAndrew Thompson 901a593f6b8SAndrew Thompson device_set_usb_desc(dev); 9023a3f90c6SAndrew Thompson 903a593f6b8SAndrew Thompson id = usbd_get_interface_descriptor(uaa->iface); 9043a3f90c6SAndrew Thompson 905e2524b2eSHans Petter Selasky /* must fill mixer info before channel info */ 9063a3f90c6SAndrew Thompson uaudio_mixer_fill_info(sc, uaa->device, id); 9073a3f90c6SAndrew Thompson 908e2524b2eSHans Petter Selasky /* fill channel info */ 909e2524b2eSHans Petter Selasky uaudio_chan_fill_info(sc, uaa->device); 910e2524b2eSHans Petter Selasky 9113a3f90c6SAndrew Thompson DPRINTF("audio rev %d.%02x\n", 9123a3f90c6SAndrew Thompson sc->sc_audio_rev >> 8, 9133a3f90c6SAndrew Thompson sc->sc_audio_rev & 0xff); 9143a3f90c6SAndrew Thompson 915ff4d5953SHans Petter Selasky if (sc->sc_mixer_count == 0) { 916ff4d5953SHans Petter Selasky if (uaa->info.idVendor == USB_VENDOR_MAUDIO && 917ff4d5953SHans Petter Selasky (uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA || 918ff4d5953SHans Petter Selasky uaa->info.idProduct == USB_PRODUCT_MAUDIO_FASTTRACKULTRA8R)) { 919ff4d5953SHans Petter Selasky DPRINTF("Generating mixer descriptors\n"); 920ff4d5953SHans Petter Selasky uaudio_mixer_controls_create_ftu(sc); 921ff4d5953SHans Petter Selasky } 922ff4d5953SHans Petter Selasky } 923ff4d5953SHans Petter Selasky 9243a3f90c6SAndrew Thompson DPRINTF("%d mixer controls\n", 9253a3f90c6SAndrew Thompson sc->sc_mixer_count); 9263a3f90c6SAndrew Thompson 927455a367fSHans Petter Selasky if (sc->sc_play_chan.num_alt > 0) { 928455a367fSHans Petter Selasky uint8_t x; 92933da3daaSHans Petter Selasky 93033da3daaSHans Petter Selasky /* 93133da3daaSHans Petter Selasky * Need to set a default alternate interface, else 93233da3daaSHans Petter Selasky * some USB audio devices might go into an infinte 93333da3daaSHans Petter Selasky * re-enumeration loop: 93433da3daaSHans Petter Selasky */ 93533da3daaSHans Petter Selasky err = usbd_set_alt_interface_index(sc->sc_udev, 93633da3daaSHans Petter Selasky sc->sc_play_chan.usb_alt[0].iface_index, 93733da3daaSHans Petter Selasky sc->sc_play_chan.usb_alt[0].iface_alt_index); 93833da3daaSHans Petter Selasky if (err) { 93933da3daaSHans Petter Selasky DPRINTF("setting of alternate index failed: %s!\n", 94033da3daaSHans Petter Selasky usbd_errstr(err)); 94133da3daaSHans Petter Selasky } 942455a367fSHans Petter Selasky for (x = 0; x != sc->sc_play_chan.num_alt; x++) { 943902514f6SHans Petter Selasky device_printf(dev, "Play: %d Hz, %d ch, %s format, " 944d3e08ca9SHans Petter Selasky "2x8ms buffer.\n", 945455a367fSHans Petter Selasky sc->sc_play_chan.usb_alt[x].sample_rate, 946455a367fSHans Petter Selasky sc->sc_play_chan.usb_alt[x].channels, 947455a367fSHans Petter Selasky sc->sc_play_chan.usb_alt[x].p_fmt->description); 948455a367fSHans Petter Selasky } 9493a3f90c6SAndrew Thompson } else { 9509b5da816SHans Petter Selasky device_printf(dev, "No playback.\n"); 9513a3f90c6SAndrew Thompson } 9523a3f90c6SAndrew Thompson 953455a367fSHans Petter Selasky if (sc->sc_rec_chan.num_alt > 0) { 954455a367fSHans Petter Selasky uint8_t x; 95533da3daaSHans Petter Selasky 95633da3daaSHans Petter Selasky /* 95733da3daaSHans Petter Selasky * Need to set a default alternate interface, else 95833da3daaSHans Petter Selasky * some USB audio devices might go into an infinte 95933da3daaSHans Petter Selasky * re-enumeration loop: 96033da3daaSHans Petter Selasky */ 96133da3daaSHans Petter Selasky err = usbd_set_alt_interface_index(sc->sc_udev, 96233da3daaSHans Petter Selasky sc->sc_rec_chan.usb_alt[0].iface_index, 96333da3daaSHans Petter Selasky sc->sc_rec_chan.usb_alt[0].iface_alt_index); 96433da3daaSHans Petter Selasky if (err) { 96533da3daaSHans Petter Selasky DPRINTF("setting of alternate index failed: %s!\n", 96633da3daaSHans Petter Selasky usbd_errstr(err)); 96733da3daaSHans Petter Selasky } 968455a367fSHans Petter Selasky for (x = 0; x != sc->sc_rec_chan.num_alt; x++) { 969902514f6SHans Petter Selasky device_printf(dev, "Record: %d Hz, %d ch, %s format, " 970d3e08ca9SHans Petter Selasky "2x8ms buffer.\n", 971455a367fSHans Petter Selasky sc->sc_rec_chan.usb_alt[x].sample_rate, 972455a367fSHans Petter Selasky sc->sc_rec_chan.usb_alt[x].channels, 973455a367fSHans Petter Selasky sc->sc_rec_chan.usb_alt[x].p_fmt->description); 974455a367fSHans Petter Selasky } 9753a3f90c6SAndrew Thompson } else { 9769b5da816SHans Petter Selasky device_printf(dev, "No recording.\n"); 9773a3f90c6SAndrew Thompson } 9783a3f90c6SAndrew Thompson 97946f0b27aSHans Petter Selasky if (sc->sc_midi_chan.valid == 0) { 98046f0b27aSHans Petter Selasky if (usbd_lookup_id_by_uaa(uaudio_vendor_midi, 98146f0b27aSHans Petter Selasky sizeof(uaudio_vendor_midi), uaa) == 0) { 98246f0b27aSHans Petter Selasky sc->sc_midi_chan.iface_index = 98346f0b27aSHans Petter Selasky (uint8_t)uaa->driver_info; 98446f0b27aSHans Petter Selasky sc->sc_midi_chan.iface_alt_index = 0; 98546f0b27aSHans Petter Selasky sc->sc_midi_chan.valid = 1; 98646f0b27aSHans Petter Selasky } 98746f0b27aSHans Petter Selasky } 98846f0b27aSHans Petter Selasky 9893a3f90c6SAndrew Thompson if (sc->sc_midi_chan.valid) { 9903a3f90c6SAndrew Thompson 9913a3f90c6SAndrew Thompson if (umidi_probe(dev)) { 9923a3f90c6SAndrew Thompson goto detach; 9933a3f90c6SAndrew Thompson } 9949b5da816SHans Petter Selasky device_printf(dev, "MIDI sequencer.\n"); 9953a3f90c6SAndrew Thompson } else { 99676b71212SHans Petter Selasky device_printf(dev, "No MIDI sequencer.\n"); 9973a3f90c6SAndrew Thompson } 9983a3f90c6SAndrew Thompson 9993a3f90c6SAndrew Thompson DPRINTF("doing child attach\n"); 10003a3f90c6SAndrew Thompson 10013a3f90c6SAndrew Thompson /* attach the children */ 10023a3f90c6SAndrew Thompson 10033a3f90c6SAndrew Thompson sc->sc_sndcard_func.func = SCF_PCM; 10043a3f90c6SAndrew Thompson 10059b5da816SHans Petter Selasky /* 10069b5da816SHans Petter Selasky * Only attach a PCM device if we have a playback, recording 10079b5da816SHans Petter Selasky * or mixer device present: 10089b5da816SHans Petter Selasky */ 1009455a367fSHans Petter Selasky if (sc->sc_play_chan.num_alt > 0 || 1010455a367fSHans Petter Selasky sc->sc_rec_chan.num_alt > 0 || 10119b5da816SHans Petter Selasky sc->sc_mix_info) { 10123a3f90c6SAndrew Thompson child = device_add_child(dev, "pcm", -1); 10133a3f90c6SAndrew Thompson 10143a3f90c6SAndrew Thompson if (child == NULL) { 10153a3f90c6SAndrew Thompson DPRINTF("out of memory\n"); 10163a3f90c6SAndrew Thompson goto detach; 10173a3f90c6SAndrew Thompson } 10183a3f90c6SAndrew Thompson device_set_ivars(child, &sc->sc_sndcard_func); 10199b5da816SHans Petter Selasky } 10203a3f90c6SAndrew Thompson 10213a3f90c6SAndrew Thompson if (bus_generic_attach(dev)) { 10223a3f90c6SAndrew Thompson DPRINTF("child attach failed\n"); 10233a3f90c6SAndrew Thompson goto detach; 10243a3f90c6SAndrew Thompson } 1025902514f6SHans Petter Selasky 102676b71212SHans Petter Selasky if (uaudio_hid_probe(sc, uaa) == 0) { 102776b71212SHans Petter Selasky device_printf(dev, "HID volume keys found.\n"); 102876b71212SHans Petter Selasky } else { 102976b71212SHans Petter Selasky device_printf(dev, "No HID volume keys found.\n"); 103076b71212SHans Petter Selasky } 103176b71212SHans Petter Selasky 1032902514f6SHans Petter Selasky /* reload all mixer settings */ 1033902514f6SHans Petter Selasky uaudio_mixer_reload_all(sc); 1034902514f6SHans Petter Selasky 10353a3f90c6SAndrew Thompson return (0); /* success */ 10363a3f90c6SAndrew Thompson 10373a3f90c6SAndrew Thompson detach: 10383a3f90c6SAndrew Thompson uaudio_detach(dev); 10393a3f90c6SAndrew Thompson return (ENXIO); 10403a3f90c6SAndrew Thompson } 10413a3f90c6SAndrew Thompson 10423a3f90c6SAndrew Thompson static void 10433a3f90c6SAndrew Thompson uaudio_pcm_setflags(device_t dev, uint32_t flags) 10443a3f90c6SAndrew Thompson { 10453a3f90c6SAndrew Thompson pcm_setflags(dev, pcm_getflags(dev) | flags); 10463a3f90c6SAndrew Thompson } 10473a3f90c6SAndrew Thompson 10483a3f90c6SAndrew Thompson int 10493a3f90c6SAndrew Thompson uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class) 10503a3f90c6SAndrew Thompson { 10513a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(device_get_parent(dev)); 10523a3f90c6SAndrew Thompson char status[SND_STATUSLEN]; 10533a3f90c6SAndrew Thompson 10543a3f90c6SAndrew Thompson uaudio_mixer_init(sc); 10553a3f90c6SAndrew Thompson 10563a3f90c6SAndrew Thompson if (sc->sc_uq_audio_swap_lr) { 10573a3f90c6SAndrew Thompson DPRINTF("hardware has swapped left and right\n"); 105890da2b28SAriff Abdullah /* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */ 10593a3f90c6SAndrew Thompson } 10603a3f90c6SAndrew Thompson if (!(sc->sc_mix_info & SOUND_MASK_PCM)) { 10613a3f90c6SAndrew Thompson 10623a3f90c6SAndrew Thompson DPRINTF("emulating master volume\n"); 10633a3f90c6SAndrew Thompson 10643a3f90c6SAndrew Thompson /* 10653a3f90c6SAndrew Thompson * Emulate missing pcm mixer controller 10663a3f90c6SAndrew Thompson * through FEEDER_VOLUME 10673a3f90c6SAndrew Thompson */ 10683a3f90c6SAndrew Thompson uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL); 10693a3f90c6SAndrew Thompson } 1070902514f6SHans Petter Selasky if (mixer_init(dev, mixer_class, sc)) 10713a3f90c6SAndrew Thompson goto detach; 10723a3f90c6SAndrew Thompson sc->sc_mixer_init = 1; 10733a3f90c6SAndrew Thompson 10742ba0f361SHans Petter Selasky mixer_hwvol_init(dev); 10752ba0f361SHans Petter Selasky 10763a3f90c6SAndrew Thompson snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio)); 10773a3f90c6SAndrew Thompson 10783a3f90c6SAndrew Thompson if (pcm_register(dev, sc, 1079455a367fSHans Petter Selasky (sc->sc_play_chan.num_alt > 0) ? 1 : 0, 1080455a367fSHans Petter Selasky (sc->sc_rec_chan.num_alt > 0) ? 1 : 0)) { 10813a3f90c6SAndrew Thompson goto detach; 10823a3f90c6SAndrew Thompson } 108390da2b28SAriff Abdullah 108490da2b28SAriff Abdullah uaudio_pcm_setflags(dev, SD_F_MPSAFE); 10853a3f90c6SAndrew Thompson sc->sc_pcm_registered = 1; 10863a3f90c6SAndrew Thompson 1087455a367fSHans Petter Selasky if (sc->sc_play_chan.num_alt > 0) { 10883a3f90c6SAndrew Thompson pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc); 10893a3f90c6SAndrew Thompson } 1090455a367fSHans Petter Selasky if (sc->sc_rec_chan.num_alt > 0) { 10913a3f90c6SAndrew Thompson pcm_addchan(dev, PCMDIR_REC, chan_class, sc); 10923a3f90c6SAndrew Thompson } 10933a3f90c6SAndrew Thompson pcm_setstatus(dev, status); 10943a3f90c6SAndrew Thompson 1095902514f6SHans Petter Selasky uaudio_mixer_register_sysctl(sc, dev); 1096902514f6SHans Petter Selasky 10973a3f90c6SAndrew Thompson return (0); /* success */ 10983a3f90c6SAndrew Thompson 10993a3f90c6SAndrew Thompson detach: 11003a3f90c6SAndrew Thompson uaudio_detach_sub(dev); 11013a3f90c6SAndrew Thompson return (ENXIO); 11023a3f90c6SAndrew Thompson } 11033a3f90c6SAndrew Thompson 11043a3f90c6SAndrew Thompson int 11053a3f90c6SAndrew Thompson uaudio_detach_sub(device_t dev) 11063a3f90c6SAndrew Thompson { 11073a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(device_get_parent(dev)); 11083a3f90c6SAndrew Thompson int error = 0; 11093a3f90c6SAndrew Thompson 11103a3f90c6SAndrew Thompson repeat: 11113a3f90c6SAndrew Thompson if (sc->sc_pcm_registered) { 11123a3f90c6SAndrew Thompson error = pcm_unregister(dev); 11133a3f90c6SAndrew Thompson } else { 11143a3f90c6SAndrew Thompson if (sc->sc_mixer_init) { 11153a3f90c6SAndrew Thompson error = mixer_uninit(dev); 11163a3f90c6SAndrew Thompson } 11173a3f90c6SAndrew Thompson } 11183a3f90c6SAndrew Thompson 11193a3f90c6SAndrew Thompson if (error) { 11203a3f90c6SAndrew Thompson device_printf(dev, "Waiting for sound application to exit!\n"); 1121a593f6b8SAndrew Thompson usb_pause_mtx(NULL, 2 * hz); 11223a3f90c6SAndrew Thompson goto repeat; /* try again */ 11233a3f90c6SAndrew Thompson } 11243a3f90c6SAndrew Thompson return (0); /* success */ 11253a3f90c6SAndrew Thompson } 11263a3f90c6SAndrew Thompson 11273a3f90c6SAndrew Thompson static int 11283a3f90c6SAndrew Thompson uaudio_detach(device_t dev) 11293a3f90c6SAndrew Thompson { 11303a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 11313a3f90c6SAndrew Thompson 1132a6ed4937SHans Petter Selasky /* 1133a6ed4937SHans Petter Selasky * Stop USB transfers early so that any audio applications 1134a6ed4937SHans Petter Selasky * will time out and close opened /dev/dspX.Y device(s), if 1135a6ed4937SHans Petter Selasky * any. 1136a6ed4937SHans Petter Selasky */ 1137455a367fSHans Petter Selasky usb_proc_explore_lock(sc->sc_udev); 1138455a367fSHans Petter Selasky sc->sc_play_chan.operation = CHAN_OP_DRAIN; 1139455a367fSHans Petter Selasky sc->sc_rec_chan.operation = CHAN_OP_DRAIN; 1140455a367fSHans Petter Selasky usb_proc_explore_mwait(sc->sc_udev, 1141455a367fSHans Petter Selasky &sc->sc_config_msg[0], &sc->sc_config_msg[1]); 1142455a367fSHans Petter Selasky usb_proc_explore_unlock(sc->sc_udev); 1143455a367fSHans Petter Selasky 1144b4380da7SHans Petter Selasky usbd_transfer_unsetup(sc->sc_play_chan.xfer, UAUDIO_NCHANBUFS + 1); 1145b4380da7SHans Petter Selasky usbd_transfer_unsetup(sc->sc_rec_chan.xfer, UAUDIO_NCHANBUFS + 1); 1146a6ed4937SHans Petter Selasky 114776b71212SHans Petter Selasky uaudio_hid_detach(sc); 114876b71212SHans Petter Selasky 1149a6ed4937SHans Petter Selasky if (bus_generic_detach(dev) != 0) { 11503a3f90c6SAndrew Thompson DPRINTF("detach failed!\n"); 11513a3f90c6SAndrew Thompson } 11523a3f90c6SAndrew Thompson sbuf_delete(&sc->sc_sndstat); 11533a3f90c6SAndrew Thompson sc->sc_sndstat_valid = 0; 11543a3f90c6SAndrew Thompson 11553a3f90c6SAndrew Thompson umidi_detach(dev); 11563a3f90c6SAndrew Thompson 1157902514f6SHans Petter Selasky /* free mixer data */ 1158902514f6SHans Petter Selasky 1159902514f6SHans Petter Selasky uaudio_mixer_ctl_free(sc); 1160902514f6SHans Petter Selasky 11613a3f90c6SAndrew Thompson return (0); 11623a3f90c6SAndrew Thompson } 11633a3f90c6SAndrew Thompson 1164455a367fSHans Petter Selasky static uint32_t 1165455a367fSHans Petter Selasky uaudio_get_buffer_size(struct uaudio_chan *ch, uint8_t alt) 1166455a367fSHans Petter Selasky { 1167455a367fSHans Petter Selasky struct uaudio_chan_alt *chan_alt = &ch->usb_alt[alt]; 1168455a367fSHans Petter Selasky /* We use 2 times 8ms of buffer */ 1169455a367fSHans Petter Selasky uint32_t buf_size = (((chan_alt->sample_rate * (UAUDIO_NFRAMES / 8)) + 1170455a367fSHans Petter Selasky 1000 - 1) / 1000) * chan_alt->sample_size; 1171455a367fSHans Petter Selasky return (buf_size); 1172455a367fSHans Petter Selasky } 1173455a367fSHans Petter Selasky 1174455a367fSHans Petter Selasky static void 1175455a367fSHans Petter Selasky uaudio_configure_msg_sub(struct uaudio_softc *sc, 1176455a367fSHans Petter Selasky struct uaudio_chan *chan, int dir) 1177455a367fSHans Petter Selasky { 1178455a367fSHans Petter Selasky struct uaudio_chan_alt *chan_alt; 1179455a367fSHans Petter Selasky uint32_t frames; 1180455a367fSHans Petter Selasky uint32_t buf_size; 1181455a367fSHans Petter Selasky uint16_t fps; 1182455a367fSHans Petter Selasky uint8_t set_alt; 1183455a367fSHans Petter Selasky uint8_t fps_shift; 1184455a367fSHans Petter Selasky uint8_t operation; 1185455a367fSHans Petter Selasky usb_error_t err; 1186455a367fSHans Petter Selasky 1187455a367fSHans Petter Selasky if (chan->num_alt <= 0) 1188455a367fSHans Petter Selasky return; 1189455a367fSHans Petter Selasky 1190455a367fSHans Petter Selasky DPRINTF("\n"); 1191455a367fSHans Petter Selasky 1192455a367fSHans Petter Selasky usb_proc_explore_lock(sc->sc_udev); 1193455a367fSHans Petter Selasky operation = chan->operation; 1194455a367fSHans Petter Selasky chan->operation = CHAN_OP_NONE; 1195455a367fSHans Petter Selasky usb_proc_explore_unlock(sc->sc_udev); 1196455a367fSHans Petter Selasky 1197455a367fSHans Petter Selasky mtx_lock(chan->pcm_mtx); 1198455a367fSHans Petter Selasky if (chan->cur_alt != chan->set_alt) 1199455a367fSHans Petter Selasky set_alt = chan->set_alt; 1200455a367fSHans Petter Selasky else 1201455a367fSHans Petter Selasky set_alt = CHAN_MAX_ALT; 1202455a367fSHans Petter Selasky mtx_unlock(chan->pcm_mtx); 1203455a367fSHans Petter Selasky 1204455a367fSHans Petter Selasky if (set_alt >= chan->num_alt) 1205455a367fSHans Petter Selasky goto done; 1206455a367fSHans Petter Selasky 1207455a367fSHans Petter Selasky chan_alt = chan->usb_alt + set_alt; 1208455a367fSHans Petter Selasky 1209455a367fSHans Petter Selasky usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1); 1210455a367fSHans Petter Selasky 1211455a367fSHans Petter Selasky err = usbd_set_alt_interface_index(sc->sc_udev, 1212455a367fSHans Petter Selasky chan_alt->iface_index, chan_alt->iface_alt_index); 1213455a367fSHans Petter Selasky if (err) { 1214455a367fSHans Petter Selasky DPRINTF("setting of alternate index failed: %s!\n", 1215455a367fSHans Petter Selasky usbd_errstr(err)); 1216455a367fSHans Petter Selasky goto error; 1217455a367fSHans Petter Selasky } 1218455a367fSHans Petter Selasky 1219455a367fSHans Petter Selasky /* 1220455a367fSHans Petter Selasky * Only set the sample rate if the channel reports that it 1221455a367fSHans Petter Selasky * supports the frequency control. 1222455a367fSHans Petter Selasky */ 1223455a367fSHans Petter Selasky 1224455a367fSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { 1225455a367fSHans Petter Selasky /* FALLTHROUGH */ 1226455a367fSHans Petter Selasky } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { 1227455a367fSHans Petter Selasky unsigned int x; 1228455a367fSHans Petter Selasky 1229455a367fSHans Petter Selasky for (x = 0; x != 256; x++) { 1230455a367fSHans Petter Selasky if (dir == PCMDIR_PLAY) { 1231455a367fSHans Petter Selasky if (!(sc->sc_mixer_clocks.bit_output[x / 8] & 1232455a367fSHans Petter Selasky (1 << (x % 8)))) { 1233455a367fSHans Petter Selasky continue; 1234455a367fSHans Petter Selasky } 1235455a367fSHans Petter Selasky } else { 1236455a367fSHans Petter Selasky if (!(sc->sc_mixer_clocks.bit_input[x / 8] & 1237455a367fSHans Petter Selasky (1 << (x % 8)))) { 1238455a367fSHans Petter Selasky continue; 1239455a367fSHans Petter Selasky } 1240455a367fSHans Petter Selasky } 1241455a367fSHans Petter Selasky 1242455a367fSHans Petter Selasky if (uaudio20_set_speed(sc->sc_udev, 1243455a367fSHans Petter Selasky sc->sc_mixer_iface_no, x, chan_alt->sample_rate)) { 1244455a367fSHans Petter Selasky /* 1245455a367fSHans Petter Selasky * If the endpoint is adaptive setting 1246455a367fSHans Petter Selasky * the speed may fail. 1247455a367fSHans Petter Selasky */ 1248455a367fSHans Petter Selasky DPRINTF("setting of sample rate failed! " 1249455a367fSHans Petter Selasky "(continuing anyway)\n"); 1250455a367fSHans Petter Selasky } 1251455a367fSHans Petter Selasky } 1252455a367fSHans Petter Selasky } else if (chan_alt->p_sed.v1->bmAttributes & UA_SED_FREQ_CONTROL) { 1253455a367fSHans Petter Selasky if (uaudio_set_speed(sc->sc_udev, 1254455a367fSHans Petter Selasky chan_alt->p_ed1->bEndpointAddress, chan_alt->sample_rate)) { 1255455a367fSHans Petter Selasky /* 1256455a367fSHans Petter Selasky * If the endpoint is adaptive setting the 1257455a367fSHans Petter Selasky * speed may fail. 1258455a367fSHans Petter Selasky */ 1259455a367fSHans Petter Selasky DPRINTF("setting of sample rate failed! " 1260455a367fSHans Petter Selasky "(continuing anyway)\n"); 1261455a367fSHans Petter Selasky } 1262455a367fSHans Petter Selasky } 1263455a367fSHans Petter Selasky if (usbd_transfer_setup(sc->sc_udev, &chan_alt->iface_index, chan->xfer, 1264455a367fSHans Petter Selasky chan_alt->usb_cfg, UAUDIO_NCHANBUFS + 1, chan, chan->pcm_mtx)) { 1265455a367fSHans Petter Selasky DPRINTF("could not allocate USB transfers!\n"); 1266455a367fSHans Petter Selasky goto error; 1267455a367fSHans Petter Selasky } 1268455a367fSHans Petter Selasky 1269455a367fSHans Petter Selasky fps = usbd_get_isoc_fps(sc->sc_udev); 1270455a367fSHans Petter Selasky 1271455a367fSHans Petter Selasky if (fps < 8000) { 1272455a367fSHans Petter Selasky /* FULL speed USB */ 1273455a367fSHans Petter Selasky frames = 8; 1274455a367fSHans Petter Selasky } else { 1275455a367fSHans Petter Selasky /* HIGH speed USB */ 1276455a367fSHans Petter Selasky frames = UAUDIO_NFRAMES; 1277455a367fSHans Petter Selasky } 1278455a367fSHans Petter Selasky 1279455a367fSHans Petter Selasky fps_shift = usbd_xfer_get_fps_shift(chan->xfer[0]); 1280455a367fSHans Petter Selasky 1281455a367fSHans Petter Selasky /* down shift number of frames per second, if any */ 1282455a367fSHans Petter Selasky fps >>= fps_shift; 1283455a367fSHans Petter Selasky frames >>= fps_shift; 1284455a367fSHans Petter Selasky 1285455a367fSHans Petter Selasky /* bytes per frame should not be zero */ 1286455a367fSHans Petter Selasky chan->bytes_per_frame[0] = 1287455a367fSHans Petter Selasky ((chan_alt->sample_rate / fps) * chan_alt->sample_size); 1288455a367fSHans Petter Selasky chan->bytes_per_frame[1] = 1289455a367fSHans Petter Selasky (((chan_alt->sample_rate + fps - 1) / fps) * chan_alt->sample_size); 1290455a367fSHans Petter Selasky 1291455a367fSHans Petter Selasky /* setup data rate dithering, if any */ 1292455a367fSHans Petter Selasky chan->frames_per_second = fps; 1293455a367fSHans Petter Selasky chan->sample_rem = chan_alt->sample_rate % fps; 1294455a367fSHans Petter Selasky chan->sample_curr = 0; 1295455a367fSHans Petter Selasky chan->frames_per_second = fps; 1296455a367fSHans Petter Selasky 1297455a367fSHans Petter Selasky /* compute required buffer size */ 1298455a367fSHans Petter Selasky buf_size = (chan->bytes_per_frame[1] * frames); 1299455a367fSHans Petter Selasky 1300455a367fSHans Petter Selasky if (buf_size > (chan->end - chan->start)) { 1301455a367fSHans Petter Selasky DPRINTF("buffer size is too big\n"); 1302455a367fSHans Petter Selasky goto error; 1303455a367fSHans Petter Selasky } 1304455a367fSHans Petter Selasky 1305455a367fSHans Petter Selasky chan->intr_frames = frames; 1306455a367fSHans Petter Selasky 1307455a367fSHans Petter Selasky DPRINTF("fps=%d sample_rem=%d\n", (int)fps, (int)chan->sample_rem); 1308455a367fSHans Petter Selasky 1309455a367fSHans Petter Selasky if (chan->intr_frames == 0) { 1310455a367fSHans Petter Selasky DPRINTF("frame shift is too high!\n"); 1311455a367fSHans Petter Selasky goto error; 1312455a367fSHans Petter Selasky } 1313455a367fSHans Petter Selasky 1314455a367fSHans Petter Selasky mtx_lock(chan->pcm_mtx); 1315455a367fSHans Petter Selasky chan->cur_alt = set_alt; 1316455a367fSHans Petter Selasky mtx_unlock(chan->pcm_mtx); 1317455a367fSHans Petter Selasky 1318455a367fSHans Petter Selasky done: 1319455a367fSHans Petter Selasky #if (UAUDIO_NCHANBUFS != 2) 1320455a367fSHans Petter Selasky #error "please update code" 1321455a367fSHans Petter Selasky #endif 1322455a367fSHans Petter Selasky switch (operation) { 1323455a367fSHans Petter Selasky case CHAN_OP_START: 1324455a367fSHans Petter Selasky mtx_lock(chan->pcm_mtx); 1325455a367fSHans Petter Selasky usbd_transfer_start(chan->xfer[0]); 1326455a367fSHans Petter Selasky usbd_transfer_start(chan->xfer[1]); 1327455a367fSHans Petter Selasky mtx_unlock(chan->pcm_mtx); 1328455a367fSHans Petter Selasky break; 1329455a367fSHans Petter Selasky case CHAN_OP_STOP: 1330455a367fSHans Petter Selasky mtx_lock(chan->pcm_mtx); 1331455a367fSHans Petter Selasky usbd_transfer_stop(chan->xfer[0]); 1332455a367fSHans Petter Selasky usbd_transfer_stop(chan->xfer[1]); 1333455a367fSHans Petter Selasky mtx_unlock(chan->pcm_mtx); 1334455a367fSHans Petter Selasky break; 1335455a367fSHans Petter Selasky default: 1336455a367fSHans Petter Selasky break; 1337455a367fSHans Petter Selasky } 1338455a367fSHans Petter Selasky return; 1339455a367fSHans Petter Selasky 1340455a367fSHans Petter Selasky error: 1341455a367fSHans Petter Selasky usbd_transfer_unsetup(chan->xfer, UAUDIO_NCHANBUFS + 1); 1342455a367fSHans Petter Selasky 1343455a367fSHans Petter Selasky mtx_lock(chan->pcm_mtx); 1344455a367fSHans Petter Selasky chan->cur_alt = CHAN_MAX_ALT; 1345455a367fSHans Petter Selasky mtx_unlock(chan->pcm_mtx); 1346455a367fSHans Petter Selasky } 1347455a367fSHans Petter Selasky 1348455a367fSHans Petter Selasky static void 1349455a367fSHans Petter Selasky uaudio_configure_msg(struct usb_proc_msg *pm) 1350455a367fSHans Petter Selasky { 1351455a367fSHans Petter Selasky struct uaudio_softc *sc = ((struct uaudio_configure_msg *)pm)->sc; 1352455a367fSHans Petter Selasky 1353455a367fSHans Petter Selasky usb_proc_explore_unlock(sc->sc_udev); 1354455a367fSHans Petter Selasky uaudio_configure_msg_sub(sc, &sc->sc_play_chan, PCMDIR_PLAY); 1355455a367fSHans Petter Selasky uaudio_configure_msg_sub(sc, &sc->sc_rec_chan, PCMDIR_REC); 1356455a367fSHans Petter Selasky usb_proc_explore_lock(sc->sc_udev); 1357455a367fSHans Petter Selasky } 1358455a367fSHans Petter Selasky 13593a3f90c6SAndrew Thompson /*========================================================================* 13603a3f90c6SAndrew Thompson * AS - Audio Stream - routines 13613a3f90c6SAndrew Thompson *========================================================================*/ 13623a3f90c6SAndrew Thompson 1363b850ecc1SAndrew Thompson #ifdef USB_DEBUG 13643a3f90c6SAndrew Thompson static void 13654c21be9bSRebecca Cran uaudio_chan_dump_ep_desc(const usb_endpoint_descriptor_audio_t *ed) 13663a3f90c6SAndrew Thompson { 13673a3f90c6SAndrew Thompson if (ed) { 13683a3f90c6SAndrew Thompson DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n" 13693a3f90c6SAndrew Thompson "bEndpointAddress=%d bmAttributes=0x%x \n" 13703a3f90c6SAndrew Thompson "wMaxPacketSize=%d bInterval=%d \n" 13713a3f90c6SAndrew Thompson "bRefresh=%d bSynchAddress=%d\n", 13723a3f90c6SAndrew Thompson ed, ed->bLength, ed->bDescriptorType, 13733a3f90c6SAndrew Thompson ed->bEndpointAddress, ed->bmAttributes, 13743a3f90c6SAndrew Thompson UGETW(ed->wMaxPacketSize), ed->bInterval, 1375e3e05e50SAndrew Thompson UEP_HAS_REFRESH(ed) ? ed->bRefresh : 0, 1376e3e05e50SAndrew Thompson UEP_HAS_SYNCADDR(ed) ? ed->bSynchAddress : 0); 13773a3f90c6SAndrew Thompson } 13783a3f90c6SAndrew Thompson } 13793a3f90c6SAndrew Thompson 13803a3f90c6SAndrew Thompson #endif 13813a3f90c6SAndrew Thompson 1382f895cc0cSHans Petter Selasky /* 1383f895cc0cSHans Petter Selasky * The following is a workaround for broken no-name USB audio devices 1384f895cc0cSHans Petter Selasky * sold by dealextreme called "3D sound". The problem is that the 1385f895cc0cSHans Petter Selasky * manufacturer computed wMaxPacketSize is too small to hold the 1386f895cc0cSHans Petter Selasky * actual data sent. In other words the device sometimes sends more 1387f895cc0cSHans Petter Selasky * data than it actually reports it can send in a single isochronous 1388f895cc0cSHans Petter Selasky * packet. 1389f895cc0cSHans Petter Selasky */ 1390f895cc0cSHans Petter Selasky static void 1391f895cc0cSHans Petter Selasky uaudio_record_fix_fs(usb_endpoint_descriptor_audio_t *ep, 1392f895cc0cSHans Petter Selasky uint32_t xps, uint32_t add) 1393f895cc0cSHans Petter Selasky { 1394f895cc0cSHans Petter Selasky uint32_t mps; 1395f895cc0cSHans Petter Selasky 1396f895cc0cSHans Petter Selasky mps = UGETW(ep->wMaxPacketSize); 1397f895cc0cSHans Petter Selasky 1398f895cc0cSHans Petter Selasky /* 1399f895cc0cSHans Petter Selasky * If the device indicates it can send more data than what the 1400f895cc0cSHans Petter Selasky * sample rate indicates, we apply the workaround. 1401f895cc0cSHans Petter Selasky */ 1402f895cc0cSHans Petter Selasky if (mps > xps) { 1403f895cc0cSHans Petter Selasky 1404f895cc0cSHans Petter Selasky /* allow additional data */ 1405f895cc0cSHans Petter Selasky xps += add; 1406f895cc0cSHans Petter Selasky 1407f895cc0cSHans Petter Selasky /* check against the maximum USB 1.x length */ 1408f895cc0cSHans Petter Selasky if (xps > 1023) 1409f895cc0cSHans Petter Selasky xps = 1023; 1410f895cc0cSHans Petter Selasky 1411f895cc0cSHans Petter Selasky /* check if we should do an update */ 1412f895cc0cSHans Petter Selasky if (mps < xps) { 1413f895cc0cSHans Petter Selasky /* simply update the wMaxPacketSize field */ 1414f895cc0cSHans Petter Selasky USETW(ep->wMaxPacketSize, xps); 1415f895cc0cSHans Petter Selasky DPRINTF("Workaround: Updated wMaxPacketSize " 1416f895cc0cSHans Petter Selasky "from %d to %d bytes.\n", 1417f895cc0cSHans Petter Selasky (int)mps, (int)xps); 1418f895cc0cSHans Petter Selasky } 1419f895cc0cSHans Petter Selasky } 1420f895cc0cSHans Petter Selasky } 1421f895cc0cSHans Petter Selasky 1422e2524b2eSHans Petter Selasky static usb_error_t 1423e2524b2eSHans Petter Selasky uaudio20_check_rate(struct usb_device *udev, uint8_t iface_no, 1424e2524b2eSHans Petter Selasky uint8_t clockid, uint32_t rate) 1425e2524b2eSHans Petter Selasky { 1426e2524b2eSHans Petter Selasky struct usb_device_request req; 1427e2524b2eSHans Petter Selasky usb_error_t error; 1428e2524b2eSHans Petter Selasky uint8_t data[255]; 1429e2524b2eSHans Petter Selasky uint16_t actlen; 1430e2524b2eSHans Petter Selasky uint16_t rates; 1431e2524b2eSHans Petter Selasky uint16_t x; 1432e2524b2eSHans Petter Selasky 1433e2524b2eSHans Petter Selasky DPRINTFN(6, "ifaceno=%d clockid=%d rate=%u\n", 1434e2524b2eSHans Petter Selasky iface_no, clockid, rate); 1435e2524b2eSHans Petter Selasky 1436e2524b2eSHans Petter Selasky req.bmRequestType = UT_READ_CLASS_INTERFACE; 1437e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_RANGE; 1438e2524b2eSHans Petter Selasky USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0); 1439e2524b2eSHans Petter Selasky USETW2(req.wIndex, clockid, iface_no); 1440e2524b2eSHans Petter Selasky USETW(req.wLength, 255); 1441e2524b2eSHans Petter Selasky 1442e2524b2eSHans Petter Selasky error = usbd_do_request_flags(udev, NULL, &req, data, 1443e2524b2eSHans Petter Selasky USB_SHORT_XFER_OK, &actlen, USB_DEFAULT_TIMEOUT); 1444e2524b2eSHans Petter Selasky 1445e2524b2eSHans Petter Selasky if (error != 0 || actlen < 2) 1446e2524b2eSHans Petter Selasky return (USB_ERR_INVAL); 1447e2524b2eSHans Petter Selasky 1448e2524b2eSHans Petter Selasky rates = data[0] | (data[1] << 8); 1449e2524b2eSHans Petter Selasky actlen = (actlen - 2) / 12; 1450e2524b2eSHans Petter Selasky 1451e2524b2eSHans Petter Selasky if (rates > actlen) { 1452e2524b2eSHans Petter Selasky DPRINTF("Too many rates\n"); 1453e2524b2eSHans Petter Selasky rates = actlen; 1454e2524b2eSHans Petter Selasky } 1455e2524b2eSHans Petter Selasky 1456e2524b2eSHans Petter Selasky for (x = 0; x != rates; x++) { 1457e2524b2eSHans Petter Selasky uint32_t min = UGETDW(data + 2 + (12 * x)); 1458e2524b2eSHans Petter Selasky uint32_t max = UGETDW(data + 6 + (12 * x)); 1459e2524b2eSHans Petter Selasky uint32_t res = UGETDW(data + 10 + (12 * x)); 1460e2524b2eSHans Petter Selasky 1461e2524b2eSHans Petter Selasky if (res == 0) { 1462e2524b2eSHans Petter Selasky DPRINTF("Zero residue\n"); 1463e2524b2eSHans Petter Selasky res = 1; 1464e2524b2eSHans Petter Selasky } 1465e2524b2eSHans Petter Selasky 1466e2524b2eSHans Petter Selasky if (min > max) { 1467e2524b2eSHans Petter Selasky DPRINTF("Swapped max and min\n"); 1468e2524b2eSHans Petter Selasky uint32_t temp; 1469e2524b2eSHans Petter Selasky temp = min; 1470e2524b2eSHans Petter Selasky min = max; 1471e2524b2eSHans Petter Selasky max = temp; 1472e2524b2eSHans Petter Selasky } 1473e2524b2eSHans Petter Selasky 1474e2524b2eSHans Petter Selasky if (rate >= min && rate <= max && 1475e2524b2eSHans Petter Selasky (((rate - min) % res) == 0)) { 1476e2524b2eSHans Petter Selasky return (0); 1477e2524b2eSHans Petter Selasky } 1478e2524b2eSHans Petter Selasky } 1479e2524b2eSHans Petter Selasky return (USB_ERR_INVAL); 1480e2524b2eSHans Petter Selasky } 1481e2524b2eSHans Petter Selasky 14823a3f90c6SAndrew Thompson static void 1483760bc48eSAndrew Thompson uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb_device *udev, 1484afbfddd9SAndrew Thompson uint32_t rate, uint8_t channels, uint8_t bit_resolution) 14853a3f90c6SAndrew Thompson { 1486760bc48eSAndrew Thompson struct usb_descriptor *desc = NULL; 1487e2524b2eSHans Petter Selasky union uaudio_asid asid = { NULL }; 1488e2524b2eSHans Petter Selasky union uaudio_asf1d asf1d = { NULL }; 1489e2524b2eSHans Petter Selasky union uaudio_sed sed = { NULL }; 1490f895cc0cSHans Petter Selasky usb_endpoint_descriptor_audio_t *ed1 = NULL; 1491e2524b2eSHans Petter Selasky const struct usb_audio_control_descriptor *acdp = NULL; 1492a593f6b8SAndrew Thompson struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); 1493760bc48eSAndrew Thompson struct usb_interface_descriptor *id; 1494e2524b2eSHans Petter Selasky const struct uaudio_format *p_fmt = NULL; 14953a3f90c6SAndrew Thompson struct uaudio_chan *chan; 1496455a367fSHans Petter Selasky struct uaudio_chan_alt *chan_alt; 1497455a367fSHans Petter Selasky uint32_t format; 14983a3f90c6SAndrew Thompson uint16_t curidx = 0xFFFF; 14993a3f90c6SAndrew Thompson uint16_t lastidx = 0xFFFF; 15003a3f90c6SAndrew Thompson uint16_t alt_index = 0; 1501e2524b2eSHans Petter Selasky uint16_t audio_rev = 0; 1502e2524b2eSHans Petter Selasky uint16_t x; 15033a3f90c6SAndrew Thompson uint8_t ep_dir; 15043a3f90c6SAndrew Thompson uint8_t bChannels; 15053a3f90c6SAndrew Thompson uint8_t bBitResolution; 15063a3f90c6SAndrew Thompson uint8_t audio_if = 0; 150725b74dabSHans Petter Selasky uint8_t uma_if_class; 15083a3f90c6SAndrew Thompson 1509a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) { 15103a3f90c6SAndrew Thompson 15113a3f90c6SAndrew Thompson if ((desc->bDescriptorType == UDESC_INTERFACE) && 15123a3f90c6SAndrew Thompson (desc->bLength >= sizeof(*id))) { 15133a3f90c6SAndrew Thompson 15143a3f90c6SAndrew Thompson id = (void *)desc; 15153a3f90c6SAndrew Thompson 15163a3f90c6SAndrew Thompson if (id->bInterfaceNumber != lastidx) { 15173a3f90c6SAndrew Thompson lastidx = id->bInterfaceNumber; 15183a3f90c6SAndrew Thompson curidx++; 15193a3f90c6SAndrew Thompson alt_index = 0; 15203a3f90c6SAndrew Thompson 15213a3f90c6SAndrew Thompson } else { 15223a3f90c6SAndrew Thompson alt_index++; 15233a3f90c6SAndrew Thompson } 15243a3f90c6SAndrew Thompson 152576b71212SHans Petter Selasky if ((!(sc->sc_hid.flags & UAUDIO_HID_VALID)) && 152676b71212SHans Petter Selasky (id->bInterfaceClass == UICLASS_HID) && 152776b71212SHans Petter Selasky (id->bInterfaceSubClass == 0) && 152876b71212SHans Petter Selasky (id->bInterfaceProtocol == 0) && 152976b71212SHans Petter Selasky (alt_index == 0) && 153076b71212SHans Petter Selasky usbd_get_iface(udev, curidx) != NULL) { 153176b71212SHans Petter Selasky DPRINTF("Found HID interface at %d\n", 153276b71212SHans Petter Selasky curidx); 153376b71212SHans Petter Selasky sc->sc_hid.flags |= UAUDIO_HID_VALID; 153476b71212SHans Petter Selasky sc->sc_hid.iface_index = curidx; 153576b71212SHans Petter Selasky } 153676b71212SHans Petter Selasky 153725b74dabSHans Petter Selasky uma_if_class = 153825b74dabSHans Petter Selasky ((id->bInterfaceClass == UICLASS_AUDIO) || 153925b74dabSHans Petter Selasky ((id->bInterfaceClass == UICLASS_VENDOR) && 154025b74dabSHans Petter Selasky (sc->sc_uq_au_vendor_class != 0))); 154125b74dabSHans Petter Selasky 154225b74dabSHans Petter Selasky if ((uma_if_class != 0) && (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) { 15433a3f90c6SAndrew Thompson audio_if = 1; 15443a3f90c6SAndrew Thompson } else { 15453a3f90c6SAndrew Thompson audio_if = 0; 15463a3f90c6SAndrew Thompson } 15473a3f90c6SAndrew Thompson 154825b74dabSHans Petter Selasky if ((uma_if_class != 0) && 15493a3f90c6SAndrew Thompson (id->bInterfaceSubClass == UISUBCLASS_MIDISTREAM)) { 15503a3f90c6SAndrew Thompson 15513a3f90c6SAndrew Thompson /* 15523a3f90c6SAndrew Thompson * XXX could allow multiple MIDI interfaces 15533a3f90c6SAndrew Thompson */ 15543a3f90c6SAndrew Thompson 15553a3f90c6SAndrew Thompson if ((sc->sc_midi_chan.valid == 0) && 1556a593f6b8SAndrew Thompson usbd_get_iface(udev, curidx)) { 15573a3f90c6SAndrew Thompson sc->sc_midi_chan.iface_index = curidx; 15583a3f90c6SAndrew Thompson sc->sc_midi_chan.iface_alt_index = alt_index; 15593a3f90c6SAndrew Thompson sc->sc_midi_chan.valid = 1; 15603a3f90c6SAndrew Thompson } 15613a3f90c6SAndrew Thompson } 1562e2524b2eSHans Petter Selasky asid.v1 = NULL; 1563e2524b2eSHans Petter Selasky asf1d.v1 = NULL; 15643a3f90c6SAndrew Thompson ed1 = NULL; 1565e2524b2eSHans Petter Selasky sed.v1 = NULL; 15663a3f90c6SAndrew Thompson } 1567e2524b2eSHans Petter Selasky 15681234097eSHans Petter Selasky if (audio_if == 0) { 1569e2524b2eSHans Petter Selasky if ((acdp == NULL) && 1570e2524b2eSHans Petter Selasky (desc->bDescriptorType == UDESC_CS_INTERFACE) && 1571e2524b2eSHans Petter Selasky (desc->bDescriptorSubtype == UDESCSUB_AC_HEADER) && 1572e2524b2eSHans Petter Selasky (desc->bLength >= sizeof(*acdp))) { 1573e2524b2eSHans Petter Selasky acdp = (void *)desc; 1574e2524b2eSHans Petter Selasky audio_rev = UGETW(acdp->bcdADC); 1575e2524b2eSHans Petter Selasky } 1576e2524b2eSHans Petter Selasky 15771234097eSHans Petter Selasky /* 15781234097eSHans Petter Selasky * Don't collect any USB audio descriptors if 15791234097eSHans Petter Selasky * this is not an USB audio stream interface. 15801234097eSHans Petter Selasky */ 15811234097eSHans Petter Selasky continue; 15821234097eSHans Petter Selasky } 15831234097eSHans Petter Selasky 1584e2524b2eSHans Petter Selasky if ((acdp != NULL) && 1585e2524b2eSHans Petter Selasky (desc->bDescriptorType == UDESC_CS_INTERFACE) && 1586e2524b2eSHans Petter Selasky (desc->bDescriptorSubtype == AS_GENERAL) && 1587e2524b2eSHans Petter Selasky (asid.v1 == NULL)) { 1588e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 1589e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 1590e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 1591e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*asid.v2)) { 1592e2524b2eSHans Petter Selasky asid.v2 = (void *)desc; 1593e2524b2eSHans Petter Selasky } 1594e2524b2eSHans Petter Selasky } else { 1595e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*asid.v1)) { 1596e2524b2eSHans Petter Selasky asid.v1 = (void *)desc; 15973a3f90c6SAndrew Thompson } 15983a3f90c6SAndrew Thompson } 1599e2524b2eSHans Petter Selasky } 1600e2524b2eSHans Petter Selasky if ((acdp != NULL) && 1601e2524b2eSHans Petter Selasky (desc->bDescriptorType == UDESC_CS_INTERFACE) && 16023a3f90c6SAndrew Thompson (desc->bDescriptorSubtype == FORMAT_TYPE) && 1603e2524b2eSHans Petter Selasky (asf1d.v1 == NULL)) { 1604e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 1605e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 1606e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 1607e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*asf1d.v2)) 1608e2524b2eSHans Petter Selasky asf1d.v2 = (void *)desc; 1609e2524b2eSHans Petter Selasky } else { 1610e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*asf1d.v1)) { 1611e2524b2eSHans Petter Selasky asf1d.v1 = (void *)desc; 1612e2524b2eSHans Petter Selasky 1613e2524b2eSHans Petter Selasky if (asf1d.v1->bFormatType != FORMAT_TYPE_I) { 16143a3f90c6SAndrew Thompson DPRINTFN(11, "ignored bFormatType = %d\n", 1615e2524b2eSHans Petter Selasky asf1d.v1->bFormatType); 1616e2524b2eSHans Petter Selasky asf1d.v1 = NULL; 16173a3f90c6SAndrew Thompson continue; 16183a3f90c6SAndrew Thompson } 1619e2524b2eSHans Petter Selasky if (desc->bLength < (sizeof(*asf1d.v1) + 1620e2524b2eSHans Petter Selasky ((asf1d.v1->bSamFreqType == 0) ? 6 : 1621e2524b2eSHans Petter Selasky (asf1d.v1->bSamFreqType * 3)))) { 1622e2524b2eSHans Petter Selasky DPRINTFN(11, "invalid descriptor, " 1623e2524b2eSHans Petter Selasky "too short\n"); 1624e2524b2eSHans Petter Selasky asf1d.v1 = NULL; 16253a3f90c6SAndrew Thompson continue; 16263a3f90c6SAndrew Thompson } 16273a3f90c6SAndrew Thompson } 16283a3f90c6SAndrew Thompson } 1629e2524b2eSHans Petter Selasky } 16303a3f90c6SAndrew Thompson if ((desc->bDescriptorType == UDESC_ENDPOINT) && 1631e2524b2eSHans Petter Selasky (desc->bLength >= UEP_MINSIZE) && 1632e2524b2eSHans Petter Selasky (ed1 == NULL)) { 16333a3f90c6SAndrew Thompson ed1 = (void *)desc; 16343a3f90c6SAndrew Thompson if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) { 16353a3f90c6SAndrew Thompson ed1 = NULL; 1636e2524b2eSHans Petter Selasky continue; 16373a3f90c6SAndrew Thompson } 16383a3f90c6SAndrew Thompson } 1639e2524b2eSHans Petter Selasky if ((acdp != NULL) && 1640e2524b2eSHans Petter Selasky (desc->bDescriptorType == UDESC_CS_ENDPOINT) && 16413a3f90c6SAndrew Thompson (desc->bDescriptorSubtype == AS_GENERAL) && 1642e2524b2eSHans Petter Selasky (sed.v1 == NULL)) { 1643e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 1644e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 1645e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 1646e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*sed.v2)) 1647e2524b2eSHans Petter Selasky sed.v2 = (void *)desc; 1648e2524b2eSHans Petter Selasky } else { 1649e2524b2eSHans Petter Selasky if (desc->bLength >= sizeof(*sed.v1)) 1650e2524b2eSHans Petter Selasky sed.v1 = (void *)desc; 16513a3f90c6SAndrew Thompson } 16523a3f90c6SAndrew Thompson } 16531234097eSHans Petter Selasky if (asid.v1 == NULL || asf1d.v1 == NULL || 16541234097eSHans Petter Selasky ed1 == NULL || sed.v1 == NULL) { 1655e2524b2eSHans Petter Selasky /* need more descriptors */ 1656e2524b2eSHans Petter Selasky continue; 1657e2524b2eSHans Petter Selasky } 16583a3f90c6SAndrew Thompson 16593a3f90c6SAndrew Thompson ep_dir = UE_GET_DIR(ed1->bEndpointAddress); 16603a3f90c6SAndrew Thompson 1661e3e05e50SAndrew Thompson /* We ignore sync endpoint information until further. */ 16623a3f90c6SAndrew Thompson 1663e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 1664e2524b2eSHans Petter Selasky goto next_ep; 1665e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 16663a3f90c6SAndrew Thompson 1667e2524b2eSHans Petter Selasky uint32_t dwFormat; 1668e2524b2eSHans Petter Selasky uint8_t bSubslotSize; 16693a3f90c6SAndrew Thompson 1670e2524b2eSHans Petter Selasky dwFormat = UGETDW(asid.v2->bmFormats); 1671e2524b2eSHans Petter Selasky bChannels = asid.v2->bNrChannels; 1672e2524b2eSHans Petter Selasky bBitResolution = asf1d.v2->bBitResolution; 1673e2524b2eSHans Petter Selasky bSubslotSize = asf1d.v2->bSubslotSize; 1674e2524b2eSHans Petter Selasky 16759b2842baSHans Petter Selasky /* Map 4-byte aligned 24-bit samples into 32-bit */ 16769b2842baSHans Petter Selasky if (bBitResolution == 24 && bSubslotSize == 4) 16779b2842baSHans Petter Selasky bBitResolution = 32; 16789b2842baSHans Petter Selasky 1679e2524b2eSHans Petter Selasky if (bBitResolution != (bSubslotSize * 8)) { 1680e2524b2eSHans Petter Selasky DPRINTF("Invalid bSubslotSize\n"); 1681e2524b2eSHans Petter Selasky goto next_ep; 1682e2524b2eSHans Petter Selasky } 1683e2524b2eSHans Petter Selasky 1684e2524b2eSHans Petter Selasky if ((bChannels != channels) || 1685e2524b2eSHans Petter Selasky (bBitResolution != bit_resolution)) { 1686e2524b2eSHans Petter Selasky DPRINTF("Wrong number of channels\n"); 1687e2524b2eSHans Petter Selasky goto next_ep; 1688e2524b2eSHans Petter Selasky } 1689e2524b2eSHans Petter Selasky 1690e2524b2eSHans Petter Selasky for (p_fmt = uaudio20_formats; 1691e2524b2eSHans Petter Selasky p_fmt->wFormat != 0; p_fmt++) { 1692e2524b2eSHans Petter Selasky if ((p_fmt->wFormat & dwFormat) && 1693e2524b2eSHans Petter Selasky (p_fmt->bPrecision == bBitResolution)) 1694e2524b2eSHans Petter Selasky break; 1695e2524b2eSHans Petter Selasky } 1696e2524b2eSHans Petter Selasky 1697e2524b2eSHans Petter Selasky if (p_fmt->wFormat == 0) { 1698e2524b2eSHans Petter Selasky DPRINTF("Unsupported audio format\n"); 1699e2524b2eSHans Petter Selasky goto next_ep; 1700e2524b2eSHans Petter Selasky } 1701e2524b2eSHans Petter Selasky 1702e2524b2eSHans Petter Selasky for (x = 0; x != 256; x++) { 1703e2524b2eSHans Petter Selasky if (ep_dir == UE_DIR_OUT) { 1704e2524b2eSHans Petter Selasky if (!(sc->sc_mixer_clocks.bit_output[x / 8] & 1705e2524b2eSHans Petter Selasky (1 << (x % 8)))) { 1706e2524b2eSHans Petter Selasky continue; 17073a3f90c6SAndrew Thompson } 17083a3f90c6SAndrew Thompson } else { 1709e2524b2eSHans Petter Selasky if (!(sc->sc_mixer_clocks.bit_input[x / 8] & 1710e2524b2eSHans Petter Selasky (1 << (x % 8)))) { 1711e2524b2eSHans Petter Selasky continue; 1712e2524b2eSHans Petter Selasky } 1713e2524b2eSHans Petter Selasky } 17143a3f90c6SAndrew Thompson 1715e2524b2eSHans Petter Selasky DPRINTF("Checking clock ID=%d\n", x); 1716e2524b2eSHans Petter Selasky 1717e2524b2eSHans Petter Selasky if (uaudio20_check_rate(udev, 1718e2524b2eSHans Petter Selasky sc->sc_mixer_iface_no, x, rate)) { 1719e2524b2eSHans Petter Selasky DPRINTF("Unsupported sampling " 1720e2524b2eSHans Petter Selasky "rate, id=%d\n", x); 1721e2524b2eSHans Petter Selasky goto next_ep; 1722e2524b2eSHans Petter Selasky } 1723e2524b2eSHans Petter Selasky } 1724e2524b2eSHans Petter Selasky } else { 1725e2524b2eSHans Petter Selasky uint16_t wFormat; 1726e2524b2eSHans Petter Selasky 1727e2524b2eSHans Petter Selasky wFormat = UGETW(asid.v1->wFormatTag); 1728e2524b2eSHans Petter Selasky bChannels = UAUDIO_MAX_CHAN(asf1d.v1->bNrChannels); 1729e2524b2eSHans Petter Selasky bBitResolution = asf1d.v1->bBitResolution; 1730e2524b2eSHans Petter Selasky 1731e2524b2eSHans Petter Selasky if (asf1d.v1->bSamFreqType == 0) { 1732e2524b2eSHans Petter Selasky DPRINTFN(16, "Sample rate: %d-%dHz\n", 1733e2524b2eSHans Petter Selasky UA_SAMP_LO(asf1d.v1), 1734e2524b2eSHans Petter Selasky UA_SAMP_HI(asf1d.v1)); 1735e2524b2eSHans Petter Selasky 1736e2524b2eSHans Petter Selasky if ((rate >= UA_SAMP_LO(asf1d.v1)) && 1737e2524b2eSHans Petter Selasky (rate <= UA_SAMP_HI(asf1d.v1))) 1738e2524b2eSHans Petter Selasky goto found_rate; 1739e2524b2eSHans Petter Selasky } else { 1740e2524b2eSHans Petter Selasky 1741e2524b2eSHans Petter Selasky for (x = 0; x < asf1d.v1->bSamFreqType; x++) { 17423a3f90c6SAndrew Thompson DPRINTFN(16, "Sample rate = %dHz\n", 1743e2524b2eSHans Petter Selasky UA_GETSAMP(asf1d.v1, x)); 17443a3f90c6SAndrew Thompson 1745e2524b2eSHans Petter Selasky if (rate == UA_GETSAMP(asf1d.v1, x)) 17463a3f90c6SAndrew Thompson goto found_rate; 17473a3f90c6SAndrew Thompson } 17483a3f90c6SAndrew Thompson } 1749e2524b2eSHans Petter Selasky goto next_ep; 17503a3f90c6SAndrew Thompson 17513a3f90c6SAndrew Thompson found_rate: 1752e2524b2eSHans Petter Selasky for (p_fmt = uaudio10_formats; 1753e2524b2eSHans Petter Selasky p_fmt->wFormat != 0; p_fmt++) { 17543a3f90c6SAndrew Thompson if ((p_fmt->wFormat == wFormat) && 1755e2524b2eSHans Petter Selasky (p_fmt->bPrecision == bBitResolution)) 1756e2524b2eSHans Petter Selasky break; 17573a3f90c6SAndrew Thompson } 1758e2524b2eSHans Petter Selasky if (p_fmt->wFormat == 0) { 1759e2524b2eSHans Petter Selasky DPRINTF("Unsupported audio format\n"); 1760e2524b2eSHans Petter Selasky goto next_ep; 17613a3f90c6SAndrew Thompson } 17623a3f90c6SAndrew Thompson 1763e2524b2eSHans Petter Selasky if ((bChannels != channels) || 1764e2524b2eSHans Petter Selasky (bBitResolution != bit_resolution)) { 1765e2524b2eSHans Petter Selasky DPRINTF("Wrong number of channels\n"); 1766e2524b2eSHans Petter Selasky goto next_ep; 1767e2524b2eSHans Petter Selasky } 1768e2524b2eSHans Petter Selasky } 17693a3f90c6SAndrew Thompson 17703a3f90c6SAndrew Thompson chan = (ep_dir == UE_DIR_IN) ? 1771e2524b2eSHans Petter Selasky &sc->sc_rec_chan : &sc->sc_play_chan; 17723a3f90c6SAndrew Thompson 1773455a367fSHans Petter Selasky if (usbd_get_iface(udev, curidx) == NULL) { 1774455a367fSHans Petter Selasky DPRINTF("Interface is not valid\n"); 1775e2524b2eSHans Petter Selasky goto next_ep; 1776e2524b2eSHans Petter Selasky } 1777455a367fSHans Petter Selasky if (chan->num_alt == CHAN_MAX_ALT) { 1778455a367fSHans Petter Selasky DPRINTF("Too many alternate settings\n"); 1779455a367fSHans Petter Selasky goto next_ep; 1780455a367fSHans Petter Selasky } 1781455a367fSHans Petter Selasky chan->set_alt = 0; 1782455a367fSHans Petter Selasky chan->cur_alt = CHAN_MAX_ALT; 17833a3f90c6SAndrew Thompson 1784455a367fSHans Petter Selasky chan_alt = &chan->usb_alt[chan->num_alt++]; 1785455a367fSHans Petter Selasky 1786b850ecc1SAndrew Thompson #ifdef USB_DEBUG 17873a3f90c6SAndrew Thompson uaudio_chan_dump_ep_desc(ed1); 17883a3f90c6SAndrew Thompson #endif 17893a3f90c6SAndrew Thompson DPRINTF("Sample rate = %dHz, channels = %d, " 17903a3f90c6SAndrew Thompson "bits = %d, format = %s\n", rate, channels, 17913a3f90c6SAndrew Thompson bit_resolution, p_fmt->description); 17923a3f90c6SAndrew Thompson 1793455a367fSHans Petter Selasky chan_alt->sample_rate = rate; 1794455a367fSHans Petter Selasky chan_alt->p_asf1d = asf1d; 1795455a367fSHans Petter Selasky chan_alt->p_ed1 = ed1; 1796455a367fSHans Petter Selasky chan_alt->p_fmt = p_fmt; 1797455a367fSHans Petter Selasky chan_alt->p_sed = sed; 1798455a367fSHans Petter Selasky chan_alt->iface_index = curidx; 1799455a367fSHans Petter Selasky chan_alt->iface_alt_index = alt_index; 1800455a367fSHans Petter Selasky 1801455a367fSHans Petter Selasky usbd_set_parent_iface(sc->sc_udev, curidx, 1802455a367fSHans Petter Selasky sc->sc_mixer_iface_index); 18033a3f90c6SAndrew Thompson 18043a3f90c6SAndrew Thompson if (ep_dir == UE_DIR_IN) 1805455a367fSHans Petter Selasky chan_alt->usb_cfg = uaudio_cfg_record; 18063a3f90c6SAndrew Thompson else 1807455a367fSHans Petter Selasky chan_alt->usb_cfg = uaudio_cfg_play; 18083a3f90c6SAndrew Thompson 1809455a367fSHans Petter Selasky chan_alt->sample_size = (UAUDIO_MAX_CHAN(channels) * 1810e2524b2eSHans Petter Selasky p_fmt->bPrecision) / 8; 1811455a367fSHans Petter Selasky chan_alt->channels = channels; 18123a3f90c6SAndrew Thompson 1813f895cc0cSHans Petter Selasky if (ep_dir == UE_DIR_IN && 1814f895cc0cSHans Petter Selasky usbd_get_speed(udev) == USB_SPEED_FULL) { 1815f895cc0cSHans Petter Selasky uaudio_record_fix_fs(ed1, 1816455a367fSHans Petter Selasky chan_alt->sample_size * (rate / 1000), 1817455a367fSHans Petter Selasky chan_alt->sample_size * (rate / 4000)); 1818f895cc0cSHans Petter Selasky } 1819f895cc0cSHans Petter Selasky 1820455a367fSHans Petter Selasky /* setup play/record format */ 1821455a367fSHans Petter Selasky 1822455a367fSHans Petter Selasky format = chan_alt->p_fmt->freebsd_fmt; 1823455a367fSHans Petter Selasky 1824455a367fSHans Petter Selasky switch (chan_alt->channels) { 1825455a367fSHans Petter Selasky case 2: 1826455a367fSHans Petter Selasky /* stereo */ 1827455a367fSHans Petter Selasky format = SND_FORMAT(format, 2, 0); 1828455a367fSHans Petter Selasky break; 1829455a367fSHans Petter Selasky case 1: 1830455a367fSHans Petter Selasky /* mono */ 1831455a367fSHans Petter Selasky format = SND_FORMAT(format, 1, 0); 1832455a367fSHans Petter Selasky break; 1833455a367fSHans Petter Selasky default: 1834455a367fSHans Petter Selasky /* surround and more */ 1835455a367fSHans Petter Selasky format = feeder_matrix_default_format( 1836455a367fSHans Petter Selasky SND_FORMAT(format, chan_alt->channels, 0)); 1837455a367fSHans Petter Selasky break; 1838455a367fSHans Petter Selasky } 1839455a367fSHans Petter Selasky 1840455a367fSHans Petter Selasky /* check if format is not supported */ 1841455a367fSHans Petter Selasky if (format == 0) { 1842455a367fSHans Petter Selasky DPRINTF("The selected audio format is not supported\n"); 1843455a367fSHans Petter Selasky chan->num_alt--; 1844455a367fSHans Petter Selasky goto next_ep; 1845455a367fSHans Petter Selasky } 1846ffae621eSHans Petter Selasky if (chan->num_alt > 1) { 1847455a367fSHans Petter Selasky /* we only accumulate one format at different sample rates */ 1848ffae621eSHans Petter Selasky if (chan->pcm_format[0] != format) { 1849455a367fSHans Petter Selasky DPRINTF("Multiple formats is not supported\n"); 1850455a367fSHans Petter Selasky chan->num_alt--; 1851455a367fSHans Petter Selasky goto next_ep; 1852455a367fSHans Petter Selasky } 1853ffae621eSHans Petter Selasky /* ignore if duplicate sample rate entry */ 1854ffae621eSHans Petter Selasky if (rate == chan->usb_alt[chan->num_alt - 2].sample_rate) { 1855ffae621eSHans Petter Selasky DPRINTF("Duplicate sample rate detected\n"); 1856ffae621eSHans Petter Selasky chan->num_alt--; 1857ffae621eSHans Petter Selasky goto next_ep; 1858ffae621eSHans Petter Selasky } 1859ffae621eSHans Petter Selasky } 1860455a367fSHans Petter Selasky chan->pcm_cap.fmtlist = chan->pcm_format; 1861455a367fSHans Petter Selasky chan->pcm_cap.fmtlist[0] = format; 1862455a367fSHans Petter Selasky 1863455a367fSHans Petter Selasky if (rate < chan->pcm_cap.minspeed || chan->pcm_cap.minspeed == 0) 1864455a367fSHans Petter Selasky chan->pcm_cap.minspeed = rate; 1865455a367fSHans Petter Selasky if (rate > chan->pcm_cap.maxspeed || chan->pcm_cap.maxspeed == 0) 1866455a367fSHans Petter Selasky chan->pcm_cap.maxspeed = rate; 1867455a367fSHans Petter Selasky 1868e2524b2eSHans Petter Selasky if (sc->sc_sndstat_valid != 0) { 18693a3f90c6SAndrew Thompson sbuf_printf(&sc->sc_sndstat, "\n\t" 1870e2524b2eSHans Petter Selasky "mode %d.%d:(%s) %dch, %dbit, %s, %dHz", 18713a3f90c6SAndrew Thompson curidx, alt_index, 18723a3f90c6SAndrew Thompson (ep_dir == UE_DIR_IN) ? "input" : "output", 1873e2524b2eSHans Petter Selasky channels, p_fmt->bPrecision, 18743a3f90c6SAndrew Thompson p_fmt->description, rate); 18753a3f90c6SAndrew Thompson } 1876e2524b2eSHans Petter Selasky 1877e2524b2eSHans Petter Selasky next_ep: 1878e2524b2eSHans Petter Selasky sed.v1 = NULL; 1879e2524b2eSHans Petter Selasky ed1 = NULL; 18803a3f90c6SAndrew Thompson } 18813a3f90c6SAndrew Thompson } 18823a3f90c6SAndrew Thompson 1883afbfddd9SAndrew Thompson /* This structure defines all the supported rates. */ 1884afbfddd9SAndrew Thompson 1885455a367fSHans Petter Selasky static const uint32_t uaudio_rate_list[CHAN_MAX_ALT] = { 18862c2752d3SHans Petter Selasky 384000, 18872c2752d3SHans Petter Selasky 352800, 18882c2752d3SHans Petter Selasky 192000, 18892c2752d3SHans Petter Selasky 176400, 1890afbfddd9SAndrew Thompson 96000, 1891455a367fSHans Petter Selasky 88200, 1892afbfddd9SAndrew Thompson 88000, 1893afbfddd9SAndrew Thompson 80000, 1894afbfddd9SAndrew Thompson 72000, 1895afbfddd9SAndrew Thompson 64000, 1896afbfddd9SAndrew Thompson 56000, 1897afbfddd9SAndrew Thompson 48000, 1898afbfddd9SAndrew Thompson 44100, 1899afbfddd9SAndrew Thompson 40000, 1900afbfddd9SAndrew Thompson 32000, 1901afbfddd9SAndrew Thompson 24000, 1902afbfddd9SAndrew Thompson 22050, 1903afbfddd9SAndrew Thompson 16000, 1904afbfddd9SAndrew Thompson 11025, 1905afbfddd9SAndrew Thompson 8000, 1906afbfddd9SAndrew Thompson 0 1907afbfddd9SAndrew Thompson }; 1908afbfddd9SAndrew Thompson 19093a3f90c6SAndrew Thompson static void 1910760bc48eSAndrew Thompson uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb_device *udev) 19113a3f90c6SAndrew Thompson { 19123a3f90c6SAndrew Thompson uint32_t rate = uaudio_default_rate; 1913afbfddd9SAndrew Thompson uint8_t z; 19143a3f90c6SAndrew Thompson uint8_t bits = uaudio_default_bits; 19153a3f90c6SAndrew Thompson uint8_t y; 19163a3f90c6SAndrew Thompson uint8_t channels = uaudio_default_channels; 19173a3f90c6SAndrew Thompson uint8_t x; 19183a3f90c6SAndrew Thompson 19193a3f90c6SAndrew Thompson bits -= (bits % 8); 19203a3f90c6SAndrew Thompson if ((bits == 0) || (bits > 32)) { 19213a3f90c6SAndrew Thompson /* set a valid value */ 19223a3f90c6SAndrew Thompson bits = 32; 19233a3f90c6SAndrew Thompson } 1924afbfddd9SAndrew Thompson if (channels == 0) { 1925afbfddd9SAndrew Thompson switch (usbd_get_speed(udev)) { 1926afbfddd9SAndrew Thompson case USB_SPEED_LOW: 1927afbfddd9SAndrew Thompson case USB_SPEED_FULL: 1928afbfddd9SAndrew Thompson /* 1929afbfddd9SAndrew Thompson * Due to high bandwidth usage and problems 1930afbfddd9SAndrew Thompson * with HIGH-speed split transactions we 1931afbfddd9SAndrew Thompson * disable surround setups on FULL-speed USB 1932afbfddd9SAndrew Thompson * by default 1933afbfddd9SAndrew Thompson */ 19341234097eSHans Petter Selasky channels = 4; 1935afbfddd9SAndrew Thompson break; 1936afbfddd9SAndrew Thompson default: 1937afbfddd9SAndrew Thompson channels = 16; 1938afbfddd9SAndrew Thompson break; 1939afbfddd9SAndrew Thompson } 1940afbfddd9SAndrew Thompson } else if (channels > 16) { 1941afbfddd9SAndrew Thompson channels = 16; 19423a3f90c6SAndrew Thompson } 19433a3f90c6SAndrew Thompson if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) { 19443a3f90c6SAndrew Thompson sc->sc_sndstat_valid = 1; 19453a3f90c6SAndrew Thompson } 19463a3f90c6SAndrew Thompson /* try to search for a valid config */ 19473a3f90c6SAndrew Thompson 19483a3f90c6SAndrew Thompson for (x = channels; x; x--) { 19493a3f90c6SAndrew Thompson for (y = bits; y; y -= 8) { 1950afbfddd9SAndrew Thompson 1951afbfddd9SAndrew Thompson /* try user defined rate, if any */ 1952afbfddd9SAndrew Thompson if (rate != 0) 1953afbfddd9SAndrew Thompson uaudio_chan_fill_info_sub(sc, udev, rate, x, y); 1954afbfddd9SAndrew Thompson 1955afbfddd9SAndrew Thompson /* try find a matching rate, if any */ 1956455a367fSHans Petter Selasky for (z = 0; uaudio_rate_list[z]; z++) 1957afbfddd9SAndrew Thompson uaudio_chan_fill_info_sub(sc, udev, uaudio_rate_list[z], x, y); 19583a3f90c6SAndrew Thompson } 19593a3f90c6SAndrew Thompson } 1960455a367fSHans Petter Selasky if (sc->sc_sndstat_valid) 19613a3f90c6SAndrew Thompson sbuf_finish(&sc->sc_sndstat); 19623a3f90c6SAndrew Thompson } 19633a3f90c6SAndrew Thompson 19643a3f90c6SAndrew Thompson static void 1965b4380da7SHans Petter Selasky uaudio_chan_play_sync_callback(struct usb_xfer *xfer, usb_error_t error) 1966b4380da7SHans Petter Selasky { 1967b4380da7SHans Petter Selasky struct uaudio_chan *ch = usbd_xfer_softc(xfer); 1968b4380da7SHans Petter Selasky struct usb_page_cache *pc; 1969455a367fSHans Petter Selasky uint64_t sample_rate = ch->usb_alt[ch->cur_alt].sample_rate; 1970b4380da7SHans Petter Selasky uint8_t buf[4]; 1971b4380da7SHans Petter Selasky uint64_t temp; 1972b4380da7SHans Petter Selasky int len; 1973b4380da7SHans Petter Selasky int actlen; 1974b4380da7SHans Petter Selasky int nframes; 1975b4380da7SHans Petter Selasky 1976b4380da7SHans Petter Selasky usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 1977b4380da7SHans Petter Selasky 1978b4380da7SHans Petter Selasky switch (USB_GET_STATE(xfer)) { 1979b4380da7SHans Petter Selasky case USB_ST_TRANSFERRED: 1980b4380da7SHans Petter Selasky 1981b4380da7SHans Petter Selasky DPRINTFN(6, "transferred %d bytes\n", actlen); 1982b4380da7SHans Petter Selasky 1983b4380da7SHans Petter Selasky if (nframes == 0) 1984b4380da7SHans Petter Selasky break; 1985b4380da7SHans Petter Selasky len = usbd_xfer_frame_len(xfer, 0); 1986b4380da7SHans Petter Selasky if (len == 0) 1987b4380da7SHans Petter Selasky break; 1988b4380da7SHans Petter Selasky if (len > sizeof(buf)) 1989b4380da7SHans Petter Selasky len = sizeof(buf); 1990b4380da7SHans Petter Selasky 1991b4380da7SHans Petter Selasky memset(buf, 0, sizeof(buf)); 1992b4380da7SHans Petter Selasky 1993b4380da7SHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 0); 1994b4380da7SHans Petter Selasky usbd_copy_out(pc, 0, buf, len); 1995b4380da7SHans Petter Selasky 1996b4380da7SHans Petter Selasky temp = UGETDW(buf); 1997b4380da7SHans Petter Selasky 1998b4380da7SHans Petter Selasky DPRINTF("Value = 0x%08x\n", (int)temp); 1999b4380da7SHans Petter Selasky 2000b4380da7SHans Petter Selasky /* auto-detect SYNC format */ 2001b4380da7SHans Petter Selasky 2002b4380da7SHans Petter Selasky if (len == 4) 2003b4380da7SHans Petter Selasky temp &= 0x0fffffff; 2004b4380da7SHans Petter Selasky 2005b4380da7SHans Petter Selasky /* check for no data */ 2006b4380da7SHans Petter Selasky 2007b4380da7SHans Petter Selasky if (temp == 0) 2008b4380da7SHans Petter Selasky break; 2009b4380da7SHans Petter Selasky 2010b4380da7SHans Petter Selasky /* correctly scale value */ 2011b4380da7SHans Petter Selasky 2012b4380da7SHans Petter Selasky temp = (temp * 125ULL) - 64; 2013b4380da7SHans Petter Selasky 2014b4380da7SHans Petter Selasky /* auto adjust */ 2015b4380da7SHans Petter Selasky 2016455a367fSHans Petter Selasky while (temp < (sample_rate - (sample_rate / 4))) 2017b4380da7SHans Petter Selasky temp *= 2; 2018b4380da7SHans Petter Selasky 2019455a367fSHans Petter Selasky while (temp > (sample_rate + (sample_rate / 2))) 2020b4380da7SHans Petter Selasky temp /= 2; 2021b4380da7SHans Petter Selasky 2022b4380da7SHans Petter Selasky /* compare */ 2023b4380da7SHans Petter Selasky 2024b4380da7SHans Petter Selasky DPRINTF("Comparing %d < %d\n", 2025455a367fSHans Petter Selasky (int)temp, (int)sample_rate); 2026b4380da7SHans Petter Selasky 2027455a367fSHans Petter Selasky if (temp == sample_rate) 2028b4380da7SHans Petter Selasky ch->last_sync_state = UAUDIO_SYNC_NONE; 2029455a367fSHans Petter Selasky else if (temp > sample_rate) 2030b4380da7SHans Petter Selasky ch->last_sync_state = UAUDIO_SYNC_MORE; 2031b4380da7SHans Petter Selasky else 2032b4380da7SHans Petter Selasky ch->last_sync_state = UAUDIO_SYNC_LESS; 2033b4380da7SHans Petter Selasky break; 2034b4380da7SHans Petter Selasky 2035b4380da7SHans Petter Selasky case USB_ST_SETUP: 2036b4380da7SHans Petter Selasky usbd_xfer_set_frames(xfer, 1); 2037b4380da7SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_framelen(xfer)); 2038b4380da7SHans Petter Selasky usbd_transfer_submit(xfer); 2039b4380da7SHans Petter Selasky break; 2040b4380da7SHans Petter Selasky 2041b4380da7SHans Petter Selasky default: /* Error */ 2042b4380da7SHans Petter Selasky break; 2043b4380da7SHans Petter Selasky } 2044b4380da7SHans Petter Selasky } 2045b4380da7SHans Petter Selasky 2046b4380da7SHans Petter Selasky static void 2047ed6d949aSAndrew Thompson uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error) 20483a3f90c6SAndrew Thompson { 2049ed6d949aSAndrew Thompson struct uaudio_chan *ch = usbd_xfer_softc(xfer); 2050ed6d949aSAndrew Thompson struct usb_page_cache *pc; 2051455a367fSHans Petter Selasky uint32_t sample_size = ch->usb_alt[ch->cur_alt].sample_size; 2052b4380da7SHans Petter Selasky uint32_t mfl; 20533a3f90c6SAndrew Thompson uint32_t total; 20543a3f90c6SAndrew Thompson uint32_t blockcount; 20553a3f90c6SAndrew Thompson uint32_t n; 20563a3f90c6SAndrew Thompson uint32_t offset; 2057afbfddd9SAndrew Thompson int actlen; 2058afbfddd9SAndrew Thompson int sumlen; 2059ed6d949aSAndrew Thompson 2060ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); 20613a3f90c6SAndrew Thompson 2062b029f6bbSAndrew Thompson if (ch->end == ch->start) { 2063b029f6bbSAndrew Thompson DPRINTF("no buffer!\n"); 2064b029f6bbSAndrew Thompson return; 2065b029f6bbSAndrew Thompson } 20663a3f90c6SAndrew Thompson 20673a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 20683a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 20693a3f90c6SAndrew Thompson tr_transferred: 2070ed6d949aSAndrew Thompson if (actlen < sumlen) { 20713a3f90c6SAndrew Thompson DPRINTF("short transfer, " 2072afbfddd9SAndrew Thompson "%d of %d bytes\n", actlen, sumlen); 20733a3f90c6SAndrew Thompson } 20743a3f90c6SAndrew Thompson chn_intr(ch->pcm_ch); 20753a3f90c6SAndrew Thompson 2076b4380da7SHans Petter Selasky /* start SYNC transfer, if any */ 2077b4380da7SHans Petter Selasky if ((ch->last_sync_time++ & 7) == 0) 2078b4380da7SHans Petter Selasky usbd_transfer_start(ch->xfer[UAUDIO_NCHANBUFS]); 2079b4380da7SHans Petter Selasky 20803a3f90c6SAndrew Thompson case USB_ST_SETUP: 2081b4380da7SHans Petter Selasky mfl = usbd_xfer_max_framelen(xfer); 2082b4380da7SHans Petter Selasky 2083b4380da7SHans Petter Selasky if (ch->bytes_per_frame[1] > mfl) { 20843a3f90c6SAndrew Thompson DPRINTF("bytes per transfer, %d, " 20853a3f90c6SAndrew Thompson "exceeds maximum, %d!\n", 2086afbfddd9SAndrew Thompson ch->bytes_per_frame[1], 2087b4380da7SHans Petter Selasky mfl); 20883a3f90c6SAndrew Thompson break; 20893a3f90c6SAndrew Thompson } 2090afbfddd9SAndrew Thompson 2091afbfddd9SAndrew Thompson blockcount = ch->intr_frames; 2092afbfddd9SAndrew Thompson 2093afbfddd9SAndrew Thompson /* setup number of frames */ 2094ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, blockcount); 2095afbfddd9SAndrew Thompson 2096afbfddd9SAndrew Thompson /* reset total length */ 2097afbfddd9SAndrew Thompson total = 0; 2098afbfddd9SAndrew Thompson 2099afbfddd9SAndrew Thompson /* setup frame lengths */ 2100afbfddd9SAndrew Thompson for (n = 0; n != blockcount; n++) { 2101b4380da7SHans Petter Selasky uint32_t frame_len; 2102b4380da7SHans Petter Selasky 2103afbfddd9SAndrew Thompson ch->sample_curr += ch->sample_rem; 2104afbfddd9SAndrew Thompson if (ch->sample_curr >= ch->frames_per_second) { 2105afbfddd9SAndrew Thompson ch->sample_curr -= ch->frames_per_second; 2106b4380da7SHans Petter Selasky frame_len = ch->bytes_per_frame[1]; 2107afbfddd9SAndrew Thompson } else { 2108b4380da7SHans Petter Selasky frame_len = ch->bytes_per_frame[0]; 2109afbfddd9SAndrew Thompson } 2110b4380da7SHans Petter Selasky 2111b4380da7SHans Petter Selasky if (n == (blockcount - 1)) { 2112b4380da7SHans Petter Selasky switch (ch->last_sync_state) { 2113b4380da7SHans Petter Selasky case UAUDIO_SYNC_MORE: 2114b4380da7SHans Petter Selasky DPRINTFN(6, "sending one sample more\n"); 2115455a367fSHans Petter Selasky if ((frame_len + sample_size) <= mfl) 2116455a367fSHans Petter Selasky frame_len += sample_size; 2117b4380da7SHans Petter Selasky ch->last_sync_state = UAUDIO_SYNC_NONE; 2118b4380da7SHans Petter Selasky break; 2119b4380da7SHans Petter Selasky case UAUDIO_SYNC_LESS: 2120b4380da7SHans Petter Selasky DPRINTFN(6, "sending one sample less\n"); 2121455a367fSHans Petter Selasky if (frame_len >= sample_size) 2122455a367fSHans Petter Selasky frame_len -= sample_size; 2123b4380da7SHans Petter Selasky ch->last_sync_state = UAUDIO_SYNC_NONE; 2124b4380da7SHans Petter Selasky break; 2125b4380da7SHans Petter Selasky default: 2126b4380da7SHans Petter Selasky break; 2127b4380da7SHans Petter Selasky } 2128b4380da7SHans Petter Selasky } 2129b4380da7SHans Petter Selasky 2130b4380da7SHans Petter Selasky usbd_xfer_set_frame_len(xfer, n, frame_len); 2131b4380da7SHans Petter Selasky total += frame_len; 2132afbfddd9SAndrew Thompson } 21333a3f90c6SAndrew Thompson 21343a3f90c6SAndrew Thompson DPRINTFN(6, "transfer %d bytes\n", total); 21353a3f90c6SAndrew Thompson 21363a3f90c6SAndrew Thompson offset = 0; 21373a3f90c6SAndrew Thompson 2138ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 21393a3f90c6SAndrew Thompson while (total > 0) { 21403a3f90c6SAndrew Thompson 21413a3f90c6SAndrew Thompson n = (ch->end - ch->cur); 21423a3f90c6SAndrew Thompson if (n > total) { 21433a3f90c6SAndrew Thompson n = total; 21443a3f90c6SAndrew Thompson } 2145ed6d949aSAndrew Thompson usbd_copy_in(pc, offset, ch->cur, n); 21463a3f90c6SAndrew Thompson 21473a3f90c6SAndrew Thompson total -= n; 21483a3f90c6SAndrew Thompson ch->cur += n; 21493a3f90c6SAndrew Thompson offset += n; 21503a3f90c6SAndrew Thompson 21513a3f90c6SAndrew Thompson if (ch->cur >= ch->end) { 21523a3f90c6SAndrew Thompson ch->cur = ch->start; 21533a3f90c6SAndrew Thompson } 21543a3f90c6SAndrew Thompson } 21553a3f90c6SAndrew Thompson 2156a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 21573a3f90c6SAndrew Thompson break; 21583a3f90c6SAndrew Thompson 21593a3f90c6SAndrew Thompson default: /* Error */ 2160ed6d949aSAndrew Thompson if (error == USB_ERR_CANCELLED) { 21613a3f90c6SAndrew Thompson break; 21623a3f90c6SAndrew Thompson } 21633a3f90c6SAndrew Thompson goto tr_transferred; 21643a3f90c6SAndrew Thompson } 21653a3f90c6SAndrew Thompson } 21663a3f90c6SAndrew Thompson 21673a3f90c6SAndrew Thompson static void 2168b4380da7SHans Petter Selasky uaudio_chan_record_sync_callback(struct usb_xfer *xfer, usb_error_t error) 2169b4380da7SHans Petter Selasky { 2170b4380da7SHans Petter Selasky /* TODO */ 2171b4380da7SHans Petter Selasky } 2172b4380da7SHans Petter Selasky 2173b4380da7SHans Petter Selasky static void 2174ed6d949aSAndrew Thompson uaudio_chan_record_callback(struct usb_xfer *xfer, usb_error_t error) 21753a3f90c6SAndrew Thompson { 2176ed6d949aSAndrew Thompson struct uaudio_chan *ch = usbd_xfer_softc(xfer); 2177ed6d949aSAndrew Thompson struct usb_page_cache *pc; 21783a3f90c6SAndrew Thompson uint32_t offset0; 21793a3f90c6SAndrew Thompson uint32_t offset1; 2180b029f6bbSAndrew Thompson uint32_t mfl; 21816d917491SHans Petter Selasky int m; 21826d917491SHans Petter Selasky int n; 2183ed6d949aSAndrew Thompson int len; 2184b029f6bbSAndrew Thompson int actlen; 2185b029f6bbSAndrew Thompson int nframes; 21866d917491SHans Petter Selasky int blockcount; 2187ed6d949aSAndrew Thompson 2188ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); 2189b029f6bbSAndrew Thompson mfl = usbd_xfer_max_framelen(xfer); 21903a3f90c6SAndrew Thompson 2191b029f6bbSAndrew Thompson if (ch->end == ch->start) { 2192b029f6bbSAndrew Thompson DPRINTF("no buffer!\n"); 2193b029f6bbSAndrew Thompson return; 2194b029f6bbSAndrew Thompson } 21953a3f90c6SAndrew Thompson 21963a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 21973a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 2198afbfddd9SAndrew Thompson 2199ed6d949aSAndrew Thompson DPRINTFN(6, "transferred %d bytes\n", actlen); 22003a3f90c6SAndrew Thompson 22013a3f90c6SAndrew Thompson offset0 = 0; 2202b029f6bbSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 22033a3f90c6SAndrew Thompson 2204ed6d949aSAndrew Thompson for (n = 0; n != nframes; n++) { 22053a3f90c6SAndrew Thompson 22063a3f90c6SAndrew Thompson offset1 = offset0; 22078f9e0ef9SAndrew Thompson len = usbd_xfer_frame_len(xfer, n); 22083a3f90c6SAndrew Thompson 2209ed6d949aSAndrew Thompson while (len > 0) { 22103a3f90c6SAndrew Thompson 22113a3f90c6SAndrew Thompson m = (ch->end - ch->cur); 22123a3f90c6SAndrew Thompson 22136d917491SHans Petter Selasky if (m > len) 2214ed6d949aSAndrew Thompson m = len; 22156d917491SHans Petter Selasky 2216ed6d949aSAndrew Thompson usbd_copy_out(pc, offset1, ch->cur, m); 22173a3f90c6SAndrew Thompson 2218ed6d949aSAndrew Thompson len -= m; 22193a3f90c6SAndrew Thompson offset1 += m; 22203a3f90c6SAndrew Thompson ch->cur += m; 22213a3f90c6SAndrew Thompson 22223a3f90c6SAndrew Thompson if (ch->cur >= ch->end) { 22233a3f90c6SAndrew Thompson ch->cur = ch->start; 22243a3f90c6SAndrew Thompson } 22253a3f90c6SAndrew Thompson } 22263a3f90c6SAndrew Thompson 2227b029f6bbSAndrew Thompson offset0 += mfl; 22283a3f90c6SAndrew Thompson } 22293a3f90c6SAndrew Thompson 22303a3f90c6SAndrew Thompson chn_intr(ch->pcm_ch); 22313a3f90c6SAndrew Thompson 22323a3f90c6SAndrew Thompson case USB_ST_SETUP: 2233b029f6bbSAndrew Thompson tr_setup: 2234afbfddd9SAndrew Thompson blockcount = ch->intr_frames; 2235afbfddd9SAndrew Thompson 2236ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, blockcount); 2237ed6d949aSAndrew Thompson for (n = 0; n < blockcount; n++) { 2238b029f6bbSAndrew Thompson usbd_xfer_set_frame_len(xfer, n, mfl); 22393a3f90c6SAndrew Thompson } 22403a3f90c6SAndrew Thompson 2241a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 2242b029f6bbSAndrew Thompson break; 22433a3f90c6SAndrew Thompson 22443a3f90c6SAndrew Thompson default: /* Error */ 2245ed6d949aSAndrew Thompson if (error == USB_ERR_CANCELLED) { 2246b029f6bbSAndrew Thompson break; 22473a3f90c6SAndrew Thompson } 2248b029f6bbSAndrew Thompson goto tr_setup; 22493a3f90c6SAndrew Thompson } 22503a3f90c6SAndrew Thompson } 22513a3f90c6SAndrew Thompson 22523a3f90c6SAndrew Thompson void * 22533a3f90c6SAndrew Thompson uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, 22543a3f90c6SAndrew Thompson struct pcm_channel *c, int dir) 22553a3f90c6SAndrew Thompson { 22563a3f90c6SAndrew Thompson struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ? 22573a3f90c6SAndrew Thompson &sc->sc_play_chan : &sc->sc_rec_chan); 22583a3f90c6SAndrew Thompson uint32_t buf_size; 2259455a367fSHans Petter Selasky uint8_t x; 22603a3f90c6SAndrew Thompson 2261455a367fSHans Petter Selasky /* store mutex and PCM channel */ 22627fb43570SAndrew Thompson 22637fb43570SAndrew Thompson ch->pcm_ch = c; 22647fb43570SAndrew Thompson ch->pcm_mtx = c->lock; 22657fb43570SAndrew Thompson 2266455a367fSHans Petter Selasky /* compute worst case buffer */ 22673a3f90c6SAndrew Thompson 2268455a367fSHans Petter Selasky buf_size = 0; 2269455a367fSHans Petter Selasky for (x = 0; x != ch->num_alt; x++) { 2270455a367fSHans Petter Selasky uint32_t temp = uaudio_get_buffer_size(ch, x); 2271455a367fSHans Petter Selasky if (temp > buf_size) 2272455a367fSHans Petter Selasky buf_size = temp; 2273afbfddd9SAndrew Thompson } 2274afbfddd9SAndrew Thompson 2275455a367fSHans Petter Selasky /* allow double buffering */ 2276b029f6bbSAndrew Thompson buf_size *= 2; 2277455a367fSHans Petter Selasky 2278455a367fSHans Petter Selasky DPRINTF("Worst case buffer is %d bytes\n", (int)buf_size); 2279b029f6bbSAndrew Thompson 2280b029f6bbSAndrew Thompson ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO); 2281b029f6bbSAndrew Thompson if (ch->buf == NULL) 2282b029f6bbSAndrew Thompson goto error; 2283b029f6bbSAndrew Thompson if (sndbuf_setup(b, ch->buf, buf_size) != 0) 2284b029f6bbSAndrew Thompson goto error; 2285b029f6bbSAndrew Thompson 2286b029f6bbSAndrew Thompson ch->start = ch->buf; 2287b029f6bbSAndrew Thompson ch->end = ch->buf + buf_size; 2288b029f6bbSAndrew Thompson ch->cur = ch->buf; 2289b029f6bbSAndrew Thompson ch->pcm_buf = b; 2290455a367fSHans Petter Selasky ch->max_buf = buf_size; 2291b029f6bbSAndrew Thompson 2292b029f6bbSAndrew Thompson if (ch->pcm_mtx == NULL) { 2293b029f6bbSAndrew Thompson DPRINTF("ERROR: PCM channels does not have a mutex!\n"); 2294b029f6bbSAndrew Thompson goto error; 2295b029f6bbSAndrew Thompson } 22963a3f90c6SAndrew Thompson return (ch); 22973a3f90c6SAndrew Thompson 22983a3f90c6SAndrew Thompson error: 22993a3f90c6SAndrew Thompson uaudio_chan_free(ch); 23003a3f90c6SAndrew Thompson return (NULL); 23013a3f90c6SAndrew Thompson } 23023a3f90c6SAndrew Thompson 23033a3f90c6SAndrew Thompson int 23043a3f90c6SAndrew Thompson uaudio_chan_free(struct uaudio_chan *ch) 23053a3f90c6SAndrew Thompson { 23063a3f90c6SAndrew Thompson if (ch->buf != NULL) { 23073a3f90c6SAndrew Thompson free(ch->buf, M_DEVBUF); 23083a3f90c6SAndrew Thompson ch->buf = NULL; 23093a3f90c6SAndrew Thompson } 2310b4380da7SHans Petter Selasky usbd_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS + 1); 23113a3f90c6SAndrew Thompson 2312455a367fSHans Petter Selasky ch->num_alt = 0; 23133a3f90c6SAndrew Thompson 23143a3f90c6SAndrew Thompson return (0); 23153a3f90c6SAndrew Thompson } 23163a3f90c6SAndrew Thompson 23173a3f90c6SAndrew Thompson int 23183a3f90c6SAndrew Thompson uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize) 23193a3f90c6SAndrew Thompson { 2320455a367fSHans Petter Selasky uint32_t temp = 2 * uaudio_get_buffer_size(ch, ch->set_alt); 2321455a367fSHans Petter Selasky 2322455a367fSHans Petter Selasky sndbuf_setup(ch->pcm_buf, ch->buf, temp); 2323455a367fSHans Petter Selasky 2324455a367fSHans Petter Selasky ch->start = ch->buf; 2325455a367fSHans Petter Selasky ch->end = ch->buf + temp; 2326455a367fSHans Petter Selasky ch->cur = ch->buf; 2327455a367fSHans Petter Selasky 2328455a367fSHans Petter Selasky return (temp / 2); 23293a3f90c6SAndrew Thompson } 23303a3f90c6SAndrew Thompson 23313a3f90c6SAndrew Thompson int 23323a3f90c6SAndrew Thompson uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize, 23333a3f90c6SAndrew Thompson uint32_t blockcount) 23343a3f90c6SAndrew Thompson { 23353a3f90c6SAndrew Thompson return (1); 23363a3f90c6SAndrew Thompson } 23373a3f90c6SAndrew Thompson 23383a3f90c6SAndrew Thompson int 23393a3f90c6SAndrew Thompson uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed) 23403a3f90c6SAndrew Thompson { 2341455a367fSHans Petter Selasky uint8_t x; 2342455a367fSHans Petter Selasky 2343455a367fSHans Petter Selasky for (x = 0; x < ch->num_alt; x++) { 2344455a367fSHans Petter Selasky if (ch->usb_alt[x].sample_rate < speed) { 2345455a367fSHans Petter Selasky /* sample rate is too low */ 2346455a367fSHans Petter Selasky break; 23473a3f90c6SAndrew Thompson } 2348455a367fSHans Petter Selasky } 2349455a367fSHans Petter Selasky 2350455a367fSHans Petter Selasky if (x != 0) 2351455a367fSHans Petter Selasky x--; 2352455a367fSHans Petter Selasky 2353455a367fSHans Petter Selasky ch->set_alt = x; 2354455a367fSHans Petter Selasky 2355455a367fSHans Petter Selasky DPRINTF("Selecting alt %d\n", (int)x); 2356455a367fSHans Petter Selasky 2357455a367fSHans Petter Selasky return (ch->usb_alt[x].sample_rate); 23583a3f90c6SAndrew Thompson } 23593a3f90c6SAndrew Thompson 23603a3f90c6SAndrew Thompson int 23613a3f90c6SAndrew Thompson uaudio_chan_getptr(struct uaudio_chan *ch) 23623a3f90c6SAndrew Thompson { 23633a3f90c6SAndrew Thompson return (ch->cur - ch->start); 23643a3f90c6SAndrew Thompson } 23653a3f90c6SAndrew Thompson 23663a3f90c6SAndrew Thompson struct pcmchan_caps * 23673a3f90c6SAndrew Thompson uaudio_chan_getcaps(struct uaudio_chan *ch) 23683a3f90c6SAndrew Thompson { 23693a3f90c6SAndrew Thompson return (&ch->pcm_cap); 23703a3f90c6SAndrew Thompson } 23713a3f90c6SAndrew Thompson 237290da2b28SAriff Abdullah static struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = { 237390da2b28SAriff Abdullah .id = SND_CHN_MATRIX_DRV, 237490da2b28SAriff Abdullah .channels = 2, 237590da2b28SAriff Abdullah .ext = 0, 237690da2b28SAriff Abdullah .map = { 237790da2b28SAriff Abdullah /* Right */ 237890da2b28SAriff Abdullah [0] = { 237990da2b28SAriff Abdullah .type = SND_CHN_T_FR, 238090da2b28SAriff Abdullah .members = 238190da2b28SAriff Abdullah SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | 238290da2b28SAriff Abdullah SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | 238390da2b28SAriff Abdullah SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR 238490da2b28SAriff Abdullah }, 238590da2b28SAriff Abdullah /* Left */ 238690da2b28SAriff Abdullah [1] = { 238790da2b28SAriff Abdullah .type = SND_CHN_T_FL, 238890da2b28SAriff Abdullah .members = 238990da2b28SAriff Abdullah SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | 239090da2b28SAriff Abdullah SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | 239190da2b28SAriff Abdullah SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL 239290da2b28SAriff Abdullah }, 239390da2b28SAriff Abdullah [2] = { 239490da2b28SAriff Abdullah .type = SND_CHN_T_MAX, 239590da2b28SAriff Abdullah .members = 0 239690da2b28SAriff Abdullah } 239790da2b28SAriff Abdullah }, 239890da2b28SAriff Abdullah .mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL, 239990da2b28SAriff Abdullah .offset = { 1, 0, -1, -1, -1, -1, -1, -1, -1, 240090da2b28SAriff Abdullah -1, -1, -1, -1, -1, -1, -1, -1, -1 } 240190da2b28SAriff Abdullah }; 240290da2b28SAriff Abdullah 240390da2b28SAriff Abdullah struct pcmchan_matrix * 240490da2b28SAriff Abdullah uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format) 240590da2b28SAriff Abdullah { 240690da2b28SAriff Abdullah struct uaudio_softc *sc; 240790da2b28SAriff Abdullah 240890da2b28SAriff Abdullah sc = ch->priv_sc; 240990da2b28SAriff Abdullah 241090da2b28SAriff Abdullah if (sc != NULL && sc->sc_uq_audio_swap_lr != 0 && 241190da2b28SAriff Abdullah AFMT_CHANNEL(format) == 2) 241290da2b28SAriff Abdullah return (&uaudio_chan_matrix_swap_2_0); 241390da2b28SAriff Abdullah 241490da2b28SAriff Abdullah return (feeder_matrix_format_map(format)); 241590da2b28SAriff Abdullah } 241690da2b28SAriff Abdullah 24173a3f90c6SAndrew Thompson int 24183a3f90c6SAndrew Thompson uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format) 24193a3f90c6SAndrew Thompson { 2420455a367fSHans Petter Selasky DPRINTF("Selecting format 0x%08x\n", (unsigned int)format); 24213a3f90c6SAndrew Thompson return (0); 24223a3f90c6SAndrew Thompson } 24233a3f90c6SAndrew Thompson 24243a3f90c6SAndrew Thompson int 24253a3f90c6SAndrew Thompson uaudio_chan_start(struct uaudio_chan *ch) 24263a3f90c6SAndrew Thompson { 2427455a367fSHans Petter Selasky struct uaudio_softc *sc = ch->priv_sc; 2428455a367fSHans Petter Selasky int do_start = 0; 24293a3f90c6SAndrew Thompson 2430455a367fSHans Petter Selasky usb_proc_explore_lock(sc->sc_udev); 2431455a367fSHans Petter Selasky if (ch->operation != CHAN_OP_DRAIN) { 2432455a367fSHans Petter Selasky if (ch->cur_alt == ch->set_alt && 2433455a367fSHans Petter Selasky ch->operation == CHAN_OP_NONE) { 2434455a367fSHans Petter Selasky /* save doing the explore task */ 2435455a367fSHans Petter Selasky do_start = 1; 2436455a367fSHans Petter Selasky } else { 2437455a367fSHans Petter Selasky ch->operation = CHAN_OP_START; 2438455a367fSHans Petter Selasky (void)usb_proc_explore_msignal(sc->sc_udev, 2439455a367fSHans Petter Selasky &sc->sc_config_msg[0], &sc->sc_config_msg[1]); 2440455a367fSHans Petter Selasky } 2441455a367fSHans Petter Selasky } 2442455a367fSHans Petter Selasky usb_proc_explore_unlock(sc->sc_udev); 2443455a367fSHans Petter Selasky 2444455a367fSHans Petter Selasky if (do_start) { 2445a593f6b8SAndrew Thompson usbd_transfer_start(ch->xfer[0]); 2446a593f6b8SAndrew Thompson usbd_transfer_start(ch->xfer[1]); 2447455a367fSHans Petter Selasky } 24483a3f90c6SAndrew Thompson return (0); 24493a3f90c6SAndrew Thompson } 24503a3f90c6SAndrew Thompson 24513a3f90c6SAndrew Thompson int 24523a3f90c6SAndrew Thompson uaudio_chan_stop(struct uaudio_chan *ch) 24533a3f90c6SAndrew Thompson { 2454455a367fSHans Petter Selasky struct uaudio_softc *sc = ch->priv_sc; 2455455a367fSHans Petter Selasky int do_stop = 0; 2456455a367fSHans Petter Selasky 2457455a367fSHans Petter Selasky usb_proc_explore_lock(sc->sc_udev); 2458455a367fSHans Petter Selasky if (ch->operation != CHAN_OP_DRAIN) { 2459455a367fSHans Petter Selasky if (ch->cur_alt == ch->set_alt && 2460455a367fSHans Petter Selasky ch->operation == CHAN_OP_NONE) { 2461455a367fSHans Petter Selasky /* save doing the explore task */ 2462455a367fSHans Petter Selasky do_stop = 1; 2463455a367fSHans Petter Selasky } else { 2464455a367fSHans Petter Selasky ch->operation = CHAN_OP_STOP; 2465455a367fSHans Petter Selasky (void)usb_proc_explore_msignal(sc->sc_udev, 2466455a367fSHans Petter Selasky &sc->sc_config_msg[0], &sc->sc_config_msg[1]); 2467455a367fSHans Petter Selasky } 2468455a367fSHans Petter Selasky } 2469455a367fSHans Petter Selasky usb_proc_explore_unlock(sc->sc_udev); 2470455a367fSHans Petter Selasky 2471455a367fSHans Petter Selasky if (do_stop) { 2472a593f6b8SAndrew Thompson usbd_transfer_stop(ch->xfer[0]); 2473a593f6b8SAndrew Thompson usbd_transfer_stop(ch->xfer[1]); 2474455a367fSHans Petter Selasky } 24753a3f90c6SAndrew Thompson return (0); 24763a3f90c6SAndrew Thompson } 24773a3f90c6SAndrew Thompson 24783a3f90c6SAndrew Thompson /*========================================================================* 24793a3f90c6SAndrew Thompson * AC - Audio Controller - routines 24803a3f90c6SAndrew Thompson *========================================================================*/ 24813a3f90c6SAndrew Thompson 2482902514f6SHans Petter Selasky static int 2483902514f6SHans Petter Selasky uaudio_mixer_sysctl_handler(SYSCTL_HANDLER_ARGS) 2484902514f6SHans Petter Selasky { 2485902514f6SHans Petter Selasky struct uaudio_softc *sc; 2486902514f6SHans Petter Selasky struct uaudio_mixer_node *pmc; 2487902514f6SHans Petter Selasky int hint; 2488902514f6SHans Petter Selasky int error; 2489902514f6SHans Petter Selasky int temp = 0; 2490902514f6SHans Petter Selasky int chan = 0; 2491902514f6SHans Petter Selasky 2492902514f6SHans Petter Selasky sc = (struct uaudio_softc *)oidp->oid_arg1; 2493902514f6SHans Petter Selasky hint = oidp->oid_arg2; 2494902514f6SHans Petter Selasky 2495902514f6SHans Petter Selasky if (sc->sc_mixer_lock == NULL) 2496902514f6SHans Petter Selasky return (ENXIO); 2497902514f6SHans Petter Selasky 2498902514f6SHans Petter Selasky /* lookup mixer node */ 2499902514f6SHans Petter Selasky 2500902514f6SHans Petter Selasky mtx_lock(sc->sc_mixer_lock); 2501902514f6SHans Petter Selasky for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) { 2502902514f6SHans Petter Selasky for (chan = 0; chan != (int)pmc->nchan; chan++) { 2503902514f6SHans Petter Selasky if (pmc->wValue[chan] != -1 && 2504902514f6SHans Petter Selasky pmc->wValue[chan] == hint) { 2505902514f6SHans Petter Selasky temp = pmc->wData[chan]; 2506902514f6SHans Petter Selasky goto found; 2507902514f6SHans Petter Selasky } 2508902514f6SHans Petter Selasky } 2509902514f6SHans Petter Selasky } 2510902514f6SHans Petter Selasky found: 2511902514f6SHans Petter Selasky mtx_unlock(sc->sc_mixer_lock); 2512902514f6SHans Petter Selasky 2513902514f6SHans Petter Selasky error = sysctl_handle_int(oidp, &temp, 0, req); 2514902514f6SHans Petter Selasky if (error != 0 || req->newptr == NULL) 2515902514f6SHans Petter Selasky return (error); 2516902514f6SHans Petter Selasky 2517902514f6SHans Petter Selasky /* update mixer value */ 2518902514f6SHans Petter Selasky 2519902514f6SHans Petter Selasky mtx_lock(sc->sc_mixer_lock); 2520902514f6SHans Petter Selasky if (pmc != NULL && 2521902514f6SHans Petter Selasky temp >= pmc->minval && 2522902514f6SHans Petter Selasky temp <= pmc->maxval) { 2523902514f6SHans Petter Selasky 2524902514f6SHans Petter Selasky pmc->wData[chan] = temp; 2525902514f6SHans Petter Selasky pmc->update[(chan / 8)] |= (1 << (chan % 8)); 2526902514f6SHans Petter Selasky 2527902514f6SHans Petter Selasky /* start the transfer, if not already started */ 2528902514f6SHans Petter Selasky usbd_transfer_start(sc->sc_mixer_xfer[0]); 2529902514f6SHans Petter Selasky } 2530902514f6SHans Petter Selasky mtx_unlock(sc->sc_mixer_lock); 2531902514f6SHans Petter Selasky 2532902514f6SHans Petter Selasky return (0); 2533902514f6SHans Petter Selasky } 2534902514f6SHans Petter Selasky 2535902514f6SHans Petter Selasky static void 2536902514f6SHans Petter Selasky uaudio_mixer_ctl_free(struct uaudio_softc *sc) 2537902514f6SHans Petter Selasky { 2538902514f6SHans Petter Selasky struct uaudio_mixer_node *p_mc; 2539902514f6SHans Petter Selasky 2540902514f6SHans Petter Selasky while ((p_mc = sc->sc_mixer_root) != NULL) { 2541902514f6SHans Petter Selasky sc->sc_mixer_root = p_mc->next; 2542902514f6SHans Petter Selasky free(p_mc, M_USBDEV); 2543902514f6SHans Petter Selasky } 2544902514f6SHans Petter Selasky } 2545902514f6SHans Petter Selasky 2546902514f6SHans Petter Selasky static void 2547902514f6SHans Petter Selasky uaudio_mixer_register_sysctl(struct uaudio_softc *sc, device_t dev) 2548902514f6SHans Petter Selasky { 2549902514f6SHans Petter Selasky struct uaudio_mixer_node *pmc; 2550902514f6SHans Petter Selasky struct sysctl_oid *mixer_tree; 2551902514f6SHans Petter Selasky struct sysctl_oid *control_tree; 2552902514f6SHans Petter Selasky char buf[32]; 2553902514f6SHans Petter Selasky int chan; 2554902514f6SHans Petter Selasky int n; 2555902514f6SHans Petter Selasky 2556ff4d5953SHans Petter Selasky mixer_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev), 2557902514f6SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "mixer", 2558902514f6SHans Petter Selasky CTLFLAG_RD, NULL, ""); 2559902514f6SHans Petter Selasky 2560902514f6SHans Petter Selasky if (mixer_tree == NULL) 2561902514f6SHans Petter Selasky return; 2562902514f6SHans Petter Selasky 2563902514f6SHans Petter Selasky for (n = 0, pmc = sc->sc_mixer_root; pmc != NULL; 2564902514f6SHans Petter Selasky pmc = pmc->next, n++) { 2565902514f6SHans Petter Selasky 2566902514f6SHans Petter Selasky for (chan = 0; chan < pmc->nchan; chan++) { 2567902514f6SHans Petter Selasky 2568902514f6SHans Petter Selasky if (pmc->nchan > 1) { 2569902514f6SHans Petter Selasky snprintf(buf, sizeof(buf), "%s_%d_%d", 2570902514f6SHans Petter Selasky pmc->name, n, chan); 2571902514f6SHans Petter Selasky } else { 2572902514f6SHans Petter Selasky snprintf(buf, sizeof(buf), "%s_%d", 2573902514f6SHans Petter Selasky pmc->name, n); 2574902514f6SHans Petter Selasky } 2575902514f6SHans Petter Selasky 2576ff4d5953SHans Petter Selasky control_tree = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev), 2577902514f6SHans Petter Selasky SYSCTL_CHILDREN(mixer_tree), OID_AUTO, buf, 2578ff4d5953SHans Petter Selasky CTLFLAG_RD, NULL, "Mixer control nodes"); 2579902514f6SHans Petter Selasky 2580902514f6SHans Petter Selasky if (control_tree == NULL) 2581902514f6SHans Petter Selasky continue; 2582902514f6SHans Petter Selasky 2583ff4d5953SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 2584902514f6SHans Petter Selasky SYSCTL_CHILDREN(control_tree), 2585902514f6SHans Petter Selasky OID_AUTO, "val", CTLTYPE_INT | CTLFLAG_RW, sc, 2586902514f6SHans Petter Selasky pmc->wValue[chan], 2587902514f6SHans Petter Selasky uaudio_mixer_sysctl_handler, "I", "Current value"); 2588902514f6SHans Petter Selasky 2589ff4d5953SHans Petter Selasky SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 2590902514f6SHans Petter Selasky SYSCTL_CHILDREN(control_tree), 2591902514f6SHans Petter Selasky OID_AUTO, "min", CTLFLAG_RD, 0, pmc->minval, 2592902514f6SHans Petter Selasky "Minimum value"); 2593902514f6SHans Petter Selasky 2594ff4d5953SHans Petter Selasky SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 2595902514f6SHans Petter Selasky SYSCTL_CHILDREN(control_tree), 2596902514f6SHans Petter Selasky OID_AUTO, "max", CTLFLAG_RD, 0, pmc->maxval, 2597902514f6SHans Petter Selasky "Maximum value"); 2598902514f6SHans Petter Selasky 2599ff4d5953SHans Petter Selasky SYSCTL_ADD_STRING(device_get_sysctl_ctx(dev), 2600902514f6SHans Petter Selasky SYSCTL_CHILDREN(control_tree), 2601902514f6SHans Petter Selasky OID_AUTO, "desc", CTLFLAG_RD, pmc->desc, 0, 2602902514f6SHans Petter Selasky "Description"); 2603902514f6SHans Petter Selasky } 2604902514f6SHans Petter Selasky } 2605902514f6SHans Petter Selasky } 2606902514f6SHans Petter Selasky 2607ff4d5953SHans Petter Selasky /* M-Audio FastTrack Ultra Mixer Description */ 2608ff4d5953SHans Petter Selasky /* Origin: Linux USB Audio driver */ 2609ff4d5953SHans Petter Selasky static void 2610ff4d5953SHans Petter Selasky uaudio_mixer_controls_create_ftu(struct uaudio_softc *sc) 2611ff4d5953SHans Petter Selasky { 2612ff4d5953SHans Petter Selasky int chx; 2613ff4d5953SHans Petter Selasky int chy; 2614ff4d5953SHans Petter Selasky 2615f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 2616f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no); 2617f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(8, 0); 2618f7e62ad0SHans Petter Selasky MIX(sc).class = UAC_OUTPUT; 2619f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_UNSIGNED_16; 2620f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2621f7e62ad0SHans Petter Selasky MIX(sc).name = "effect"; 2622f7e62ad0SHans Petter Selasky MIX(sc).minval = 0; 2623f7e62ad0SHans Petter Selasky MIX(sc).maxval = 7; 2624f7e62ad0SHans Petter Selasky MIX(sc).mul = 7; 2625f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2626f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2627f7e62ad0SHans Petter Selasky strlcpy(MIX(sc).desc, "Room1,2,3,Hall1,2,Plate,Delay,Echo", sizeof(MIX(sc).desc)); 2628f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl_sub(sc, &MIX(sc)); 2629ff4d5953SHans Petter Selasky 2630f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 2631f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no); 2632ff4d5953SHans Petter Selasky 2633ff4d5953SHans Petter Selasky for (chx = 0; chx != 8; chx++) { 2634ff4d5953SHans Petter Selasky for (chy = 0; chy != 8; chy++) { 2635ff4d5953SHans Petter Selasky 2636f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1); 2637f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 2638f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2639f7e62ad0SHans Petter Selasky MIX(sc).name = "mix_rec"; 2640f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2641f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2642f7e62ad0SHans Petter Selasky MIX(sc).val_default = 0; 2643f7e62ad0SHans Petter Selasky snprintf(MIX(sc).desc, sizeof(MIX(sc).desc), 2644ff4d5953SHans Petter Selasky "AIn%d - Out%d Record Volume", chy + 1, chx + 1); 2645ff4d5953SHans Petter Selasky 2646f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 2647ff4d5953SHans Petter Selasky 2648f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(chx + 1, chy + 1 + 8); 2649f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 2650f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2651f7e62ad0SHans Petter Selasky MIX(sc).name = "mix_play"; 2652f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2653f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2654f7e62ad0SHans Petter Selasky MIX(sc).val_default = (chx == chy) ? 2 : 0; 2655f7e62ad0SHans Petter Selasky snprintf(MIX(sc).desc, sizeof(MIX(sc).desc), 2656ff4d5953SHans Petter Selasky "DIn%d - Out%d Playback Volume", chy + 1, chx + 1); 2657ff4d5953SHans Petter Selasky 2658f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 2659ff4d5953SHans Petter Selasky } 2660ff4d5953SHans Petter Selasky } 2661ff4d5953SHans Petter Selasky 2662f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 2663f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no); 2664f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(2, 0); 2665f7e62ad0SHans Petter Selasky MIX(sc).class = UAC_OUTPUT; 2666f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_8; 2667f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2668f7e62ad0SHans Petter Selasky MIX(sc).name = "effect_vol"; 2669f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2670f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2671f7e62ad0SHans Petter Selasky MIX(sc).minval = 0; 2672f7e62ad0SHans Petter Selasky MIX(sc).maxval = 0x7f; 2673f7e62ad0SHans Petter Selasky MIX(sc).mul = 0x7f; 2674f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2675f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2676f7e62ad0SHans Petter Selasky strlcpy(MIX(sc).desc, "Effect Volume", sizeof(MIX(sc).desc)); 2677f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl_sub(sc, &MIX(sc)); 2678ff4d5953SHans Petter Selasky 2679f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 2680f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no); 2681f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(3, 0); 2682f7e62ad0SHans Petter Selasky MIX(sc).class = UAC_OUTPUT; 2683f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 2684f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2685f7e62ad0SHans Petter Selasky MIX(sc).name = "effect_dur"; 2686f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2687f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2688f7e62ad0SHans Petter Selasky MIX(sc).minval = 0; 2689f7e62ad0SHans Petter Selasky MIX(sc).maxval = 0x7f00; 2690f7e62ad0SHans Petter Selasky MIX(sc).mul = 0x7f00; 2691f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2692f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2693f7e62ad0SHans Petter Selasky strlcpy(MIX(sc).desc, "Effect Duration", sizeof(MIX(sc).desc)); 2694f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl_sub(sc, &MIX(sc)); 2695ff4d5953SHans Petter Selasky 2696f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 2697f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(6, sc->sc_mixer_iface_no); 2698f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(4, 0); 2699f7e62ad0SHans Petter Selasky MIX(sc).class = UAC_OUTPUT; 2700f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_8; 2701f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2702f7e62ad0SHans Petter Selasky MIX(sc).name = "effect_fb"; 2703f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2704f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2705f7e62ad0SHans Petter Selasky MIX(sc).minval = 0; 2706f7e62ad0SHans Petter Selasky MIX(sc).maxval = 0x7f; 2707f7e62ad0SHans Petter Selasky MIX(sc).mul = 0x7f; 2708f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2709f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2710f7e62ad0SHans Petter Selasky strlcpy(MIX(sc).desc, "Effect Feedback Volume", sizeof(MIX(sc).desc)); 2711f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl_sub(sc, &MIX(sc)); 2712ff4d5953SHans Petter Selasky 2713f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 2714f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(7, sc->sc_mixer_iface_no); 2715ff4d5953SHans Petter Selasky for (chy = 0; chy != 4; chy++) { 2716ff4d5953SHans Petter Selasky 2717f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(7, chy + 1); 2718f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 2719f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2720f7e62ad0SHans Petter Selasky MIX(sc).name = "effect_ret"; 2721f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2722f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2723f7e62ad0SHans Petter Selasky snprintf(MIX(sc).desc, sizeof(MIX(sc).desc), 2724ff4d5953SHans Petter Selasky "Effect Return %d Volume", chy + 1); 2725ff4d5953SHans Petter Selasky 2726f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 2727ff4d5953SHans Petter Selasky } 2728ff4d5953SHans Petter Selasky 2729f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 2730f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(5, sc->sc_mixer_iface_no); 2731ff4d5953SHans Petter Selasky 2732ff4d5953SHans Petter Selasky for (chy = 0; chy != 8; chy++) { 2733f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1); 2734f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 2735f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2736f7e62ad0SHans Petter Selasky MIX(sc).name = "effect_send"; 2737f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2738f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2739f7e62ad0SHans Petter Selasky snprintf(MIX(sc).desc, sizeof(MIX(sc).desc), 2740ff4d5953SHans Petter Selasky "Effect Send AIn%d Volume", chy + 1); 2741ff4d5953SHans Petter Selasky 2742f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 2743ff4d5953SHans Petter Selasky 2744f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(9, chy + 1); 2745f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 2746f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 2747f7e62ad0SHans Petter Selasky MIX(sc).name = "effect_send"; 2748f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 2749f7e62ad0SHans Petter Selasky MIX(sc).update[0] = 1; 2750f7e62ad0SHans Petter Selasky snprintf(MIX(sc).desc, sizeof(MIX(sc).desc), 2751ff4d5953SHans Petter Selasky "Effect Send DIn%d Volume", chy + 1 + 8); 2752ff4d5953SHans Petter Selasky 2753f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 2754ff4d5953SHans Petter Selasky } 2755ff4d5953SHans Petter Selasky } 2756ff4d5953SHans Petter Selasky 2757902514f6SHans Petter Selasky static void 2758902514f6SHans Petter Selasky uaudio_mixer_reload_all(struct uaudio_softc *sc) 2759902514f6SHans Petter Selasky { 2760902514f6SHans Petter Selasky struct uaudio_mixer_node *pmc; 2761902514f6SHans Petter Selasky int chan; 2762902514f6SHans Petter Selasky 2763902514f6SHans Petter Selasky if (sc->sc_mixer_lock == NULL) 2764902514f6SHans Petter Selasky return; 2765902514f6SHans Petter Selasky 2766902514f6SHans Petter Selasky mtx_lock(sc->sc_mixer_lock); 2767902514f6SHans Petter Selasky for (pmc = sc->sc_mixer_root; pmc != NULL; pmc = pmc->next) { 2768ff4d5953SHans Petter Selasky /* use reset defaults for non-oss controlled settings */ 2769ff4d5953SHans Petter Selasky if (pmc->ctl == SOUND_MIXER_NRDEVICES) 2770ff4d5953SHans Petter Selasky continue; 2771902514f6SHans Petter Selasky for (chan = 0; chan < pmc->nchan; chan++) 2772902514f6SHans Petter Selasky pmc->update[chan / 8] |= (1 << (chan % 8)); 2773902514f6SHans Petter Selasky } 2774902514f6SHans Petter Selasky usbd_transfer_start(sc->sc_mixer_xfer[0]); 277576b71212SHans Petter Selasky 277676b71212SHans Petter Selasky /* start HID volume keys, if any */ 277776b71212SHans Petter Selasky usbd_transfer_start(sc->sc_hid.xfer[0]); 2778902514f6SHans Petter Selasky mtx_unlock(sc->sc_mixer_lock); 2779902514f6SHans Petter Selasky } 2780902514f6SHans Petter Selasky 27813a3f90c6SAndrew Thompson static void 27823a3f90c6SAndrew Thompson uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) 27833a3f90c6SAndrew Thompson { 27843a3f90c6SAndrew Thompson struct uaudio_mixer_node *p_mc_new = 27853a3f90c6SAndrew Thompson malloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK); 2786ff4d5953SHans Petter Selasky int ch; 27873a3f90c6SAndrew Thompson 27886f068a43SHans Petter Selasky if (p_mc_new != NULL) { 27896f068a43SHans Petter Selasky memcpy(p_mc_new, mc, sizeof(*p_mc_new)); 27903a3f90c6SAndrew Thompson p_mc_new->next = sc->sc_mixer_root; 27913a3f90c6SAndrew Thompson sc->sc_mixer_root = p_mc_new; 27923a3f90c6SAndrew Thompson sc->sc_mixer_count++; 2793ff4d5953SHans Petter Selasky 2794ff4d5953SHans Petter Selasky /* set default value for all channels */ 2795ff4d5953SHans Petter Selasky for (ch = 0; ch < p_mc_new->nchan; ch++) { 2796ff4d5953SHans Petter Selasky switch (p_mc_new->val_default) { 2797ff4d5953SHans Petter Selasky case 1: 279858e8ac5cSHans Petter Selasky /* 50% */ 2799ff4d5953SHans Petter Selasky p_mc_new->wData[ch] = (p_mc_new->maxval + p_mc_new->minval) / 2; 2800ff4d5953SHans Petter Selasky break; 2801ff4d5953SHans Petter Selasky case 2: 280258e8ac5cSHans Petter Selasky /* 100% */ 2803ff4d5953SHans Petter Selasky p_mc_new->wData[ch] = p_mc_new->maxval; 2804ff4d5953SHans Petter Selasky break; 2805ff4d5953SHans Petter Selasky default: 280658e8ac5cSHans Petter Selasky /* 0% */ 2807ff4d5953SHans Petter Selasky p_mc_new->wData[ch] = p_mc_new->minval; 2808ff4d5953SHans Petter Selasky break; 2809ff4d5953SHans Petter Selasky } 2810ff4d5953SHans Petter Selasky } 28113a3f90c6SAndrew Thompson } else { 28123a3f90c6SAndrew Thompson DPRINTF("out of memory\n"); 28133a3f90c6SAndrew Thompson } 28143a3f90c6SAndrew Thompson } 28153a3f90c6SAndrew Thompson 28163a3f90c6SAndrew Thompson static void 28173a3f90c6SAndrew Thompson uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc) 28183a3f90c6SAndrew Thompson { 28193a3f90c6SAndrew Thompson int32_t res; 28203a3f90c6SAndrew Thompson 28213a3f90c6SAndrew Thompson if (mc->class < UAC_NCLASSES) { 28223a3f90c6SAndrew Thompson DPRINTF("adding %s.%d\n", 28233a3f90c6SAndrew Thompson uac_names[mc->class], mc->ctl); 28243a3f90c6SAndrew Thompson } else { 28253a3f90c6SAndrew Thompson DPRINTF("adding %d\n", mc->ctl); 28263a3f90c6SAndrew Thompson } 28273a3f90c6SAndrew Thompson 28283a3f90c6SAndrew Thompson if (mc->type == MIX_ON_OFF) { 28293a3f90c6SAndrew Thompson mc->minval = 0; 28303a3f90c6SAndrew Thompson mc->maxval = 1; 28313a3f90c6SAndrew Thompson } else if (mc->type == MIX_SELECTOR) { 28323a3f90c6SAndrew Thompson } else { 28333a3f90c6SAndrew Thompson 28343a3f90c6SAndrew Thompson /* determine min and max values */ 28353a3f90c6SAndrew Thompson 2836e2524b2eSHans Petter Selasky mc->minval = uaudio_mixer_get(sc->sc_udev, 2837e2524b2eSHans Petter Selasky sc->sc_audio_rev, GET_MIN, mc); 2838e2524b2eSHans Petter Selasky mc->maxval = uaudio_mixer_get(sc->sc_udev, 2839e2524b2eSHans Petter Selasky sc->sc_audio_rev, GET_MAX, mc); 28403a3f90c6SAndrew Thompson 2841b029f6bbSAndrew Thompson /* check if max and min was swapped */ 28423a3f90c6SAndrew Thompson 28433a3f90c6SAndrew Thompson if (mc->maxval < mc->minval) { 2844b029f6bbSAndrew Thompson res = mc->maxval; 28453a3f90c6SAndrew Thompson mc->maxval = mc->minval; 2846b029f6bbSAndrew Thompson mc->minval = res; 28473a3f90c6SAndrew Thompson } 2848b029f6bbSAndrew Thompson 2849b029f6bbSAndrew Thompson /* compute value range */ 2850b029f6bbSAndrew Thompson mc->mul = mc->maxval - mc->minval; 2851b029f6bbSAndrew Thompson if (mc->mul == 0) 2852b029f6bbSAndrew Thompson mc->mul = 1; 2853b029f6bbSAndrew Thompson 2854b029f6bbSAndrew Thompson /* compute value alignment */ 2855e2524b2eSHans Petter Selasky res = uaudio_mixer_get(sc->sc_udev, 2856e2524b2eSHans Petter Selasky sc->sc_audio_rev, GET_RES, mc); 28577fb43570SAndrew Thompson 28587fb43570SAndrew Thompson DPRINTF("Resolution = %d\n", (int)res); 2859b029f6bbSAndrew Thompson } 2860b029f6bbSAndrew Thompson 28613a3f90c6SAndrew Thompson uaudio_mixer_add_ctl_sub(sc, mc); 28623a3f90c6SAndrew Thompson 2863b850ecc1SAndrew Thompson #ifdef USB_DEBUG 28643a3f90c6SAndrew Thompson if (uaudio_debug > 2) { 28653a3f90c6SAndrew Thompson uint8_t i; 28663a3f90c6SAndrew Thompson 28673a3f90c6SAndrew Thompson for (i = 0; i < mc->nchan; i++) { 28683a3f90c6SAndrew Thompson DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]); 28693a3f90c6SAndrew Thompson } 28703a3f90c6SAndrew Thompson DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' " 28713a3f90c6SAndrew Thompson "min=%d max=%d\n", 28723a3f90c6SAndrew Thompson mc->wIndex, mc->type, mc->ctl, 28733a3f90c6SAndrew Thompson mc->minval, mc->maxval); 28743a3f90c6SAndrew Thompson } 28753a3f90c6SAndrew Thompson #endif 28763a3f90c6SAndrew Thompson } 28773a3f90c6SAndrew Thompson 28783a3f90c6SAndrew Thompson static void 28793a3f90c6SAndrew Thompson uaudio_mixer_add_mixer(struct uaudio_softc *sc, 28803a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 28813a3f90c6SAndrew Thompson { 2882e2524b2eSHans Petter Selasky const struct usb_audio_mixer_unit_0 *d0 = iot[id].u.mu_v1; 28834c21be9bSRebecca Cran const struct usb_audio_mixer_unit_1 *d1; 28843a3f90c6SAndrew Thompson 28853a3f90c6SAndrew Thompson uint32_t bno; /* bit number */ 28863a3f90c6SAndrew Thompson uint32_t p; /* bit number accumulator */ 28873a3f90c6SAndrew Thompson uint32_t mo; /* matching outputs */ 28883a3f90c6SAndrew Thompson uint32_t mc; /* matching channels */ 28893a3f90c6SAndrew Thompson uint32_t ichs; /* input channels */ 28903a3f90c6SAndrew Thompson uint32_t ochs; /* output channels */ 28913a3f90c6SAndrew Thompson uint32_t c; 28923a3f90c6SAndrew Thompson uint32_t chs; /* channels */ 28933a3f90c6SAndrew Thompson uint32_t i; 28943a3f90c6SAndrew Thompson uint32_t o; 28953a3f90c6SAndrew Thompson 28963a3f90c6SAndrew Thompson DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 28973a3f90c6SAndrew Thompson d0->bUnitId, d0->bNrInPins); 28983a3f90c6SAndrew Thompson 28993a3f90c6SAndrew Thompson /* compute the number of input channels */ 29003a3f90c6SAndrew Thompson 29013a3f90c6SAndrew Thompson ichs = 0; 29023a3f90c6SAndrew Thompson for (i = 0; i < d0->bNrInPins; i++) { 2903e2524b2eSHans Petter Selasky ichs += uaudio_mixer_get_cluster( 2904e2524b2eSHans Petter Selasky d0->baSourceId[i], iot).bNrChannels; 29053a3f90c6SAndrew Thompson } 29063a3f90c6SAndrew Thompson 29073a3f90c6SAndrew Thompson d1 = (const void *)(d0->baSourceId + d0->bNrInPins); 29083a3f90c6SAndrew Thompson 29093a3f90c6SAndrew Thompson /* and the number of output channels */ 29103a3f90c6SAndrew Thompson 29113a3f90c6SAndrew Thompson ochs = d1->bNrChannels; 29123a3f90c6SAndrew Thompson 29133a3f90c6SAndrew Thompson DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs); 29143a3f90c6SAndrew Thompson 2915f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 29163a3f90c6SAndrew Thompson 2917f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 2918f7e62ad0SHans Petter Selasky uaudio_mixer_determine_class(&iot[id], &MIX(sc)); 2919f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 29203a3f90c6SAndrew Thompson 2921e2524b2eSHans Petter Selasky if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) 29223a3f90c6SAndrew Thompson return; 2923e2524b2eSHans Petter Selasky 29243a3f90c6SAndrew Thompson for (p = i = 0; i < d0->bNrInPins; i++) { 2925e2524b2eSHans Petter Selasky chs = uaudio_mixer_get_cluster( 2926e2524b2eSHans Petter Selasky d0->baSourceId[i], iot).bNrChannels; 29273a3f90c6SAndrew Thompson mc = 0; 29283a3f90c6SAndrew Thompson for (c = 0; c < chs; c++) { 29293a3f90c6SAndrew Thompson mo = 0; 29303a3f90c6SAndrew Thompson for (o = 0; o < ochs; o++) { 29313a3f90c6SAndrew Thompson bno = ((p + c) * ochs) + o; 2932e2524b2eSHans Petter Selasky if (BIT_TEST(d1->bmControls, bno)) 29333a3f90c6SAndrew Thompson mo++; 29343a3f90c6SAndrew Thompson } 2935e2524b2eSHans Petter Selasky if (mo == 1) 29363a3f90c6SAndrew Thompson mc++; 29373a3f90c6SAndrew Thompson } 29383a3f90c6SAndrew Thompson if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { 29393a3f90c6SAndrew Thompson 29403a3f90c6SAndrew Thompson /* repeat bit-scan */ 29413a3f90c6SAndrew Thompson 29423a3f90c6SAndrew Thompson mc = 0; 29433a3f90c6SAndrew Thompson for (c = 0; c < chs; c++) { 29443a3f90c6SAndrew Thompson for (o = 0; o < ochs; o++) { 29453a3f90c6SAndrew Thompson bno = ((p + c) * ochs) + o; 2946e2524b2eSHans Petter Selasky if (BIT_TEST(d1->bmControls, bno)) 2947f7e62ad0SHans Petter Selasky MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); 29483a3f90c6SAndrew Thompson } 29493a3f90c6SAndrew Thompson } 2950f7e62ad0SHans Petter Selasky MIX(sc).nchan = chs; 2951f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 2952e2524b2eSHans Petter Selasky } 2953e2524b2eSHans Petter Selasky p += chs; 2954e2524b2eSHans Petter Selasky } 2955e2524b2eSHans Petter Selasky } 2956e2524b2eSHans Petter Selasky 2957e2524b2eSHans Petter Selasky static void 2958e2524b2eSHans Petter Selasky uaudio20_mixer_add_mixer(struct uaudio_softc *sc, 2959e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *iot, int id) 2960e2524b2eSHans Petter Selasky { 2961e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_0 *d0 = iot[id].u.mu_v2; 2962e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_1 *d1; 2963e2524b2eSHans Petter Selasky 2964e2524b2eSHans Petter Selasky uint32_t bno; /* bit number */ 2965e2524b2eSHans Petter Selasky uint32_t p; /* bit number accumulator */ 2966e2524b2eSHans Petter Selasky uint32_t mo; /* matching outputs */ 2967e2524b2eSHans Petter Selasky uint32_t mc; /* matching channels */ 2968e2524b2eSHans Petter Selasky uint32_t ichs; /* input channels */ 2969e2524b2eSHans Petter Selasky uint32_t ochs; /* output channels */ 2970e2524b2eSHans Petter Selasky uint32_t c; 2971e2524b2eSHans Petter Selasky uint32_t chs; /* channels */ 2972e2524b2eSHans Petter Selasky uint32_t i; 2973e2524b2eSHans Petter Selasky uint32_t o; 2974e2524b2eSHans Petter Selasky 2975e2524b2eSHans Petter Selasky DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 2976e2524b2eSHans Petter Selasky d0->bUnitId, d0->bNrInPins); 2977e2524b2eSHans Petter Selasky 2978e2524b2eSHans Petter Selasky /* compute the number of input channels */ 2979e2524b2eSHans Petter Selasky 2980e2524b2eSHans Petter Selasky ichs = 0; 2981e2524b2eSHans Petter Selasky for (i = 0; i < d0->bNrInPins; i++) { 2982e2524b2eSHans Petter Selasky ichs += uaudio20_mixer_get_cluster( 2983e2524b2eSHans Petter Selasky d0->baSourceId[i], iot).bNrChannels; 2984e2524b2eSHans Petter Selasky } 2985e2524b2eSHans Petter Selasky 2986e2524b2eSHans Petter Selasky d1 = (const void *)(d0->baSourceId + d0->bNrInPins); 2987e2524b2eSHans Petter Selasky 2988e2524b2eSHans Petter Selasky /* and the number of output channels */ 2989e2524b2eSHans Petter Selasky 2990e2524b2eSHans Petter Selasky ochs = d1->bNrChannels; 2991e2524b2eSHans Petter Selasky 2992e2524b2eSHans Petter Selasky DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs); 2993e2524b2eSHans Petter Selasky 2994f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 2995e2524b2eSHans Petter Selasky 2996f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 2997f7e62ad0SHans Petter Selasky uaudio20_mixer_determine_class(&iot[id], &MIX(sc)); 2998f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 2999e2524b2eSHans Petter Selasky 3000e2524b2eSHans Petter Selasky if (uaudio20_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) 3001e2524b2eSHans Petter Selasky return; 3002e2524b2eSHans Petter Selasky 3003e2524b2eSHans Petter Selasky for (p = i = 0; i < d0->bNrInPins; i++) { 3004e2524b2eSHans Petter Selasky chs = uaudio20_mixer_get_cluster( 3005e2524b2eSHans Petter Selasky d0->baSourceId[i], iot).bNrChannels; 3006e2524b2eSHans Petter Selasky mc = 0; 3007e2524b2eSHans Petter Selasky for (c = 0; c < chs; c++) { 3008e2524b2eSHans Petter Selasky mo = 0; 3009e2524b2eSHans Petter Selasky for (o = 0; o < ochs; o++) { 3010e2524b2eSHans Petter Selasky bno = ((p + c) * ochs) + o; 3011e2524b2eSHans Petter Selasky if (BIT_TEST(d1->bmControls, bno)) 3012e2524b2eSHans Petter Selasky mo++; 3013e2524b2eSHans Petter Selasky } 3014e2524b2eSHans Petter Selasky if (mo == 1) 3015e2524b2eSHans Petter Selasky mc++; 3016e2524b2eSHans Petter Selasky } 3017e2524b2eSHans Petter Selasky if ((mc == chs) && (chs <= MIX_MAX_CHAN)) { 3018e2524b2eSHans Petter Selasky 3019e2524b2eSHans Petter Selasky /* repeat bit-scan */ 3020e2524b2eSHans Petter Selasky 3021e2524b2eSHans Petter Selasky mc = 0; 3022e2524b2eSHans Petter Selasky for (c = 0; c < chs; c++) { 3023e2524b2eSHans Petter Selasky for (o = 0; o < ochs; o++) { 3024e2524b2eSHans Petter Selasky bno = ((p + c) * ochs) + o; 3025e2524b2eSHans Petter Selasky if (BIT_TEST(d1->bmControls, bno)) 3026f7e62ad0SHans Petter Selasky MIX(sc).wValue[mc++] = MAKE_WORD(p + c + 1, o + 1); 3027e2524b2eSHans Petter Selasky } 3028e2524b2eSHans Petter Selasky } 3029f7e62ad0SHans Petter Selasky MIX(sc).nchan = chs; 3030f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 30313a3f90c6SAndrew Thompson } 30323a3f90c6SAndrew Thompson p += chs; 30333a3f90c6SAndrew Thompson } 30343a3f90c6SAndrew Thompson } 30353a3f90c6SAndrew Thompson 30363a3f90c6SAndrew Thompson static void 30373a3f90c6SAndrew Thompson uaudio_mixer_add_selector(struct uaudio_softc *sc, 30383a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 30393a3f90c6SAndrew Thompson { 3040e2524b2eSHans Petter Selasky const struct usb_audio_selector_unit *d = iot[id].u.su_v1; 30413a3f90c6SAndrew Thompson uint16_t i; 30423a3f90c6SAndrew Thompson 30433a3f90c6SAndrew Thompson DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 30443a3f90c6SAndrew Thompson d->bUnitId, d->bNrInPins); 30453a3f90c6SAndrew Thompson 3046902514f6SHans Petter Selasky if (d->bNrInPins == 0) 30473a3f90c6SAndrew Thompson return; 3048902514f6SHans Petter Selasky 3049f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 30503a3f90c6SAndrew Thompson 3051f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); 3052f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(0, 0); 3053f7e62ad0SHans Petter Selasky uaudio_mixer_determine_class(&iot[id], &MIX(sc)); 3054f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 3055f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SELECTOR; 3056f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 3057f7e62ad0SHans Petter Selasky MIX(sc).minval = 1; 3058f7e62ad0SHans Petter Selasky MIX(sc).maxval = d->bNrInPins; 3059f7e62ad0SHans Petter Selasky MIX(sc).name = "selector"; 3060902514f6SHans Petter Selasky 3061902514f6SHans Petter Selasky i = d->baSourceId[d->bNrInPins]; 3062902514f6SHans Petter Selasky if (i == 0 || 3063902514f6SHans Petter Selasky usbd_req_get_string_any(sc->sc_udev, NULL, 3064f7e62ad0SHans Petter Selasky MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) { 3065f7e62ad0SHans Petter Selasky MIX(sc).desc[0] = 0; 3066902514f6SHans Petter Selasky } 30673a3f90c6SAndrew Thompson 3068f7e62ad0SHans Petter Selasky if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN) { 3069f7e62ad0SHans Petter Selasky MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN; 30703a3f90c6SAndrew Thompson } 3071f7e62ad0SHans Petter Selasky MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval); 30723a3f90c6SAndrew Thompson for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) { 3073f7e62ad0SHans Petter Selasky MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES; 30743a3f90c6SAndrew Thompson } 30753a3f90c6SAndrew Thompson 3076f7e62ad0SHans Petter Selasky for (i = 0; i < MIX(sc).maxval; i++) { 3077f7e62ad0SHans Petter Selasky MIX(sc).slctrtype[i] = uaudio_mixer_feature_name( 3078f7e62ad0SHans Petter Selasky &iot[d->baSourceId[i]], &MIX(sc)); 3079e2524b2eSHans Petter Selasky } 3080e2524b2eSHans Petter Selasky 3081f7e62ad0SHans Petter Selasky MIX(sc).class = 0; /* not used */ 3082e2524b2eSHans Petter Selasky 3083f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 3084e2524b2eSHans Petter Selasky } 3085e2524b2eSHans Petter Selasky 3086e2524b2eSHans Petter Selasky static void 3087e2524b2eSHans Petter Selasky uaudio20_mixer_add_selector(struct uaudio_softc *sc, 3088e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *iot, int id) 3089e2524b2eSHans Petter Selasky { 3090e2524b2eSHans Petter Selasky const struct usb_audio20_selector_unit *d = iot[id].u.su_v2; 3091e2524b2eSHans Petter Selasky uint16_t i; 3092e2524b2eSHans Petter Selasky 3093e2524b2eSHans Petter Selasky DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 3094e2524b2eSHans Petter Selasky d->bUnitId, d->bNrInPins); 3095e2524b2eSHans Petter Selasky 3096e2524b2eSHans Petter Selasky if (d->bNrInPins == 0) 3097e2524b2eSHans Petter Selasky return; 3098e2524b2eSHans Petter Selasky 3099f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 3100e2524b2eSHans Petter Selasky 3101f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); 3102f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(0, 0); 3103f7e62ad0SHans Petter Selasky uaudio20_mixer_determine_class(&iot[id], &MIX(sc)); 3104f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 3105f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SELECTOR; 3106f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 3107f7e62ad0SHans Petter Selasky MIX(sc).minval = 1; 3108f7e62ad0SHans Petter Selasky MIX(sc).maxval = d->bNrInPins; 3109f7e62ad0SHans Petter Selasky MIX(sc).name = "selector"; 3110902514f6SHans Petter Selasky 3111902514f6SHans Petter Selasky i = d->baSourceId[d->bNrInPins]; 3112902514f6SHans Petter Selasky if (i == 0 || 3113902514f6SHans Petter Selasky usbd_req_get_string_any(sc->sc_udev, NULL, 3114f7e62ad0SHans Petter Selasky MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) { 3115f7e62ad0SHans Petter Selasky MIX(sc).desc[0] = 0; 3116902514f6SHans Petter Selasky } 3117e2524b2eSHans Petter Selasky 3118f7e62ad0SHans Petter Selasky if (MIX(sc).maxval > MAX_SELECTOR_INPUT_PIN) 3119f7e62ad0SHans Petter Selasky MIX(sc).maxval = MAX_SELECTOR_INPUT_PIN; 3120e2524b2eSHans Petter Selasky 3121f7e62ad0SHans Petter Selasky MIX(sc).mul = (MIX(sc).maxval - MIX(sc).minval); 3122e2524b2eSHans Petter Selasky for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) 3123f7e62ad0SHans Petter Selasky MIX(sc).slctrtype[i] = SOUND_MIXER_NRDEVICES; 3124e2524b2eSHans Petter Selasky 3125f7e62ad0SHans Petter Selasky for (i = 0; i < MIX(sc).maxval; i++) { 3126f7e62ad0SHans Petter Selasky MIX(sc).slctrtype[i] = uaudio20_mixer_feature_name( 3127f7e62ad0SHans Petter Selasky &iot[d->baSourceId[i]], &MIX(sc)); 31283a3f90c6SAndrew Thompson } 31293a3f90c6SAndrew Thompson 3130f7e62ad0SHans Petter Selasky MIX(sc).class = 0; /* not used */ 31313a3f90c6SAndrew Thompson 3132f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 31333a3f90c6SAndrew Thompson } 31343a3f90c6SAndrew Thompson 31353a3f90c6SAndrew Thompson static uint32_t 31364c21be9bSRebecca Cran uaudio_mixer_feature_get_bmaControls(const struct usb_audio_feature_unit *d, 31376d917491SHans Petter Selasky uint8_t i) 31383a3f90c6SAndrew Thompson { 31393a3f90c6SAndrew Thompson uint32_t temp = 0; 31406d917491SHans Petter Selasky uint32_t offset = (i * d->bControlSize); 31413a3f90c6SAndrew Thompson 31423a3f90c6SAndrew Thompson if (d->bControlSize > 0) { 31433a3f90c6SAndrew Thompson temp |= d->bmaControls[offset]; 31443a3f90c6SAndrew Thompson if (d->bControlSize > 1) { 31453a3f90c6SAndrew Thompson temp |= d->bmaControls[offset + 1] << 8; 31463a3f90c6SAndrew Thompson if (d->bControlSize > 2) { 31473a3f90c6SAndrew Thompson temp |= d->bmaControls[offset + 2] << 16; 31483a3f90c6SAndrew Thompson if (d->bControlSize > 3) { 31493a3f90c6SAndrew Thompson temp |= d->bmaControls[offset + 3] << 24; 31503a3f90c6SAndrew Thompson } 31513a3f90c6SAndrew Thompson } 31523a3f90c6SAndrew Thompson } 31533a3f90c6SAndrew Thompson } 31543a3f90c6SAndrew Thompson return (temp); 31553a3f90c6SAndrew Thompson } 31563a3f90c6SAndrew Thompson 31573a3f90c6SAndrew Thompson static void 31583a3f90c6SAndrew Thompson uaudio_mixer_add_feature(struct uaudio_softc *sc, 31593a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 31603a3f90c6SAndrew Thompson { 3161e2524b2eSHans Petter Selasky const struct usb_audio_feature_unit *d = iot[id].u.fu_v1; 31623a3f90c6SAndrew Thompson uint32_t fumask; 31633a3f90c6SAndrew Thompson uint32_t mmask; 31643a3f90c6SAndrew Thompson uint32_t cmask; 31653a3f90c6SAndrew Thompson uint16_t mixernumber; 31663a3f90c6SAndrew Thompson uint8_t nchan; 31673a3f90c6SAndrew Thompson uint8_t chan; 31683a3f90c6SAndrew Thompson uint8_t ctl; 31693a3f90c6SAndrew Thompson uint8_t i; 31703a3f90c6SAndrew Thompson 3171902514f6SHans Petter Selasky if (d->bControlSize == 0) 31723a3f90c6SAndrew Thompson return; 3173902514f6SHans Petter Selasky 3174f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 31753a3f90c6SAndrew Thompson 31763a3f90c6SAndrew Thompson nchan = (d->bLength - 7) / d->bControlSize; 31773a3f90c6SAndrew Thompson mmask = uaudio_mixer_feature_get_bmaControls(d, 0); 31783a3f90c6SAndrew Thompson cmask = 0; 31793a3f90c6SAndrew Thompson 3180902514f6SHans Petter Selasky if (nchan == 0) 31813a3f90c6SAndrew Thompson return; 3182902514f6SHans Petter Selasky 31833a3f90c6SAndrew Thompson /* figure out what we can control */ 31843a3f90c6SAndrew Thompson 31853a3f90c6SAndrew Thompson for (chan = 1; chan < nchan; chan++) { 31863a3f90c6SAndrew Thompson DPRINTFN(10, "chan=%d mask=%x\n", 31873a3f90c6SAndrew Thompson chan, uaudio_mixer_feature_get_bmaControls(d, chan)); 31883a3f90c6SAndrew Thompson 31893a3f90c6SAndrew Thompson cmask |= uaudio_mixer_feature_get_bmaControls(d, chan); 31903a3f90c6SAndrew Thompson } 31913a3f90c6SAndrew Thompson 31923a3f90c6SAndrew Thompson if (nchan > MIX_MAX_CHAN) { 31933a3f90c6SAndrew Thompson nchan = MIX_MAX_CHAN; 31943a3f90c6SAndrew Thompson } 3195f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); 31963a3f90c6SAndrew Thompson 3197902514f6SHans Petter Selasky i = d->bmaControls[d->bControlSize]; 3198902514f6SHans Petter Selasky if (i == 0 || 3199902514f6SHans Petter Selasky usbd_req_get_string_any(sc->sc_udev, NULL, 3200f7e62ad0SHans Petter Selasky MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) { 3201f7e62ad0SHans Petter Selasky MIX(sc).desc[0] = 0; 3202902514f6SHans Petter Selasky } 3203902514f6SHans Petter Selasky 32043a3f90c6SAndrew Thompson for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) { 32053a3f90c6SAndrew Thompson 32063a3f90c6SAndrew Thompson fumask = FU_MASK(ctl); 32073a3f90c6SAndrew Thompson 32083a3f90c6SAndrew Thompson DPRINTFN(5, "ctl=%d fumask=0x%04x\n", 32093a3f90c6SAndrew Thompson ctl, fumask); 32103a3f90c6SAndrew Thompson 32113a3f90c6SAndrew Thompson if (mmask & fumask) { 3212f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 3213f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(ctl, 0); 32143a3f90c6SAndrew Thompson } else if (cmask & fumask) { 3215f7e62ad0SHans Petter Selasky MIX(sc).nchan = nchan - 1; 32163a3f90c6SAndrew Thompson for (i = 1; i < nchan; i++) { 32173a3f90c6SAndrew Thompson if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask) 3218f7e62ad0SHans Petter Selasky MIX(sc).wValue[i - 1] = MAKE_WORD(ctl, i); 32193a3f90c6SAndrew Thompson else 3220f7e62ad0SHans Petter Selasky MIX(sc).wValue[i - 1] = -1; 32213a3f90c6SAndrew Thompson } 32223a3f90c6SAndrew Thompson } else { 32233a3f90c6SAndrew Thompson continue; 32243a3f90c6SAndrew Thompson } 32253a3f90c6SAndrew Thompson 3226f7e62ad0SHans Petter Selasky mixernumber = uaudio_mixer_feature_name(&iot[id], &MIX(sc)); 32273a3f90c6SAndrew Thompson 32283a3f90c6SAndrew Thompson switch (ctl) { 32293a3f90c6SAndrew Thompson case MUTE_CONTROL: 3230f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3231f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 3232f7e62ad0SHans Petter Selasky MIX(sc).name = "mute"; 32333a3f90c6SAndrew Thompson break; 32343a3f90c6SAndrew Thompson 32353a3f90c6SAndrew Thompson case VOLUME_CONTROL: 3236f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 3237f7e62ad0SHans Petter Selasky MIX(sc).ctl = mixernumber; 3238f7e62ad0SHans Petter Selasky MIX(sc).name = "vol"; 32393a3f90c6SAndrew Thompson break; 32403a3f90c6SAndrew Thompson 32413a3f90c6SAndrew Thompson case BASS_CONTROL: 3242f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_8; 3243f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_BASS; 3244f7e62ad0SHans Petter Selasky MIX(sc).name = "bass"; 32453a3f90c6SAndrew Thompson break; 32463a3f90c6SAndrew Thompson 32473a3f90c6SAndrew Thompson case MID_CONTROL: 3248f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_8; 3249f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 3250f7e62ad0SHans Petter Selasky MIX(sc).name = "mid"; 32513a3f90c6SAndrew Thompson break; 32523a3f90c6SAndrew Thompson 32533a3f90c6SAndrew Thompson case TREBLE_CONTROL: 3254f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_8; 3255f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_TREBLE; 3256f7e62ad0SHans Petter Selasky MIX(sc).name = "treble"; 32573a3f90c6SAndrew Thompson break; 32583a3f90c6SAndrew Thompson 32593a3f90c6SAndrew Thompson case GRAPHIC_EQUALIZER_CONTROL: 32603a3f90c6SAndrew Thompson continue; /* XXX don't add anything */ 32613a3f90c6SAndrew Thompson 32623a3f90c6SAndrew Thompson case AGC_CONTROL: 3263f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3264f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 3265f7e62ad0SHans Petter Selasky MIX(sc).name = "agc"; 32663a3f90c6SAndrew Thompson break; 32673a3f90c6SAndrew Thompson 32683a3f90c6SAndrew Thompson case DELAY_CONTROL: 3269f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_UNSIGNED_16; 3270f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 3271f7e62ad0SHans Petter Selasky MIX(sc).name = "delay"; 32723a3f90c6SAndrew Thompson break; 32733a3f90c6SAndrew Thompson 32743a3f90c6SAndrew Thompson case BASS_BOOST_CONTROL: 3275f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3276f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 3277f7e62ad0SHans Petter Selasky MIX(sc).name = "boost"; 32783a3f90c6SAndrew Thompson break; 32793a3f90c6SAndrew Thompson 32803a3f90c6SAndrew Thompson case LOUDNESS_CONTROL: 3281f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3282f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ 3283f7e62ad0SHans Petter Selasky MIX(sc).name = "loudness"; 32843a3f90c6SAndrew Thompson break; 32853a3f90c6SAndrew Thompson 32863a3f90c6SAndrew Thompson default: 3287f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_UNKNOWN; 32883a3f90c6SAndrew Thompson break; 32893a3f90c6SAndrew Thompson } 32903a3f90c6SAndrew Thompson 3291f7e62ad0SHans Petter Selasky if (MIX(sc).type != MIX_UNKNOWN) 3292f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 32933a3f90c6SAndrew Thompson } 32943a3f90c6SAndrew Thompson } 3295e2524b2eSHans Petter Selasky 3296e2524b2eSHans Petter Selasky static void 3297e2524b2eSHans Petter Selasky uaudio20_mixer_add_feature(struct uaudio_softc *sc, 3298e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *iot, int id) 3299e2524b2eSHans Petter Selasky { 3300e2524b2eSHans Petter Selasky const struct usb_audio20_feature_unit *d = iot[id].u.fu_v2; 3301e2524b2eSHans Petter Selasky uint32_t ctl; 3302e2524b2eSHans Petter Selasky uint32_t mmask; 3303e2524b2eSHans Petter Selasky uint32_t cmask; 3304e2524b2eSHans Petter Selasky uint16_t mixernumber; 3305e2524b2eSHans Petter Selasky uint8_t nchan; 3306e2524b2eSHans Petter Selasky uint8_t chan; 3307e2524b2eSHans Petter Selasky uint8_t i; 3308e2524b2eSHans Petter Selasky uint8_t what; 3309e2524b2eSHans Petter Selasky 3310e2524b2eSHans Petter Selasky if (UGETDW(d->bmaControls[0]) == 0) 3311e2524b2eSHans Petter Selasky return; 3312e2524b2eSHans Petter Selasky 3313f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 3314e2524b2eSHans Petter Selasky 3315e2524b2eSHans Petter Selasky nchan = (d->bLength - 6) / 4; 3316e2524b2eSHans Petter Selasky mmask = UGETDW(d->bmaControls[0]); 3317e2524b2eSHans Petter Selasky cmask = 0; 3318e2524b2eSHans Petter Selasky 3319e2524b2eSHans Petter Selasky if (nchan == 0) 3320e2524b2eSHans Petter Selasky return; 3321e2524b2eSHans Petter Selasky 3322e2524b2eSHans Petter Selasky /* figure out what we can control */ 3323e2524b2eSHans Petter Selasky 3324e2524b2eSHans Petter Selasky for (chan = 1; chan < nchan; chan++) 3325e2524b2eSHans Petter Selasky cmask |= UGETDW(d->bmaControls[chan]); 3326e2524b2eSHans Petter Selasky 3327e2524b2eSHans Petter Selasky if (nchan > MIX_MAX_CHAN) 3328e2524b2eSHans Petter Selasky nchan = MIX_MAX_CHAN; 3329e2524b2eSHans Petter Selasky 3330f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no); 3331e2524b2eSHans Petter Selasky 3332902514f6SHans Petter Selasky i = d->bmaControls[nchan][0]; 3333902514f6SHans Petter Selasky if (i == 0 || 3334902514f6SHans Petter Selasky usbd_req_get_string_any(sc->sc_udev, NULL, 3335f7e62ad0SHans Petter Selasky MIX(sc).desc, sizeof(MIX(sc).desc), i) != 0) { 3336f7e62ad0SHans Petter Selasky MIX(sc).desc[0] = 0; 3337902514f6SHans Petter Selasky } 3338902514f6SHans Petter Selasky 3339e2524b2eSHans Petter Selasky for (ctl = 3; ctl != 0; ctl <<= 2) { 3340e2524b2eSHans Petter Selasky 3341f7e62ad0SHans Petter Selasky mixernumber = uaudio20_mixer_feature_name(&iot[id], &MIX(sc)); 3342e2524b2eSHans Petter Selasky 3343e2524b2eSHans Petter Selasky switch (ctl) { 3344e2524b2eSHans Petter Selasky case (3 << 0): 3345f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3346f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; 3347f7e62ad0SHans Petter Selasky MIX(sc).name = "mute"; 3348e2524b2eSHans Petter Selasky what = MUTE_CONTROL; 3349e2524b2eSHans Petter Selasky break; 3350e2524b2eSHans Petter Selasky case (3 << 2): 3351f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 3352f7e62ad0SHans Petter Selasky MIX(sc).ctl = mixernumber; 3353f7e62ad0SHans Petter Selasky MIX(sc).name = "vol"; 3354e2524b2eSHans Petter Selasky what = VOLUME_CONTROL; 3355e2524b2eSHans Petter Selasky break; 3356e2524b2eSHans Petter Selasky case (3 << 4): 3357f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_8; 3358f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_BASS; 3359f7e62ad0SHans Petter Selasky MIX(sc).name = "bass"; 3360e2524b2eSHans Petter Selasky what = BASS_CONTROL; 3361e2524b2eSHans Petter Selasky break; 3362e2524b2eSHans Petter Selasky case (3 << 6): 3363f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_8; 3364f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 3365f7e62ad0SHans Petter Selasky MIX(sc).name = "mid"; 3366e2524b2eSHans Petter Selasky what = MID_CONTROL; 3367e2524b2eSHans Petter Selasky break; 3368e2524b2eSHans Petter Selasky case (3 << 8): 3369f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_8; 3370f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_TREBLE; 3371f7e62ad0SHans Petter Selasky MIX(sc).name = "treble"; 3372e2524b2eSHans Petter Selasky what = TREBLE_CONTROL; 3373e2524b2eSHans Petter Selasky break; 3374e2524b2eSHans Petter Selasky case (3 << 12): 3375f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3376f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 3377f7e62ad0SHans Petter Selasky MIX(sc).name = "agc"; 3378e2524b2eSHans Petter Selasky what = AGC_CONTROL; 3379e2524b2eSHans Petter Selasky break; 3380e2524b2eSHans Petter Selasky case (3 << 14): 3381f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_UNSIGNED_16; 3382f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 3383f7e62ad0SHans Petter Selasky MIX(sc).name = "delay"; 3384e2524b2eSHans Petter Selasky what = DELAY_CONTROL; 3385e2524b2eSHans Petter Selasky break; 3386e2524b2eSHans Petter Selasky case (3 << 16): 3387f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3388f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ 3389f7e62ad0SHans Petter Selasky MIX(sc).name = "boost"; 3390e2524b2eSHans Petter Selasky what = BASS_BOOST_CONTROL; 3391e2524b2eSHans Petter Selasky break; 3392e2524b2eSHans Petter Selasky case (3 << 18): 3393f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3394f7e62ad0SHans Petter Selasky MIX(sc).ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ 3395f7e62ad0SHans Petter Selasky MIX(sc).name = "loudness"; 3396e2524b2eSHans Petter Selasky what = LOUDNESS_CONTROL; 3397e2524b2eSHans Petter Selasky break; 3398e2524b2eSHans Petter Selasky case (3 << 20): 3399f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 3400f7e62ad0SHans Petter Selasky MIX(sc).ctl = mixernumber; 3401f7e62ad0SHans Petter Selasky MIX(sc).name = "igain"; 3402e2524b2eSHans Petter Selasky what = INPUT_GAIN_CONTROL; 3403e2524b2eSHans Petter Selasky break; 3404e2524b2eSHans Petter Selasky case (3 << 22): 3405f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_SIGNED_16; 3406f7e62ad0SHans Petter Selasky MIX(sc).ctl = mixernumber; 3407f7e62ad0SHans Petter Selasky MIX(sc).name = "igainpad"; 3408e2524b2eSHans Petter Selasky what = INPUT_GAIN_PAD_CONTROL; 3409e2524b2eSHans Petter Selasky break; 3410e2524b2eSHans Petter Selasky default: 3411e2524b2eSHans Petter Selasky continue; 3412e2524b2eSHans Petter Selasky } 3413e2524b2eSHans Petter Selasky 3414e2524b2eSHans Petter Selasky if ((mmask & ctl) == ctl) { 3415f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 3416f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(what, 0); 3417e2524b2eSHans Petter Selasky } else if ((cmask & ctl) == ctl) { 3418f7e62ad0SHans Petter Selasky MIX(sc).nchan = nchan - 1; 3419e2524b2eSHans Petter Selasky for (i = 1; i < nchan; i++) { 3420e2524b2eSHans Petter Selasky if ((UGETDW(d->bmaControls[i]) & ctl) == ctl) 3421f7e62ad0SHans Petter Selasky MIX(sc).wValue[i - 1] = MAKE_WORD(what, i); 3422e2524b2eSHans Petter Selasky else 3423f7e62ad0SHans Petter Selasky MIX(sc).wValue[i - 1] = -1; 3424e2524b2eSHans Petter Selasky } 3425e2524b2eSHans Petter Selasky } else { 3426e2524b2eSHans Petter Selasky continue; 3427e2524b2eSHans Petter Selasky } 3428e2524b2eSHans Petter Selasky 3429f7e62ad0SHans Petter Selasky if (MIX(sc).type != MIX_UNKNOWN) 3430f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 3431e2524b2eSHans Petter Selasky } 34323a3f90c6SAndrew Thompson } 34333a3f90c6SAndrew Thompson 34343a3f90c6SAndrew Thompson static void 34353a3f90c6SAndrew Thompson uaudio_mixer_add_processing_updown(struct uaudio_softc *sc, 34363a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 34373a3f90c6SAndrew Thompson { 3438e2524b2eSHans Petter Selasky const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1; 34394c21be9bSRebecca Cran const struct usb_audio_processing_unit_1 *d1 = 34403a3f90c6SAndrew Thompson (const void *)(d0->baSourceId + d0->bNrInPins); 34414c21be9bSRebecca Cran const struct usb_audio_processing_unit_updown *ud = 34423a3f90c6SAndrew Thompson (const void *)(d1->bmControls + d1->bControlSize); 34433a3f90c6SAndrew Thompson uint8_t i; 34443a3f90c6SAndrew Thompson 34453a3f90c6SAndrew Thompson if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) { 34463a3f90c6SAndrew Thompson return; 34473a3f90c6SAndrew Thompson } 34483a3f90c6SAndrew Thompson if (uaudio_mixer_verify_desc(d0, sizeof(*ud) + (2 * ud->bNrModes)) 34493a3f90c6SAndrew Thompson == NULL) { 34503a3f90c6SAndrew Thompson return; 34513a3f90c6SAndrew Thompson } 34523a3f90c6SAndrew Thompson DPRINTFN(3, "bUnitId=%d bNrModes=%d\n", 34533a3f90c6SAndrew Thompson d0->bUnitId, ud->bNrModes); 34543a3f90c6SAndrew Thompson 34553a3f90c6SAndrew Thompson if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) { 34563a3f90c6SAndrew Thompson DPRINTF("no mode select\n"); 34573a3f90c6SAndrew Thompson return; 34583a3f90c6SAndrew Thompson } 3459f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 34603a3f90c6SAndrew Thompson 3461f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 3462f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 3463f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0); 3464f7e62ad0SHans Petter Selasky uaudio_mixer_determine_class(&iot[id], &MIX(sc)); 3465f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; /* XXX */ 34663a3f90c6SAndrew Thompson 34673a3f90c6SAndrew Thompson for (i = 0; i < ud->bNrModes; i++) { 34683a3f90c6SAndrew Thompson DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i])); 34693a3f90c6SAndrew Thompson /* XXX */ 34703a3f90c6SAndrew Thompson } 34713a3f90c6SAndrew Thompson 3472f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 34733a3f90c6SAndrew Thompson } 34743a3f90c6SAndrew Thompson 34753a3f90c6SAndrew Thompson static void 34763a3f90c6SAndrew Thompson uaudio_mixer_add_processing(struct uaudio_softc *sc, 34773a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 34783a3f90c6SAndrew Thompson { 3479e2524b2eSHans Petter Selasky const struct usb_audio_processing_unit_0 *d0 = iot[id].u.pu_v1; 34804c21be9bSRebecca Cran const struct usb_audio_processing_unit_1 *d1 = 34813a3f90c6SAndrew Thompson (const void *)(d0->baSourceId + d0->bNrInPins); 34823a3f90c6SAndrew Thompson uint16_t ptype; 34833a3f90c6SAndrew Thompson 3484f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 34853a3f90c6SAndrew Thompson 34863a3f90c6SAndrew Thompson ptype = UGETW(d0->wProcessType); 34873a3f90c6SAndrew Thompson 34883a3f90c6SAndrew Thompson DPRINTFN(3, "wProcessType=%d bUnitId=%d " 34893a3f90c6SAndrew Thompson "bNrInPins=%d\n", ptype, d0->bUnitId, d0->bNrInPins); 34903a3f90c6SAndrew Thompson 34913a3f90c6SAndrew Thompson if (d1->bControlSize == 0) { 34923a3f90c6SAndrew Thompson return; 34933a3f90c6SAndrew Thompson } 34943a3f90c6SAndrew Thompson if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) { 3495f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 3496f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 3497f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0); 3498f7e62ad0SHans Petter Selasky uaudio_mixer_determine_class(&iot[id], &MIX(sc)); 3499f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 3500f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 35013a3f90c6SAndrew Thompson } 35023a3f90c6SAndrew Thompson switch (ptype) { 35033a3f90c6SAndrew Thompson case UPDOWNMIX_PROCESS: 35043a3f90c6SAndrew Thompson uaudio_mixer_add_processing_updown(sc, iot, id); 35053a3f90c6SAndrew Thompson break; 35063a3f90c6SAndrew Thompson 35073a3f90c6SAndrew Thompson case DOLBY_PROLOGIC_PROCESS: 35083a3f90c6SAndrew Thompson case P3D_STEREO_EXTENDER_PROCESS: 35093a3f90c6SAndrew Thompson case REVERBATION_PROCESS: 35103a3f90c6SAndrew Thompson case CHORUS_PROCESS: 35113a3f90c6SAndrew Thompson case DYN_RANGE_COMP_PROCESS: 35123a3f90c6SAndrew Thompson default: 35133a3f90c6SAndrew Thompson DPRINTF("unit %d, type=%d is not implemented\n", 35143a3f90c6SAndrew Thompson d0->bUnitId, ptype); 35153a3f90c6SAndrew Thompson break; 35163a3f90c6SAndrew Thompson } 35173a3f90c6SAndrew Thompson } 35183a3f90c6SAndrew Thompson 35193a3f90c6SAndrew Thompson static void 35203a3f90c6SAndrew Thompson uaudio_mixer_add_extension(struct uaudio_softc *sc, 35213a3f90c6SAndrew Thompson const struct uaudio_terminal_node *iot, int id) 35223a3f90c6SAndrew Thompson { 3523e2524b2eSHans Petter Selasky const struct usb_audio_extension_unit_0 *d0 = iot[id].u.eu_v1; 35244c21be9bSRebecca Cran const struct usb_audio_extension_unit_1 *d1 = 35253a3f90c6SAndrew Thompson (const void *)(d0->baSourceId + d0->bNrInPins); 35263a3f90c6SAndrew Thompson 35273a3f90c6SAndrew Thompson DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n", 35283a3f90c6SAndrew Thompson d0->bUnitId, d0->bNrInPins); 35293a3f90c6SAndrew Thompson 35303a3f90c6SAndrew Thompson if (sc->sc_uq_au_no_xu) { 35313a3f90c6SAndrew Thompson return; 35323a3f90c6SAndrew Thompson } 35333a3f90c6SAndrew Thompson if (d1->bControlSize == 0) { 35343a3f90c6SAndrew Thompson return; 35353a3f90c6SAndrew Thompson } 35363a3f90c6SAndrew Thompson if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) { 35373a3f90c6SAndrew Thompson 3538f7e62ad0SHans Petter Selasky memset(&MIX(sc), 0, sizeof(MIX(sc))); 35393a3f90c6SAndrew Thompson 3540f7e62ad0SHans Petter Selasky MIX(sc).wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no); 3541f7e62ad0SHans Petter Selasky MIX(sc).nchan = 1; 3542f7e62ad0SHans Petter Selasky MIX(sc).wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0); 3543f7e62ad0SHans Petter Selasky uaudio_mixer_determine_class(&iot[id], &MIX(sc)); 3544f7e62ad0SHans Petter Selasky MIX(sc).type = MIX_ON_OFF; 35453a3f90c6SAndrew Thompson 3546f7e62ad0SHans Petter Selasky uaudio_mixer_add_ctl(sc, &MIX(sc)); 35473a3f90c6SAndrew Thompson } 35483a3f90c6SAndrew Thompson } 35493a3f90c6SAndrew Thompson 35503a3f90c6SAndrew Thompson static const void * 35513a3f90c6SAndrew Thompson uaudio_mixer_verify_desc(const void *arg, uint32_t len) 35523a3f90c6SAndrew Thompson { 35534c21be9bSRebecca Cran const struct usb_audio_mixer_unit_1 *d1; 35544c21be9bSRebecca Cran const struct usb_audio_extension_unit_1 *e1; 35554c21be9bSRebecca Cran const struct usb_audio_processing_unit_1 *u1; 35563a3f90c6SAndrew Thompson 35573a3f90c6SAndrew Thompson union { 3558760bc48eSAndrew Thompson const struct usb_descriptor *desc; 35594c21be9bSRebecca Cran const struct usb_audio_input_terminal *it; 35604c21be9bSRebecca Cran const struct usb_audio_output_terminal *ot; 35614c21be9bSRebecca Cran const struct usb_audio_mixer_unit_0 *mu; 35624c21be9bSRebecca Cran const struct usb_audio_selector_unit *su; 35634c21be9bSRebecca Cran const struct usb_audio_feature_unit *fu; 35644c21be9bSRebecca Cran const struct usb_audio_processing_unit_0 *pu; 35654c21be9bSRebecca Cran const struct usb_audio_extension_unit_0 *eu; 35663a3f90c6SAndrew Thompson } u; 35673a3f90c6SAndrew Thompson 35683a3f90c6SAndrew Thompson u.desc = arg; 35693a3f90c6SAndrew Thompson 35703a3f90c6SAndrew Thompson if (u.desc == NULL) { 35713a3f90c6SAndrew Thompson goto error; 35723a3f90c6SAndrew Thompson } 35733a3f90c6SAndrew Thompson if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) { 35743a3f90c6SAndrew Thompson goto error; 35753a3f90c6SAndrew Thompson } 35763a3f90c6SAndrew Thompson switch (u.desc->bDescriptorSubtype) { 35773a3f90c6SAndrew Thompson case UDESCSUB_AC_INPUT: 35783a3f90c6SAndrew Thompson len += sizeof(*u.it); 35793a3f90c6SAndrew Thompson break; 35803a3f90c6SAndrew Thompson 35813a3f90c6SAndrew Thompson case UDESCSUB_AC_OUTPUT: 35823a3f90c6SAndrew Thompson len += sizeof(*u.ot); 35833a3f90c6SAndrew Thompson break; 35843a3f90c6SAndrew Thompson 35853a3f90c6SAndrew Thompson case UDESCSUB_AC_MIXER: 35863a3f90c6SAndrew Thompson len += sizeof(*u.mu); 35873a3f90c6SAndrew Thompson 35883a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 35893a3f90c6SAndrew Thompson goto error; 35903a3f90c6SAndrew Thompson } 35913a3f90c6SAndrew Thompson len += u.mu->bNrInPins; 35923a3f90c6SAndrew Thompson 35933a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 35943a3f90c6SAndrew Thompson goto error; 35953a3f90c6SAndrew Thompson } 35963a3f90c6SAndrew Thompson d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins); 35973a3f90c6SAndrew Thompson 35983a3f90c6SAndrew Thompson len += sizeof(*d1); 35993a3f90c6SAndrew Thompson break; 36003a3f90c6SAndrew Thompson 36013a3f90c6SAndrew Thompson case UDESCSUB_AC_SELECTOR: 36023a3f90c6SAndrew Thompson len += sizeof(*u.su); 36033a3f90c6SAndrew Thompson 36043a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 36053a3f90c6SAndrew Thompson goto error; 36063a3f90c6SAndrew Thompson } 3607902514f6SHans Petter Selasky len += u.su->bNrInPins + 1; 36083a3f90c6SAndrew Thompson break; 36093a3f90c6SAndrew Thompson 36103a3f90c6SAndrew Thompson case UDESCSUB_AC_FEATURE: 3611902514f6SHans Petter Selasky len += sizeof(*u.fu) + 1; 3612902514f6SHans Petter Selasky 3613902514f6SHans Petter Selasky if (u.desc->bLength < len) 3614902514f6SHans Petter Selasky goto error; 3615902514f6SHans Petter Selasky 3616902514f6SHans Petter Selasky len += u.fu->bControlSize; 36173a3f90c6SAndrew Thompson break; 36183a3f90c6SAndrew Thompson 36193a3f90c6SAndrew Thompson case UDESCSUB_AC_PROCESSING: 36203a3f90c6SAndrew Thompson len += sizeof(*u.pu); 36213a3f90c6SAndrew Thompson 36223a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 36233a3f90c6SAndrew Thompson goto error; 36243a3f90c6SAndrew Thompson } 36253a3f90c6SAndrew Thompson len += u.pu->bNrInPins; 36263a3f90c6SAndrew Thompson 36273a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 36283a3f90c6SAndrew Thompson goto error; 36293a3f90c6SAndrew Thompson } 36303a3f90c6SAndrew Thompson u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins); 36313a3f90c6SAndrew Thompson 36323a3f90c6SAndrew Thompson len += sizeof(*u1); 36333a3f90c6SAndrew Thompson 36343a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 36353a3f90c6SAndrew Thompson goto error; 36363a3f90c6SAndrew Thompson } 36373a3f90c6SAndrew Thompson len += u1->bControlSize; 36383a3f90c6SAndrew Thompson 36393a3f90c6SAndrew Thompson break; 36403a3f90c6SAndrew Thompson 36413a3f90c6SAndrew Thompson case UDESCSUB_AC_EXTENSION: 36423a3f90c6SAndrew Thompson len += sizeof(*u.eu); 36433a3f90c6SAndrew Thompson 36443a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 36453a3f90c6SAndrew Thompson goto error; 36463a3f90c6SAndrew Thompson } 36473a3f90c6SAndrew Thompson len += u.eu->bNrInPins; 36483a3f90c6SAndrew Thompson 36493a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 36503a3f90c6SAndrew Thompson goto error; 36513a3f90c6SAndrew Thompson } 36523a3f90c6SAndrew Thompson e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins); 36533a3f90c6SAndrew Thompson 36543a3f90c6SAndrew Thompson len += sizeof(*e1); 36553a3f90c6SAndrew Thompson 36563a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 36573a3f90c6SAndrew Thompson goto error; 36583a3f90c6SAndrew Thompson } 36593a3f90c6SAndrew Thompson len += e1->bControlSize; 36603a3f90c6SAndrew Thompson break; 36613a3f90c6SAndrew Thompson 36623a3f90c6SAndrew Thompson default: 36633a3f90c6SAndrew Thompson goto error; 36643a3f90c6SAndrew Thompson } 36653a3f90c6SAndrew Thompson 36663a3f90c6SAndrew Thompson if (u.desc->bLength < len) { 36673a3f90c6SAndrew Thompson goto error; 36683a3f90c6SAndrew Thompson } 36693a3f90c6SAndrew Thompson return (u.desc); 36703a3f90c6SAndrew Thompson 36713a3f90c6SAndrew Thompson error: 36723a3f90c6SAndrew Thompson if (u.desc) { 36733a3f90c6SAndrew Thompson DPRINTF("invalid descriptor, type=%d, " 36743a3f90c6SAndrew Thompson "sub_type=%d, len=%d of %d bytes\n", 36753a3f90c6SAndrew Thompson u.desc->bDescriptorType, 36763a3f90c6SAndrew Thompson u.desc->bDescriptorSubtype, 36773a3f90c6SAndrew Thompson u.desc->bLength, len); 36783a3f90c6SAndrew Thompson } 36793a3f90c6SAndrew Thompson return (NULL); 36803a3f90c6SAndrew Thompson } 36813a3f90c6SAndrew Thompson 3682e2524b2eSHans Petter Selasky static const void * 3683e2524b2eSHans Petter Selasky uaudio20_mixer_verify_desc(const void *arg, uint32_t len) 36843a3f90c6SAndrew Thompson { 3685e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_1 *d1; 3686e2524b2eSHans Petter Selasky const struct usb_audio20_extension_unit_1 *e1; 3687e2524b2eSHans Petter Selasky const struct usb_audio20_processing_unit_1 *u1; 3688e2524b2eSHans Petter Selasky const struct usb_audio20_clock_selector_unit_1 *c1; 36893a3f90c6SAndrew Thompson 3690e2524b2eSHans Petter Selasky union { 3691e2524b2eSHans Petter Selasky const struct usb_descriptor *desc; 3692e2524b2eSHans Petter Selasky const struct usb_audio20_clock_source_unit *csrc; 3693e2524b2eSHans Petter Selasky const struct usb_audio20_clock_selector_unit_0 *csel; 3694e2524b2eSHans Petter Selasky const struct usb_audio20_clock_multiplier_unit *cmul; 3695e2524b2eSHans Petter Selasky const struct usb_audio20_input_terminal *it; 3696e2524b2eSHans Petter Selasky const struct usb_audio20_output_terminal *ot; 3697e2524b2eSHans Petter Selasky const struct usb_audio20_mixer_unit_0 *mu; 3698e2524b2eSHans Petter Selasky const struct usb_audio20_selector_unit *su; 3699e2524b2eSHans Petter Selasky const struct usb_audio20_feature_unit *fu; 3700e2524b2eSHans Petter Selasky const struct usb_audio20_sample_rate_unit *ru; 3701e2524b2eSHans Petter Selasky const struct usb_audio20_processing_unit_0 *pu; 3702e2524b2eSHans Petter Selasky const struct usb_audio20_extension_unit_0 *eu; 3703e2524b2eSHans Petter Selasky const struct usb_audio20_effect_unit *ef; 3704e2524b2eSHans Petter Selasky } u; 37053a3f90c6SAndrew Thompson 3706e2524b2eSHans Petter Selasky u.desc = arg; 37073a3f90c6SAndrew Thompson 3708e2524b2eSHans Petter Selasky if (u.desc == NULL) 3709e2524b2eSHans Petter Selasky goto error; 3710e2524b2eSHans Petter Selasky 3711e2524b2eSHans Petter Selasky if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) 3712e2524b2eSHans Petter Selasky goto error; 3713e2524b2eSHans Petter Selasky 3714e2524b2eSHans Petter Selasky switch (u.desc->bDescriptorSubtype) { 3715e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 3716e2524b2eSHans Petter Selasky len += sizeof(*u.it); 3717e2524b2eSHans Petter Selasky break; 3718e2524b2eSHans Petter Selasky 3719e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 3720e2524b2eSHans Petter Selasky len += sizeof(*u.ot); 3721e2524b2eSHans Petter Selasky break; 3722e2524b2eSHans Petter Selasky 3723e2524b2eSHans Petter Selasky case UDESCSUB_AC_MIXER: 3724e2524b2eSHans Petter Selasky len += sizeof(*u.mu); 3725e2524b2eSHans Petter Selasky 3726e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3727e2524b2eSHans Petter Selasky goto error; 3728e2524b2eSHans Petter Selasky len += u.mu->bNrInPins; 3729e2524b2eSHans Petter Selasky 3730e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3731e2524b2eSHans Petter Selasky goto error; 3732e2524b2eSHans Petter Selasky 3733e2524b2eSHans Petter Selasky d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins); 3734e2524b2eSHans Petter Selasky 3735e2524b2eSHans Petter Selasky len += sizeof(*d1) + d1->bNrChannels; 3736e2524b2eSHans Petter Selasky break; 3737e2524b2eSHans Petter Selasky 3738e2524b2eSHans Petter Selasky case UDESCSUB_AC_SELECTOR: 3739e2524b2eSHans Petter Selasky len += sizeof(*u.su); 3740e2524b2eSHans Petter Selasky 3741e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3742e2524b2eSHans Petter Selasky goto error; 3743e2524b2eSHans Petter Selasky 3744902514f6SHans Petter Selasky len += u.su->bNrInPins + 1; 3745e2524b2eSHans Petter Selasky break; 3746e2524b2eSHans Petter Selasky 3747e2524b2eSHans Petter Selasky case UDESCSUB_AC_FEATURE: 3748e2524b2eSHans Petter Selasky len += sizeof(*u.fu) + 1; 3749e2524b2eSHans Petter Selasky break; 3750e2524b2eSHans Petter Selasky 3751e2524b2eSHans Petter Selasky case UDESCSUB_AC_EFFECT: 3752e2524b2eSHans Petter Selasky len += sizeof(*u.ef) + 4; 3753e2524b2eSHans Petter Selasky break; 3754e2524b2eSHans Petter Selasky 3755e2524b2eSHans Petter Selasky case UDESCSUB_AC_PROCESSING_V2: 3756e2524b2eSHans Petter Selasky len += sizeof(*u.pu); 3757e2524b2eSHans Petter Selasky 3758e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3759e2524b2eSHans Petter Selasky goto error; 3760e2524b2eSHans Petter Selasky 3761e2524b2eSHans Petter Selasky len += u.pu->bNrInPins; 3762e2524b2eSHans Petter Selasky 3763e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3764e2524b2eSHans Petter Selasky goto error; 3765e2524b2eSHans Petter Selasky 3766e2524b2eSHans Petter Selasky u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins); 3767e2524b2eSHans Petter Selasky 3768e2524b2eSHans Petter Selasky len += sizeof(*u1); 3769e2524b2eSHans Petter Selasky break; 3770e2524b2eSHans Petter Selasky 3771e2524b2eSHans Petter Selasky case UDESCSUB_AC_EXTENSION_V2: 3772e2524b2eSHans Petter Selasky len += sizeof(*u.eu); 3773e2524b2eSHans Petter Selasky 3774e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3775e2524b2eSHans Petter Selasky goto error; 3776e2524b2eSHans Petter Selasky 3777e2524b2eSHans Petter Selasky len += u.eu->bNrInPins; 3778e2524b2eSHans Petter Selasky 3779e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3780e2524b2eSHans Petter Selasky goto error; 3781e2524b2eSHans Petter Selasky 3782e2524b2eSHans Petter Selasky e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins); 3783e2524b2eSHans Petter Selasky 3784e2524b2eSHans Petter Selasky len += sizeof(*e1); 3785e2524b2eSHans Petter Selasky break; 3786e2524b2eSHans Petter Selasky 3787e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SRC: 3788e2524b2eSHans Petter Selasky len += sizeof(*u.csrc); 3789e2524b2eSHans Petter Selasky break; 3790e2524b2eSHans Petter Selasky 3791e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SEL: 3792e2524b2eSHans Petter Selasky len += sizeof(*u.csel); 3793e2524b2eSHans Petter Selasky 3794e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3795e2524b2eSHans Petter Selasky goto error; 3796e2524b2eSHans Petter Selasky 3797e2524b2eSHans Petter Selasky len += u.csel->bNrInPins; 3798e2524b2eSHans Petter Selasky 3799e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3800e2524b2eSHans Petter Selasky goto error; 3801e2524b2eSHans Petter Selasky 3802e2524b2eSHans Petter Selasky c1 = (const void *)(u.csel->baCSourceId + u.csel->bNrInPins); 3803e2524b2eSHans Petter Selasky 3804e2524b2eSHans Petter Selasky len += sizeof(*c1); 3805e2524b2eSHans Petter Selasky break; 3806e2524b2eSHans Petter Selasky 3807e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_MUL: 3808e2524b2eSHans Petter Selasky len += sizeof(*u.cmul); 3809e2524b2eSHans Petter Selasky break; 3810e2524b2eSHans Petter Selasky 3811e2524b2eSHans Petter Selasky case UDESCSUB_AC_SAMPLE_RT: 3812e2524b2eSHans Petter Selasky len += sizeof(*u.ru); 3813e2524b2eSHans Petter Selasky break; 3814e2524b2eSHans Petter Selasky 3815e2524b2eSHans Petter Selasky default: 3816e2524b2eSHans Petter Selasky goto error; 38173a3f90c6SAndrew Thompson } 38183a3f90c6SAndrew Thompson 3819e2524b2eSHans Petter Selasky if (u.desc->bLength < len) 3820e2524b2eSHans Petter Selasky goto error; 3821e2524b2eSHans Petter Selasky 3822e2524b2eSHans Petter Selasky return (u.desc); 3823e2524b2eSHans Petter Selasky 3824e2524b2eSHans Petter Selasky error: 3825e2524b2eSHans Petter Selasky if (u.desc) { 3826e2524b2eSHans Petter Selasky DPRINTF("invalid descriptor, type=%d, " 3827e2524b2eSHans Petter Selasky "sub_type=%d, len=%d of %d bytes\n", 3828e2524b2eSHans Petter Selasky u.desc->bDescriptorType, 3829e2524b2eSHans Petter Selasky u.desc->bDescriptorSubtype, 3830e2524b2eSHans Petter Selasky u.desc->bLength, len); 3831e2524b2eSHans Petter Selasky } 3832e2524b2eSHans Petter Selasky return (NULL); 3833e2524b2eSHans Petter Selasky } 38343a3f90c6SAndrew Thompson 38354c21be9bSRebecca Cran static struct usb_audio_cluster 38363a3f90c6SAndrew Thompson uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) 38373a3f90c6SAndrew Thompson { 38384c21be9bSRebecca Cran struct usb_audio_cluster r; 3839760bc48eSAndrew Thompson const struct usb_descriptor *dp; 38403a3f90c6SAndrew Thompson uint8_t i; 38413a3f90c6SAndrew Thompson 38423a3f90c6SAndrew Thompson for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) { /* avoid infinite loops */ 38433a3f90c6SAndrew Thompson dp = iot[id].u.desc; 38443a3f90c6SAndrew Thompson if (dp == NULL) { 38453a3f90c6SAndrew Thompson goto error; 38463a3f90c6SAndrew Thompson } 38473a3f90c6SAndrew Thompson switch (dp->bDescriptorSubtype) { 38483a3f90c6SAndrew Thompson case UDESCSUB_AC_INPUT: 3849e2524b2eSHans Petter Selasky r.bNrChannels = iot[id].u.it_v1->bNrChannels; 3850e2524b2eSHans Petter Selasky r.wChannelConfig[0] = iot[id].u.it_v1->wChannelConfig[0]; 3851e2524b2eSHans Petter Selasky r.wChannelConfig[1] = iot[id].u.it_v1->wChannelConfig[1]; 3852e2524b2eSHans Petter Selasky r.iChannelNames = iot[id].u.it_v1->iChannelNames; 38533a3f90c6SAndrew Thompson goto done; 38543a3f90c6SAndrew Thompson 38553a3f90c6SAndrew Thompson case UDESCSUB_AC_OUTPUT: 3856e2524b2eSHans Petter Selasky id = iot[id].u.ot_v1->bSourceId; 38573a3f90c6SAndrew Thompson break; 38583a3f90c6SAndrew Thompson 38593a3f90c6SAndrew Thompson case UDESCSUB_AC_MIXER: 38604c21be9bSRebecca Cran r = *(const struct usb_audio_cluster *) 3861e2524b2eSHans Petter Selasky &iot[id].u.mu_v1->baSourceId[ 3862e2524b2eSHans Petter Selasky iot[id].u.mu_v1->bNrInPins]; 38633a3f90c6SAndrew Thompson goto done; 38643a3f90c6SAndrew Thompson 38653a3f90c6SAndrew Thompson case UDESCSUB_AC_SELECTOR: 3866e2524b2eSHans Petter Selasky if (iot[id].u.su_v1->bNrInPins > 0) { 38673a3f90c6SAndrew Thompson /* XXX This is not really right */ 3868e2524b2eSHans Petter Selasky id = iot[id].u.su_v1->baSourceId[0]; 38693a3f90c6SAndrew Thompson } 38703a3f90c6SAndrew Thompson break; 38713a3f90c6SAndrew Thompson 38723a3f90c6SAndrew Thompson case UDESCSUB_AC_FEATURE: 3873e2524b2eSHans Petter Selasky id = iot[id].u.fu_v1->bSourceId; 38743a3f90c6SAndrew Thompson break; 38753a3f90c6SAndrew Thompson 38763a3f90c6SAndrew Thompson case UDESCSUB_AC_PROCESSING: 38774c21be9bSRebecca Cran r = *((const struct usb_audio_cluster *) 3878e2524b2eSHans Petter Selasky &iot[id].u.pu_v1->baSourceId[ 3879e2524b2eSHans Petter Selasky iot[id].u.pu_v1->bNrInPins]); 38803a3f90c6SAndrew Thompson goto done; 38813a3f90c6SAndrew Thompson 38823a3f90c6SAndrew Thompson case UDESCSUB_AC_EXTENSION: 38834c21be9bSRebecca Cran r = *((const struct usb_audio_cluster *) 3884e2524b2eSHans Petter Selasky &iot[id].u.eu_v1->baSourceId[ 3885e2524b2eSHans Petter Selasky iot[id].u.eu_v1->bNrInPins]); 38863a3f90c6SAndrew Thompson goto done; 38873a3f90c6SAndrew Thompson 38883a3f90c6SAndrew Thompson default: 38893a3f90c6SAndrew Thompson goto error; 38903a3f90c6SAndrew Thompson } 38913a3f90c6SAndrew Thompson } 38923a3f90c6SAndrew Thompson error: 38933a3f90c6SAndrew Thompson DPRINTF("bad data\n"); 38946f068a43SHans Petter Selasky memset(&r, 0, sizeof(r)); 38953a3f90c6SAndrew Thompson done: 38963a3f90c6SAndrew Thompson return (r); 38973a3f90c6SAndrew Thompson } 38983a3f90c6SAndrew Thompson 3899e2524b2eSHans Petter Selasky static struct usb_audio20_cluster 3900e2524b2eSHans Petter Selasky uaudio20_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot) 39013a3f90c6SAndrew Thompson { 3902e2524b2eSHans Petter Selasky struct usb_audio20_cluster r; 3903e2524b2eSHans Petter Selasky const struct usb_descriptor *dp; 3904e2524b2eSHans Petter Selasky uint8_t i; 39053a3f90c6SAndrew Thompson 3906e2524b2eSHans Petter Selasky for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) { /* avoid infinite loops */ 3907e2524b2eSHans Petter Selasky dp = iot[id].u.desc; 3908e2524b2eSHans Petter Selasky if (dp == NULL) 3909e2524b2eSHans Petter Selasky goto error; 3910e2524b2eSHans Petter Selasky 3911e2524b2eSHans Petter Selasky switch (dp->bDescriptorSubtype) { 3912e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 3913e2524b2eSHans Petter Selasky r.bNrChannels = iot[id].u.it_v2->bNrChannels; 3914e2524b2eSHans Petter Selasky r.bmChannelConfig[0] = iot[id].u.it_v2->bmChannelConfig[0]; 3915e2524b2eSHans Petter Selasky r.bmChannelConfig[1] = iot[id].u.it_v2->bmChannelConfig[1]; 3916e2524b2eSHans Petter Selasky r.bmChannelConfig[2] = iot[id].u.it_v2->bmChannelConfig[2]; 3917e2524b2eSHans Petter Selasky r.bmChannelConfig[3] = iot[id].u.it_v2->bmChannelConfig[3]; 3918e2524b2eSHans Petter Selasky r.iChannelNames = iot[id].u.it_v2->iTerminal; 3919e2524b2eSHans Petter Selasky goto done; 3920e2524b2eSHans Petter Selasky 3921e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 3922e2524b2eSHans Petter Selasky id = iot[id].u.ot_v2->bSourceId; 39233a3f90c6SAndrew Thompson break; 39243a3f90c6SAndrew Thompson 3925e2524b2eSHans Petter Selasky case UDESCSUB_AC_MIXER: 3926e2524b2eSHans Petter Selasky r = *(const struct usb_audio20_cluster *) 3927e2524b2eSHans Petter Selasky &iot[id].u.mu_v2->baSourceId[ 3928e2524b2eSHans Petter Selasky iot[id].u.mu_v2->bNrInPins]; 3929e2524b2eSHans Petter Selasky goto done; 3930e2524b2eSHans Petter Selasky 3931e2524b2eSHans Petter Selasky case UDESCSUB_AC_SELECTOR: 3932e2524b2eSHans Petter Selasky if (iot[id].u.su_v2->bNrInPins > 0) { 3933e2524b2eSHans Petter Selasky /* XXX This is not really right */ 3934e2524b2eSHans Petter Selasky id = iot[id].u.su_v2->baSourceId[0]; 3935e2524b2eSHans Petter Selasky } 3936e2524b2eSHans Petter Selasky break; 3937e2524b2eSHans Petter Selasky 3938e2524b2eSHans Petter Selasky case UDESCSUB_AC_SAMPLE_RT: 3939e2524b2eSHans Petter Selasky id = iot[id].u.ru_v2->bSourceId; 3940e2524b2eSHans Petter Selasky break; 3941e2524b2eSHans Petter Selasky 3942e2524b2eSHans Petter Selasky case UDESCSUB_AC_EFFECT: 3943e2524b2eSHans Petter Selasky id = iot[id].u.ef_v2->bSourceId; 3944e2524b2eSHans Petter Selasky break; 3945e2524b2eSHans Petter Selasky 3946e2524b2eSHans Petter Selasky case UDESCSUB_AC_FEATURE: 3947e2524b2eSHans Petter Selasky id = iot[id].u.fu_v2->bSourceId; 3948e2524b2eSHans Petter Selasky break; 3949e2524b2eSHans Petter Selasky 3950e2524b2eSHans Petter Selasky case UDESCSUB_AC_PROCESSING_V2: 3951e2524b2eSHans Petter Selasky r = *((const struct usb_audio20_cluster *) 3952e2524b2eSHans Petter Selasky &iot[id].u.pu_v2->baSourceId[ 3953e2524b2eSHans Petter Selasky iot[id].u.pu_v2->bNrInPins]); 3954e2524b2eSHans Petter Selasky goto done; 3955e2524b2eSHans Petter Selasky 3956e2524b2eSHans Petter Selasky case UDESCSUB_AC_EXTENSION_V2: 3957e2524b2eSHans Petter Selasky r = *((const struct usb_audio20_cluster *) 3958e2524b2eSHans Petter Selasky &iot[id].u.eu_v2->baSourceId[ 3959e2524b2eSHans Petter Selasky iot[id].u.eu_v2->bNrInPins]); 3960e2524b2eSHans Petter Selasky goto done; 3961e2524b2eSHans Petter Selasky 3962e2524b2eSHans Petter Selasky default: 3963e2524b2eSHans Petter Selasky goto error; 3964e2524b2eSHans Petter Selasky } 3965e2524b2eSHans Petter Selasky } 3966e2524b2eSHans Petter Selasky error: 3967e2524b2eSHans Petter Selasky DPRINTF("Bad data!\n"); 3968e2524b2eSHans Petter Selasky memset(&r, 0, sizeof(r)); 3969e2524b2eSHans Petter Selasky done: 3970e2524b2eSHans Petter Selasky return (r); 3971e2524b2eSHans Petter Selasky } 39723a3f90c6SAndrew Thompson 39733a3f90c6SAndrew Thompson static uint16_t 39743a3f90c6SAndrew Thompson uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot, 39753a3f90c6SAndrew Thompson struct uaudio_mixer_node *mix) 39763a3f90c6SAndrew Thompson { 39773a3f90c6SAndrew Thompson uint16_t terminal_type = 0x0000; 39783a3f90c6SAndrew Thompson const struct uaudio_terminal_node *input[2]; 39793a3f90c6SAndrew Thompson const struct uaudio_terminal_node *output[2]; 39803a3f90c6SAndrew Thompson 39813a3f90c6SAndrew Thompson input[0] = uaudio_mixer_get_input(iot, 0); 39823a3f90c6SAndrew Thompson input[1] = uaudio_mixer_get_input(iot, 1); 39833a3f90c6SAndrew Thompson 39843a3f90c6SAndrew Thompson output[0] = uaudio_mixer_get_output(iot, 0); 39853a3f90c6SAndrew Thompson output[1] = uaudio_mixer_get_output(iot, 1); 39863a3f90c6SAndrew Thompson 39873a3f90c6SAndrew Thompson /* 39883a3f90c6SAndrew Thompson * check if there is only 39893a3f90c6SAndrew Thompson * one output terminal: 39903a3f90c6SAndrew Thompson */ 39913a3f90c6SAndrew Thompson if (output[0] && (!output[1])) { 3992e2524b2eSHans Petter Selasky terminal_type = 3993e2524b2eSHans Petter Selasky UGETW(output[0]->u.ot_v1->wTerminalType); 39943a3f90c6SAndrew Thompson } 39953a3f90c6SAndrew Thompson /* 39963a3f90c6SAndrew Thompson * If the only output terminal is USB, 39973a3f90c6SAndrew Thompson * the class is UAC_RECORD. 39983a3f90c6SAndrew Thompson */ 39993a3f90c6SAndrew Thompson if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { 40003a3f90c6SAndrew Thompson 40013a3f90c6SAndrew Thompson mix->class = UAC_RECORD; 40023a3f90c6SAndrew Thompson if (input[0] && (!input[1])) { 4003e2524b2eSHans Petter Selasky terminal_type = 4004e2524b2eSHans Petter Selasky UGETW(input[0]->u.it_v1->wTerminalType); 40053a3f90c6SAndrew Thompson } else { 40063a3f90c6SAndrew Thompson terminal_type = 0; 40073a3f90c6SAndrew Thompson } 40083a3f90c6SAndrew Thompson goto done; 40093a3f90c6SAndrew Thompson } 40103a3f90c6SAndrew Thompson /* 40113a3f90c6SAndrew Thompson * if the unit is connected to just 40123a3f90c6SAndrew Thompson * one input terminal, the 40133a3f90c6SAndrew Thompson * class is UAC_INPUT: 40143a3f90c6SAndrew Thompson */ 40153a3f90c6SAndrew Thompson if (input[0] && (!input[1])) { 40163a3f90c6SAndrew Thompson mix->class = UAC_INPUT; 4017e2524b2eSHans Petter Selasky terminal_type = 4018e2524b2eSHans Petter Selasky UGETW(input[0]->u.it_v1->wTerminalType); 4019e2524b2eSHans Petter Selasky goto done; 4020e2524b2eSHans Petter Selasky } 4021e2524b2eSHans Petter Selasky /* 4022e2524b2eSHans Petter Selasky * Otherwise, the class is UAC_OUTPUT. 4023e2524b2eSHans Petter Selasky */ 4024e2524b2eSHans Petter Selasky mix->class = UAC_OUTPUT; 4025e2524b2eSHans Petter Selasky done: 4026e2524b2eSHans Petter Selasky return (terminal_type); 4027e2524b2eSHans Petter Selasky } 4028e2524b2eSHans Petter Selasky 4029e2524b2eSHans Petter Selasky static uint16_t 4030e2524b2eSHans Petter Selasky uaudio20_mixer_determine_class(const struct uaudio_terminal_node *iot, 4031e2524b2eSHans Petter Selasky struct uaudio_mixer_node *mix) 4032e2524b2eSHans Petter Selasky { 4033e2524b2eSHans Petter Selasky uint16_t terminal_type = 0x0000; 4034e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *input[2]; 4035e2524b2eSHans Petter Selasky const struct uaudio_terminal_node *output[2]; 4036e2524b2eSHans Petter Selasky 4037e2524b2eSHans Petter Selasky input[0] = uaudio_mixer_get_input(iot, 0); 4038e2524b2eSHans Petter Selasky input[1] = uaudio_mixer_get_input(iot, 1); 4039e2524b2eSHans Petter Selasky 4040e2524b2eSHans Petter Selasky output[0] = uaudio_mixer_get_output(iot, 0); 4041e2524b2eSHans Petter Selasky output[1] = uaudio_mixer_get_output(iot, 1); 4042e2524b2eSHans Petter Selasky 4043e2524b2eSHans Petter Selasky /* 4044e2524b2eSHans Petter Selasky * check if there is only 4045e2524b2eSHans Petter Selasky * one output terminal: 4046e2524b2eSHans Petter Selasky */ 4047e2524b2eSHans Petter Selasky if (output[0] && (!output[1])) 4048e2524b2eSHans Petter Selasky terminal_type = UGETW(output[0]->u.ot_v2->wTerminalType); 4049e2524b2eSHans Petter Selasky /* 4050e2524b2eSHans Petter Selasky * If the only output terminal is USB, 4051e2524b2eSHans Petter Selasky * the class is UAC_RECORD. 4052e2524b2eSHans Petter Selasky */ 4053e2524b2eSHans Petter Selasky if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { 4054e2524b2eSHans Petter Selasky 4055e2524b2eSHans Petter Selasky mix->class = UAC_RECORD; 4056e2524b2eSHans Petter Selasky if (input[0] && (!input[1])) { 4057e2524b2eSHans Petter Selasky terminal_type = 4058e2524b2eSHans Petter Selasky UGETW(input[0]->u.it_v2->wTerminalType); 4059e2524b2eSHans Petter Selasky } else { 4060e2524b2eSHans Petter Selasky terminal_type = 0; 4061e2524b2eSHans Petter Selasky } 4062e2524b2eSHans Petter Selasky goto done; 4063e2524b2eSHans Petter Selasky } 4064e2524b2eSHans Petter Selasky /* 4065e2524b2eSHans Petter Selasky * if the unit is connected to just 4066e2524b2eSHans Petter Selasky * one input terminal, the 4067e2524b2eSHans Petter Selasky * class is UAC_INPUT: 4068e2524b2eSHans Petter Selasky */ 4069e2524b2eSHans Petter Selasky if (input[0] && (!input[1])) { 4070e2524b2eSHans Petter Selasky mix->class = UAC_INPUT; 4071e2524b2eSHans Petter Selasky terminal_type = 4072e2524b2eSHans Petter Selasky UGETW(input[0]->u.it_v2->wTerminalType); 40733a3f90c6SAndrew Thompson goto done; 40743a3f90c6SAndrew Thompson } 40753a3f90c6SAndrew Thompson /* 40763a3f90c6SAndrew Thompson * Otherwise, the class is UAC_OUTPUT. 40773a3f90c6SAndrew Thompson */ 40783a3f90c6SAndrew Thompson mix->class = UAC_OUTPUT; 40793a3f90c6SAndrew Thompson done: 40803a3f90c6SAndrew Thompson return (terminal_type); 40813a3f90c6SAndrew Thompson } 40823a3f90c6SAndrew Thompson 40833a3f90c6SAndrew Thompson struct uaudio_tt_to_feature { 40843a3f90c6SAndrew Thompson uint16_t terminal_type; 40853a3f90c6SAndrew Thompson uint16_t feature; 40863a3f90c6SAndrew Thompson }; 40873a3f90c6SAndrew Thompson 40883a3f90c6SAndrew Thompson static const struct uaudio_tt_to_feature uaudio_tt_to_feature[] = { 40893a3f90c6SAndrew Thompson 40903a3f90c6SAndrew Thompson {UAT_STREAM, SOUND_MIXER_PCM}, 40913a3f90c6SAndrew Thompson 40923a3f90c6SAndrew Thompson {UATI_MICROPHONE, SOUND_MIXER_MIC}, 40933a3f90c6SAndrew Thompson {UATI_DESKMICROPHONE, SOUND_MIXER_MIC}, 40943a3f90c6SAndrew Thompson {UATI_PERSONALMICROPHONE, SOUND_MIXER_MIC}, 40953a3f90c6SAndrew Thompson {UATI_OMNIMICROPHONE, SOUND_MIXER_MIC}, 40963a3f90c6SAndrew Thompson {UATI_MICROPHONEARRAY, SOUND_MIXER_MIC}, 40973a3f90c6SAndrew Thompson {UATI_PROCMICROPHONEARR, SOUND_MIXER_MIC}, 40983a3f90c6SAndrew Thompson 40993a3f90c6SAndrew Thompson {UATO_SPEAKER, SOUND_MIXER_SPEAKER}, 41003a3f90c6SAndrew Thompson {UATO_DESKTOPSPEAKER, SOUND_MIXER_SPEAKER}, 41013a3f90c6SAndrew Thompson {UATO_ROOMSPEAKER, SOUND_MIXER_SPEAKER}, 41023a3f90c6SAndrew Thompson {UATO_COMMSPEAKER, SOUND_MIXER_SPEAKER}, 41033a3f90c6SAndrew Thompson 41043a3f90c6SAndrew Thompson {UATE_ANALOGCONN, SOUND_MIXER_LINE}, 41053a3f90c6SAndrew Thompson {UATE_LINECONN, SOUND_MIXER_LINE}, 41063a3f90c6SAndrew Thompson {UATE_LEGACYCONN, SOUND_MIXER_LINE}, 41073a3f90c6SAndrew Thompson 41083a3f90c6SAndrew Thompson {UATE_DIGITALAUIFC, SOUND_MIXER_ALTPCM}, 41093a3f90c6SAndrew Thompson {UATE_SPDIF, SOUND_MIXER_ALTPCM}, 41103a3f90c6SAndrew Thompson {UATE_1394DA, SOUND_MIXER_ALTPCM}, 41113a3f90c6SAndrew Thompson {UATE_1394DV, SOUND_MIXER_ALTPCM}, 41123a3f90c6SAndrew Thompson 41133a3f90c6SAndrew Thompson {UATF_CDPLAYER, SOUND_MIXER_CD}, 41143a3f90c6SAndrew Thompson 41153a3f90c6SAndrew Thompson {UATF_SYNTHESIZER, SOUND_MIXER_SYNTH}, 41163a3f90c6SAndrew Thompson 41173a3f90c6SAndrew Thompson {UATF_VIDEODISCAUDIO, SOUND_MIXER_VIDEO}, 41183a3f90c6SAndrew Thompson {UATF_DVDAUDIO, SOUND_MIXER_VIDEO}, 41193a3f90c6SAndrew Thompson {UATF_TVTUNERAUDIO, SOUND_MIXER_VIDEO}, 41203a3f90c6SAndrew Thompson 41213a3f90c6SAndrew Thompson /* telephony terminal types */ 41223a3f90c6SAndrew Thompson {UATT_UNDEFINED, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ 41233a3f90c6SAndrew Thompson {UATT_PHONELINE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ 41243a3f90c6SAndrew Thompson {UATT_TELEPHONE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ 41253a3f90c6SAndrew Thompson {UATT_DOWNLINEPHONE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */ 41263a3f90c6SAndrew Thompson 41273a3f90c6SAndrew Thompson {UATF_RADIORECV, SOUND_MIXER_RADIO}, 41283a3f90c6SAndrew Thompson {UATF_RADIOXMIT, SOUND_MIXER_RADIO}, 41293a3f90c6SAndrew Thompson 41303a3f90c6SAndrew Thompson {UAT_UNDEFINED, SOUND_MIXER_VOLUME}, 41313a3f90c6SAndrew Thompson {UAT_VENDOR, SOUND_MIXER_VOLUME}, 41323a3f90c6SAndrew Thompson {UATI_UNDEFINED, SOUND_MIXER_VOLUME}, 41333a3f90c6SAndrew Thompson 41343a3f90c6SAndrew Thompson /* output terminal types */ 41353a3f90c6SAndrew Thompson {UATO_UNDEFINED, SOUND_MIXER_VOLUME}, 41363a3f90c6SAndrew Thompson {UATO_DISPLAYAUDIO, SOUND_MIXER_VOLUME}, 41373a3f90c6SAndrew Thompson {UATO_SUBWOOFER, SOUND_MIXER_VOLUME}, 41383a3f90c6SAndrew Thompson {UATO_HEADPHONES, SOUND_MIXER_VOLUME}, 41393a3f90c6SAndrew Thompson 41403a3f90c6SAndrew Thompson /* bidir terminal types */ 41413a3f90c6SAndrew Thompson {UATB_UNDEFINED, SOUND_MIXER_VOLUME}, 41423a3f90c6SAndrew Thompson {UATB_HANDSET, SOUND_MIXER_VOLUME}, 41433a3f90c6SAndrew Thompson {UATB_HEADSET, SOUND_MIXER_VOLUME}, 41443a3f90c6SAndrew Thompson {UATB_SPEAKERPHONE, SOUND_MIXER_VOLUME}, 41453a3f90c6SAndrew Thompson {UATB_SPEAKERPHONEESUP, SOUND_MIXER_VOLUME}, 41463a3f90c6SAndrew Thompson {UATB_SPEAKERPHONEECANC, SOUND_MIXER_VOLUME}, 41473a3f90c6SAndrew Thompson 41483a3f90c6SAndrew Thompson /* external terminal types */ 41493a3f90c6SAndrew Thompson {UATE_UNDEFINED, SOUND_MIXER_VOLUME}, 41503a3f90c6SAndrew Thompson 41513a3f90c6SAndrew Thompson /* embedded function terminal types */ 41523a3f90c6SAndrew Thompson {UATF_UNDEFINED, SOUND_MIXER_VOLUME}, 41533a3f90c6SAndrew Thompson {UATF_CALIBNOISE, SOUND_MIXER_VOLUME}, 41543a3f90c6SAndrew Thompson {UATF_EQUNOISE, SOUND_MIXER_VOLUME}, 41553a3f90c6SAndrew Thompson {UATF_DAT, SOUND_MIXER_VOLUME}, 41563a3f90c6SAndrew Thompson {UATF_DCC, SOUND_MIXER_VOLUME}, 41573a3f90c6SAndrew Thompson {UATF_MINIDISK, SOUND_MIXER_VOLUME}, 41583a3f90c6SAndrew Thompson {UATF_ANALOGTAPE, SOUND_MIXER_VOLUME}, 41593a3f90c6SAndrew Thompson {UATF_PHONOGRAPH, SOUND_MIXER_VOLUME}, 41603a3f90c6SAndrew Thompson {UATF_VCRAUDIO, SOUND_MIXER_VOLUME}, 41613a3f90c6SAndrew Thompson {UATF_SATELLITE, SOUND_MIXER_VOLUME}, 41623a3f90c6SAndrew Thompson {UATF_CABLETUNER, SOUND_MIXER_VOLUME}, 41633a3f90c6SAndrew Thompson {UATF_DSS, SOUND_MIXER_VOLUME}, 41643a3f90c6SAndrew Thompson {UATF_MULTITRACK, SOUND_MIXER_VOLUME}, 41653a3f90c6SAndrew Thompson {0xffff, SOUND_MIXER_VOLUME}, 41663a3f90c6SAndrew Thompson 41673a3f90c6SAndrew Thompson /* default */ 41683a3f90c6SAndrew Thompson {0x0000, SOUND_MIXER_VOLUME}, 41693a3f90c6SAndrew Thompson }; 41703a3f90c6SAndrew Thompson 41713a3f90c6SAndrew Thompson static uint16_t 41723a3f90c6SAndrew Thompson uaudio_mixer_feature_name(const struct uaudio_terminal_node *iot, 41733a3f90c6SAndrew Thompson struct uaudio_mixer_node *mix) 41743a3f90c6SAndrew Thompson { 41753a3f90c6SAndrew Thompson const struct uaudio_tt_to_feature *uat = uaudio_tt_to_feature; 41763a3f90c6SAndrew Thompson uint16_t terminal_type = uaudio_mixer_determine_class(iot, mix); 41773a3f90c6SAndrew Thompson 41783a3f90c6SAndrew Thompson if ((mix->class == UAC_RECORD) && (terminal_type == 0)) { 41793a3f90c6SAndrew Thompson return (SOUND_MIXER_IMIX); 41803a3f90c6SAndrew Thompson } 41813a3f90c6SAndrew Thompson while (uat->terminal_type) { 41823a3f90c6SAndrew Thompson if (uat->terminal_type == terminal_type) { 41833a3f90c6SAndrew Thompson break; 41843a3f90c6SAndrew Thompson } 41853a3f90c6SAndrew Thompson uat++; 41863a3f90c6SAndrew Thompson } 41873a3f90c6SAndrew Thompson 4188e2524b2eSHans Petter Selasky DPRINTF("terminal_type=0x%04x -> %d\n", 4189e2524b2eSHans Petter Selasky terminal_type, uat->feature); 4190e2524b2eSHans Petter Selasky 4191e2524b2eSHans Petter Selasky return (uat->feature); 4192e2524b2eSHans Petter Selasky } 4193e2524b2eSHans Petter Selasky 4194e2524b2eSHans Petter Selasky static uint16_t 4195e2524b2eSHans Petter Selasky uaudio20_mixer_feature_name(const struct uaudio_terminal_node *iot, 4196e2524b2eSHans Petter Selasky struct uaudio_mixer_node *mix) 4197e2524b2eSHans Petter Selasky { 4198e2524b2eSHans Petter Selasky const struct uaudio_tt_to_feature *uat; 4199e2524b2eSHans Petter Selasky uint16_t terminal_type = uaudio20_mixer_determine_class(iot, mix); 4200e2524b2eSHans Petter Selasky 4201e2524b2eSHans Petter Selasky if ((mix->class == UAC_RECORD) && (terminal_type == 0)) 4202e2524b2eSHans Petter Selasky return (SOUND_MIXER_IMIX); 4203e2524b2eSHans Petter Selasky 4204e2524b2eSHans Petter Selasky for (uat = uaudio_tt_to_feature; uat->terminal_type != 0; uat++) { 4205e2524b2eSHans Petter Selasky if (uat->terminal_type == terminal_type) 4206e2524b2eSHans Petter Selasky break; 4207e2524b2eSHans Petter Selasky } 4208e2524b2eSHans Petter Selasky 4209e2524b2eSHans Petter Selasky DPRINTF("terminal_type=0x%04x -> %d\n", 42103a3f90c6SAndrew Thompson terminal_type, uat->feature); 42113a3f90c6SAndrew Thompson 42123a3f90c6SAndrew Thompson return (uat->feature); 42133a3f90c6SAndrew Thompson } 42143a3f90c6SAndrew Thompson 42156d917491SHans Petter Selasky static const struct uaudio_terminal_node * 42166d917491SHans Petter Selasky uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t i) 42173a3f90c6SAndrew Thompson { 42183a3f90c6SAndrew Thompson struct uaudio_terminal_node *root = iot->root; 42193a3f90c6SAndrew Thompson uint8_t n; 42203a3f90c6SAndrew Thompson 42213a3f90c6SAndrew Thompson n = iot->usr.id_max; 42223a3f90c6SAndrew Thompson do { 42233a3f90c6SAndrew Thompson if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) { 42246d917491SHans Petter Selasky if (!i--) 42253a3f90c6SAndrew Thompson return (root + n); 42263a3f90c6SAndrew Thompson } 42273a3f90c6SAndrew Thompson } while (n--); 42283a3f90c6SAndrew Thompson 42293a3f90c6SAndrew Thompson return (NULL); 42303a3f90c6SAndrew Thompson } 42313a3f90c6SAndrew Thompson 42326d917491SHans Petter Selasky static const struct uaudio_terminal_node * 42336d917491SHans Petter Selasky uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t i) 42343a3f90c6SAndrew Thompson { 42353a3f90c6SAndrew Thompson struct uaudio_terminal_node *root = iot->root; 42363a3f90c6SAndrew Thompson uint8_t n; 42373a3f90c6SAndrew Thompson 42383a3f90c6SAndrew Thompson n = iot->usr.id_max; 42393a3f90c6SAndrew Thompson do { 42403a3f90c6SAndrew Thompson if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) { 42416d917491SHans Petter Selasky if (!i--) 42423a3f90c6SAndrew Thompson return (root + n); 42433a3f90c6SAndrew Thompson } 42443a3f90c6SAndrew Thompson } while (n--); 42453a3f90c6SAndrew Thompson 42463a3f90c6SAndrew Thompson return (NULL); 42473a3f90c6SAndrew Thompson } 42483a3f90c6SAndrew Thompson 42493a3f90c6SAndrew Thompson static void 42503a3f90c6SAndrew Thompson uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root, 42513a3f90c6SAndrew Thompson const uint8_t *p_id, uint8_t n_id, 42523a3f90c6SAndrew Thompson struct uaudio_search_result *info) 42533a3f90c6SAndrew Thompson { 42543a3f90c6SAndrew Thompson struct uaudio_terminal_node *iot; 42553a3f90c6SAndrew Thompson uint8_t n; 42563a3f90c6SAndrew Thompson uint8_t i; 4257e2524b2eSHans Petter Selasky uint8_t is_last; 42583a3f90c6SAndrew Thompson 4259e2524b2eSHans Petter Selasky top: 42603a3f90c6SAndrew Thompson for (n = 0; n < n_id; n++) { 42613a3f90c6SAndrew Thompson 42623a3f90c6SAndrew Thompson i = p_id[n]; 42633a3f90c6SAndrew Thompson 4264e2524b2eSHans Petter Selasky if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { 42653a3f90c6SAndrew Thompson DPRINTF("avoided going into a circle at id=%d!\n", i); 4266e2524b2eSHans Petter Selasky return; 42673a3f90c6SAndrew Thompson } 42683a3f90c6SAndrew Thompson 4269e2524b2eSHans Petter Selasky info->recurse_level++; 4270e2524b2eSHans Petter Selasky 42713a3f90c6SAndrew Thompson iot = (root + i); 42723a3f90c6SAndrew Thompson 4273e2524b2eSHans Petter Selasky if (iot->u.desc == NULL) 42743a3f90c6SAndrew Thompson continue; 4275e2524b2eSHans Petter Selasky 4276e2524b2eSHans Petter Selasky is_last = ((n + 1) == n_id); 4277e2524b2eSHans Petter Selasky 42783a3f90c6SAndrew Thompson switch (iot->u.desc->bDescriptorSubtype) { 42793a3f90c6SAndrew Thompson case UDESCSUB_AC_INPUT: 42803a3f90c6SAndrew Thompson info->bit_input[i / 8] |= (1 << (i % 8)); 42813a3f90c6SAndrew Thompson break; 42823a3f90c6SAndrew Thompson 42833a3f90c6SAndrew Thompson case UDESCSUB_AC_FEATURE: 4284e2524b2eSHans Petter Selasky if (is_last) { 4285e2524b2eSHans Petter Selasky p_id = &iot->u.fu_v1->bSourceId; 4286e2524b2eSHans Petter Selasky n_id = 1; 4287e2524b2eSHans Petter Selasky goto top; 4288e2524b2eSHans Petter Selasky } 4289e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 4290e2524b2eSHans Petter Selasky root, &iot->u.fu_v1->bSourceId, 1, info); 42913a3f90c6SAndrew Thompson break; 42923a3f90c6SAndrew Thompson 42933a3f90c6SAndrew Thompson case UDESCSUB_AC_OUTPUT: 4294e2524b2eSHans Petter Selasky if (is_last) { 4295e2524b2eSHans Petter Selasky p_id = &iot->u.ot_v1->bSourceId; 4296e2524b2eSHans Petter Selasky n_id = 1; 4297e2524b2eSHans Petter Selasky goto top; 4298e2524b2eSHans Petter Selasky } 4299e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 4300e2524b2eSHans Petter Selasky root, &iot->u.ot_v1->bSourceId, 1, info); 43013a3f90c6SAndrew Thompson break; 43023a3f90c6SAndrew Thompson 43033a3f90c6SAndrew Thompson case UDESCSUB_AC_MIXER: 4304e2524b2eSHans Petter Selasky if (is_last) { 4305e2524b2eSHans Petter Selasky p_id = iot->u.mu_v1->baSourceId; 4306e2524b2eSHans Petter Selasky n_id = iot->u.mu_v1->bNrInPins; 4307e2524b2eSHans Petter Selasky goto top; 4308e2524b2eSHans Petter Selasky } 4309e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 4310e2524b2eSHans Petter Selasky root, iot->u.mu_v1->baSourceId, 4311e2524b2eSHans Petter Selasky iot->u.mu_v1->bNrInPins, info); 43123a3f90c6SAndrew Thompson break; 43133a3f90c6SAndrew Thompson 43143a3f90c6SAndrew Thompson case UDESCSUB_AC_SELECTOR: 4315e2524b2eSHans Petter Selasky if (is_last) { 4316e2524b2eSHans Petter Selasky p_id = iot->u.su_v1->baSourceId; 4317e2524b2eSHans Petter Selasky n_id = iot->u.su_v1->bNrInPins; 4318e2524b2eSHans Petter Selasky goto top; 4319e2524b2eSHans Petter Selasky } 4320e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 4321e2524b2eSHans Petter Selasky root, iot->u.su_v1->baSourceId, 4322e2524b2eSHans Petter Selasky iot->u.su_v1->bNrInPins, info); 43233a3f90c6SAndrew Thompson break; 43243a3f90c6SAndrew Thompson 43253a3f90c6SAndrew Thompson case UDESCSUB_AC_PROCESSING: 4326e2524b2eSHans Petter Selasky if (is_last) { 4327e2524b2eSHans Petter Selasky p_id = iot->u.pu_v1->baSourceId; 4328e2524b2eSHans Petter Selasky n_id = iot->u.pu_v1->bNrInPins; 4329e2524b2eSHans Petter Selasky goto top; 4330e2524b2eSHans Petter Selasky } 4331e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 4332e2524b2eSHans Petter Selasky root, iot->u.pu_v1->baSourceId, 4333e2524b2eSHans Petter Selasky iot->u.pu_v1->bNrInPins, info); 43343a3f90c6SAndrew Thompson break; 43353a3f90c6SAndrew Thompson 43363a3f90c6SAndrew Thompson case UDESCSUB_AC_EXTENSION: 4337e2524b2eSHans Petter Selasky if (is_last) { 4338e2524b2eSHans Petter Selasky p_id = iot->u.eu_v1->baSourceId; 4339e2524b2eSHans Petter Selasky n_id = iot->u.eu_v1->bNrInPins; 4340e2524b2eSHans Petter Selasky goto top; 4341e2524b2eSHans Petter Selasky } 4342e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub( 4343e2524b2eSHans Petter Selasky root, iot->u.eu_v1->baSourceId, 4344e2524b2eSHans Petter Selasky iot->u.eu_v1->bNrInPins, info); 43453a3f90c6SAndrew Thompson break; 43463a3f90c6SAndrew Thompson 43473a3f90c6SAndrew Thompson default: 43483a3f90c6SAndrew Thompson break; 43493a3f90c6SAndrew Thompson } 43503a3f90c6SAndrew Thompson } 4351e2524b2eSHans Petter Selasky } 4352e2524b2eSHans Petter Selasky 4353e2524b2eSHans Petter Selasky static void 4354e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub(struct uaudio_terminal_node *root, 4355e2524b2eSHans Petter Selasky const uint8_t *p_id, uint8_t n_id, 4356e2524b2eSHans Petter Selasky struct uaudio_search_result *info) 4357e2524b2eSHans Petter Selasky { 4358e2524b2eSHans Petter Selasky struct uaudio_terminal_node *iot; 4359e2524b2eSHans Petter Selasky uint8_t n; 4360e2524b2eSHans Petter Selasky uint8_t i; 4361e2524b2eSHans Petter Selasky uint8_t is_last; 4362e2524b2eSHans Petter Selasky 4363e2524b2eSHans Petter Selasky top: 4364e2524b2eSHans Petter Selasky for (n = 0; n < n_id; n++) { 4365e2524b2eSHans Petter Selasky 4366e2524b2eSHans Petter Selasky i = p_id[n]; 4367e2524b2eSHans Petter Selasky 4368e2524b2eSHans Petter Selasky if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { 4369e2524b2eSHans Petter Selasky DPRINTF("avoided going into a circle at id=%d!\n", i); 4370e2524b2eSHans Petter Selasky return; 4371e2524b2eSHans Petter Selasky } 4372e2524b2eSHans Petter Selasky 4373e2524b2eSHans Petter Selasky info->recurse_level++; 4374e2524b2eSHans Petter Selasky 4375e2524b2eSHans Petter Selasky iot = (root + i); 4376e2524b2eSHans Petter Selasky 4377e2524b2eSHans Petter Selasky if (iot->u.desc == NULL) 4378e2524b2eSHans Petter Selasky continue; 4379e2524b2eSHans Petter Selasky 4380e2524b2eSHans Petter Selasky is_last = ((n + 1) == n_id); 4381e2524b2eSHans Petter Selasky 4382e2524b2eSHans Petter Selasky switch (iot->u.desc->bDescriptorSubtype) { 4383e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 4384e2524b2eSHans Petter Selasky info->bit_input[i / 8] |= (1 << (i % 8)); 4385e2524b2eSHans Petter Selasky break; 4386e2524b2eSHans Petter Selasky 4387e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 4388e2524b2eSHans Petter Selasky if (is_last) { 4389e2524b2eSHans Petter Selasky p_id = &iot->u.ot_v2->bSourceId; 4390e2524b2eSHans Petter Selasky n_id = 1; 4391e2524b2eSHans Petter Selasky goto top; 4392e2524b2eSHans Petter Selasky } 4393e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 4394e2524b2eSHans Petter Selasky root, &iot->u.ot_v2->bSourceId, 1, info); 4395e2524b2eSHans Petter Selasky break; 4396e2524b2eSHans Petter Selasky 4397e2524b2eSHans Petter Selasky case UDESCSUB_AC_MIXER: 4398e2524b2eSHans Petter Selasky if (is_last) { 4399e2524b2eSHans Petter Selasky p_id = iot->u.mu_v2->baSourceId; 4400e2524b2eSHans Petter Selasky n_id = iot->u.mu_v2->bNrInPins; 4401e2524b2eSHans Petter Selasky goto top; 4402e2524b2eSHans Petter Selasky } 4403e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 4404e2524b2eSHans Petter Selasky root, iot->u.mu_v2->baSourceId, 4405e2524b2eSHans Petter Selasky iot->u.mu_v2->bNrInPins, info); 4406e2524b2eSHans Petter Selasky break; 4407e2524b2eSHans Petter Selasky 4408e2524b2eSHans Petter Selasky case UDESCSUB_AC_SELECTOR: 4409e2524b2eSHans Petter Selasky if (is_last) { 4410e2524b2eSHans Petter Selasky p_id = iot->u.su_v2->baSourceId; 4411e2524b2eSHans Petter Selasky n_id = iot->u.su_v2->bNrInPins; 4412e2524b2eSHans Petter Selasky goto top; 4413e2524b2eSHans Petter Selasky } 4414e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 4415e2524b2eSHans Petter Selasky root, iot->u.su_v2->baSourceId, 4416e2524b2eSHans Petter Selasky iot->u.su_v2->bNrInPins, info); 4417e2524b2eSHans Petter Selasky break; 4418e2524b2eSHans Petter Selasky 4419e2524b2eSHans Petter Selasky case UDESCSUB_AC_SAMPLE_RT: 4420e2524b2eSHans Petter Selasky if (is_last) { 4421e2524b2eSHans Petter Selasky p_id = &iot->u.ru_v2->bSourceId; 4422e2524b2eSHans Petter Selasky n_id = 1; 4423e2524b2eSHans Petter Selasky goto top; 4424e2524b2eSHans Petter Selasky } 4425e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 4426e2524b2eSHans Petter Selasky root, &iot->u.ru_v2->bSourceId, 4427e2524b2eSHans Petter Selasky 1, info); 4428e2524b2eSHans Petter Selasky break; 4429e2524b2eSHans Petter Selasky 4430e2524b2eSHans Petter Selasky case UDESCSUB_AC_EFFECT: 4431e2524b2eSHans Petter Selasky if (is_last) { 4432e2524b2eSHans Petter Selasky p_id = &iot->u.ef_v2->bSourceId; 4433e2524b2eSHans Petter Selasky n_id = 1; 4434e2524b2eSHans Petter Selasky goto top; 4435e2524b2eSHans Petter Selasky } 4436e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 4437e2524b2eSHans Petter Selasky root, &iot->u.ef_v2->bSourceId, 4438e2524b2eSHans Petter Selasky 1, info); 4439e2524b2eSHans Petter Selasky break; 4440e2524b2eSHans Petter Selasky 4441e2524b2eSHans Petter Selasky case UDESCSUB_AC_FEATURE: 4442e2524b2eSHans Petter Selasky if (is_last) { 4443e2524b2eSHans Petter Selasky p_id = &iot->u.fu_v2->bSourceId; 4444e2524b2eSHans Petter Selasky n_id = 1; 4445e2524b2eSHans Petter Selasky goto top; 4446e2524b2eSHans Petter Selasky } 4447e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 4448e2524b2eSHans Petter Selasky root, &iot->u.fu_v2->bSourceId, 1, info); 4449e2524b2eSHans Petter Selasky break; 4450e2524b2eSHans Petter Selasky 4451e2524b2eSHans Petter Selasky case UDESCSUB_AC_PROCESSING_V2: 4452e2524b2eSHans Petter Selasky if (is_last) { 4453e2524b2eSHans Petter Selasky p_id = iot->u.pu_v2->baSourceId; 4454e2524b2eSHans Petter Selasky n_id = iot->u.pu_v2->bNrInPins; 4455e2524b2eSHans Petter Selasky goto top; 4456e2524b2eSHans Petter Selasky } 4457e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 4458e2524b2eSHans Petter Selasky root, iot->u.pu_v2->baSourceId, 4459e2524b2eSHans Petter Selasky iot->u.pu_v2->bNrInPins, info); 4460e2524b2eSHans Petter Selasky break; 4461e2524b2eSHans Petter Selasky 4462e2524b2eSHans Petter Selasky case UDESCSUB_AC_EXTENSION_V2: 4463e2524b2eSHans Petter Selasky if (is_last) { 4464e2524b2eSHans Petter Selasky p_id = iot->u.eu_v2->baSourceId; 4465e2524b2eSHans Petter Selasky n_id = iot->u.eu_v2->bNrInPins; 4466e2524b2eSHans Petter Selasky goto top; 4467e2524b2eSHans Petter Selasky } 4468e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub( 4469e2524b2eSHans Petter Selasky root, iot->u.eu_v2->baSourceId, 4470e2524b2eSHans Petter Selasky iot->u.eu_v2->bNrInPins, info); 4471e2524b2eSHans Petter Selasky break; 4472e2524b2eSHans Petter Selasky default: 4473e2524b2eSHans Petter Selasky break; 4474e2524b2eSHans Petter Selasky } 4475e2524b2eSHans Petter Selasky } 4476e2524b2eSHans Petter Selasky } 4477e2524b2eSHans Petter Selasky 4478e2524b2eSHans Petter Selasky static void 4479e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(struct uaudio_terminal_node *root, 4480e2524b2eSHans Petter Selasky const uint8_t *p_id, uint8_t n_id, 4481e2524b2eSHans Petter Selasky struct uaudio_search_result *info) 4482e2524b2eSHans Petter Selasky { 4483e2524b2eSHans Petter Selasky struct uaudio_terminal_node *iot; 4484e2524b2eSHans Petter Selasky uint8_t n; 4485e2524b2eSHans Petter Selasky uint8_t i; 4486e2524b2eSHans Petter Selasky uint8_t is_last; 4487e2524b2eSHans Petter Selasky uint8_t id; 4488e2524b2eSHans Petter Selasky 4489e2524b2eSHans Petter Selasky top: 4490e2524b2eSHans Petter Selasky for (n = 0; n < n_id; n++) { 4491e2524b2eSHans Petter Selasky 4492e2524b2eSHans Petter Selasky i = p_id[n]; 4493e2524b2eSHans Petter Selasky 4494e2524b2eSHans Petter Selasky if (info->recurse_level == UAUDIO_RECURSE_LIMIT) { 4495e2524b2eSHans Petter Selasky DPRINTF("avoided going into a circle at id=%d!\n", i); 4496e2524b2eSHans Petter Selasky return; 4497e2524b2eSHans Petter Selasky } 4498e2524b2eSHans Petter Selasky 4499e2524b2eSHans Petter Selasky info->recurse_level++; 4500e2524b2eSHans Petter Selasky 4501e2524b2eSHans Petter Selasky iot = (root + i); 4502e2524b2eSHans Petter Selasky 4503e2524b2eSHans Petter Selasky if (iot->u.desc == NULL) 4504e2524b2eSHans Petter Selasky continue; 4505e2524b2eSHans Petter Selasky 4506e2524b2eSHans Petter Selasky is_last = ((n + 1) == n_id); 4507e2524b2eSHans Petter Selasky 4508e2524b2eSHans Petter Selasky switch (iot->u.desc->bDescriptorSubtype) { 4509e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 4510e2524b2eSHans Petter Selasky info->is_input = 1; 4511e2524b2eSHans Petter Selasky if (is_last) { 4512e2524b2eSHans Petter Selasky p_id = &iot->u.it_v2->bCSourceId; 4513e2524b2eSHans Petter Selasky n_id = 1; 4514e2524b2eSHans Petter Selasky goto top; 4515e2524b2eSHans Petter Selasky } 4516e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(root, 4517e2524b2eSHans Petter Selasky &iot->u.it_v2->bCSourceId, 1, info); 4518e2524b2eSHans Petter Selasky break; 4519e2524b2eSHans Petter Selasky 4520e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 4521e2524b2eSHans Petter Selasky info->is_input = 0; 4522e2524b2eSHans Petter Selasky if (is_last) { 4523e2524b2eSHans Petter Selasky p_id = &iot->u.ot_v2->bCSourceId; 4524e2524b2eSHans Petter Selasky n_id = 1; 4525e2524b2eSHans Petter Selasky goto top; 4526e2524b2eSHans Petter Selasky } 4527e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(root, 4528e2524b2eSHans Petter Selasky &iot->u.ot_v2->bCSourceId, 1, info); 4529e2524b2eSHans Petter Selasky break; 4530e2524b2eSHans Petter Selasky 4531e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SEL: 4532e2524b2eSHans Petter Selasky if (is_last) { 4533e2524b2eSHans Petter Selasky p_id = iot->u.csel_v2->baCSourceId; 4534e2524b2eSHans Petter Selasky n_id = iot->u.csel_v2->bNrInPins; 4535e2524b2eSHans Petter Selasky goto top; 4536e2524b2eSHans Petter Selasky } 4537e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(root, 4538e2524b2eSHans Petter Selasky iot->u.csel_v2->baCSourceId, 4539e2524b2eSHans Petter Selasky iot->u.csel_v2->bNrInPins, info); 4540e2524b2eSHans Petter Selasky break; 4541e2524b2eSHans Petter Selasky 4542e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_MUL: 4543e2524b2eSHans Petter Selasky if (is_last) { 4544e2524b2eSHans Petter Selasky p_id = &iot->u.cmul_v2->bCSourceId; 4545e2524b2eSHans Petter Selasky n_id = 1; 4546e2524b2eSHans Petter Selasky goto top; 4547e2524b2eSHans Petter Selasky } 4548e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(root, 4549e2524b2eSHans Petter Selasky &iot->u.cmul_v2->bCSourceId, 4550e2524b2eSHans Petter Selasky 1, info); 4551e2524b2eSHans Petter Selasky break; 4552e2524b2eSHans Petter Selasky 4553e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SRC: 4554e2524b2eSHans Petter Selasky 4555e2524b2eSHans Petter Selasky id = iot->u.csrc_v2->bClockId; 4556e2524b2eSHans Petter Selasky 4557e2524b2eSHans Petter Selasky switch (info->is_input) { 4558e2524b2eSHans Petter Selasky case 0: 4559e2524b2eSHans Petter Selasky info->bit_output[id / 8] |= (1 << (id % 8)); 4560e2524b2eSHans Petter Selasky break; 4561e2524b2eSHans Petter Selasky case 1: 4562e2524b2eSHans Petter Selasky info->bit_input[id / 8] |= (1 << (id % 8)); 4563e2524b2eSHans Petter Selasky break; 4564e2524b2eSHans Petter Selasky default: 4565e2524b2eSHans Petter Selasky break; 4566e2524b2eSHans Petter Selasky } 4567e2524b2eSHans Petter Selasky break; 4568e2524b2eSHans Petter Selasky 4569e2524b2eSHans Petter Selasky default: 4570e2524b2eSHans Petter Selasky break; 4571e2524b2eSHans Petter Selasky } 4572e2524b2eSHans Petter Selasky } 45733a3f90c6SAndrew Thompson } 45743a3f90c6SAndrew Thompson 45753a3f90c6SAndrew Thompson static void 45763a3f90c6SAndrew Thompson uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id, 45773a3f90c6SAndrew Thompson uint8_t n_id, struct uaudio_search_result *info) 45783a3f90c6SAndrew Thompson { 45793a3f90c6SAndrew Thompson struct uaudio_terminal_node *iot = (root + id); 45803a3f90c6SAndrew Thompson uint8_t j; 45813a3f90c6SAndrew Thompson 45823a3f90c6SAndrew Thompson j = n_id; 45833a3f90c6SAndrew Thompson do { 45843a3f90c6SAndrew Thompson if ((j != id) && ((root + j)->u.desc) && 45853a3f90c6SAndrew Thompson ((root + j)->u.desc->bDescriptorSubtype == UDESCSUB_AC_OUTPUT)) { 45863a3f90c6SAndrew Thompson 45873a3f90c6SAndrew Thompson /* 45883a3f90c6SAndrew Thompson * "j" (output) <--- virtual wire <--- "id" (input) 45893a3f90c6SAndrew Thompson * 45903a3f90c6SAndrew Thompson * if "j" has "id" on the input, then "id" have "j" on 45913a3f90c6SAndrew Thompson * the output, because they are connected: 45923a3f90c6SAndrew Thompson */ 45933a3f90c6SAndrew Thompson if ((root + j)->usr.bit_input[id / 8] & (1 << (id % 8))) { 45943a3f90c6SAndrew Thompson iot->usr.bit_output[j / 8] |= (1 << (j % 8)); 45953a3f90c6SAndrew Thompson } 45963a3f90c6SAndrew Thompson } 45973a3f90c6SAndrew Thompson } while (j--); 45983a3f90c6SAndrew Thompson } 45993a3f90c6SAndrew Thompson 46003a3f90c6SAndrew Thompson static void 4601e2524b2eSHans Petter Selasky uaudio_mixer_fill_info(struct uaudio_softc *sc, 4602e2524b2eSHans Petter Selasky struct usb_device *udev, void *desc) 46033a3f90c6SAndrew Thompson { 46044c21be9bSRebecca Cran const struct usb_audio_control_descriptor *acdp; 4605a593f6b8SAndrew Thompson struct usb_config_descriptor *cd = usbd_get_config_descriptor(udev); 4606760bc48eSAndrew Thompson const struct usb_descriptor *dp; 46074c21be9bSRebecca Cran const struct usb_audio_unit *au; 46083a3f90c6SAndrew Thompson struct uaudio_terminal_node *iot = NULL; 46093a3f90c6SAndrew Thompson uint16_t wTotalLen; 46103a3f90c6SAndrew Thompson uint8_t ID_max = 0; /* inclusive */ 46113a3f90c6SAndrew Thompson uint8_t i; 46123a3f90c6SAndrew Thompson 4613a593f6b8SAndrew Thompson desc = usb_desc_foreach(cd, desc); 46143a3f90c6SAndrew Thompson 46153a3f90c6SAndrew Thompson if (desc == NULL) { 46163a3f90c6SAndrew Thompson DPRINTF("no Audio Control header\n"); 46173a3f90c6SAndrew Thompson goto done; 46183a3f90c6SAndrew Thompson } 46193a3f90c6SAndrew Thompson acdp = desc; 46203a3f90c6SAndrew Thompson 46213a3f90c6SAndrew Thompson if ((acdp->bLength < sizeof(*acdp)) || 46223a3f90c6SAndrew Thompson (acdp->bDescriptorType != UDESC_CS_INTERFACE) || 46233a3f90c6SAndrew Thompson (acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)) { 46243a3f90c6SAndrew Thompson DPRINTF("invalid Audio Control header\n"); 46253a3f90c6SAndrew Thompson goto done; 46263a3f90c6SAndrew Thompson } 46273a3f90c6SAndrew Thompson /* "wTotalLen" is allowed to be corrupt */ 46283a3f90c6SAndrew Thompson wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength; 46293a3f90c6SAndrew Thompson 46303a3f90c6SAndrew Thompson /* get USB audio revision */ 46313a3f90c6SAndrew Thompson sc->sc_audio_rev = UGETW(acdp->bcdADC); 46323a3f90c6SAndrew Thompson 46333a3f90c6SAndrew Thompson DPRINTFN(3, "found AC header, vers=%03x, len=%d\n", 46343a3f90c6SAndrew Thompson sc->sc_audio_rev, wTotalLen); 46353a3f90c6SAndrew Thompson 46363a3f90c6SAndrew Thompson iot = malloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP, 46373a3f90c6SAndrew Thompson M_WAITOK | M_ZERO); 46383a3f90c6SAndrew Thompson 46393a3f90c6SAndrew Thompson if (iot == NULL) { 46403a3f90c6SAndrew Thompson DPRINTF("no memory!\n"); 46413a3f90c6SAndrew Thompson goto done; 46423a3f90c6SAndrew Thompson } 4643a593f6b8SAndrew Thompson while ((desc = usb_desc_foreach(cd, desc))) { 46443a3f90c6SAndrew Thompson 46453a3f90c6SAndrew Thompson dp = desc; 46463a3f90c6SAndrew Thompson 46473a3f90c6SAndrew Thompson if (dp->bLength > wTotalLen) { 46483a3f90c6SAndrew Thompson break; 46493a3f90c6SAndrew Thompson } else { 46503a3f90c6SAndrew Thompson wTotalLen -= dp->bLength; 46513a3f90c6SAndrew Thompson } 46523a3f90c6SAndrew Thompson 4653e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) 4654e2524b2eSHans Petter Selasky au = NULL; 4655e2524b2eSHans Petter Selasky else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) 4656e2524b2eSHans Petter Selasky au = uaudio20_mixer_verify_desc(dp, 0); 4657e2524b2eSHans Petter Selasky else 46583a3f90c6SAndrew Thompson au = uaudio_mixer_verify_desc(dp, 0); 46593a3f90c6SAndrew Thompson 46603a3f90c6SAndrew Thompson if (au) { 46613a3f90c6SAndrew Thompson iot[au->bUnitId].u.desc = (const void *)au; 4662e2524b2eSHans Petter Selasky if (au->bUnitId > ID_max) 46633a3f90c6SAndrew Thompson ID_max = au->bUnitId; 46643a3f90c6SAndrew Thompson } 46653a3f90c6SAndrew Thompson } 46663a3f90c6SAndrew Thompson 46673a3f90c6SAndrew Thompson DPRINTF("Maximum ID=%d\n", ID_max); 46683a3f90c6SAndrew Thompson 46693a3f90c6SAndrew Thompson /* 46703a3f90c6SAndrew Thompson * determine sourcing inputs for 46713a3f90c6SAndrew Thompson * all nodes in the tree: 46723a3f90c6SAndrew Thompson */ 46733a3f90c6SAndrew Thompson i = ID_max; 46743a3f90c6SAndrew Thompson do { 4675e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { 4676e2524b2eSHans Petter Selasky /* FALLTHROUGH */ 4677e2524b2eSHans Petter Selasky } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { 4678e2524b2eSHans Petter Selasky uaudio20_mixer_find_inputs_sub(iot, 4679e2524b2eSHans Petter Selasky &i, 1, &((iot + i)->usr)); 4680e2524b2eSHans Petter Selasky 4681e2524b2eSHans Petter Selasky sc->sc_mixer_clocks.is_input = 255; 4682e2524b2eSHans Petter Selasky sc->sc_mixer_clocks.recurse_level = 0; 4683e2524b2eSHans Petter Selasky 4684e2524b2eSHans Petter Selasky uaudio20_mixer_find_clocks_sub(iot, 4685e2524b2eSHans Petter Selasky &i, 1, &sc->sc_mixer_clocks); 4686e2524b2eSHans Petter Selasky } else { 4687e2524b2eSHans Petter Selasky uaudio_mixer_find_inputs_sub(iot, 4688e2524b2eSHans Petter Selasky &i, 1, &((iot + i)->usr)); 4689e2524b2eSHans Petter Selasky } 46903a3f90c6SAndrew Thompson } while (i--); 46913a3f90c6SAndrew Thompson 46923a3f90c6SAndrew Thompson /* 46933a3f90c6SAndrew Thompson * determine outputs for 46943a3f90c6SAndrew Thompson * all nodes in the tree: 46953a3f90c6SAndrew Thompson */ 46963a3f90c6SAndrew Thompson i = ID_max; 46973a3f90c6SAndrew Thompson do { 4698e2524b2eSHans Petter Selasky uaudio_mixer_find_outputs_sub(iot, 4699e2524b2eSHans Petter Selasky i, ID_max, &((iot + i)->usr)); 47003a3f90c6SAndrew Thompson } while (i--); 47013a3f90c6SAndrew Thompson 47023a3f90c6SAndrew Thompson /* set "id_max" and "root" */ 47033a3f90c6SAndrew Thompson 47043a3f90c6SAndrew Thompson i = ID_max; 47053a3f90c6SAndrew Thompson do { 47063a3f90c6SAndrew Thompson (iot + i)->usr.id_max = ID_max; 47073a3f90c6SAndrew Thompson (iot + i)->root = iot; 47083a3f90c6SAndrew Thompson } while (i--); 47093a3f90c6SAndrew Thompson 47103a3f90c6SAndrew Thompson /* 4711e2524b2eSHans Petter Selasky * Scan the config to create a linked list of "mixer" nodes: 47123a3f90c6SAndrew Thompson */ 47133a3f90c6SAndrew Thompson 47143a3f90c6SAndrew Thompson i = ID_max; 47153a3f90c6SAndrew Thompson do { 47163a3f90c6SAndrew Thompson dp = iot[i].u.desc; 47173a3f90c6SAndrew Thompson 4718e2524b2eSHans Petter Selasky if (dp == NULL) 47193a3f90c6SAndrew Thompson continue; 4720e2524b2eSHans Petter Selasky 47213a3f90c6SAndrew Thompson DPRINTFN(11, "id=%d subtype=%d\n", 47223a3f90c6SAndrew Thompson i, dp->bDescriptorSubtype); 47233a3f90c6SAndrew Thompson 4724e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { 4725e2524b2eSHans Petter Selasky continue; 4726e2524b2eSHans Petter Selasky } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { 4727e2524b2eSHans Petter Selasky 47283a3f90c6SAndrew Thompson switch (dp->bDescriptorSubtype) { 47293a3f90c6SAndrew Thompson case UDESCSUB_AC_HEADER: 47303a3f90c6SAndrew Thompson DPRINTF("unexpected AC header\n"); 47313a3f90c6SAndrew Thompson break; 47323a3f90c6SAndrew Thompson 47333a3f90c6SAndrew Thompson case UDESCSUB_AC_INPUT: 4734e2524b2eSHans Petter Selasky case UDESCSUB_AC_OUTPUT: 4735e2524b2eSHans Petter Selasky case UDESCSUB_AC_PROCESSING_V2: 4736e2524b2eSHans Petter Selasky case UDESCSUB_AC_EXTENSION_V2: 4737e2524b2eSHans Petter Selasky case UDESCSUB_AC_EFFECT: 4738e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SRC: 4739e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_SEL: 4740e2524b2eSHans Petter Selasky case UDESCSUB_AC_CLOCK_MUL: 4741e2524b2eSHans Petter Selasky case UDESCSUB_AC_SAMPLE_RT: 47423a3f90c6SAndrew Thompson break; 47433a3f90c6SAndrew Thompson 4744e2524b2eSHans Petter Selasky case UDESCSUB_AC_MIXER: 4745e2524b2eSHans Petter Selasky uaudio20_mixer_add_mixer(sc, iot, i); 4746e2524b2eSHans Petter Selasky break; 4747e2524b2eSHans Petter Selasky 4748e2524b2eSHans Petter Selasky case UDESCSUB_AC_SELECTOR: 4749e2524b2eSHans Petter Selasky uaudio20_mixer_add_selector(sc, iot, i); 4750e2524b2eSHans Petter Selasky break; 4751e2524b2eSHans Petter Selasky 4752e2524b2eSHans Petter Selasky case UDESCSUB_AC_FEATURE: 4753e2524b2eSHans Petter Selasky uaudio20_mixer_add_feature(sc, iot, i); 4754e2524b2eSHans Petter Selasky break; 4755e2524b2eSHans Petter Selasky 4756e2524b2eSHans Petter Selasky default: 4757e2524b2eSHans Petter Selasky DPRINTF("bad AC desc subtype=0x%02x\n", 4758e2524b2eSHans Petter Selasky dp->bDescriptorSubtype); 4759e2524b2eSHans Petter Selasky break; 4760e2524b2eSHans Petter Selasky } 4761e2524b2eSHans Petter Selasky continue; 4762e2524b2eSHans Petter Selasky } 4763e2524b2eSHans Petter Selasky 4764e2524b2eSHans Petter Selasky switch (dp->bDescriptorSubtype) { 4765e2524b2eSHans Petter Selasky case UDESCSUB_AC_HEADER: 4766e2524b2eSHans Petter Selasky DPRINTF("unexpected AC header\n"); 4767e2524b2eSHans Petter Selasky break; 4768e2524b2eSHans Petter Selasky 4769e2524b2eSHans Petter Selasky case UDESCSUB_AC_INPUT: 47703a3f90c6SAndrew Thompson case UDESCSUB_AC_OUTPUT: 47713a3f90c6SAndrew Thompson break; 47723a3f90c6SAndrew Thompson 47733a3f90c6SAndrew Thompson case UDESCSUB_AC_MIXER: 47743a3f90c6SAndrew Thompson uaudio_mixer_add_mixer(sc, iot, i); 47753a3f90c6SAndrew Thompson break; 47763a3f90c6SAndrew Thompson 47773a3f90c6SAndrew Thompson case UDESCSUB_AC_SELECTOR: 47783a3f90c6SAndrew Thompson uaudio_mixer_add_selector(sc, iot, i); 47793a3f90c6SAndrew Thompson break; 47803a3f90c6SAndrew Thompson 47813a3f90c6SAndrew Thompson case UDESCSUB_AC_FEATURE: 47823a3f90c6SAndrew Thompson uaudio_mixer_add_feature(sc, iot, i); 47833a3f90c6SAndrew Thompson break; 47843a3f90c6SAndrew Thompson 47853a3f90c6SAndrew Thompson case UDESCSUB_AC_PROCESSING: 47863a3f90c6SAndrew Thompson uaudio_mixer_add_processing(sc, iot, i); 47873a3f90c6SAndrew Thompson break; 47883a3f90c6SAndrew Thompson 47893a3f90c6SAndrew Thompson case UDESCSUB_AC_EXTENSION: 47903a3f90c6SAndrew Thompson uaudio_mixer_add_extension(sc, iot, i); 47913a3f90c6SAndrew Thompson break; 47923a3f90c6SAndrew Thompson 47933a3f90c6SAndrew Thompson default: 47943a3f90c6SAndrew Thompson DPRINTF("bad AC desc subtype=0x%02x\n", 47953a3f90c6SAndrew Thompson dp->bDescriptorSubtype); 47963a3f90c6SAndrew Thompson break; 47973a3f90c6SAndrew Thompson } 47983a3f90c6SAndrew Thompson 47993a3f90c6SAndrew Thompson } while (i--); 48003a3f90c6SAndrew Thompson 48013a3f90c6SAndrew Thompson done: 48023a3f90c6SAndrew Thompson free(iot, M_TEMP); 48033a3f90c6SAndrew Thompson } 48043a3f90c6SAndrew Thompson 4805e2524b2eSHans Petter Selasky static int 4806e2524b2eSHans Petter Selasky uaudio_mixer_get(struct usb_device *udev, uint16_t audio_rev, 4807e2524b2eSHans Petter Selasky uint8_t what, struct uaudio_mixer_node *mc) 48083a3f90c6SAndrew Thompson { 4809760bc48eSAndrew Thompson struct usb_device_request req; 4810e2524b2eSHans Petter Selasky int val; 4811e2524b2eSHans Petter Selasky uint8_t data[2 + (2 * 3)]; 4812e0a69b51SAndrew Thompson usb_error_t err; 48133a3f90c6SAndrew Thompson 4814e2524b2eSHans Petter Selasky if (mc->wValue[0] == -1) 48153a3f90c6SAndrew Thompson return (0); 4816e2524b2eSHans Petter Selasky 4817e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) 4818e2524b2eSHans Petter Selasky return (0); 4819e2524b2eSHans Petter Selasky else if (audio_rev >= UAUDIO_VERSION_20) { 4820e2524b2eSHans Petter Selasky if (what == GET_CUR) { 4821e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_CUR; 4822e2524b2eSHans Petter Selasky USETW(req.wLength, 2); 4823e2524b2eSHans Petter Selasky } else { 4824e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_RANGE; 4825e2524b2eSHans Petter Selasky USETW(req.wLength, 8); 48263a3f90c6SAndrew Thompson } 4827e2524b2eSHans Petter Selasky } else { 4828e2524b2eSHans Petter Selasky uint16_t len = MIX_SIZE(mc->type); 4829e2524b2eSHans Petter Selasky 48303a3f90c6SAndrew Thompson req.bRequest = what; 4831e2524b2eSHans Petter Selasky USETW(req.wLength, len); 4832e2524b2eSHans Petter Selasky } 4833e2524b2eSHans Petter Selasky 4834e2524b2eSHans Petter Selasky req.bmRequestType = UT_READ_CLASS_INTERFACE; 48353a3f90c6SAndrew Thompson USETW(req.wValue, mc->wValue[0]); 48363a3f90c6SAndrew Thompson USETW(req.wIndex, mc->wIndex); 4837e2524b2eSHans Petter Selasky 4838e2524b2eSHans Petter Selasky memset(data, 0, sizeof(data)); 48393a3f90c6SAndrew Thompson 4840f24b6817SAlfred Perlstein err = usbd_do_request(udev, NULL, &req, data); 48413a3f90c6SAndrew Thompson if (err) { 4842a593f6b8SAndrew Thompson DPRINTF("err=%s\n", usbd_errstr(err)); 48433a3f90c6SAndrew Thompson return (0); 48443a3f90c6SAndrew Thompson } 4845e2524b2eSHans Petter Selasky 4846e2524b2eSHans Petter Selasky if (audio_rev >= UAUDIO_VERSION_30) { 4847e2524b2eSHans Petter Selasky val = 0; 4848e2524b2eSHans Petter Selasky } else if (audio_rev >= UAUDIO_VERSION_20) { 4849e2524b2eSHans Petter Selasky switch (what) { 4850e2524b2eSHans Petter Selasky case GET_CUR: 48513a3f90c6SAndrew Thompson val = (data[0] | (data[1] << 8)); 4852e2524b2eSHans Petter Selasky break; 4853e2524b2eSHans Petter Selasky case GET_MIN: 4854e2524b2eSHans Petter Selasky val = (data[2] | (data[3] << 8)); 4855e2524b2eSHans Petter Selasky break; 4856e2524b2eSHans Petter Selasky case GET_MAX: 4857e2524b2eSHans Petter Selasky val = (data[4] | (data[5] << 8)); 4858e2524b2eSHans Petter Selasky break; 4859e2524b2eSHans Petter Selasky case GET_RES: 4860e2524b2eSHans Petter Selasky val = (data[6] | (data[7] << 8)); 4861e2524b2eSHans Petter Selasky break; 4862e2524b2eSHans Petter Selasky default: 4863e2524b2eSHans Petter Selasky val = 0; 4864e2524b2eSHans Petter Selasky break; 4865e2524b2eSHans Petter Selasky } 4866e2524b2eSHans Petter Selasky } else { 4867e2524b2eSHans Petter Selasky val = (data[0] | (data[1] << 8)); 4868e2524b2eSHans Petter Selasky } 4869e2524b2eSHans Petter Selasky 4870e2524b2eSHans Petter Selasky if (what == GET_CUR || what == GET_MIN || what == GET_MAX) 4871e2524b2eSHans Petter Selasky val = uaudio_mixer_signext(mc->type, val); 48723a3f90c6SAndrew Thompson 48733a3f90c6SAndrew Thompson DPRINTFN(3, "val=%d\n", val); 48743a3f90c6SAndrew Thompson 48753a3f90c6SAndrew Thompson return (val); 48763a3f90c6SAndrew Thompson } 48773a3f90c6SAndrew Thompson 48783a3f90c6SAndrew Thompson static void 4879ed6d949aSAndrew Thompson uaudio_mixer_write_cfg_callback(struct usb_xfer *xfer, usb_error_t error) 48803a3f90c6SAndrew Thompson { 4881760bc48eSAndrew Thompson struct usb_device_request req; 4882ed6d949aSAndrew Thompson struct uaudio_softc *sc = usbd_xfer_softc(xfer); 48833a3f90c6SAndrew Thompson struct uaudio_mixer_node *mc = sc->sc_mixer_curr; 4884ed6d949aSAndrew Thompson struct usb_page_cache *pc; 48853a3f90c6SAndrew Thompson uint16_t len; 48863a3f90c6SAndrew Thompson uint8_t repeat = 1; 48873a3f90c6SAndrew Thompson uint8_t update; 48883a3f90c6SAndrew Thompson uint8_t chan; 48893a3f90c6SAndrew Thompson uint8_t buf[2]; 48903a3f90c6SAndrew Thompson 48913a3f90c6SAndrew Thompson DPRINTF("\n"); 48923a3f90c6SAndrew Thompson 48933a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 48943a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 48953a3f90c6SAndrew Thompson tr_transferred: 48963a3f90c6SAndrew Thompson case USB_ST_SETUP: 48973a3f90c6SAndrew Thompson tr_setup: 48983a3f90c6SAndrew Thompson 48993a3f90c6SAndrew Thompson if (mc == NULL) { 49003a3f90c6SAndrew Thompson mc = sc->sc_mixer_root; 49013a3f90c6SAndrew Thompson sc->sc_mixer_curr = mc; 49023a3f90c6SAndrew Thompson sc->sc_mixer_chan = 0; 49033a3f90c6SAndrew Thompson repeat = 0; 49043a3f90c6SAndrew Thompson } 49053a3f90c6SAndrew Thompson while (mc) { 49063a3f90c6SAndrew Thompson while (sc->sc_mixer_chan < mc->nchan) { 49073a3f90c6SAndrew Thompson 49083a3f90c6SAndrew Thompson chan = sc->sc_mixer_chan; 49093a3f90c6SAndrew Thompson 49103a3f90c6SAndrew Thompson sc->sc_mixer_chan++; 49113a3f90c6SAndrew Thompson 49123a3f90c6SAndrew Thompson update = ((mc->update[chan / 8] & (1 << (chan % 8))) && 49133a3f90c6SAndrew Thompson (mc->wValue[chan] != -1)); 49143a3f90c6SAndrew Thompson 49153a3f90c6SAndrew Thompson mc->update[chan / 8] &= ~(1 << (chan % 8)); 49163a3f90c6SAndrew Thompson 49173a3f90c6SAndrew Thompson if (update) { 49183a3f90c6SAndrew Thompson 49193a3f90c6SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 49203a3f90c6SAndrew Thompson USETW(req.wValue, mc->wValue[chan]); 49213a3f90c6SAndrew Thompson USETW(req.wIndex, mc->wIndex); 49223a3f90c6SAndrew Thompson 4923e2524b2eSHans Petter Selasky if (sc->sc_audio_rev >= UAUDIO_VERSION_30) { 4924e2524b2eSHans Petter Selasky return; 4925e2524b2eSHans Petter Selasky } else if (sc->sc_audio_rev >= UAUDIO_VERSION_20) { 4926e2524b2eSHans Petter Selasky len = 2; 4927e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_CUR; 4928e2524b2eSHans Petter Selasky USETW(req.wLength, len); 4929e2524b2eSHans Petter Selasky } else { 4930e2524b2eSHans Petter Selasky len = MIX_SIZE(mc->type); 4931e2524b2eSHans Petter Selasky req.bRequest = SET_CUR; 4932e2524b2eSHans Petter Selasky USETW(req.wLength, len); 4933e2524b2eSHans Petter Selasky } 4934e2524b2eSHans Petter Selasky 49353a3f90c6SAndrew Thompson buf[0] = (mc->wData[chan] & 0xFF); 49363a3f90c6SAndrew Thompson buf[1] = (mc->wData[chan] >> 8) & 0xFF; 4937e2524b2eSHans Petter Selasky 4938ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 4939ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, &req, sizeof(req)); 4940ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 1); 4941ed6d949aSAndrew Thompson usbd_copy_in(pc, 0, buf, len); 49423a3f90c6SAndrew Thompson 4943ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 4944ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, len); 4945ed6d949aSAndrew Thompson usbd_xfer_set_frames(xfer, len ? 2 : 1); 4946a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 49473a3f90c6SAndrew Thompson return; 49483a3f90c6SAndrew Thompson } 49493a3f90c6SAndrew Thompson } 49503a3f90c6SAndrew Thompson 49513a3f90c6SAndrew Thompson mc = mc->next; 49523a3f90c6SAndrew Thompson sc->sc_mixer_curr = mc; 49533a3f90c6SAndrew Thompson sc->sc_mixer_chan = 0; 49543a3f90c6SAndrew Thompson } 49553a3f90c6SAndrew Thompson 49563a3f90c6SAndrew Thompson if (repeat) { 49573a3f90c6SAndrew Thompson goto tr_setup; 49583a3f90c6SAndrew Thompson } 49593a3f90c6SAndrew Thompson break; 49603a3f90c6SAndrew Thompson 49613a3f90c6SAndrew Thompson default: /* Error */ 4962ed6d949aSAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 4963ed6d949aSAndrew Thompson if (error == USB_ERR_CANCELLED) { 49643a3f90c6SAndrew Thompson /* do nothing - we are detaching */ 49653a3f90c6SAndrew Thompson break; 49663a3f90c6SAndrew Thompson } 49673a3f90c6SAndrew Thompson goto tr_transferred; 49683a3f90c6SAndrew Thompson } 49693a3f90c6SAndrew Thompson } 49703a3f90c6SAndrew Thompson 4971e0a69b51SAndrew Thompson static usb_error_t 4972760bc48eSAndrew Thompson uaudio_set_speed(struct usb_device *udev, uint8_t endpt, uint32_t speed) 49733a3f90c6SAndrew Thompson { 4974760bc48eSAndrew Thompson struct usb_device_request req; 49753a3f90c6SAndrew Thompson uint8_t data[3]; 49763a3f90c6SAndrew Thompson 49773a3f90c6SAndrew Thompson DPRINTFN(6, "endpt=%d speed=%u\n", endpt, speed); 49783a3f90c6SAndrew Thompson 49793a3f90c6SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_ENDPOINT; 49803a3f90c6SAndrew Thompson req.bRequest = SET_CUR; 49813a3f90c6SAndrew Thompson USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0); 49823a3f90c6SAndrew Thompson USETW(req.wIndex, endpt); 49833a3f90c6SAndrew Thompson USETW(req.wLength, 3); 49843a3f90c6SAndrew Thompson data[0] = speed; 49853a3f90c6SAndrew Thompson data[1] = speed >> 8; 49863a3f90c6SAndrew Thompson data[2] = speed >> 16; 49873a3f90c6SAndrew Thompson 4988f24b6817SAlfred Perlstein return (usbd_do_request(udev, NULL, &req, data)); 49893a3f90c6SAndrew Thompson } 49903a3f90c6SAndrew Thompson 4991e2524b2eSHans Petter Selasky static usb_error_t 4992e2524b2eSHans Petter Selasky uaudio20_set_speed(struct usb_device *udev, uint8_t iface_no, 4993e2524b2eSHans Petter Selasky uint8_t clockid, uint32_t speed) 4994e2524b2eSHans Petter Selasky { 4995e2524b2eSHans Petter Selasky struct usb_device_request req; 4996e2524b2eSHans Petter Selasky uint8_t data[4]; 4997e2524b2eSHans Petter Selasky 4998e2524b2eSHans Petter Selasky DPRINTFN(6, "ifaceno=%d clockid=%d speed=%u\n", 4999e2524b2eSHans Petter Selasky iface_no, clockid, speed); 5000e2524b2eSHans Petter Selasky 5001e2524b2eSHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 5002e2524b2eSHans Petter Selasky req.bRequest = UA20_CS_CUR; 5003e2524b2eSHans Petter Selasky USETW2(req.wValue, UA20_CS_SAM_FREQ_CONTROL, 0); 5004e2524b2eSHans Petter Selasky USETW2(req.wIndex, clockid, iface_no); 5005e2524b2eSHans Petter Selasky USETW(req.wLength, 4); 5006e2524b2eSHans Petter Selasky data[0] = speed; 5007e2524b2eSHans Petter Selasky data[1] = speed >> 8; 5008e2524b2eSHans Petter Selasky data[2] = speed >> 16; 5009e2524b2eSHans Petter Selasky data[3] = speed >> 24; 5010e2524b2eSHans Petter Selasky 5011e2524b2eSHans Petter Selasky return (usbd_do_request(udev, NULL, &req, data)); 5012e2524b2eSHans Petter Selasky } 5013e2524b2eSHans Petter Selasky 50143a3f90c6SAndrew Thompson static int 50153a3f90c6SAndrew Thompson uaudio_mixer_signext(uint8_t type, int val) 50163a3f90c6SAndrew Thompson { 50173a3f90c6SAndrew Thompson if (!MIX_UNSIGNED(type)) { 50183a3f90c6SAndrew Thompson if (MIX_SIZE(type) == 2) { 50193a3f90c6SAndrew Thompson val = (int16_t)val; 50203a3f90c6SAndrew Thompson } else { 50213a3f90c6SAndrew Thompson val = (int8_t)val; 50223a3f90c6SAndrew Thompson } 50233a3f90c6SAndrew Thompson } 50243a3f90c6SAndrew Thompson return (val); 50253a3f90c6SAndrew Thompson } 50263a3f90c6SAndrew Thompson 50273a3f90c6SAndrew Thompson static int 50283a3f90c6SAndrew Thompson uaudio_mixer_bsd2value(struct uaudio_mixer_node *mc, int32_t val) 50293a3f90c6SAndrew Thompson { 50303a3f90c6SAndrew Thompson if (mc->type == MIX_ON_OFF) { 50313a3f90c6SAndrew Thompson val = (val != 0); 50323a3f90c6SAndrew Thompson } else if (mc->type == MIX_SELECTOR) { 50333a3f90c6SAndrew Thompson if ((val < mc->minval) || 50343a3f90c6SAndrew Thompson (val > mc->maxval)) { 50353a3f90c6SAndrew Thompson val = mc->minval; 50363a3f90c6SAndrew Thompson } 50373a3f90c6SAndrew Thompson } else { 5038b029f6bbSAndrew Thompson 5039b029f6bbSAndrew Thompson /* compute actual volume */ 5040b029f6bbSAndrew Thompson val = (val * mc->mul) / 255; 5041b029f6bbSAndrew Thompson 5042b029f6bbSAndrew Thompson /* add lower offset */ 5043b029f6bbSAndrew Thompson val = val + mc->minval; 5044b029f6bbSAndrew Thompson 5045b029f6bbSAndrew Thompson /* make sure we don't write a value out of range */ 5046b029f6bbSAndrew Thompson if (val > mc->maxval) 5047b029f6bbSAndrew Thompson val = mc->maxval; 5048b029f6bbSAndrew Thompson else if (val < mc->minval) 5049b029f6bbSAndrew Thompson val = mc->minval; 50503a3f90c6SAndrew Thompson } 50513a3f90c6SAndrew Thompson 50523a3f90c6SAndrew Thompson DPRINTFN(6, "type=0x%03x val=%d min=%d max=%d val=%d\n", 50533a3f90c6SAndrew Thompson mc->type, val, mc->minval, mc->maxval, val); 50543a3f90c6SAndrew Thompson return (val); 50553a3f90c6SAndrew Thompson } 50563a3f90c6SAndrew Thompson 50573a3f90c6SAndrew Thompson static void 50583a3f90c6SAndrew Thompson uaudio_mixer_ctl_set(struct uaudio_softc *sc, struct uaudio_mixer_node *mc, 50593a3f90c6SAndrew Thompson uint8_t chan, int32_t val) 50603a3f90c6SAndrew Thompson { 50613a3f90c6SAndrew Thompson val = uaudio_mixer_bsd2value(mc, val); 50623a3f90c6SAndrew Thompson 50633a3f90c6SAndrew Thompson mc->update[chan / 8] |= (1 << (chan % 8)); 50643a3f90c6SAndrew Thompson mc->wData[chan] = val; 50653a3f90c6SAndrew Thompson 50663a3f90c6SAndrew Thompson /* start the transfer, if not already started */ 50673a3f90c6SAndrew Thompson 5068a593f6b8SAndrew Thompson usbd_transfer_start(sc->sc_mixer_xfer[0]); 50693a3f90c6SAndrew Thompson } 50703a3f90c6SAndrew Thompson 50713a3f90c6SAndrew Thompson static void 50723a3f90c6SAndrew Thompson uaudio_mixer_init(struct uaudio_softc *sc) 50733a3f90c6SAndrew Thompson { 50743a3f90c6SAndrew Thompson struct uaudio_mixer_node *mc; 50753a3f90c6SAndrew Thompson int32_t i; 50763a3f90c6SAndrew Thompson 50773a3f90c6SAndrew Thompson for (mc = sc->sc_mixer_root; mc; 50783a3f90c6SAndrew Thompson mc = mc->next) { 50793a3f90c6SAndrew Thompson 50803a3f90c6SAndrew Thompson if (mc->ctl != SOUND_MIXER_NRDEVICES) { 50813a3f90c6SAndrew Thompson /* 50823a3f90c6SAndrew Thompson * Set device mask bits. See 50833a3f90c6SAndrew Thompson * /usr/include/machine/soundcard.h 50843a3f90c6SAndrew Thompson */ 50853a3f90c6SAndrew Thompson sc->sc_mix_info |= (1 << mc->ctl); 50863a3f90c6SAndrew Thompson } 50873a3f90c6SAndrew Thompson if ((mc->ctl == SOUND_MIXER_NRDEVICES) && 50883a3f90c6SAndrew Thompson (mc->type == MIX_SELECTOR)) { 50893a3f90c6SAndrew Thompson 50903a3f90c6SAndrew Thompson for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { 50913a3f90c6SAndrew Thompson if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) { 50923a3f90c6SAndrew Thompson continue; 50933a3f90c6SAndrew Thompson } 50943a3f90c6SAndrew Thompson sc->sc_recsrc_info |= 1 << mc->slctrtype[i - 1]; 50953a3f90c6SAndrew Thompson } 50963a3f90c6SAndrew Thompson } 50973a3f90c6SAndrew Thompson } 50983a3f90c6SAndrew Thompson } 50993a3f90c6SAndrew Thompson 51003a3f90c6SAndrew Thompson int 51013a3f90c6SAndrew Thompson uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m) 51023a3f90c6SAndrew Thompson { 51033a3f90c6SAndrew Thompson DPRINTF("\n"); 51043a3f90c6SAndrew Thompson 5105902514f6SHans Petter Selasky sc->sc_mixer_lock = mixer_get_lock(m); 510676b71212SHans Petter Selasky sc->sc_mixer_dev = m; 5107902514f6SHans Petter Selasky 5108a593f6b8SAndrew Thompson if (usbd_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index, 51093a3f90c6SAndrew Thompson sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc, 5110902514f6SHans Petter Selasky sc->sc_mixer_lock)) { 51113a3f90c6SAndrew Thompson DPRINTFN(0, "could not allocate USB " 51123a3f90c6SAndrew Thompson "transfer for audio mixer!\n"); 51133a3f90c6SAndrew Thompson return (ENOMEM); 51143a3f90c6SAndrew Thompson } 51153a3f90c6SAndrew Thompson if (!(sc->sc_mix_info & SOUND_MASK_VOLUME)) { 51163a3f90c6SAndrew Thompson mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM); 51173a3f90c6SAndrew Thompson mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); 51183a3f90c6SAndrew Thompson } 51193a3f90c6SAndrew Thompson mix_setdevs(m, sc->sc_mix_info); 51203a3f90c6SAndrew Thompson mix_setrecdevs(m, sc->sc_recsrc_info); 51213a3f90c6SAndrew Thompson return (0); 51223a3f90c6SAndrew Thompson } 51233a3f90c6SAndrew Thompson 51243a3f90c6SAndrew Thompson int 51253a3f90c6SAndrew Thompson uaudio_mixer_uninit_sub(struct uaudio_softc *sc) 51263a3f90c6SAndrew Thompson { 51273a3f90c6SAndrew Thompson DPRINTF("\n"); 51283a3f90c6SAndrew Thompson 5129a593f6b8SAndrew Thompson usbd_transfer_unsetup(sc->sc_mixer_xfer, 1); 51303a3f90c6SAndrew Thompson 5131902514f6SHans Petter Selasky sc->sc_mixer_lock = NULL; 5132902514f6SHans Petter Selasky 51333a3f90c6SAndrew Thompson return (0); 51343a3f90c6SAndrew Thompson } 51353a3f90c6SAndrew Thompson 51363a3f90c6SAndrew Thompson void 51373a3f90c6SAndrew Thompson uaudio_mixer_set(struct uaudio_softc *sc, unsigned type, 51383a3f90c6SAndrew Thompson unsigned left, unsigned right) 51393a3f90c6SAndrew Thompson { 51403a3f90c6SAndrew Thompson struct uaudio_mixer_node *mc; 5141902514f6SHans Petter Selasky int chan; 51423a3f90c6SAndrew Thompson 5143902514f6SHans Petter Selasky for (mc = sc->sc_mixer_root; mc != NULL; mc = mc->next) { 51443a3f90c6SAndrew Thompson 51453a3f90c6SAndrew Thompson if (mc->ctl == type) { 5146902514f6SHans Petter Selasky for (chan = 0; chan < mc->nchan; chan++) { 5147902514f6SHans Petter Selasky uaudio_mixer_ctl_set(sc, mc, chan, 5148902514f6SHans Petter Selasky (int)((chan == 0 ? left : right) * 5149902514f6SHans Petter Selasky 255) / 100); 51503a3f90c6SAndrew Thompson } 51513a3f90c6SAndrew Thompson } 51523a3f90c6SAndrew Thompson } 51533a3f90c6SAndrew Thompson } 51543a3f90c6SAndrew Thompson 51553a3f90c6SAndrew Thompson uint32_t 51563a3f90c6SAndrew Thompson uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src) 51573a3f90c6SAndrew Thompson { 51583a3f90c6SAndrew Thompson struct uaudio_mixer_node *mc; 51593a3f90c6SAndrew Thompson uint32_t mask; 51603a3f90c6SAndrew Thompson uint32_t temp; 51613a3f90c6SAndrew Thompson int32_t i; 51623a3f90c6SAndrew Thompson 51633a3f90c6SAndrew Thompson for (mc = sc->sc_mixer_root; mc; 51643a3f90c6SAndrew Thompson mc = mc->next) { 51653a3f90c6SAndrew Thompson 51663a3f90c6SAndrew Thompson if ((mc->ctl == SOUND_MIXER_NRDEVICES) && 51673a3f90c6SAndrew Thompson (mc->type == MIX_SELECTOR)) { 51683a3f90c6SAndrew Thompson 51693a3f90c6SAndrew Thompson /* compute selector mask */ 51703a3f90c6SAndrew Thompson 51713a3f90c6SAndrew Thompson mask = 0; 51723a3f90c6SAndrew Thompson for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { 51733a3f90c6SAndrew Thompson mask |= (1 << mc->slctrtype[i - 1]); 51743a3f90c6SAndrew Thompson } 51753a3f90c6SAndrew Thompson 51763a3f90c6SAndrew Thompson temp = mask & src; 51773a3f90c6SAndrew Thompson if (temp == 0) { 51783a3f90c6SAndrew Thompson continue; 51793a3f90c6SAndrew Thompson } 51803a3f90c6SAndrew Thompson /* find the first set bit */ 51813a3f90c6SAndrew Thompson temp = (-temp) & temp; 51823a3f90c6SAndrew Thompson 51833a3f90c6SAndrew Thompson /* update "src" */ 51843a3f90c6SAndrew Thompson src &= ~mask; 51853a3f90c6SAndrew Thompson src |= temp; 51863a3f90c6SAndrew Thompson 51873a3f90c6SAndrew Thompson for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) { 51883a3f90c6SAndrew Thompson if (temp != (1 << mc->slctrtype[i - 1])) { 51893a3f90c6SAndrew Thompson continue; 51903a3f90c6SAndrew Thompson } 51913a3f90c6SAndrew Thompson uaudio_mixer_ctl_set(sc, mc, 0, i); 51923a3f90c6SAndrew Thompson break; 51933a3f90c6SAndrew Thompson } 51943a3f90c6SAndrew Thompson } 51953a3f90c6SAndrew Thompson } 51963a3f90c6SAndrew Thompson return (src); 51973a3f90c6SAndrew Thompson } 51983a3f90c6SAndrew Thompson 51993a3f90c6SAndrew Thompson /*========================================================================* 52003a3f90c6SAndrew Thompson * MIDI support routines 52013a3f90c6SAndrew Thompson *========================================================================*/ 52023a3f90c6SAndrew Thompson 52033a3f90c6SAndrew Thompson static void 5204ed6d949aSAndrew Thompson umidi_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) 52053a3f90c6SAndrew Thompson { 5206ed6d949aSAndrew Thompson struct umidi_chan *chan = usbd_xfer_softc(xfer); 52073a3f90c6SAndrew Thompson struct umidi_sub_chan *sub; 5208ed6d949aSAndrew Thompson struct usb_page_cache *pc; 52096f068a43SHans Petter Selasky uint8_t buf[4]; 52103a3f90c6SAndrew Thompson uint8_t cmd_len; 52113a3f90c6SAndrew Thompson uint8_t cn; 52123a3f90c6SAndrew Thompson uint16_t pos; 5213ed6d949aSAndrew Thompson int actlen; 5214ed6d949aSAndrew Thompson 5215ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 52163a3f90c6SAndrew Thompson 52173a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 52183a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 52193a3f90c6SAndrew Thompson 5220ed6d949aSAndrew Thompson DPRINTF("actlen=%d bytes\n", actlen); 52213a3f90c6SAndrew Thompson 52223a3f90c6SAndrew Thompson pos = 0; 5223ed6d949aSAndrew Thompson pc = usbd_xfer_get_frame(xfer, 0); 52243a3f90c6SAndrew Thompson 5225ed6d949aSAndrew Thompson while (actlen >= 4) { 52263a3f90c6SAndrew Thompson 52276f068a43SHans Petter Selasky /* copy out the MIDI data */ 52286f068a43SHans Petter Selasky usbd_copy_out(pc, pos, buf, 4); 52296f068a43SHans Petter Selasky /* command length */ 52306f068a43SHans Petter Selasky cmd_len = umidi_cmd_to_len[buf[0] & 0xF]; 52316f068a43SHans Petter Selasky /* cable number */ 52326f068a43SHans Petter Selasky cn = buf[0] >> 4; 52336f068a43SHans Petter Selasky /* 52346f068a43SHans Petter Selasky * Lookup sub-channel. The index is range 52356f068a43SHans Petter Selasky * checked below. 52366f068a43SHans Petter Selasky */ 52373a3f90c6SAndrew Thompson sub = &chan->sub[cn]; 52383a3f90c6SAndrew Thompson 52396f068a43SHans Petter Selasky if ((cmd_len != 0) && 52406f068a43SHans Petter Selasky (cn < chan->max_cable) && 52416f068a43SHans Petter Selasky (sub->read_open != 0)) { 52423a3f90c6SAndrew Thompson 52436f068a43SHans Petter Selasky /* Send data to the application */ 52446f068a43SHans Petter Selasky usb_fifo_put_data_linear( 52456f068a43SHans Petter Selasky sub->fifo.fp[USB_FIFO_RX], 52466f068a43SHans Petter Selasky buf + 1, cmd_len, 1); 52476f068a43SHans Petter Selasky } 5248ed6d949aSAndrew Thompson actlen -= 4; 52493a3f90c6SAndrew Thompson pos += 4; 52503a3f90c6SAndrew Thompson } 52513a3f90c6SAndrew Thompson 52523a3f90c6SAndrew Thompson case USB_ST_SETUP: 52533a3f90c6SAndrew Thompson DPRINTF("start\n"); 52546f068a43SHans Petter Selasky tr_setup: 5255ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 5256a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 52576f068a43SHans Petter Selasky break; 52583a3f90c6SAndrew Thompson 52593a3f90c6SAndrew Thompson default: 5260ed6d949aSAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 52613a3f90c6SAndrew Thompson 5262ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 52633a3f90c6SAndrew Thompson /* try to clear stall first */ 52646f068a43SHans Petter Selasky usbd_xfer_set_stall(xfer); 52656f068a43SHans Petter Selasky goto tr_setup; 52663a3f90c6SAndrew Thompson } 52676f068a43SHans Petter Selasky break; 52683a3f90c6SAndrew Thompson } 52693a3f90c6SAndrew Thompson } 52703a3f90c6SAndrew Thompson 52713a3f90c6SAndrew Thompson /* 52723a3f90c6SAndrew Thompson * The following statemachine, that converts MIDI commands to 52733a3f90c6SAndrew Thompson * USB MIDI packets, derives from Linux's usbmidi.c, which 52743a3f90c6SAndrew Thompson * was written by "Clemens Ladisch": 52753a3f90c6SAndrew Thompson * 52763a3f90c6SAndrew Thompson * Returns: 52773a3f90c6SAndrew Thompson * 0: No command 52783a3f90c6SAndrew Thompson * Else: Command is complete 52793a3f90c6SAndrew Thompson */ 52803a3f90c6SAndrew Thompson static uint8_t 52813a3f90c6SAndrew Thompson umidi_convert_to_usb(struct umidi_sub_chan *sub, uint8_t cn, uint8_t b) 52823a3f90c6SAndrew Thompson { 52833a3f90c6SAndrew Thompson uint8_t p0 = (cn << 4); 52843a3f90c6SAndrew Thompson 52853a3f90c6SAndrew Thompson if (b >= 0xf8) { 52863a3f90c6SAndrew Thompson sub->temp_0[0] = p0 | 0x0f; 52873a3f90c6SAndrew Thompson sub->temp_0[1] = b; 52883a3f90c6SAndrew Thompson sub->temp_0[2] = 0; 52893a3f90c6SAndrew Thompson sub->temp_0[3] = 0; 52903a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_0; 52913a3f90c6SAndrew Thompson return (1); 52923a3f90c6SAndrew Thompson 52933a3f90c6SAndrew Thompson } else if (b >= 0xf0) { 52943a3f90c6SAndrew Thompson switch (b) { 52953a3f90c6SAndrew Thompson case 0xf0: /* system exclusive begin */ 52963a3f90c6SAndrew Thompson sub->temp_1[1] = b; 52973a3f90c6SAndrew Thompson sub->state = UMIDI_ST_SYSEX_1; 52983a3f90c6SAndrew Thompson break; 52993a3f90c6SAndrew Thompson case 0xf1: /* MIDI time code */ 53003a3f90c6SAndrew Thompson case 0xf3: /* song select */ 53013a3f90c6SAndrew Thompson sub->temp_1[1] = b; 53023a3f90c6SAndrew Thompson sub->state = UMIDI_ST_1PARAM; 53033a3f90c6SAndrew Thompson break; 53043a3f90c6SAndrew Thompson case 0xf2: /* song position pointer */ 53053a3f90c6SAndrew Thompson sub->temp_1[1] = b; 53063a3f90c6SAndrew Thompson sub->state = UMIDI_ST_2PARAM_1; 53073a3f90c6SAndrew Thompson break; 53083a3f90c6SAndrew Thompson case 0xf4: /* unknown */ 53093a3f90c6SAndrew Thompson case 0xf5: /* unknown */ 53103a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 53113a3f90c6SAndrew Thompson break; 53123a3f90c6SAndrew Thompson case 0xf6: /* tune request */ 53133a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x05; 53143a3f90c6SAndrew Thompson sub->temp_1[1] = 0xf6; 53153a3f90c6SAndrew Thompson sub->temp_1[2] = 0; 53163a3f90c6SAndrew Thompson sub->temp_1[3] = 0; 53173a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 53183a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 53193a3f90c6SAndrew Thompson return (1); 53203a3f90c6SAndrew Thompson 53213a3f90c6SAndrew Thompson case 0xf7: /* system exclusive end */ 53223a3f90c6SAndrew Thompson switch (sub->state) { 53233a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_0: 53243a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x05; 53253a3f90c6SAndrew Thompson sub->temp_1[1] = 0xf7; 53263a3f90c6SAndrew Thompson sub->temp_1[2] = 0; 53273a3f90c6SAndrew Thompson sub->temp_1[3] = 0; 53283a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 53293a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 53303a3f90c6SAndrew Thompson return (1); 53313a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_1: 53323a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x06; 53333a3f90c6SAndrew Thompson sub->temp_1[2] = 0xf7; 53343a3f90c6SAndrew Thompson sub->temp_1[3] = 0; 53353a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 53363a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 53373a3f90c6SAndrew Thompson return (1); 53383a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_2: 53393a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x07; 53403a3f90c6SAndrew Thompson sub->temp_1[3] = 0xf7; 53413a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 53423a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 53433a3f90c6SAndrew Thompson return (1); 53443a3f90c6SAndrew Thompson } 53453a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 53463a3f90c6SAndrew Thompson break; 53473a3f90c6SAndrew Thompson } 53483a3f90c6SAndrew Thompson } else if (b >= 0x80) { 53493a3f90c6SAndrew Thompson sub->temp_1[1] = b; 53503a3f90c6SAndrew Thompson if ((b >= 0xc0) && (b <= 0xdf)) { 53513a3f90c6SAndrew Thompson sub->state = UMIDI_ST_1PARAM; 53523a3f90c6SAndrew Thompson } else { 53533a3f90c6SAndrew Thompson sub->state = UMIDI_ST_2PARAM_1; 53543a3f90c6SAndrew Thompson } 53553a3f90c6SAndrew Thompson } else { /* b < 0x80 */ 53563a3f90c6SAndrew Thompson switch (sub->state) { 53573a3f90c6SAndrew Thompson case UMIDI_ST_1PARAM: 53583a3f90c6SAndrew Thompson if (sub->temp_1[1] < 0xf0) { 53593a3f90c6SAndrew Thompson p0 |= sub->temp_1[1] >> 4; 53603a3f90c6SAndrew Thompson } else { 53613a3f90c6SAndrew Thompson p0 |= 0x02; 53623a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 53633a3f90c6SAndrew Thompson } 53643a3f90c6SAndrew Thompson sub->temp_1[0] = p0; 53653a3f90c6SAndrew Thompson sub->temp_1[2] = b; 53663a3f90c6SAndrew Thompson sub->temp_1[3] = 0; 53673a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 53683a3f90c6SAndrew Thompson return (1); 53693a3f90c6SAndrew Thompson case UMIDI_ST_2PARAM_1: 53703a3f90c6SAndrew Thompson sub->temp_1[2] = b; 53713a3f90c6SAndrew Thompson sub->state = UMIDI_ST_2PARAM_2; 53723a3f90c6SAndrew Thompson break; 53733a3f90c6SAndrew Thompson case UMIDI_ST_2PARAM_2: 53743a3f90c6SAndrew Thompson if (sub->temp_1[1] < 0xf0) { 53753a3f90c6SAndrew Thompson p0 |= sub->temp_1[1] >> 4; 53763a3f90c6SAndrew Thompson sub->state = UMIDI_ST_2PARAM_1; 53773a3f90c6SAndrew Thompson } else { 53783a3f90c6SAndrew Thompson p0 |= 0x03; 53793a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 53803a3f90c6SAndrew Thompson } 53813a3f90c6SAndrew Thompson sub->temp_1[0] = p0; 53823a3f90c6SAndrew Thompson sub->temp_1[3] = b; 53833a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 53843a3f90c6SAndrew Thompson return (1); 53853a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_0: 53863a3f90c6SAndrew Thompson sub->temp_1[1] = b; 53873a3f90c6SAndrew Thompson sub->state = UMIDI_ST_SYSEX_1; 53883a3f90c6SAndrew Thompson break; 53893a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_1: 53903a3f90c6SAndrew Thompson sub->temp_1[2] = b; 53913a3f90c6SAndrew Thompson sub->state = UMIDI_ST_SYSEX_2; 53923a3f90c6SAndrew Thompson break; 53933a3f90c6SAndrew Thompson case UMIDI_ST_SYSEX_2: 53943a3f90c6SAndrew Thompson sub->temp_1[0] = p0 | 0x04; 53953a3f90c6SAndrew Thompson sub->temp_1[3] = b; 53963a3f90c6SAndrew Thompson sub->temp_cmd = sub->temp_1; 53973a3f90c6SAndrew Thompson sub->state = UMIDI_ST_SYSEX_0; 53983a3f90c6SAndrew Thompson return (1); 53996f068a43SHans Petter Selasky default: 54006f068a43SHans Petter Selasky break; 54013a3f90c6SAndrew Thompson } 54023a3f90c6SAndrew Thompson } 54033a3f90c6SAndrew Thompson return (0); 54043a3f90c6SAndrew Thompson } 54053a3f90c6SAndrew Thompson 54063a3f90c6SAndrew Thompson static void 5407ed6d949aSAndrew Thompson umidi_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) 54083a3f90c6SAndrew Thompson { 5409ed6d949aSAndrew Thompson struct umidi_chan *chan = usbd_xfer_softc(xfer); 54103a3f90c6SAndrew Thompson struct umidi_sub_chan *sub; 5411ed6d949aSAndrew Thompson struct usb_page_cache *pc; 54123a3f90c6SAndrew Thompson uint32_t actlen; 5413910f1dcfSHans Petter Selasky uint16_t nframes; 54143a3f90c6SAndrew Thompson uint8_t buf; 54153a3f90c6SAndrew Thompson uint8_t start_cable; 54163a3f90c6SAndrew Thompson uint8_t tr_any; 5417ed6d949aSAndrew Thompson int len; 5418ed6d949aSAndrew Thompson 5419ed6d949aSAndrew Thompson usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 54203a3f90c6SAndrew Thompson 5421910f1dcfSHans Petter Selasky /* 5422910f1dcfSHans Petter Selasky * NOTE: Some MIDI devices only accept 4 bytes of data per 5423910f1dcfSHans Petter Selasky * short terminated USB transfer. 5424910f1dcfSHans Petter Selasky */ 54253a3f90c6SAndrew Thompson switch (USB_GET_STATE(xfer)) { 54263a3f90c6SAndrew Thompson case USB_ST_TRANSFERRED: 5427ed6d949aSAndrew Thompson DPRINTF("actlen=%d bytes\n", len); 54283a3f90c6SAndrew Thompson 54293a3f90c6SAndrew Thompson case USB_ST_SETUP: 54306f068a43SHans Petter Selasky tr_setup: 54313a3f90c6SAndrew Thompson DPRINTF("start\n"); 54323a3f90c6SAndrew Thompson 5433910f1dcfSHans Petter Selasky nframes = 0; /* reset */ 54343a3f90c6SAndrew Thompson start_cable = chan->curr_cable; 54353a3f90c6SAndrew Thompson tr_any = 0; 54364944c3a8SHans Petter Selasky pc = usbd_xfer_get_frame(xfer, 0); 54373a3f90c6SAndrew Thompson 54383a3f90c6SAndrew Thompson while (1) { 54393a3f90c6SAndrew Thompson 54403a3f90c6SAndrew Thompson /* round robin de-queueing */ 54413a3f90c6SAndrew Thompson 54423a3f90c6SAndrew Thompson sub = &chan->sub[chan->curr_cable]; 54433a3f90c6SAndrew Thompson 54443a3f90c6SAndrew Thompson if (sub->write_open) { 5445910f1dcfSHans Petter Selasky usb_fifo_get_data_linear(sub->fifo.fp[USB_FIFO_TX], 5446910f1dcfSHans Petter Selasky &buf, 1, &actlen, 0); 54473a3f90c6SAndrew Thompson } else { 54483a3f90c6SAndrew Thompson actlen = 0; 54493a3f90c6SAndrew Thompson } 54503a3f90c6SAndrew Thompson 54513a3f90c6SAndrew Thompson if (actlen) { 54523a3f90c6SAndrew Thompson 54533a3f90c6SAndrew Thompson tr_any = 1; 54543a3f90c6SAndrew Thompson 5455910f1dcfSHans Petter Selasky DPRINTF("byte=0x%02x from FIFO %u\n", buf, 5456910f1dcfSHans Petter Selasky (unsigned int)chan->curr_cable); 54573a3f90c6SAndrew Thompson 54583a3f90c6SAndrew Thompson if (umidi_convert_to_usb(sub, chan->curr_cable, buf)) { 54593a3f90c6SAndrew Thompson 5460910f1dcfSHans Petter Selasky DPRINTF("sub=0x%02x 0x%02x 0x%02x 0x%02x\n", 54613a3f90c6SAndrew Thompson sub->temp_cmd[0], sub->temp_cmd[1], 54623a3f90c6SAndrew Thompson sub->temp_cmd[2], sub->temp_cmd[3]); 54633a3f90c6SAndrew Thompson 54644944c3a8SHans Petter Selasky usbd_copy_in(pc, nframes * 4, sub->temp_cmd, 4); 5465910f1dcfSHans Petter Selasky 5466910f1dcfSHans Petter Selasky nframes++; 54674944c3a8SHans Petter Selasky 54684944c3a8SHans Petter Selasky if ((nframes >= UMIDI_TX_FRAMES) || (chan->single_command != 0)) 54693a3f90c6SAndrew Thompson break; 54703a3f90c6SAndrew Thompson } else { 54713a3f90c6SAndrew Thompson continue; 54723a3f90c6SAndrew Thompson } 54733a3f90c6SAndrew Thompson } 5474910f1dcfSHans Petter Selasky 54753a3f90c6SAndrew Thompson chan->curr_cable++; 5476910f1dcfSHans Petter Selasky if (chan->curr_cable >= chan->max_cable) 54773a3f90c6SAndrew Thompson chan->curr_cable = 0; 5478910f1dcfSHans Petter Selasky 54793a3f90c6SAndrew Thompson if (chan->curr_cable == start_cable) { 5480910f1dcfSHans Petter Selasky if (tr_any == 0) 54813a3f90c6SAndrew Thompson break; 54823a3f90c6SAndrew Thompson tr_any = 0; 54833a3f90c6SAndrew Thompson } 54843a3f90c6SAndrew Thompson } 54853a3f90c6SAndrew Thompson 54864944c3a8SHans Petter Selasky if (nframes != 0) { 5487910f1dcfSHans Petter Selasky DPRINTF("Transferring %d frames\n", (int)nframes); 54884944c3a8SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, 4 * nframes); 5489a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 54903a3f90c6SAndrew Thompson } 54916f068a43SHans Petter Selasky break; 54923a3f90c6SAndrew Thompson 54933a3f90c6SAndrew Thompson default: /* Error */ 54943a3f90c6SAndrew Thompson 5495ed6d949aSAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 54963a3f90c6SAndrew Thompson 5497ed6d949aSAndrew Thompson if (error != USB_ERR_CANCELLED) { 54983a3f90c6SAndrew Thompson /* try to clear stall first */ 54996f068a43SHans Petter Selasky usbd_xfer_set_stall(xfer); 55006f068a43SHans Petter Selasky goto tr_setup; 55013a3f90c6SAndrew Thompson } 55026f068a43SHans Petter Selasky break; 55033a3f90c6SAndrew Thompson } 55043a3f90c6SAndrew Thompson } 55053a3f90c6SAndrew Thompson 55063a3f90c6SAndrew Thompson static struct umidi_sub_chan * 5507760bc48eSAndrew Thompson umidi_sub_by_fifo(struct usb_fifo *fifo) 55083a3f90c6SAndrew Thompson { 5509ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 55103a3f90c6SAndrew Thompson struct umidi_sub_chan *sub; 55113a3f90c6SAndrew Thompson uint32_t n; 55123a3f90c6SAndrew Thompson 55133a3f90c6SAndrew Thompson for (n = 0; n < UMIDI_CABLES_MAX; n++) { 55143a3f90c6SAndrew Thompson sub = &chan->sub[n]; 55153a3f90c6SAndrew Thompson if ((sub->fifo.fp[USB_FIFO_RX] == fifo) || 55163a3f90c6SAndrew Thompson (sub->fifo.fp[USB_FIFO_TX] == fifo)) { 55173a3f90c6SAndrew Thompson return (sub); 55183a3f90c6SAndrew Thompson } 55193a3f90c6SAndrew Thompson } 55203a3f90c6SAndrew Thompson 5521760bc48eSAndrew Thompson panic("%s:%d cannot find usb_fifo!\n", 55223a3f90c6SAndrew Thompson __FILE__, __LINE__); 55233a3f90c6SAndrew Thompson 55243a3f90c6SAndrew Thompson return (NULL); 55253a3f90c6SAndrew Thompson } 55263a3f90c6SAndrew Thompson 55273a3f90c6SAndrew Thompson static void 5528760bc48eSAndrew Thompson umidi_start_read(struct usb_fifo *fifo) 55293a3f90c6SAndrew Thompson { 5530ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 55313a3f90c6SAndrew Thompson 55326f068a43SHans Petter Selasky usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]); 55333a3f90c6SAndrew Thompson } 55343a3f90c6SAndrew Thompson 55353a3f90c6SAndrew Thompson static void 5536760bc48eSAndrew Thompson umidi_stop_read(struct usb_fifo *fifo) 55373a3f90c6SAndrew Thompson { 5538ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 55393a3f90c6SAndrew Thompson struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); 55403a3f90c6SAndrew Thompson 55413a3f90c6SAndrew Thompson DPRINTF("\n"); 55423a3f90c6SAndrew Thompson 55433a3f90c6SAndrew Thompson sub->read_open = 0; 55443a3f90c6SAndrew Thompson 55453a3f90c6SAndrew Thompson if (--(chan->read_open_refcount) == 0) { 55463a3f90c6SAndrew Thompson /* 55473a3f90c6SAndrew Thompson * XXX don't stop the read transfer here, hence that causes 55483a3f90c6SAndrew Thompson * problems with some MIDI adapters 55493a3f90c6SAndrew Thompson */ 55503a3f90c6SAndrew Thompson DPRINTF("(stopping read transfer)\n"); 55513a3f90c6SAndrew Thompson } 55523a3f90c6SAndrew Thompson } 55533a3f90c6SAndrew Thompson 55543a3f90c6SAndrew Thompson static void 5555760bc48eSAndrew Thompson umidi_start_write(struct usb_fifo *fifo) 55563a3f90c6SAndrew Thompson { 5557ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 55583a3f90c6SAndrew Thompson 55596f068a43SHans Petter Selasky usbd_transfer_start(chan->xfer[UMIDI_TX_TRANSFER]); 55603a3f90c6SAndrew Thompson } 55613a3f90c6SAndrew Thompson 55623a3f90c6SAndrew Thompson static void 5563760bc48eSAndrew Thompson umidi_stop_write(struct usb_fifo *fifo) 55643a3f90c6SAndrew Thompson { 5565ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 55663a3f90c6SAndrew Thompson struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); 55673a3f90c6SAndrew Thompson 55683a3f90c6SAndrew Thompson DPRINTF("\n"); 55693a3f90c6SAndrew Thompson 55703a3f90c6SAndrew Thompson sub->write_open = 0; 55713a3f90c6SAndrew Thompson 55723a3f90c6SAndrew Thompson if (--(chan->write_open_refcount) == 0) { 55733a3f90c6SAndrew Thompson DPRINTF("(stopping write transfer)\n"); 55746f068a43SHans Petter Selasky usbd_transfer_stop(chan->xfer[UMIDI_TX_TRANSFER]); 55753a3f90c6SAndrew Thompson } 55763a3f90c6SAndrew Thompson } 55773a3f90c6SAndrew Thompson 55783a3f90c6SAndrew Thompson static int 5579760bc48eSAndrew Thompson umidi_open(struct usb_fifo *fifo, int fflags) 55803a3f90c6SAndrew Thompson { 5581ed6d949aSAndrew Thompson struct umidi_chan *chan = usb_fifo_softc(fifo); 55823a3f90c6SAndrew Thompson struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo); 55833a3f90c6SAndrew Thompson 55843a3f90c6SAndrew Thompson if (fflags & FREAD) { 5585a593f6b8SAndrew Thompson if (usb_fifo_alloc_buffer(fifo, 4, (1024 / 4))) { 55863a3f90c6SAndrew Thompson return (ENOMEM); 55873a3f90c6SAndrew Thompson } 55887e6e6b67SAndrew Thompson mtx_lock(&chan->mtx); 55893a3f90c6SAndrew Thompson chan->read_open_refcount++; 55903a3f90c6SAndrew Thompson sub->read_open = 1; 55917e6e6b67SAndrew Thompson mtx_unlock(&chan->mtx); 55923a3f90c6SAndrew Thompson } 55933a3f90c6SAndrew Thompson if (fflags & FWRITE) { 5594a593f6b8SAndrew Thompson if (usb_fifo_alloc_buffer(fifo, 32, (1024 / 32))) { 55953a3f90c6SAndrew Thompson return (ENOMEM); 55963a3f90c6SAndrew Thompson } 55973a3f90c6SAndrew Thompson /* clear stall first */ 55987e6e6b67SAndrew Thompson mtx_lock(&chan->mtx); 55993a3f90c6SAndrew Thompson chan->write_open_refcount++; 56003a3f90c6SAndrew Thompson sub->write_open = 1; 56013a3f90c6SAndrew Thompson 56023a3f90c6SAndrew Thompson /* reset */ 56033a3f90c6SAndrew Thompson sub->state = UMIDI_ST_UNKNOWN; 56047e6e6b67SAndrew Thompson mtx_unlock(&chan->mtx); 56053a3f90c6SAndrew Thompson } 56063a3f90c6SAndrew Thompson return (0); /* success */ 56073a3f90c6SAndrew Thompson } 56083a3f90c6SAndrew Thompson 56093a3f90c6SAndrew Thompson static void 5610760bc48eSAndrew Thompson umidi_close(struct usb_fifo *fifo, int fflags) 56113a3f90c6SAndrew Thompson { 56123a3f90c6SAndrew Thompson if (fflags & FREAD) { 5613a593f6b8SAndrew Thompson usb_fifo_free_buffer(fifo); 56143a3f90c6SAndrew Thompson } 56153a3f90c6SAndrew Thompson if (fflags & FWRITE) { 5616a593f6b8SAndrew Thompson usb_fifo_free_buffer(fifo); 56173a3f90c6SAndrew Thompson } 56183a3f90c6SAndrew Thompson } 56193a3f90c6SAndrew Thompson 56203a3f90c6SAndrew Thompson 56213a3f90c6SAndrew Thompson static int 5622760bc48eSAndrew Thompson umidi_ioctl(struct usb_fifo *fifo, u_long cmd, void *data, 5623ee3e3ff5SAndrew Thompson int fflags) 56243a3f90c6SAndrew Thompson { 56253a3f90c6SAndrew Thompson return (ENODEV); 56263a3f90c6SAndrew Thompson } 56273a3f90c6SAndrew Thompson 56283a3f90c6SAndrew Thompson static void 56293a3f90c6SAndrew Thompson umidi_init(device_t dev) 56303a3f90c6SAndrew Thompson { 56313a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 56323a3f90c6SAndrew Thompson struct umidi_chan *chan = &sc->sc_midi_chan; 56333a3f90c6SAndrew Thompson 56343a3f90c6SAndrew Thompson mtx_init(&chan->mtx, "umidi lock", NULL, MTX_DEF | MTX_RECURSE); 56353a3f90c6SAndrew Thompson } 56363a3f90c6SAndrew Thompson 5637760bc48eSAndrew Thompson static struct usb_fifo_methods umidi_fifo_methods = { 56383a3f90c6SAndrew Thompson .f_start_read = &umidi_start_read, 56393a3f90c6SAndrew Thompson .f_start_write = &umidi_start_write, 56403a3f90c6SAndrew Thompson .f_stop_read = &umidi_stop_read, 56413a3f90c6SAndrew Thompson .f_stop_write = &umidi_stop_write, 56423a3f90c6SAndrew Thompson .f_open = &umidi_open, 56433a3f90c6SAndrew Thompson .f_close = &umidi_close, 56443a3f90c6SAndrew Thompson .f_ioctl = &umidi_ioctl, 56453a3f90c6SAndrew Thompson .basename[0] = "umidi", 56463a3f90c6SAndrew Thompson }; 56473a3f90c6SAndrew Thompson 564825b74dabSHans Petter Selasky static int 56493a3f90c6SAndrew Thompson umidi_probe(device_t dev) 56503a3f90c6SAndrew Thompson { 56513a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 5652760bc48eSAndrew Thompson struct usb_attach_arg *uaa = device_get_ivars(dev); 56533a3f90c6SAndrew Thompson struct umidi_chan *chan = &sc->sc_midi_chan; 56543a3f90c6SAndrew Thompson struct umidi_sub_chan *sub; 56553a3f90c6SAndrew Thompson int unit = device_get_unit(dev); 56563a3f90c6SAndrew Thompson int error; 56573a3f90c6SAndrew Thompson uint32_t n; 56583a3f90c6SAndrew Thompson 56594944c3a8SHans Petter Selasky if (usb_test_quirk(uaa, UQ_SINGLE_CMD_MIDI)) 56604944c3a8SHans Petter Selasky chan->single_command = 1; 56614944c3a8SHans Petter Selasky 5662a593f6b8SAndrew Thompson if (usbd_set_alt_interface_index(sc->sc_udev, chan->iface_index, 56633a3f90c6SAndrew Thompson chan->iface_alt_index)) { 56643a3f90c6SAndrew Thompson DPRINTF("setting of alternate index failed!\n"); 56653a3f90c6SAndrew Thompson goto detach; 56663a3f90c6SAndrew Thompson } 56676f068a43SHans Petter Selasky usbd_set_parent_iface(sc->sc_udev, chan->iface_index, 56686f068a43SHans Petter Selasky sc->sc_mixer_iface_index); 56693a3f90c6SAndrew Thompson 5670a593f6b8SAndrew Thompson error = usbd_transfer_setup(uaa->device, &chan->iface_index, 56713a3f90c6SAndrew Thompson chan->xfer, umidi_config, UMIDI_N_TRANSFER, 56723a3f90c6SAndrew Thompson chan, &chan->mtx); 56733a3f90c6SAndrew Thompson if (error) { 5674a593f6b8SAndrew Thompson DPRINTF("error=%s\n", usbd_errstr(error)); 56753a3f90c6SAndrew Thompson goto detach; 56763a3f90c6SAndrew Thompson } 5677e91fe3a9SHans Petter Selasky 5678e91fe3a9SHans Petter Selasky /* 5679e91fe3a9SHans Petter Selasky * Some USB MIDI device makers couldn't resist using 5680e91fe3a9SHans Petter Selasky * wMaxPacketSize = 4 for RX and TX BULK endpoints, although 5681e91fe3a9SHans Petter Selasky * that size is an unsupported value for FULL speed BULK 5682e91fe3a9SHans Petter Selasky * endpoints. The same applies to some HIGH speed MIDI devices 5683e91fe3a9SHans Petter Selasky * which are using a wMaxPacketSize different from 512 bytes. 5684e91fe3a9SHans Petter Selasky * 5685e91fe3a9SHans Petter Selasky * Refer to section 5.8.3 in USB 2.0 PDF: Cite: "All Host 5686e91fe3a9SHans Petter Selasky * Controllers are required to have support for 8-, 16-, 32-, 5687e91fe3a9SHans Petter Selasky * and 64-byte maximum packet sizes for full-speed bulk 5688e91fe3a9SHans Petter Selasky * endpoints and 512 bytes for high-speed bulk endpoints." 5689e91fe3a9SHans Petter Selasky */ 5690e91fe3a9SHans Petter Selasky if (usbd_xfer_maxp_was_clamped(chan->xfer[UMIDI_TX_TRANSFER])) 5691e91fe3a9SHans Petter Selasky chan->single_command = 1; 5692e91fe3a9SHans Petter Selasky 5693e91fe3a9SHans Petter Selasky if (chan->single_command != 0) 5694e91fe3a9SHans Petter Selasky device_printf(dev, "Single command MIDI quirk enabled\n"); 5695e91fe3a9SHans Petter Selasky 56963a3f90c6SAndrew Thompson if ((chan->max_cable > UMIDI_CABLES_MAX) || 56973a3f90c6SAndrew Thompson (chan->max_cable == 0)) { 56983a3f90c6SAndrew Thompson chan->max_cable = UMIDI_CABLES_MAX; 56993a3f90c6SAndrew Thompson } 57003a3f90c6SAndrew Thompson 57013a3f90c6SAndrew Thompson for (n = 0; n < chan->max_cable; n++) { 57023a3f90c6SAndrew Thompson 57033a3f90c6SAndrew Thompson sub = &chan->sub[n]; 57043a3f90c6SAndrew Thompson 5705a593f6b8SAndrew Thompson error = usb_fifo_attach(sc->sc_udev, chan, &chan->mtx, 57063a3f90c6SAndrew Thompson &umidi_fifo_methods, &sub->fifo, unit, n, 5707ee3e3ff5SAndrew Thompson chan->iface_index, 5708ee3e3ff5SAndrew Thompson UID_ROOT, GID_OPERATOR, 0644); 57093a3f90c6SAndrew Thompson if (error) { 57103a3f90c6SAndrew Thompson goto detach; 57113a3f90c6SAndrew Thompson } 57123a3f90c6SAndrew Thompson } 57133a3f90c6SAndrew Thompson 57143a3f90c6SAndrew Thompson mtx_lock(&chan->mtx); 57153a3f90c6SAndrew Thompson 57163a3f90c6SAndrew Thompson /* 57176f068a43SHans Petter Selasky * NOTE: At least one device will not work properly unless the 57186f068a43SHans Petter Selasky * BULK IN pipe is open all the time. This might have to do 57196f068a43SHans Petter Selasky * about that the internal queues of the device overflow if we 57206f068a43SHans Petter Selasky * don't read them regularly. 57213a3f90c6SAndrew Thompson */ 57226f068a43SHans Petter Selasky usbd_transfer_start(chan->xfer[UMIDI_RX_TRANSFER]); 57233a3f90c6SAndrew Thompson 57243a3f90c6SAndrew Thompson mtx_unlock(&chan->mtx); 57253a3f90c6SAndrew Thompson 57263a3f90c6SAndrew Thompson return (0); /* success */ 57273a3f90c6SAndrew Thompson 57283a3f90c6SAndrew Thompson detach: 57293a3f90c6SAndrew Thompson return (ENXIO); /* failure */ 57303a3f90c6SAndrew Thompson } 57313a3f90c6SAndrew Thompson 573225b74dabSHans Petter Selasky static int 57333a3f90c6SAndrew Thompson umidi_detach(device_t dev) 57343a3f90c6SAndrew Thompson { 57353a3f90c6SAndrew Thompson struct uaudio_softc *sc = device_get_softc(dev); 57363a3f90c6SAndrew Thompson struct umidi_chan *chan = &sc->sc_midi_chan; 57373a3f90c6SAndrew Thompson uint32_t n; 57383a3f90c6SAndrew Thompson 57393a3f90c6SAndrew Thompson for (n = 0; n < UMIDI_CABLES_MAX; n++) { 5740a593f6b8SAndrew Thompson usb_fifo_detach(&chan->sub[n].fifo); 57413a3f90c6SAndrew Thompson } 57423a3f90c6SAndrew Thompson 57433a3f90c6SAndrew Thompson mtx_lock(&chan->mtx); 57443a3f90c6SAndrew Thompson 57456f068a43SHans Petter Selasky usbd_transfer_stop(chan->xfer[UMIDI_RX_TRANSFER]); 57463a3f90c6SAndrew Thompson 57473a3f90c6SAndrew Thompson mtx_unlock(&chan->mtx); 57483a3f90c6SAndrew Thompson 5749a593f6b8SAndrew Thompson usbd_transfer_unsetup(chan->xfer, UMIDI_N_TRANSFER); 57503a3f90c6SAndrew Thompson 57513a3f90c6SAndrew Thompson mtx_destroy(&chan->mtx); 57523a3f90c6SAndrew Thompson 57533a3f90c6SAndrew Thompson return (0); 57543a3f90c6SAndrew Thompson } 57553a3f90c6SAndrew Thompson 575676b71212SHans Petter Selasky static void 575776b71212SHans Petter Selasky uaudio_hid_rx_callback(struct usb_xfer *xfer, usb_error_t error) 575876b71212SHans Petter Selasky { 575976b71212SHans Petter Selasky struct uaudio_softc *sc = usbd_xfer_softc(xfer); 576076b71212SHans Petter Selasky const uint8_t *buffer = usbd_xfer_get_frame_buffer(xfer, 0); 576176b71212SHans Petter Selasky struct snd_mixer *m; 576276b71212SHans Petter Selasky uint8_t id; 576376b71212SHans Petter Selasky int actlen; 576476b71212SHans Petter Selasky 576576b71212SHans Petter Selasky usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); 576676b71212SHans Petter Selasky 576776b71212SHans Petter Selasky switch (USB_GET_STATE(xfer)) { 576876b71212SHans Petter Selasky case USB_ST_TRANSFERRED: 576976b71212SHans Petter Selasky DPRINTF("actlen=%d\n", actlen); 577076b71212SHans Petter Selasky 577176b71212SHans Petter Selasky if (actlen != 0 && 577276b71212SHans Petter Selasky (sc->sc_hid.flags & UAUDIO_HID_HAS_ID)) { 577376b71212SHans Petter Selasky id = *buffer; 577476b71212SHans Petter Selasky buffer++; 577576b71212SHans Petter Selasky actlen--; 577676b71212SHans Petter Selasky } else { 577776b71212SHans Petter Selasky id = 0; 577876b71212SHans Petter Selasky } 577976b71212SHans Petter Selasky 578076b71212SHans Petter Selasky m = sc->sc_mixer_dev; 578176b71212SHans Petter Selasky 57822ba0f361SHans Petter Selasky if ((sc->sc_hid.flags & UAUDIO_HID_HAS_MUTE) && 57832ba0f361SHans Petter Selasky (sc->sc_hid.mute_id == id) && 57842ba0f361SHans Petter Selasky hid_get_data(buffer, actlen, 57852ba0f361SHans Petter Selasky &sc->sc_hid.mute_loc)) { 57862ba0f361SHans Petter Selasky 57872ba0f361SHans Petter Selasky DPRINTF("Mute toggle\n"); 57882ba0f361SHans Petter Selasky 57892ba0f361SHans Petter Selasky mixer_hwvol_mute_locked(m); 57902ba0f361SHans Petter Selasky } 57912ba0f361SHans Petter Selasky 579276b71212SHans Petter Selasky if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_UP) && 579376b71212SHans Petter Selasky (sc->sc_hid.volume_up_id == id) && 579476b71212SHans Petter Selasky hid_get_data(buffer, actlen, 579576b71212SHans Petter Selasky &sc->sc_hid.volume_up_loc)) { 579676b71212SHans Petter Selasky 579776b71212SHans Petter Selasky DPRINTF("Volume Up\n"); 579876b71212SHans Petter Selasky 57992ba0f361SHans Petter Selasky mixer_hwvol_step_locked(m, 1, 1); 580076b71212SHans Petter Selasky } 580176b71212SHans Petter Selasky 580276b71212SHans Petter Selasky if ((sc->sc_hid.flags & UAUDIO_HID_HAS_VOLUME_DOWN) && 580376b71212SHans Petter Selasky (sc->sc_hid.volume_down_id == id) && 580476b71212SHans Petter Selasky hid_get_data(buffer, actlen, 580576b71212SHans Petter Selasky &sc->sc_hid.volume_down_loc)) { 580676b71212SHans Petter Selasky 580776b71212SHans Petter Selasky DPRINTF("Volume Down\n"); 580876b71212SHans Petter Selasky 58092ba0f361SHans Petter Selasky mixer_hwvol_step_locked(m, -1, -1); 581076b71212SHans Petter Selasky } 581176b71212SHans Petter Selasky 581276b71212SHans Petter Selasky case USB_ST_SETUP: 581376b71212SHans Petter Selasky tr_setup: 581476b71212SHans Petter Selasky /* check if we can put more data into the FIFO */ 581576b71212SHans Petter Selasky usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); 581676b71212SHans Petter Selasky usbd_transfer_submit(xfer); 581776b71212SHans Petter Selasky break; 581876b71212SHans Petter Selasky 581976b71212SHans Petter Selasky default: /* Error */ 5820e5359a3bSHans Petter Selasky 5821e5359a3bSHans Petter Selasky DPRINTF("error=%s\n", usbd_errstr(error)); 5822e5359a3bSHans Petter Selasky 582376b71212SHans Petter Selasky if (error != USB_ERR_CANCELLED) { 5824e5359a3bSHans Petter Selasky /* try to clear stall first */ 582576b71212SHans Petter Selasky usbd_xfer_set_stall(xfer); 582676b71212SHans Petter Selasky goto tr_setup; 582776b71212SHans Petter Selasky } 582876b71212SHans Petter Selasky break; 582976b71212SHans Petter Selasky } 583076b71212SHans Petter Selasky } 583176b71212SHans Petter Selasky 583276b71212SHans Petter Selasky static int 583376b71212SHans Petter Selasky uaudio_hid_probe(struct uaudio_softc *sc, 583476b71212SHans Petter Selasky struct usb_attach_arg *uaa) 583576b71212SHans Petter Selasky { 583676b71212SHans Petter Selasky void *d_ptr; 583776b71212SHans Petter Selasky uint32_t flags; 583876b71212SHans Petter Selasky uint16_t d_len; 583976b71212SHans Petter Selasky uint8_t id; 584076b71212SHans Petter Selasky int error; 584176b71212SHans Petter Selasky 584276b71212SHans Petter Selasky if (!(sc->sc_hid.flags & UAUDIO_HID_VALID)) 584376b71212SHans Petter Selasky return (-1); 584476b71212SHans Petter Selasky 584576b71212SHans Petter Selasky if (sc->sc_mixer_lock == NULL) 584676b71212SHans Petter Selasky return (-1); 584776b71212SHans Petter Selasky 584876b71212SHans Petter Selasky /* Get HID descriptor */ 584976b71212SHans Petter Selasky error = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, 585076b71212SHans Petter Selasky &d_len, M_TEMP, sc->sc_hid.iface_index); 585176b71212SHans Petter Selasky 585276b71212SHans Petter Selasky if (error) { 585376b71212SHans Petter Selasky DPRINTF("error reading report description\n"); 585476b71212SHans Petter Selasky return (-1); 585576b71212SHans Petter Selasky } 585676b71212SHans Petter Selasky 585776b71212SHans Petter Selasky /* check if there is an ID byte */ 585876b71212SHans Petter Selasky hid_report_size(d_ptr, d_len, hid_input, &id); 585976b71212SHans Petter Selasky 586076b71212SHans Petter Selasky if (id != 0) 586176b71212SHans Petter Selasky sc->sc_hid.flags |= UAUDIO_HID_HAS_ID; 586276b71212SHans Petter Selasky 586376b71212SHans Petter Selasky if (hid_locate(d_ptr, d_len, 586476b71212SHans Petter Selasky HID_USAGE2(HUP_CONSUMER, 0xE9 /* Volume Increment */), 586576b71212SHans Petter Selasky hid_input, 0, &sc->sc_hid.volume_up_loc, &flags, 586676b71212SHans Petter Selasky &sc->sc_hid.volume_up_id)) { 586776b71212SHans Petter Selasky if (flags & HIO_VARIABLE) 586876b71212SHans Petter Selasky sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_UP; 586976b71212SHans Petter Selasky DPRINTFN(1, "Found Volume Up key\n"); 587076b71212SHans Petter Selasky } 587176b71212SHans Petter Selasky 587276b71212SHans Petter Selasky if (hid_locate(d_ptr, d_len, 587376b71212SHans Petter Selasky HID_USAGE2(HUP_CONSUMER, 0xEA /* Volume Decrement */), 587476b71212SHans Petter Selasky hid_input, 0, &sc->sc_hid.volume_down_loc, &flags, 587576b71212SHans Petter Selasky &sc->sc_hid.volume_down_id)) { 587676b71212SHans Petter Selasky if (flags & HIO_VARIABLE) 587776b71212SHans Petter Selasky sc->sc_hid.flags |= UAUDIO_HID_HAS_VOLUME_DOWN; 587876b71212SHans Petter Selasky DPRINTFN(1, "Found Volume Down key\n"); 587976b71212SHans Petter Selasky } 588076b71212SHans Petter Selasky 58812ba0f361SHans Petter Selasky if (hid_locate(d_ptr, d_len, 58822ba0f361SHans Petter Selasky HID_USAGE2(HUP_CONSUMER, 0xE2 /* Mute */), 58832ba0f361SHans Petter Selasky hid_input, 0, &sc->sc_hid.mute_loc, &flags, 58842ba0f361SHans Petter Selasky &sc->sc_hid.mute_id)) { 58852ba0f361SHans Petter Selasky if (flags & HIO_VARIABLE) 58862ba0f361SHans Petter Selasky sc->sc_hid.flags |= UAUDIO_HID_HAS_MUTE; 58872ba0f361SHans Petter Selasky DPRINTFN(1, "Found Mute key\n"); 58882ba0f361SHans Petter Selasky } 58892ba0f361SHans Petter Selasky 589076b71212SHans Petter Selasky free(d_ptr, M_TEMP); 589176b71212SHans Petter Selasky 589276b71212SHans Petter Selasky if (!(sc->sc_hid.flags & (UAUDIO_HID_HAS_VOLUME_UP | 58932ba0f361SHans Petter Selasky UAUDIO_HID_HAS_VOLUME_DOWN | 58942ba0f361SHans Petter Selasky UAUDIO_HID_HAS_MUTE))) { 589576b71212SHans Petter Selasky DPRINTFN(1, "Did not find any volume related keys\n"); 589676b71212SHans Petter Selasky return (-1); 589776b71212SHans Petter Selasky } 589876b71212SHans Petter Selasky 589976b71212SHans Petter Selasky /* prevent the uhid driver from attaching */ 590076b71212SHans Petter Selasky usbd_set_parent_iface(uaa->device, sc->sc_hid.iface_index, 590176b71212SHans Petter Selasky sc->sc_mixer_iface_index); 590276b71212SHans Petter Selasky 590376b71212SHans Petter Selasky /* allocate USB transfers */ 590476b71212SHans Petter Selasky error = usbd_transfer_setup(uaa->device, &sc->sc_hid.iface_index, 590576b71212SHans Petter Selasky sc->sc_hid.xfer, uaudio_hid_config, UAUDIO_HID_N_TRANSFER, 590676b71212SHans Petter Selasky sc, sc->sc_mixer_lock); 590776b71212SHans Petter Selasky if (error) { 590876b71212SHans Petter Selasky DPRINTF("error=%s\n", usbd_errstr(error)); 590976b71212SHans Petter Selasky return (-1); 591076b71212SHans Petter Selasky } 591176b71212SHans Petter Selasky return (0); 591276b71212SHans Petter Selasky } 591376b71212SHans Petter Selasky 591476b71212SHans Petter Selasky static void 591576b71212SHans Petter Selasky uaudio_hid_detach(struct uaudio_softc *sc) 591676b71212SHans Petter Selasky { 591776b71212SHans Petter Selasky usbd_transfer_unsetup(sc->sc_hid.xfer, UAUDIO_HID_N_TRANSFER); 591876b71212SHans Petter Selasky } 591976b71212SHans Petter Selasky 5920af26e3dfSHans Petter Selasky DRIVER_MODULE_ORDERED(uaudio, uhub, uaudio_driver, uaudio_devclass, NULL, 0, SI_ORDER_ANY); 59213a3f90c6SAndrew Thompson MODULE_DEPEND(uaudio, usb, 1, 1, 1); 59223a3f90c6SAndrew Thompson MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 59233a3f90c6SAndrew Thompson MODULE_VERSION(uaudio, 1); 5924