162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (Tentative) USB Audio Driver for ALSA 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Mixer control part 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Many codes borrowed from audio.c by 1062306a36Sopenharmony_ci * Alan Cox (alan@lxorguk.ukuu.org.uk) 1162306a36Sopenharmony_ci * Thomas Sailer (sailer@ife.ee.ethz.ch) 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * TODOs, for both the mixer and the streaming interfaces: 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * - support for UAC2 effect units 1862306a36Sopenharmony_ci * - support for graphical equalizers 1962306a36Sopenharmony_ci * - RANGE and MEM set commands (UAC2) 2062306a36Sopenharmony_ci * - RANGE and MEM interrupt dispatchers (UAC2) 2162306a36Sopenharmony_ci * - audio channel clustering (UAC2) 2262306a36Sopenharmony_ci * - audio sample rate converter units (UAC2) 2362306a36Sopenharmony_ci * - proper handling of clock multipliers (UAC2) 2462306a36Sopenharmony_ci * - dispatch clock change notifications (UAC2) 2562306a36Sopenharmony_ci * - stop PCM streams which use a clock that became invalid 2662306a36Sopenharmony_ci * - stop PCM streams which use a clock selector that has changed 2762306a36Sopenharmony_ci * - parse available sample rates again when clock sources changed 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/bitops.h> 3162306a36Sopenharmony_ci#include <linux/init.h> 3262306a36Sopenharmony_ci#include <linux/list.h> 3362306a36Sopenharmony_ci#include <linux/log2.h> 3462306a36Sopenharmony_ci#include <linux/slab.h> 3562306a36Sopenharmony_ci#include <linux/string.h> 3662306a36Sopenharmony_ci#include <linux/usb.h> 3762306a36Sopenharmony_ci#include <linux/usb/audio.h> 3862306a36Sopenharmony_ci#include <linux/usb/audio-v2.h> 3962306a36Sopenharmony_ci#include <linux/usb/audio-v3.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include <sound/core.h> 4262306a36Sopenharmony_ci#include <sound/control.h> 4362306a36Sopenharmony_ci#include <sound/hwdep.h> 4462306a36Sopenharmony_ci#include <sound/info.h> 4562306a36Sopenharmony_ci#include <sound/tlv.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include "usbaudio.h" 4862306a36Sopenharmony_ci#include "mixer.h" 4962306a36Sopenharmony_ci#include "helper.h" 5062306a36Sopenharmony_ci#include "mixer_quirks.h" 5162306a36Sopenharmony_ci#include "power.h" 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define MAX_ID_ELEMS 256 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct usb_audio_term { 5662306a36Sopenharmony_ci int id; 5762306a36Sopenharmony_ci int type; 5862306a36Sopenharmony_ci int channels; 5962306a36Sopenharmony_ci unsigned int chconfig; 6062306a36Sopenharmony_ci int name; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct usbmix_name_map; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct mixer_build { 6662306a36Sopenharmony_ci struct snd_usb_audio *chip; 6762306a36Sopenharmony_ci struct usb_mixer_interface *mixer; 6862306a36Sopenharmony_ci unsigned char *buffer; 6962306a36Sopenharmony_ci unsigned int buflen; 7062306a36Sopenharmony_ci DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS); 7162306a36Sopenharmony_ci DECLARE_BITMAP(termbitmap, MAX_ID_ELEMS); 7262306a36Sopenharmony_ci struct usb_audio_term oterm; 7362306a36Sopenharmony_ci const struct usbmix_name_map *map; 7462306a36Sopenharmony_ci const struct usbmix_selector_map *selector_map; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/*E-mu 0202/0404/0204 eXtension Unit(XU) control*/ 7862306a36Sopenharmony_cienum { 7962306a36Sopenharmony_ci USB_XU_CLOCK_RATE = 0xe301, 8062306a36Sopenharmony_ci USB_XU_CLOCK_SOURCE = 0xe302, 8162306a36Sopenharmony_ci USB_XU_DIGITAL_IO_STATUS = 0xe303, 8262306a36Sopenharmony_ci USB_XU_DEVICE_OPTIONS = 0xe304, 8362306a36Sopenharmony_ci USB_XU_DIRECT_MONITORING = 0xe305, 8462306a36Sopenharmony_ci USB_XU_METERING = 0xe306 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_cienum { 8762306a36Sopenharmony_ci USB_XU_CLOCK_SOURCE_SELECTOR = 0x02, /* clock source*/ 8862306a36Sopenharmony_ci USB_XU_CLOCK_RATE_SELECTOR = 0x03, /* clock rate */ 8962306a36Sopenharmony_ci USB_XU_DIGITAL_FORMAT_SELECTOR = 0x01, /* the spdif format */ 9062306a36Sopenharmony_ci USB_XU_SOFT_LIMIT_SELECTOR = 0x03 /* soft limiter */ 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * manual mapping of mixer names 9562306a36Sopenharmony_ci * if the mixer topology is too complicated and the parsed names are 9662306a36Sopenharmony_ci * ambiguous, add the entries in usbmixer_maps.c. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci#include "mixer_maps.c" 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic const struct usbmix_name_map * 10162306a36Sopenharmony_cifind_map(const struct usbmix_name_map *p, int unitid, int control) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci if (!p) 10462306a36Sopenharmony_ci return NULL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci for (; p->id; p++) { 10762306a36Sopenharmony_ci if (p->id == unitid && 10862306a36Sopenharmony_ci (!control || !p->control || control == p->control)) 10962306a36Sopenharmony_ci return p; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci return NULL; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* get the mapped name if the unit matches */ 11562306a36Sopenharmony_cistatic int 11662306a36Sopenharmony_cicheck_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci int len; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (!p || !p->name) 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci buflen--; 12462306a36Sopenharmony_ci len = strscpy(buf, p->name, buflen); 12562306a36Sopenharmony_ci return len < 0 ? buflen : len; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* ignore the error value if ignore_ctl_error flag is set */ 12962306a36Sopenharmony_ci#define filter_error(cval, err) \ 13062306a36Sopenharmony_ci ((cval)->head.mixer->ignore_ctl_error ? 0 : (err)) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* check whether the control should be ignored */ 13362306a36Sopenharmony_cistatic inline int 13462306a36Sopenharmony_cicheck_ignored_ctl(const struct usbmix_name_map *p) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci if (!p || p->name || p->dB) 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci return 1; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* dB mapping */ 14262306a36Sopenharmony_cistatic inline void check_mapped_dB(const struct usbmix_name_map *p, 14362306a36Sopenharmony_ci struct usb_mixer_elem_info *cval) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci if (p && p->dB) { 14662306a36Sopenharmony_ci cval->dBmin = p->dB->min; 14762306a36Sopenharmony_ci cval->dBmax = p->dB->max; 14862306a36Sopenharmony_ci cval->min_mute = p->dB->min_mute; 14962306a36Sopenharmony_ci cval->initialized = 1; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* get the mapped selector source name */ 15462306a36Sopenharmony_cistatic int check_mapped_selector_name(struct mixer_build *state, int unitid, 15562306a36Sopenharmony_ci int index, char *buf, int buflen) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci const struct usbmix_selector_map *p; 15862306a36Sopenharmony_ci int len; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!state->selector_map) 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci for (p = state->selector_map; p->id; p++) { 16362306a36Sopenharmony_ci if (p->id == unitid && index < p->count) { 16462306a36Sopenharmony_ci len = strscpy(buf, p->names[index], buflen); 16562306a36Sopenharmony_ci return len < 0 ? buflen : len; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * find an audio control unit with the given unit id 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_cistatic void *find_audio_control_unit(struct mixer_build *state, 17562306a36Sopenharmony_ci unsigned char unit) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci /* we just parse the header */ 17862306a36Sopenharmony_ci struct uac_feature_unit_descriptor *hdr = NULL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr, 18162306a36Sopenharmony_ci USB_DT_CS_INTERFACE)) != NULL) { 18262306a36Sopenharmony_ci if (hdr->bLength >= 4 && 18362306a36Sopenharmony_ci hdr->bDescriptorSubtype >= UAC_INPUT_TERMINAL && 18462306a36Sopenharmony_ci hdr->bDescriptorSubtype <= UAC3_SAMPLE_RATE_CONVERTER && 18562306a36Sopenharmony_ci hdr->bUnitID == unit) 18662306a36Sopenharmony_ci return hdr; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return NULL; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * copy a string with the given id 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic int snd_usb_copy_string_desc(struct snd_usb_audio *chip, 19662306a36Sopenharmony_ci int index, char *buf, int maxlen) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci int len = usb_string(chip->dev, index, buf, maxlen - 1); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (len < 0) 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci buf[len] = 0; 20462306a36Sopenharmony_ci return len; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * convert from the byte/word on usb descriptor to the zero-based integer 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_cistatic int convert_signed_value(struct usb_mixer_elem_info *cval, int val) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci switch (cval->val_type) { 21362306a36Sopenharmony_ci case USB_MIXER_BOOLEAN: 21462306a36Sopenharmony_ci return !!val; 21562306a36Sopenharmony_ci case USB_MIXER_INV_BOOLEAN: 21662306a36Sopenharmony_ci return !val; 21762306a36Sopenharmony_ci case USB_MIXER_U8: 21862306a36Sopenharmony_ci val &= 0xff; 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci case USB_MIXER_S8: 22162306a36Sopenharmony_ci val &= 0xff; 22262306a36Sopenharmony_ci if (val >= 0x80) 22362306a36Sopenharmony_ci val -= 0x100; 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci case USB_MIXER_U16: 22662306a36Sopenharmony_ci val &= 0xffff; 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case USB_MIXER_S16: 22962306a36Sopenharmony_ci val &= 0xffff; 23062306a36Sopenharmony_ci if (val >= 0x8000) 23162306a36Sopenharmony_ci val -= 0x10000; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci return val; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* 23862306a36Sopenharmony_ci * convert from the zero-based int to the byte/word for usb descriptor 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_cistatic int convert_bytes_value(struct usb_mixer_elem_info *cval, int val) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci switch (cval->val_type) { 24362306a36Sopenharmony_ci case USB_MIXER_BOOLEAN: 24462306a36Sopenharmony_ci return !!val; 24562306a36Sopenharmony_ci case USB_MIXER_INV_BOOLEAN: 24662306a36Sopenharmony_ci return !val; 24762306a36Sopenharmony_ci case USB_MIXER_S8: 24862306a36Sopenharmony_ci case USB_MIXER_U8: 24962306a36Sopenharmony_ci return val & 0xff; 25062306a36Sopenharmony_ci case USB_MIXER_S16: 25162306a36Sopenharmony_ci case USB_MIXER_U16: 25262306a36Sopenharmony_ci return val & 0xffff; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci return 0; /* not reached */ 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int get_relative_value(struct usb_mixer_elem_info *cval, int val) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci if (!cval->res) 26062306a36Sopenharmony_ci cval->res = 1; 26162306a36Sopenharmony_ci if (val < cval->min) 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci else if (val >= cval->max) 26462306a36Sopenharmony_ci return DIV_ROUND_UP(cval->max - cval->min, cval->res); 26562306a36Sopenharmony_ci else 26662306a36Sopenharmony_ci return (val - cval->min) / cval->res; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int get_abs_value(struct usb_mixer_elem_info *cval, int val) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci if (val < 0) 27262306a36Sopenharmony_ci return cval->min; 27362306a36Sopenharmony_ci if (!cval->res) 27462306a36Sopenharmony_ci cval->res = 1; 27562306a36Sopenharmony_ci val *= cval->res; 27662306a36Sopenharmony_ci val += cval->min; 27762306a36Sopenharmony_ci if (val > cval->max) 27862306a36Sopenharmony_ci return cval->max; 27962306a36Sopenharmony_ci return val; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int uac2_ctl_value_size(int val_type) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci switch (val_type) { 28562306a36Sopenharmony_ci case USB_MIXER_S32: 28662306a36Sopenharmony_ci case USB_MIXER_U32: 28762306a36Sopenharmony_ci return 4; 28862306a36Sopenharmony_ci case USB_MIXER_S16: 28962306a36Sopenharmony_ci case USB_MIXER_U16: 29062306a36Sopenharmony_ci return 2; 29162306a36Sopenharmony_ci default: 29262306a36Sopenharmony_ci return 1; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci return 0; /* unreachable */ 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* 29962306a36Sopenharmony_ci * retrieve a mixer value 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic inline int mixer_ctrl_intf(struct usb_mixer_interface *mixer) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci return get_iface_desc(mixer->hostif)->bInterfaceNumber; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, 30862306a36Sopenharmony_ci int validx, int *value_ret) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct snd_usb_audio *chip = cval->head.mixer->chip; 31162306a36Sopenharmony_ci unsigned char buf[2]; 31262306a36Sopenharmony_ci int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; 31362306a36Sopenharmony_ci int timeout = 10; 31462306a36Sopenharmony_ci int idx = 0, err; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci err = snd_usb_lock_shutdown(chip); 31762306a36Sopenharmony_ci if (err < 0) 31862306a36Sopenharmony_ci return -EIO; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci while (timeout-- > 0) { 32162306a36Sopenharmony_ci idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); 32262306a36Sopenharmony_ci err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, 32362306a36Sopenharmony_ci USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 32462306a36Sopenharmony_ci validx, idx, buf, val_len); 32562306a36Sopenharmony_ci if (err >= val_len) { 32662306a36Sopenharmony_ci *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); 32762306a36Sopenharmony_ci err = 0; 32862306a36Sopenharmony_ci goto out; 32962306a36Sopenharmony_ci } else if (err == -ETIMEDOUT) { 33062306a36Sopenharmony_ci goto out; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci usb_audio_dbg(chip, 33462306a36Sopenharmony_ci "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", 33562306a36Sopenharmony_ci request, validx, idx, cval->val_type); 33662306a36Sopenharmony_ci err = -EINVAL; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci out: 33962306a36Sopenharmony_ci snd_usb_unlock_shutdown(chip); 34062306a36Sopenharmony_ci return err; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, 34462306a36Sopenharmony_ci int validx, int *value_ret) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct snd_usb_audio *chip = cval->head.mixer->chip; 34762306a36Sopenharmony_ci /* enough space for one range */ 34862306a36Sopenharmony_ci unsigned char buf[sizeof(__u16) + 3 * sizeof(__u32)]; 34962306a36Sopenharmony_ci unsigned char *val; 35062306a36Sopenharmony_ci int idx = 0, ret, val_size, size; 35162306a36Sopenharmony_ci __u8 bRequest; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci val_size = uac2_ctl_value_size(cval->val_type); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (request == UAC_GET_CUR) { 35662306a36Sopenharmony_ci bRequest = UAC2_CS_CUR; 35762306a36Sopenharmony_ci size = val_size; 35862306a36Sopenharmony_ci } else { 35962306a36Sopenharmony_ci bRequest = UAC2_CS_RANGE; 36062306a36Sopenharmony_ci size = sizeof(__u16) + 3 * val_size; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci memset(buf, 0, sizeof(buf)); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (snd_usb_lock_shutdown(chip)) 36662306a36Sopenharmony_ci return -EIO; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); 36962306a36Sopenharmony_ci ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, 37062306a36Sopenharmony_ci USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 37162306a36Sopenharmony_ci validx, idx, buf, size); 37262306a36Sopenharmony_ci snd_usb_unlock_shutdown(chip); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (ret < 0) { 37562306a36Sopenharmony_ci usb_audio_dbg(chip, 37662306a36Sopenharmony_ci "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", 37762306a36Sopenharmony_ci request, validx, idx, cval->val_type); 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* FIXME: how should we handle multiple triplets here? */ 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci switch (request) { 38462306a36Sopenharmony_ci case UAC_GET_CUR: 38562306a36Sopenharmony_ci val = buf; 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci case UAC_GET_MIN: 38862306a36Sopenharmony_ci val = buf + sizeof(__u16); 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci case UAC_GET_MAX: 39162306a36Sopenharmony_ci val = buf + sizeof(__u16) + val_size; 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci case UAC_GET_RES: 39462306a36Sopenharmony_ci val = buf + sizeof(__u16) + val_size * 2; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci default: 39762306a36Sopenharmony_ci return -EINVAL; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci *value_ret = convert_signed_value(cval, 40162306a36Sopenharmony_ci snd_usb_combine_bytes(val, val_size)); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int get_ctl_value(struct usb_mixer_elem_info *cval, int request, 40762306a36Sopenharmony_ci int validx, int *value_ret) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci validx += cval->idx_off; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return (cval->head.mixer->protocol == UAC_VERSION_1) ? 41262306a36Sopenharmony_ci get_ctl_value_v1(cval, request, validx, value_ret) : 41362306a36Sopenharmony_ci get_ctl_value_v2(cval, request, validx, value_ret); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int get_cur_ctl_value(struct usb_mixer_elem_info *cval, 41762306a36Sopenharmony_ci int validx, int *value) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci return get_ctl_value(cval, UAC_GET_CUR, validx, value); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* channel = 0: master, 1 = first channel */ 42362306a36Sopenharmony_cistatic inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval, 42462306a36Sopenharmony_ci int channel, int *value) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci return get_ctl_value(cval, UAC_GET_CUR, 42762306a36Sopenharmony_ci (cval->control << 8) | channel, 42862306a36Sopenharmony_ci value); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ciint snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, 43262306a36Sopenharmony_ci int channel, int index, int *value) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci int err; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (cval->cached & (1 << channel)) { 43762306a36Sopenharmony_ci *value = cval->cache_val[index]; 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci err = get_cur_mix_raw(cval, channel, value); 44162306a36Sopenharmony_ci if (err < 0) { 44262306a36Sopenharmony_ci if (!cval->head.mixer->ignore_ctl_error) 44362306a36Sopenharmony_ci usb_audio_dbg(cval->head.mixer->chip, 44462306a36Sopenharmony_ci "cannot get current value for control %d ch %d: err = %d\n", 44562306a36Sopenharmony_ci cval->control, channel, err); 44662306a36Sopenharmony_ci return err; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci cval->cached |= 1 << channel; 44962306a36Sopenharmony_ci cval->cache_val[index] = *value; 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* 45462306a36Sopenharmony_ci * set a mixer value 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciint snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, 45862306a36Sopenharmony_ci int request, int validx, int value_set) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct snd_usb_audio *chip = cval->head.mixer->chip; 46162306a36Sopenharmony_ci unsigned char buf[4]; 46262306a36Sopenharmony_ci int idx = 0, val_len, err, timeout = 10; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci validx += cval->idx_off; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (cval->head.mixer->protocol == UAC_VERSION_1) { 46862306a36Sopenharmony_ci val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; 46962306a36Sopenharmony_ci } else { /* UAC_VERSION_2/3 */ 47062306a36Sopenharmony_ci val_len = uac2_ctl_value_size(cval->val_type); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* FIXME */ 47362306a36Sopenharmony_ci if (request != UAC_SET_CUR) { 47462306a36Sopenharmony_ci usb_audio_dbg(chip, "RANGE setting not yet supported\n"); 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci request = UAC2_CS_CUR; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci value_set = convert_bytes_value(cval, value_set); 48262306a36Sopenharmony_ci buf[0] = value_set & 0xff; 48362306a36Sopenharmony_ci buf[1] = (value_set >> 8) & 0xff; 48462306a36Sopenharmony_ci buf[2] = (value_set >> 16) & 0xff; 48562306a36Sopenharmony_ci buf[3] = (value_set >> 24) & 0xff; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci err = snd_usb_lock_shutdown(chip); 48862306a36Sopenharmony_ci if (err < 0) 48962306a36Sopenharmony_ci return -EIO; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci while (timeout-- > 0) { 49262306a36Sopenharmony_ci idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); 49362306a36Sopenharmony_ci err = snd_usb_ctl_msg(chip->dev, 49462306a36Sopenharmony_ci usb_sndctrlpipe(chip->dev, 0), request, 49562306a36Sopenharmony_ci USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, 49662306a36Sopenharmony_ci validx, idx, buf, val_len); 49762306a36Sopenharmony_ci if (err >= 0) { 49862306a36Sopenharmony_ci err = 0; 49962306a36Sopenharmony_ci goto out; 50062306a36Sopenharmony_ci } else if (err == -ETIMEDOUT) { 50162306a36Sopenharmony_ci goto out; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci usb_audio_dbg(chip, "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n", 50562306a36Sopenharmony_ci request, validx, idx, cval->val_type, buf[0], buf[1]); 50662306a36Sopenharmony_ci err = -EINVAL; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci out: 50962306a36Sopenharmony_ci snd_usb_unlock_shutdown(chip); 51062306a36Sopenharmony_ci return err; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int set_cur_ctl_value(struct usb_mixer_elem_info *cval, 51462306a36Sopenharmony_ci int validx, int value) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ciint snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, 52062306a36Sopenharmony_ci int index, int value) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci int err; 52362306a36Sopenharmony_ci unsigned int read_only = (channel == 0) ? 52462306a36Sopenharmony_ci cval->master_readonly : 52562306a36Sopenharmony_ci cval->ch_readonly & (1 << (channel - 1)); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (read_only) { 52862306a36Sopenharmony_ci usb_audio_dbg(cval->head.mixer->chip, 52962306a36Sopenharmony_ci "%s(): channel %d of control %d is read_only\n", 53062306a36Sopenharmony_ci __func__, channel, cval->control); 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci err = snd_usb_mixer_set_ctl_value(cval, 53562306a36Sopenharmony_ci UAC_SET_CUR, (cval->control << 8) | channel, 53662306a36Sopenharmony_ci value); 53762306a36Sopenharmony_ci if (err < 0) 53862306a36Sopenharmony_ci return err; 53962306a36Sopenharmony_ci cval->cached |= 1 << channel; 54062306a36Sopenharmony_ci cval->cache_val[index] = value; 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/* 54562306a36Sopenharmony_ci * TLV callback for mixer volume controls 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ciint snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, 54862306a36Sopenharmony_ci unsigned int size, unsigned int __user *_tlv) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 55162306a36Sopenharmony_ci DECLARE_TLV_DB_MINMAX(scale, 0, 0); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (size < sizeof(scale)) 55462306a36Sopenharmony_ci return -ENOMEM; 55562306a36Sopenharmony_ci if (cval->min_mute) 55662306a36Sopenharmony_ci scale[0] = SNDRV_CTL_TLVT_DB_MINMAX_MUTE; 55762306a36Sopenharmony_ci scale[2] = cval->dBmin; 55862306a36Sopenharmony_ci scale[3] = cval->dBmax; 55962306a36Sopenharmony_ci if (copy_to_user(_tlv, scale, sizeof(scale))) 56062306a36Sopenharmony_ci return -EFAULT; 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* 56562306a36Sopenharmony_ci * parser routines begin here... 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int parse_audio_unit(struct mixer_build *state, int unitid); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* 57262306a36Sopenharmony_ci * check if the input/output channel routing is enabled on the given bitmap. 57362306a36Sopenharmony_ci * used for mixer unit parser 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_cistatic int check_matrix_bitmap(unsigned char *bmap, 57662306a36Sopenharmony_ci int ich, int och, int num_outs) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci int idx = ich * num_outs + och; 57962306a36Sopenharmony_ci return bmap[idx >> 3] & (0x80 >> (idx & 7)); 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/* 58362306a36Sopenharmony_ci * add an alsa control element 58462306a36Sopenharmony_ci * search and increment the index until an empty slot is found. 58562306a36Sopenharmony_ci * 58662306a36Sopenharmony_ci * if failed, give up and free the control instance. 58762306a36Sopenharmony_ci */ 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciint snd_usb_mixer_add_list(struct usb_mixer_elem_list *list, 59062306a36Sopenharmony_ci struct snd_kcontrol *kctl, 59162306a36Sopenharmony_ci bool is_std_info) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct usb_mixer_interface *mixer = list->mixer; 59462306a36Sopenharmony_ci int err; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci while (snd_ctl_find_id(mixer->chip->card, &kctl->id)) 59762306a36Sopenharmony_ci kctl->id.index++; 59862306a36Sopenharmony_ci err = snd_ctl_add(mixer->chip->card, kctl); 59962306a36Sopenharmony_ci if (err < 0) { 60062306a36Sopenharmony_ci usb_audio_dbg(mixer->chip, "cannot add control (err = %d)\n", 60162306a36Sopenharmony_ci err); 60262306a36Sopenharmony_ci return err; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci list->kctl = kctl; 60562306a36Sopenharmony_ci list->is_std_info = is_std_info; 60662306a36Sopenharmony_ci list->next_id_elem = mixer->id_elems[list->id]; 60762306a36Sopenharmony_ci mixer->id_elems[list->id] = list; 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/* 61262306a36Sopenharmony_ci * get a terminal name string 61362306a36Sopenharmony_ci */ 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic struct iterm_name_combo { 61662306a36Sopenharmony_ci int type; 61762306a36Sopenharmony_ci char *name; 61862306a36Sopenharmony_ci} iterm_names[] = { 61962306a36Sopenharmony_ci { 0x0300, "Output" }, 62062306a36Sopenharmony_ci { 0x0301, "Speaker" }, 62162306a36Sopenharmony_ci { 0x0302, "Headphone" }, 62262306a36Sopenharmony_ci { 0x0303, "HMD Audio" }, 62362306a36Sopenharmony_ci { 0x0304, "Desktop Speaker" }, 62462306a36Sopenharmony_ci { 0x0305, "Room Speaker" }, 62562306a36Sopenharmony_ci { 0x0306, "Com Speaker" }, 62662306a36Sopenharmony_ci { 0x0307, "LFE" }, 62762306a36Sopenharmony_ci { 0x0600, "External In" }, 62862306a36Sopenharmony_ci { 0x0601, "Analog In" }, 62962306a36Sopenharmony_ci { 0x0602, "Digital In" }, 63062306a36Sopenharmony_ci { 0x0603, "Line" }, 63162306a36Sopenharmony_ci { 0x0604, "Legacy In" }, 63262306a36Sopenharmony_ci { 0x0605, "IEC958 In" }, 63362306a36Sopenharmony_ci { 0x0606, "1394 DA Stream" }, 63462306a36Sopenharmony_ci { 0x0607, "1394 DV Stream" }, 63562306a36Sopenharmony_ci { 0x0700, "Embedded" }, 63662306a36Sopenharmony_ci { 0x0701, "Noise Source" }, 63762306a36Sopenharmony_ci { 0x0702, "Equalization Noise" }, 63862306a36Sopenharmony_ci { 0x0703, "CD" }, 63962306a36Sopenharmony_ci { 0x0704, "DAT" }, 64062306a36Sopenharmony_ci { 0x0705, "DCC" }, 64162306a36Sopenharmony_ci { 0x0706, "MiniDisk" }, 64262306a36Sopenharmony_ci { 0x0707, "Analog Tape" }, 64362306a36Sopenharmony_ci { 0x0708, "Phonograph" }, 64462306a36Sopenharmony_ci { 0x0709, "VCR Audio" }, 64562306a36Sopenharmony_ci { 0x070a, "Video Disk Audio" }, 64662306a36Sopenharmony_ci { 0x070b, "DVD Audio" }, 64762306a36Sopenharmony_ci { 0x070c, "TV Tuner Audio" }, 64862306a36Sopenharmony_ci { 0x070d, "Satellite Rec Audio" }, 64962306a36Sopenharmony_ci { 0x070e, "Cable Tuner Audio" }, 65062306a36Sopenharmony_ci { 0x070f, "DSS Audio" }, 65162306a36Sopenharmony_ci { 0x0710, "Radio Receiver" }, 65262306a36Sopenharmony_ci { 0x0711, "Radio Transmitter" }, 65362306a36Sopenharmony_ci { 0x0712, "Multi-Track Recorder" }, 65462306a36Sopenharmony_ci { 0x0713, "Synthesizer" }, 65562306a36Sopenharmony_ci { 0 }, 65662306a36Sopenharmony_ci}; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iterm, 65962306a36Sopenharmony_ci unsigned char *name, int maxlen, int term_only) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct iterm_name_combo *names; 66262306a36Sopenharmony_ci int len; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (iterm->name) { 66562306a36Sopenharmony_ci len = snd_usb_copy_string_desc(chip, iterm->name, 66662306a36Sopenharmony_ci name, maxlen); 66762306a36Sopenharmony_ci if (len) 66862306a36Sopenharmony_ci return len; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* virtual type - not a real terminal */ 67262306a36Sopenharmony_ci if (iterm->type >> 16) { 67362306a36Sopenharmony_ci if (term_only) 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci switch (iterm->type >> 16) { 67662306a36Sopenharmony_ci case UAC3_SELECTOR_UNIT: 67762306a36Sopenharmony_ci strcpy(name, "Selector"); 67862306a36Sopenharmony_ci return 8; 67962306a36Sopenharmony_ci case UAC3_PROCESSING_UNIT: 68062306a36Sopenharmony_ci strcpy(name, "Process Unit"); 68162306a36Sopenharmony_ci return 12; 68262306a36Sopenharmony_ci case UAC3_EXTENSION_UNIT: 68362306a36Sopenharmony_ci strcpy(name, "Ext Unit"); 68462306a36Sopenharmony_ci return 8; 68562306a36Sopenharmony_ci case UAC3_MIXER_UNIT: 68662306a36Sopenharmony_ci strcpy(name, "Mixer"); 68762306a36Sopenharmony_ci return 5; 68862306a36Sopenharmony_ci default: 68962306a36Sopenharmony_ci return sprintf(name, "Unit %d", iterm->id); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci switch (iterm->type & 0xff00) { 69462306a36Sopenharmony_ci case 0x0100: 69562306a36Sopenharmony_ci strcpy(name, "PCM"); 69662306a36Sopenharmony_ci return 3; 69762306a36Sopenharmony_ci case 0x0200: 69862306a36Sopenharmony_ci strcpy(name, "Mic"); 69962306a36Sopenharmony_ci return 3; 70062306a36Sopenharmony_ci case 0x0400: 70162306a36Sopenharmony_ci strcpy(name, "Headset"); 70262306a36Sopenharmony_ci return 7; 70362306a36Sopenharmony_ci case 0x0500: 70462306a36Sopenharmony_ci strcpy(name, "Phone"); 70562306a36Sopenharmony_ci return 5; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci for (names = iterm_names; names->type; names++) { 70962306a36Sopenharmony_ci if (names->type == iterm->type) { 71062306a36Sopenharmony_ci strcpy(name, names->name); 71162306a36Sopenharmony_ci return strlen(names->name); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return 0; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/* 71962306a36Sopenharmony_ci * Get logical cluster information for UAC3 devices. 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_cistatic int get_cluster_channels_v3(struct mixer_build *state, unsigned int cluster_id) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct uac3_cluster_header_descriptor c_header; 72462306a36Sopenharmony_ci int err; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci err = snd_usb_ctl_msg(state->chip->dev, 72762306a36Sopenharmony_ci usb_rcvctrlpipe(state->chip->dev, 0), 72862306a36Sopenharmony_ci UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, 72962306a36Sopenharmony_ci USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 73062306a36Sopenharmony_ci cluster_id, 73162306a36Sopenharmony_ci snd_usb_ctrl_intf(state->chip), 73262306a36Sopenharmony_ci &c_header, sizeof(c_header)); 73362306a36Sopenharmony_ci if (err < 0) 73462306a36Sopenharmony_ci goto error; 73562306a36Sopenharmony_ci if (err != sizeof(c_header)) { 73662306a36Sopenharmony_ci err = -EIO; 73762306a36Sopenharmony_ci goto error; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return c_header.bNrChannels; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cierror: 74362306a36Sopenharmony_ci usb_audio_err(state->chip, "cannot request logical cluster ID: %d (err: %d)\n", cluster_id, err); 74462306a36Sopenharmony_ci return err; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci/* 74862306a36Sopenharmony_ci * Get number of channels for a Mixer Unit. 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_cistatic int uac_mixer_unit_get_channels(struct mixer_build *state, 75162306a36Sopenharmony_ci struct uac_mixer_unit_descriptor *desc) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci int mu_channels; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci switch (state->mixer->protocol) { 75662306a36Sopenharmony_ci case UAC_VERSION_1: 75762306a36Sopenharmony_ci case UAC_VERSION_2: 75862306a36Sopenharmony_ci default: 75962306a36Sopenharmony_ci if (desc->bLength < sizeof(*desc) + desc->bNrInPins + 1) 76062306a36Sopenharmony_ci return 0; /* no bmControls -> skip */ 76162306a36Sopenharmony_ci mu_channels = uac_mixer_unit_bNrChannels(desc); 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci case UAC_VERSION_3: 76462306a36Sopenharmony_ci mu_channels = get_cluster_channels_v3(state, 76562306a36Sopenharmony_ci uac3_mixer_unit_wClusterDescrID(desc)); 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return mu_channels; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/* 77362306a36Sopenharmony_ci * Parse Input Terminal Unit 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_cistatic int __check_input_term(struct mixer_build *state, int id, 77662306a36Sopenharmony_ci struct usb_audio_term *term); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic int parse_term_uac1_iterm_unit(struct mixer_build *state, 77962306a36Sopenharmony_ci struct usb_audio_term *term, 78062306a36Sopenharmony_ci void *p1, int id) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct uac_input_terminal_descriptor *d = p1; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci term->type = le16_to_cpu(d->wTerminalType); 78562306a36Sopenharmony_ci term->channels = d->bNrChannels; 78662306a36Sopenharmony_ci term->chconfig = le16_to_cpu(d->wChannelConfig); 78762306a36Sopenharmony_ci term->name = d->iTerminal; 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int parse_term_uac2_iterm_unit(struct mixer_build *state, 79262306a36Sopenharmony_ci struct usb_audio_term *term, 79362306a36Sopenharmony_ci void *p1, int id) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct uac2_input_terminal_descriptor *d = p1; 79662306a36Sopenharmony_ci int err; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* call recursively to verify the referenced clock entity */ 79962306a36Sopenharmony_ci err = __check_input_term(state, d->bCSourceID, term); 80062306a36Sopenharmony_ci if (err < 0) 80162306a36Sopenharmony_ci return err; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* save input term properties after recursion, 80462306a36Sopenharmony_ci * to ensure they are not overriden by the recursion calls 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci term->id = id; 80762306a36Sopenharmony_ci term->type = le16_to_cpu(d->wTerminalType); 80862306a36Sopenharmony_ci term->channels = d->bNrChannels; 80962306a36Sopenharmony_ci term->chconfig = le32_to_cpu(d->bmChannelConfig); 81062306a36Sopenharmony_ci term->name = d->iTerminal; 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int parse_term_uac3_iterm_unit(struct mixer_build *state, 81562306a36Sopenharmony_ci struct usb_audio_term *term, 81662306a36Sopenharmony_ci void *p1, int id) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct uac3_input_terminal_descriptor *d = p1; 81962306a36Sopenharmony_ci int err; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* call recursively to verify the referenced clock entity */ 82262306a36Sopenharmony_ci err = __check_input_term(state, d->bCSourceID, term); 82362306a36Sopenharmony_ci if (err < 0) 82462306a36Sopenharmony_ci return err; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* save input term properties after recursion, 82762306a36Sopenharmony_ci * to ensure they are not overriden by the recursion calls 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci term->id = id; 83062306a36Sopenharmony_ci term->type = le16_to_cpu(d->wTerminalType); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci err = get_cluster_channels_v3(state, le16_to_cpu(d->wClusterDescrID)); 83362306a36Sopenharmony_ci if (err < 0) 83462306a36Sopenharmony_ci return err; 83562306a36Sopenharmony_ci term->channels = err; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* REVISIT: UAC3 IT doesn't have channels cfg */ 83862306a36Sopenharmony_ci term->chconfig = 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci term->name = le16_to_cpu(d->wTerminalDescrStr); 84162306a36Sopenharmony_ci return 0; 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic int parse_term_mixer_unit(struct mixer_build *state, 84562306a36Sopenharmony_ci struct usb_audio_term *term, 84662306a36Sopenharmony_ci void *p1, int id) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct uac_mixer_unit_descriptor *d = p1; 84962306a36Sopenharmony_ci int protocol = state->mixer->protocol; 85062306a36Sopenharmony_ci int err; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci err = uac_mixer_unit_get_channels(state, d); 85362306a36Sopenharmony_ci if (err <= 0) 85462306a36Sopenharmony_ci return err; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci term->type = UAC3_MIXER_UNIT << 16; /* virtual type */ 85762306a36Sopenharmony_ci term->channels = err; 85862306a36Sopenharmony_ci if (protocol != UAC_VERSION_3) { 85962306a36Sopenharmony_ci term->chconfig = uac_mixer_unit_wChannelConfig(d, protocol); 86062306a36Sopenharmony_ci term->name = uac_mixer_unit_iMixer(d); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic int parse_term_selector_unit(struct mixer_build *state, 86662306a36Sopenharmony_ci struct usb_audio_term *term, 86762306a36Sopenharmony_ci void *p1, int id) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct uac_selector_unit_descriptor *d = p1; 87062306a36Sopenharmony_ci int err; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* call recursively to retrieve the channel info */ 87362306a36Sopenharmony_ci err = __check_input_term(state, d->baSourceID[0], term); 87462306a36Sopenharmony_ci if (err < 0) 87562306a36Sopenharmony_ci return err; 87662306a36Sopenharmony_ci term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ 87762306a36Sopenharmony_ci term->id = id; 87862306a36Sopenharmony_ci if (state->mixer->protocol != UAC_VERSION_3) 87962306a36Sopenharmony_ci term->name = uac_selector_unit_iSelector(d); 88062306a36Sopenharmony_ci return 0; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic int parse_term_proc_unit(struct mixer_build *state, 88462306a36Sopenharmony_ci struct usb_audio_term *term, 88562306a36Sopenharmony_ci void *p1, int id, int vtype) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct uac_processing_unit_descriptor *d = p1; 88862306a36Sopenharmony_ci int protocol = state->mixer->protocol; 88962306a36Sopenharmony_ci int err; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (d->bNrInPins) { 89262306a36Sopenharmony_ci /* call recursively to retrieve the channel info */ 89362306a36Sopenharmony_ci err = __check_input_term(state, d->baSourceID[0], term); 89462306a36Sopenharmony_ci if (err < 0) 89562306a36Sopenharmony_ci return err; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci term->type = vtype << 16; /* virtual type */ 89962306a36Sopenharmony_ci term->id = id; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (protocol == UAC_VERSION_3) 90262306a36Sopenharmony_ci return 0; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (!term->channels) { 90562306a36Sopenharmony_ci term->channels = uac_processing_unit_bNrChannels(d); 90662306a36Sopenharmony_ci term->chconfig = uac_processing_unit_wChannelConfig(d, protocol); 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci term->name = uac_processing_unit_iProcessing(d, protocol); 90962306a36Sopenharmony_ci return 0; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic int parse_term_effect_unit(struct mixer_build *state, 91362306a36Sopenharmony_ci struct usb_audio_term *term, 91462306a36Sopenharmony_ci void *p1, int id) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci struct uac2_effect_unit_descriptor *d = p1; 91762306a36Sopenharmony_ci int err; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci err = __check_input_term(state, d->bSourceID, term); 92062306a36Sopenharmony_ci if (err < 0) 92162306a36Sopenharmony_ci return err; 92262306a36Sopenharmony_ci term->type = UAC3_EFFECT_UNIT << 16; /* virtual type */ 92362306a36Sopenharmony_ci term->id = id; 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic int parse_term_uac2_clock_source(struct mixer_build *state, 92862306a36Sopenharmony_ci struct usb_audio_term *term, 92962306a36Sopenharmony_ci void *p1, int id) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct uac_clock_source_descriptor *d = p1; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ 93462306a36Sopenharmony_ci term->id = id; 93562306a36Sopenharmony_ci term->name = d->iClockSource; 93662306a36Sopenharmony_ci return 0; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic int parse_term_uac3_clock_source(struct mixer_build *state, 94062306a36Sopenharmony_ci struct usb_audio_term *term, 94162306a36Sopenharmony_ci void *p1, int id) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct uac3_clock_source_descriptor *d = p1; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci term->type = UAC3_CLOCK_SOURCE << 16; /* virtual type */ 94662306a36Sopenharmony_ci term->id = id; 94762306a36Sopenharmony_ci term->name = le16_to_cpu(d->wClockSourceStr); 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci#define PTYPE(a, b) ((a) << 8 | (b)) 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci/* 95462306a36Sopenharmony_ci * parse the source unit recursively until it reaches to a terminal 95562306a36Sopenharmony_ci * or a branched unit. 95662306a36Sopenharmony_ci */ 95762306a36Sopenharmony_cistatic int __check_input_term(struct mixer_build *state, int id, 95862306a36Sopenharmony_ci struct usb_audio_term *term) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci int protocol = state->mixer->protocol; 96162306a36Sopenharmony_ci void *p1; 96262306a36Sopenharmony_ci unsigned char *hdr; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci for (;;) { 96562306a36Sopenharmony_ci /* a loop in the terminal chain? */ 96662306a36Sopenharmony_ci if (test_and_set_bit(id, state->termbitmap)) 96762306a36Sopenharmony_ci return -EINVAL; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci p1 = find_audio_control_unit(state, id); 97062306a36Sopenharmony_ci if (!p1) 97162306a36Sopenharmony_ci break; 97262306a36Sopenharmony_ci if (!snd_usb_validate_audio_desc(p1, protocol)) 97362306a36Sopenharmony_ci break; /* bad descriptor */ 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci hdr = p1; 97662306a36Sopenharmony_ci term->id = id; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci switch (PTYPE(protocol, hdr[2])) { 97962306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT): 98062306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT): 98162306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): { 98262306a36Sopenharmony_ci /* the header is the same for all versions */ 98362306a36Sopenharmony_ci struct uac_feature_unit_descriptor *d = p1; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci id = d->bSourceID; 98662306a36Sopenharmony_ci break; /* continue to parse */ 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): 98962306a36Sopenharmony_ci return parse_term_uac1_iterm_unit(state, term, p1, id); 99062306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): 99162306a36Sopenharmony_ci return parse_term_uac2_iterm_unit(state, term, p1, id); 99262306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL): 99362306a36Sopenharmony_ci return parse_term_uac3_iterm_unit(state, term, p1, id); 99462306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT): 99562306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT): 99662306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT): 99762306a36Sopenharmony_ci return parse_term_mixer_unit(state, term, p1, id); 99862306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT): 99962306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT): 100062306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR): 100162306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT): 100262306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR): 100362306a36Sopenharmony_ci return parse_term_selector_unit(state, term, p1, id); 100462306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT): 100562306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2): 100662306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT): 100762306a36Sopenharmony_ci return parse_term_proc_unit(state, term, p1, id, 100862306a36Sopenharmony_ci UAC3_PROCESSING_UNIT); 100962306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): 101062306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): 101162306a36Sopenharmony_ci return parse_term_effect_unit(state, term, p1, id); 101262306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): 101362306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): 101462306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): 101562306a36Sopenharmony_ci return parse_term_proc_unit(state, term, p1, id, 101662306a36Sopenharmony_ci UAC3_EXTENSION_UNIT); 101762306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE): 101862306a36Sopenharmony_ci return parse_term_uac2_clock_source(state, term, p1, id); 101962306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE): 102062306a36Sopenharmony_ci return parse_term_uac3_clock_source(state, term, p1, id); 102162306a36Sopenharmony_ci default: 102262306a36Sopenharmony_ci return -ENODEV; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci return -ENODEV; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic int check_input_term(struct mixer_build *state, int id, 103062306a36Sopenharmony_ci struct usb_audio_term *term) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci memset(term, 0, sizeof(*term)); 103362306a36Sopenharmony_ci memset(state->termbitmap, 0, sizeof(state->termbitmap)); 103462306a36Sopenharmony_ci return __check_input_term(state, id, term); 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci/* 103862306a36Sopenharmony_ci * Feature Unit 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci/* feature unit control information */ 104262306a36Sopenharmony_cistruct usb_feature_control_info { 104362306a36Sopenharmony_ci int control; 104462306a36Sopenharmony_ci const char *name; 104562306a36Sopenharmony_ci int type; /* data type for uac1 */ 104662306a36Sopenharmony_ci int type_uac2; /* data type for uac2 if different from uac1, else -1 */ 104762306a36Sopenharmony_ci}; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic const struct usb_feature_control_info audio_feature_info[] = { 105062306a36Sopenharmony_ci { UAC_FU_MUTE, "Mute", USB_MIXER_INV_BOOLEAN, -1 }, 105162306a36Sopenharmony_ci { UAC_FU_VOLUME, "Volume", USB_MIXER_S16, -1 }, 105262306a36Sopenharmony_ci { UAC_FU_BASS, "Tone Control - Bass", USB_MIXER_S8, -1 }, 105362306a36Sopenharmony_ci { UAC_FU_MID, "Tone Control - Mid", USB_MIXER_S8, -1 }, 105462306a36Sopenharmony_ci { UAC_FU_TREBLE, "Tone Control - Treble", USB_MIXER_S8, -1 }, 105562306a36Sopenharmony_ci { UAC_FU_GRAPHIC_EQUALIZER, "Graphic Equalizer", USB_MIXER_S8, -1 }, /* FIXME: not implemented yet */ 105662306a36Sopenharmony_ci { UAC_FU_AUTOMATIC_GAIN, "Auto Gain Control", USB_MIXER_BOOLEAN, -1 }, 105762306a36Sopenharmony_ci { UAC_FU_DELAY, "Delay Control", USB_MIXER_U16, USB_MIXER_U32 }, 105862306a36Sopenharmony_ci { UAC_FU_BASS_BOOST, "Bass Boost", USB_MIXER_BOOLEAN, -1 }, 105962306a36Sopenharmony_ci { UAC_FU_LOUDNESS, "Loudness", USB_MIXER_BOOLEAN, -1 }, 106062306a36Sopenharmony_ci /* UAC2 specific */ 106162306a36Sopenharmony_ci { UAC2_FU_INPUT_GAIN, "Input Gain Control", USB_MIXER_S16, -1 }, 106262306a36Sopenharmony_ci { UAC2_FU_INPUT_GAIN_PAD, "Input Gain Pad Control", USB_MIXER_S16, -1 }, 106362306a36Sopenharmony_ci { UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 }, 106462306a36Sopenharmony_ci}; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic void usb_mixer_elem_info_free(struct usb_mixer_elem_info *cval) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci kfree(cval); 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci/* private_free callback */ 107262306a36Sopenharmony_civoid snd_usb_mixer_elem_free(struct snd_kcontrol *kctl) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci usb_mixer_elem_info_free(kctl->private_data); 107562306a36Sopenharmony_ci kctl->private_data = NULL; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci/* 107962306a36Sopenharmony_ci * interface to ALSA control for feature/mixer units 108062306a36Sopenharmony_ci */ 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci/* volume control quirks */ 108362306a36Sopenharmony_cistatic void volume_control_quirks(struct usb_mixer_elem_info *cval, 108462306a36Sopenharmony_ci struct snd_kcontrol *kctl) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct snd_usb_audio *chip = cval->head.mixer->chip; 108762306a36Sopenharmony_ci switch (chip->usb_id) { 108862306a36Sopenharmony_ci case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ 108962306a36Sopenharmony_ci case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ 109062306a36Sopenharmony_ci if (strcmp(kctl->id.name, "Effect Duration") == 0) { 109162306a36Sopenharmony_ci cval->min = 0x0000; 109262306a36Sopenharmony_ci cval->max = 0xffff; 109362306a36Sopenharmony_ci cval->res = 0x00e6; 109462306a36Sopenharmony_ci break; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci if (strcmp(kctl->id.name, "Effect Volume") == 0 || 109762306a36Sopenharmony_ci strcmp(kctl->id.name, "Effect Feedback Volume") == 0) { 109862306a36Sopenharmony_ci cval->min = 0x00; 109962306a36Sopenharmony_ci cval->max = 0xff; 110062306a36Sopenharmony_ci break; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci if (strstr(kctl->id.name, "Effect Return") != NULL) { 110362306a36Sopenharmony_ci cval->min = 0xb706; 110462306a36Sopenharmony_ci cval->max = 0xff7b; 110562306a36Sopenharmony_ci cval->res = 0x0073; 110662306a36Sopenharmony_ci break; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci if ((strstr(kctl->id.name, "Playback Volume") != NULL) || 110962306a36Sopenharmony_ci (strstr(kctl->id.name, "Effect Send") != NULL)) { 111062306a36Sopenharmony_ci cval->min = 0xb5fb; /* -73 dB = 0xb6ff */ 111162306a36Sopenharmony_ci cval->max = 0xfcfe; 111262306a36Sopenharmony_ci cval->res = 0x0073; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ 111762306a36Sopenharmony_ci case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ 111862306a36Sopenharmony_ci if (strcmp(kctl->id.name, "Effect Duration") == 0) { 111962306a36Sopenharmony_ci usb_audio_info(chip, 112062306a36Sopenharmony_ci "set quirk for FTU Effect Duration\n"); 112162306a36Sopenharmony_ci cval->min = 0x0000; 112262306a36Sopenharmony_ci cval->max = 0x7f00; 112362306a36Sopenharmony_ci cval->res = 0x0100; 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci if (strcmp(kctl->id.name, "Effect Volume") == 0 || 112762306a36Sopenharmony_ci strcmp(kctl->id.name, "Effect Feedback Volume") == 0) { 112862306a36Sopenharmony_ci usb_audio_info(chip, 112962306a36Sopenharmony_ci "set quirks for FTU Effect Feedback/Volume\n"); 113062306a36Sopenharmony_ci cval->min = 0x00; 113162306a36Sopenharmony_ci cval->max = 0x7f; 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci break; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci case USB_ID(0x0d8c, 0x0103): 113762306a36Sopenharmony_ci if (!strcmp(kctl->id.name, "PCM Playback Volume")) { 113862306a36Sopenharmony_ci usb_audio_info(chip, 113962306a36Sopenharmony_ci "set volume quirk for CM102-A+/102S+\n"); 114062306a36Sopenharmony_ci cval->min = -256; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci break; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci case USB_ID(0x0471, 0x0101): 114562306a36Sopenharmony_ci case USB_ID(0x0471, 0x0104): 114662306a36Sopenharmony_ci case USB_ID(0x0471, 0x0105): 114762306a36Sopenharmony_ci case USB_ID(0x0672, 0x1041): 114862306a36Sopenharmony_ci /* quirk for UDA1321/N101. 114962306a36Sopenharmony_ci * note that detection between firmware 2.1.1.7 (N101) 115062306a36Sopenharmony_ci * and later 2.1.1.21 is not very clear from datasheets. 115162306a36Sopenharmony_ci * I hope that the min value is -15360 for newer firmware --jk 115262306a36Sopenharmony_ci */ 115362306a36Sopenharmony_ci if (!strcmp(kctl->id.name, "PCM Playback Volume") && 115462306a36Sopenharmony_ci cval->min == -15616) { 115562306a36Sopenharmony_ci usb_audio_info(chip, 115662306a36Sopenharmony_ci "set volume quirk for UDA1321/N101 chip\n"); 115762306a36Sopenharmony_ci cval->max = -256; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci break; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci case USB_ID(0x046d, 0x09a4): 116262306a36Sopenharmony_ci if (!strcmp(kctl->id.name, "Mic Capture Volume")) { 116362306a36Sopenharmony_ci usb_audio_info(chip, 116462306a36Sopenharmony_ci "set volume quirk for QuickCam E3500\n"); 116562306a36Sopenharmony_ci cval->min = 6080; 116662306a36Sopenharmony_ci cval->max = 8768; 116762306a36Sopenharmony_ci cval->res = 192; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci break; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci case USB_ID(0x046d, 0x0807): /* Logitech Webcam C500 */ 117262306a36Sopenharmony_ci case USB_ID(0x046d, 0x0808): 117362306a36Sopenharmony_ci case USB_ID(0x046d, 0x0809): 117462306a36Sopenharmony_ci case USB_ID(0x046d, 0x0819): /* Logitech Webcam C210 */ 117562306a36Sopenharmony_ci case USB_ID(0x046d, 0x081b): /* HD Webcam c310 */ 117662306a36Sopenharmony_ci case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */ 117762306a36Sopenharmony_ci case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */ 117862306a36Sopenharmony_ci case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */ 117962306a36Sopenharmony_ci case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */ 118062306a36Sopenharmony_ci case USB_ID(0x046d, 0x0991): 118162306a36Sopenharmony_ci case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */ 118262306a36Sopenharmony_ci /* Most audio usb devices lie about volume resolution. 118362306a36Sopenharmony_ci * Most Logitech webcams have res = 384. 118462306a36Sopenharmony_ci * Probably there is some logitech magic behind this number --fishor 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_ci if (!strcmp(kctl->id.name, "Mic Capture Volume")) { 118762306a36Sopenharmony_ci usb_audio_info(chip, 118862306a36Sopenharmony_ci "set resolution quirk: cval->res = 384\n"); 118962306a36Sopenharmony_ci cval->res = 384; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci break; 119262306a36Sopenharmony_ci case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */ 119362306a36Sopenharmony_ci if ((strstr(kctl->id.name, "Playback Volume") != NULL) || 119462306a36Sopenharmony_ci strstr(kctl->id.name, "Capture Volume") != NULL) { 119562306a36Sopenharmony_ci cval->min >>= 8; 119662306a36Sopenharmony_ci cval->max = 0; 119762306a36Sopenharmony_ci cval->res = 1; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci break; 120062306a36Sopenharmony_ci case USB_ID(0x1224, 0x2a25): /* Jieli Technology USB PHY 2.0 */ 120162306a36Sopenharmony_ci if (!strcmp(kctl->id.name, "Mic Capture Volume")) { 120262306a36Sopenharmony_ci usb_audio_info(chip, 120362306a36Sopenharmony_ci "set resolution quirk: cval->res = 16\n"); 120462306a36Sopenharmony_ci cval->res = 16; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci break; 120762306a36Sopenharmony_ci case USB_ID(0x1bcf, 0x2283): /* NexiGo N930AF FHD Webcam */ 120862306a36Sopenharmony_ci if (!strcmp(kctl->id.name, "Mic Capture Volume")) { 120962306a36Sopenharmony_ci usb_audio_info(chip, 121062306a36Sopenharmony_ci "set resolution quirk: cval->res = 16\n"); 121162306a36Sopenharmony_ci cval->res = 16; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci break; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci/* forcibly initialize the current mixer value; if GET_CUR fails, set to 121862306a36Sopenharmony_ci * the minimum as default 121962306a36Sopenharmony_ci */ 122062306a36Sopenharmony_cistatic void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci int val, err; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci err = snd_usb_get_cur_mix_value(cval, ch, idx, &val); 122562306a36Sopenharmony_ci if (!err) 122662306a36Sopenharmony_ci return; 122762306a36Sopenharmony_ci if (!cval->head.mixer->ignore_ctl_error) 122862306a36Sopenharmony_ci usb_audio_warn(cval->head.mixer->chip, 122962306a36Sopenharmony_ci "%d:%d: failed to get current value for ch %d (%d)\n", 123062306a36Sopenharmony_ci cval->head.id, mixer_ctrl_intf(cval->head.mixer), 123162306a36Sopenharmony_ci ch, err); 123262306a36Sopenharmony_ci snd_usb_set_cur_mix_value(cval, ch, idx, cval->min); 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci/* 123662306a36Sopenharmony_ci * retrieve the minimum and maximum values for the specified control 123762306a36Sopenharmony_ci */ 123862306a36Sopenharmony_cistatic int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, 123962306a36Sopenharmony_ci int default_min, struct snd_kcontrol *kctl) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci int i, idx; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci /* for failsafe */ 124462306a36Sopenharmony_ci cval->min = default_min; 124562306a36Sopenharmony_ci cval->max = cval->min + 1; 124662306a36Sopenharmony_ci cval->res = 1; 124762306a36Sopenharmony_ci cval->dBmin = cval->dBmax = 0; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (cval->val_type == USB_MIXER_BOOLEAN || 125062306a36Sopenharmony_ci cval->val_type == USB_MIXER_INV_BOOLEAN) { 125162306a36Sopenharmony_ci cval->initialized = 1; 125262306a36Sopenharmony_ci } else { 125362306a36Sopenharmony_ci int minchn = 0; 125462306a36Sopenharmony_ci if (cval->cmask) { 125562306a36Sopenharmony_ci for (i = 0; i < MAX_CHANNELS; i++) 125662306a36Sopenharmony_ci if (cval->cmask & (1 << i)) { 125762306a36Sopenharmony_ci minchn = i + 1; 125862306a36Sopenharmony_ci break; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || 126262306a36Sopenharmony_ci get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { 126362306a36Sopenharmony_ci usb_audio_err(cval->head.mixer->chip, 126462306a36Sopenharmony_ci "%d:%d: cannot get min/max values for control %d (id %d)\n", 126562306a36Sopenharmony_ci cval->head.id, mixer_ctrl_intf(cval->head.mixer), 126662306a36Sopenharmony_ci cval->control, cval->head.id); 126762306a36Sopenharmony_ci return -EINVAL; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci if (get_ctl_value(cval, UAC_GET_RES, 127062306a36Sopenharmony_ci (cval->control << 8) | minchn, 127162306a36Sopenharmony_ci &cval->res) < 0) { 127262306a36Sopenharmony_ci cval->res = 1; 127362306a36Sopenharmony_ci } else if (cval->head.mixer->protocol == UAC_VERSION_1) { 127462306a36Sopenharmony_ci int last_valid_res = cval->res; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci while (cval->res > 1) { 127762306a36Sopenharmony_ci if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES, 127862306a36Sopenharmony_ci (cval->control << 8) | minchn, 127962306a36Sopenharmony_ci cval->res / 2) < 0) 128062306a36Sopenharmony_ci break; 128162306a36Sopenharmony_ci cval->res /= 2; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci if (get_ctl_value(cval, UAC_GET_RES, 128462306a36Sopenharmony_ci (cval->control << 8) | minchn, &cval->res) < 0) 128562306a36Sopenharmony_ci cval->res = last_valid_res; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci if (cval->res == 0) 128862306a36Sopenharmony_ci cval->res = 1; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci /* Additional checks for the proper resolution 129162306a36Sopenharmony_ci * 129262306a36Sopenharmony_ci * Some devices report smaller resolutions than actually 129362306a36Sopenharmony_ci * reacting. They don't return errors but simply clip 129462306a36Sopenharmony_ci * to the lower aligned value. 129562306a36Sopenharmony_ci */ 129662306a36Sopenharmony_ci if (cval->min + cval->res < cval->max) { 129762306a36Sopenharmony_ci int last_valid_res = cval->res; 129862306a36Sopenharmony_ci int saved, test, check; 129962306a36Sopenharmony_ci if (get_cur_mix_raw(cval, minchn, &saved) < 0) 130062306a36Sopenharmony_ci goto no_res_check; 130162306a36Sopenharmony_ci for (;;) { 130262306a36Sopenharmony_ci test = saved; 130362306a36Sopenharmony_ci if (test < cval->max) 130462306a36Sopenharmony_ci test += cval->res; 130562306a36Sopenharmony_ci else 130662306a36Sopenharmony_ci test -= cval->res; 130762306a36Sopenharmony_ci if (test < cval->min || test > cval->max || 130862306a36Sopenharmony_ci snd_usb_set_cur_mix_value(cval, minchn, 0, test) || 130962306a36Sopenharmony_ci get_cur_mix_raw(cval, minchn, &check)) { 131062306a36Sopenharmony_ci cval->res = last_valid_res; 131162306a36Sopenharmony_ci break; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci if (test == check) 131462306a36Sopenharmony_ci break; 131562306a36Sopenharmony_ci cval->res *= 2; 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci snd_usb_set_cur_mix_value(cval, minchn, 0, saved); 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cino_res_check: 132162306a36Sopenharmony_ci cval->initialized = 1; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (kctl) 132562306a36Sopenharmony_ci volume_control_quirks(cval, kctl); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci /* USB descriptions contain the dB scale in 1/256 dB unit 132862306a36Sopenharmony_ci * while ALSA TLV contains in 1/100 dB unit 132962306a36Sopenharmony_ci */ 133062306a36Sopenharmony_ci cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256; 133162306a36Sopenharmony_ci cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256; 133262306a36Sopenharmony_ci if (cval->dBmin > cval->dBmax) { 133362306a36Sopenharmony_ci /* something is wrong; assume it's either from/to 0dB */ 133462306a36Sopenharmony_ci if (cval->dBmin < 0) 133562306a36Sopenharmony_ci cval->dBmax = 0; 133662306a36Sopenharmony_ci else if (cval->dBmin > 0) 133762306a36Sopenharmony_ci cval->dBmin = 0; 133862306a36Sopenharmony_ci if (cval->dBmin > cval->dBmax) { 133962306a36Sopenharmony_ci /* totally crap, return an error */ 134062306a36Sopenharmony_ci return -EINVAL; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci } else { 134362306a36Sopenharmony_ci /* if the max volume is too low, it's likely a bogus range; 134462306a36Sopenharmony_ci * here we use -96dB as the threshold 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_ci if (cval->dBmax <= -9600) { 134762306a36Sopenharmony_ci usb_audio_info(cval->head.mixer->chip, 134862306a36Sopenharmony_ci "%d:%d: bogus dB values (%d/%d), disabling dB reporting\n", 134962306a36Sopenharmony_ci cval->head.id, mixer_ctrl_intf(cval->head.mixer), 135062306a36Sopenharmony_ci cval->dBmin, cval->dBmax); 135162306a36Sopenharmony_ci cval->dBmin = cval->dBmax = 0; 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* initialize all elements */ 135662306a36Sopenharmony_ci if (!cval->cmask) { 135762306a36Sopenharmony_ci init_cur_mix_raw(cval, 0, 0); 135862306a36Sopenharmony_ci } else { 135962306a36Sopenharmony_ci idx = 0; 136062306a36Sopenharmony_ci for (i = 0; i < MAX_CHANNELS; i++) { 136162306a36Sopenharmony_ci if (cval->cmask & (1 << i)) { 136262306a36Sopenharmony_ci init_cur_mix_raw(cval, i + 1, idx); 136362306a36Sopenharmony_ci idx++; 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return 0; 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci#define get_min_max(cval, def) get_min_max_with_quirks(cval, def, NULL) 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci/* get a feature/mixer unit info */ 137462306a36Sopenharmony_cistatic int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, 137562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (cval->val_type == USB_MIXER_BOOLEAN || 138062306a36Sopenharmony_ci cval->val_type == USB_MIXER_INV_BOOLEAN) 138162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 138262306a36Sopenharmony_ci else 138362306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 138462306a36Sopenharmony_ci uinfo->count = cval->channels; 138562306a36Sopenharmony_ci if (cval->val_type == USB_MIXER_BOOLEAN || 138662306a36Sopenharmony_ci cval->val_type == USB_MIXER_INV_BOOLEAN) { 138762306a36Sopenharmony_ci uinfo->value.integer.min = 0; 138862306a36Sopenharmony_ci uinfo->value.integer.max = 1; 138962306a36Sopenharmony_ci } else { 139062306a36Sopenharmony_ci if (!cval->initialized) { 139162306a36Sopenharmony_ci get_min_max_with_quirks(cval, 0, kcontrol); 139262306a36Sopenharmony_ci if (cval->initialized && cval->dBmin >= cval->dBmax) { 139362306a36Sopenharmony_ci kcontrol->vd[0].access &= 139462306a36Sopenharmony_ci ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ | 139562306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); 139662306a36Sopenharmony_ci snd_ctl_notify(cval->head.mixer->chip->card, 139762306a36Sopenharmony_ci SNDRV_CTL_EVENT_MASK_INFO, 139862306a36Sopenharmony_ci &kcontrol->id); 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci uinfo->value.integer.min = 0; 140262306a36Sopenharmony_ci uinfo->value.integer.max = 140362306a36Sopenharmony_ci DIV_ROUND_UP(cval->max - cval->min, cval->res); 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci return 0; 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci/* get the current value from feature/mixer unit */ 140962306a36Sopenharmony_cistatic int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, 141062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 141362306a36Sopenharmony_ci int c, cnt, val, err; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = cval->min; 141662306a36Sopenharmony_ci if (cval->cmask) { 141762306a36Sopenharmony_ci cnt = 0; 141862306a36Sopenharmony_ci for (c = 0; c < MAX_CHANNELS; c++) { 141962306a36Sopenharmony_ci if (!(cval->cmask & (1 << c))) 142062306a36Sopenharmony_ci continue; 142162306a36Sopenharmony_ci err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val); 142262306a36Sopenharmony_ci if (err < 0) 142362306a36Sopenharmony_ci return filter_error(cval, err); 142462306a36Sopenharmony_ci val = get_relative_value(cval, val); 142562306a36Sopenharmony_ci ucontrol->value.integer.value[cnt] = val; 142662306a36Sopenharmony_ci cnt++; 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci return 0; 142962306a36Sopenharmony_ci } else { 143062306a36Sopenharmony_ci /* master channel */ 143162306a36Sopenharmony_ci err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); 143262306a36Sopenharmony_ci if (err < 0) 143362306a36Sopenharmony_ci return filter_error(cval, err); 143462306a36Sopenharmony_ci val = get_relative_value(cval, val); 143562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = val; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci return 0; 143862306a36Sopenharmony_ci} 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci/* put the current value to feature/mixer unit */ 144162306a36Sopenharmony_cistatic int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, 144262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 144362306a36Sopenharmony_ci{ 144462306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 144562306a36Sopenharmony_ci int c, cnt, val, oval, err; 144662306a36Sopenharmony_ci int changed = 0; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (cval->cmask) { 144962306a36Sopenharmony_ci cnt = 0; 145062306a36Sopenharmony_ci for (c = 0; c < MAX_CHANNELS; c++) { 145162306a36Sopenharmony_ci if (!(cval->cmask & (1 << c))) 145262306a36Sopenharmony_ci continue; 145362306a36Sopenharmony_ci err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval); 145462306a36Sopenharmony_ci if (err < 0) 145562306a36Sopenharmony_ci return filter_error(cval, err); 145662306a36Sopenharmony_ci val = ucontrol->value.integer.value[cnt]; 145762306a36Sopenharmony_ci val = get_abs_value(cval, val); 145862306a36Sopenharmony_ci if (oval != val) { 145962306a36Sopenharmony_ci snd_usb_set_cur_mix_value(cval, c + 1, cnt, val); 146062306a36Sopenharmony_ci changed = 1; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci cnt++; 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci } else { 146562306a36Sopenharmony_ci /* master channel */ 146662306a36Sopenharmony_ci err = snd_usb_get_cur_mix_value(cval, 0, 0, &oval); 146762306a36Sopenharmony_ci if (err < 0) 146862306a36Sopenharmony_ci return filter_error(cval, err); 146962306a36Sopenharmony_ci val = ucontrol->value.integer.value[0]; 147062306a36Sopenharmony_ci val = get_abs_value(cval, val); 147162306a36Sopenharmony_ci if (val != oval) { 147262306a36Sopenharmony_ci snd_usb_set_cur_mix_value(cval, 0, 0, val); 147362306a36Sopenharmony_ci changed = 1; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci return changed; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci/* get the boolean value from the master channel of a UAC control */ 148062306a36Sopenharmony_cistatic int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol, 148162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 148462306a36Sopenharmony_ci int val, err; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); 148762306a36Sopenharmony_ci if (err < 0) 148862306a36Sopenharmony_ci return filter_error(cval, err); 148962306a36Sopenharmony_ci val = (val != 0); 149062306a36Sopenharmony_ci ucontrol->value.integer.value[0] = val; 149162306a36Sopenharmony_ci return 0; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic int get_connector_value(struct usb_mixer_elem_info *cval, 149562306a36Sopenharmony_ci char *name, int *val) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci struct snd_usb_audio *chip = cval->head.mixer->chip; 149862306a36Sopenharmony_ci int idx = 0, validx, ret; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci validx = cval->control << 8 | 0; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci ret = snd_usb_lock_shutdown(chip) ? -EIO : 0; 150362306a36Sopenharmony_ci if (ret) 150462306a36Sopenharmony_ci goto error; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); 150762306a36Sopenharmony_ci if (cval->head.mixer->protocol == UAC_VERSION_2) { 150862306a36Sopenharmony_ci struct uac2_connectors_ctl_blk uac2_conn; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR, 151162306a36Sopenharmony_ci USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 151262306a36Sopenharmony_ci validx, idx, &uac2_conn, sizeof(uac2_conn)); 151362306a36Sopenharmony_ci if (val) 151462306a36Sopenharmony_ci *val = !!uac2_conn.bNrChannels; 151562306a36Sopenharmony_ci } else { /* UAC_VERSION_3 */ 151662306a36Sopenharmony_ci struct uac3_insertion_ctl_blk uac3_conn; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), UAC2_CS_CUR, 151962306a36Sopenharmony_ci USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 152062306a36Sopenharmony_ci validx, idx, &uac3_conn, sizeof(uac3_conn)); 152162306a36Sopenharmony_ci if (val) 152262306a36Sopenharmony_ci *val = !!uac3_conn.bmConInserted; 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci snd_usb_unlock_shutdown(chip); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (ret < 0) { 152862306a36Sopenharmony_ci if (name && strstr(name, "Speaker")) { 152962306a36Sopenharmony_ci if (val) 153062306a36Sopenharmony_ci *val = 1; 153162306a36Sopenharmony_ci return 0; 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_cierror: 153462306a36Sopenharmony_ci usb_audio_err(chip, 153562306a36Sopenharmony_ci "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", 153662306a36Sopenharmony_ci UAC_GET_CUR, validx, idx, cval->val_type); 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci if (val) 153962306a36Sopenharmony_ci *val = 0; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci return filter_error(cval, ret); 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci return ret; 154562306a36Sopenharmony_ci} 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci/* get the connectors status and report it as boolean type */ 154862306a36Sopenharmony_cistatic int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol, 154962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 155062306a36Sopenharmony_ci{ 155162306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 155262306a36Sopenharmony_ci int ret, val; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci ret = get_connector_value(cval, kcontrol->id.name, &val); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (ret < 0) 155762306a36Sopenharmony_ci return ret; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = val; 156062306a36Sopenharmony_ci return 0; 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_cistatic const struct snd_kcontrol_new usb_feature_unit_ctl = { 156462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 156562306a36Sopenharmony_ci .name = "", /* will be filled later manually */ 156662306a36Sopenharmony_ci .info = mixer_ctl_feature_info, 156762306a36Sopenharmony_ci .get = mixer_ctl_feature_get, 156862306a36Sopenharmony_ci .put = mixer_ctl_feature_put, 156962306a36Sopenharmony_ci}; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci/* the read-only variant */ 157262306a36Sopenharmony_cistatic const struct snd_kcontrol_new usb_feature_unit_ctl_ro = { 157362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 157462306a36Sopenharmony_ci .name = "", /* will be filled later manually */ 157562306a36Sopenharmony_ci .info = mixer_ctl_feature_info, 157662306a36Sopenharmony_ci .get = mixer_ctl_feature_get, 157762306a36Sopenharmony_ci .put = NULL, 157862306a36Sopenharmony_ci}; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci/* 158162306a36Sopenharmony_ci * A control which shows the boolean value from reading a UAC control on 158262306a36Sopenharmony_ci * the master channel. 158362306a36Sopenharmony_ci */ 158462306a36Sopenharmony_cistatic const struct snd_kcontrol_new usb_bool_master_control_ctl_ro = { 158562306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_CARD, 158662306a36Sopenharmony_ci .name = "", /* will be filled later manually */ 158762306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 158862306a36Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 158962306a36Sopenharmony_ci .get = mixer_ctl_master_bool_get, 159062306a36Sopenharmony_ci .put = NULL, 159162306a36Sopenharmony_ci}; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cistatic const struct snd_kcontrol_new usb_connector_ctl_ro = { 159462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_CARD, 159562306a36Sopenharmony_ci .name = "", /* will be filled later manually */ 159662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 159762306a36Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 159862306a36Sopenharmony_ci .get = mixer_ctl_connector_get, 159962306a36Sopenharmony_ci .put = NULL, 160062306a36Sopenharmony_ci}; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci/* 160362306a36Sopenharmony_ci * This symbol is exported in order to allow the mixer quirks to 160462306a36Sopenharmony_ci * hook up to the standard feature unit control mechanism 160562306a36Sopenharmony_ci */ 160662306a36Sopenharmony_ciconst struct snd_kcontrol_new *snd_usb_feature_unit_ctl = &usb_feature_unit_ctl; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci/* 160962306a36Sopenharmony_ci * build a feature control 161062306a36Sopenharmony_ci */ 161162306a36Sopenharmony_cistatic size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str) 161262306a36Sopenharmony_ci{ 161362306a36Sopenharmony_ci return strlcat(kctl->id.name, str, sizeof(kctl->id.name)); 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci/* 161762306a36Sopenharmony_ci * A lot of headsets/headphones have a "Speaker" mixer. Make sure we 161862306a36Sopenharmony_ci * rename it to "Headphone". We determine if something is a headphone 161962306a36Sopenharmony_ci * similar to how udev determines form factor. 162062306a36Sopenharmony_ci */ 162162306a36Sopenharmony_cistatic void check_no_speaker_on_headset(struct snd_kcontrol *kctl, 162262306a36Sopenharmony_ci struct snd_card *card) 162362306a36Sopenharmony_ci{ 162462306a36Sopenharmony_ci static const char * const names_to_check[] = { 162562306a36Sopenharmony_ci "Headset", "headset", "Headphone", "headphone", NULL}; 162662306a36Sopenharmony_ci const char * const *s; 162762306a36Sopenharmony_ci bool found = false; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci if (strcmp("Speaker", kctl->id.name)) 163062306a36Sopenharmony_ci return; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci for (s = names_to_check; *s; s++) 163362306a36Sopenharmony_ci if (strstr(card->shortname, *s)) { 163462306a36Sopenharmony_ci found = true; 163562306a36Sopenharmony_ci break; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (!found) 163962306a36Sopenharmony_ci return; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci snd_ctl_rename(card, kctl, "Headphone"); 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_cistatic const struct usb_feature_control_info *get_feature_control_info(int control) 164562306a36Sopenharmony_ci{ 164662306a36Sopenharmony_ci int i; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(audio_feature_info); ++i) { 164962306a36Sopenharmony_ci if (audio_feature_info[i].control == control) 165062306a36Sopenharmony_ci return &audio_feature_info[i]; 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci return NULL; 165362306a36Sopenharmony_ci} 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_cistatic void __build_feature_ctl(struct usb_mixer_interface *mixer, 165662306a36Sopenharmony_ci const struct usbmix_name_map *imap, 165762306a36Sopenharmony_ci unsigned int ctl_mask, int control, 165862306a36Sopenharmony_ci struct usb_audio_term *iterm, 165962306a36Sopenharmony_ci struct usb_audio_term *oterm, 166062306a36Sopenharmony_ci int unitid, int nameid, int readonly_mask) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci const struct usb_feature_control_info *ctl_info; 166362306a36Sopenharmony_ci unsigned int len = 0; 166462306a36Sopenharmony_ci int mapped_name = 0; 166562306a36Sopenharmony_ci struct snd_kcontrol *kctl; 166662306a36Sopenharmony_ci struct usb_mixer_elem_info *cval; 166762306a36Sopenharmony_ci const struct usbmix_name_map *map; 166862306a36Sopenharmony_ci unsigned int range; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (control == UAC_FU_GRAPHIC_EQUALIZER) { 167162306a36Sopenharmony_ci /* FIXME: not supported yet */ 167262306a36Sopenharmony_ci return; 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci map = find_map(imap, unitid, control); 167662306a36Sopenharmony_ci if (check_ignored_ctl(map)) 167762306a36Sopenharmony_ci return; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci cval = kzalloc(sizeof(*cval), GFP_KERNEL); 168062306a36Sopenharmony_ci if (!cval) 168162306a36Sopenharmony_ci return; 168262306a36Sopenharmony_ci snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); 168362306a36Sopenharmony_ci cval->control = control; 168462306a36Sopenharmony_ci cval->cmask = ctl_mask; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci ctl_info = get_feature_control_info(control); 168762306a36Sopenharmony_ci if (!ctl_info) { 168862306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 168962306a36Sopenharmony_ci return; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci if (mixer->protocol == UAC_VERSION_1) 169262306a36Sopenharmony_ci cval->val_type = ctl_info->type; 169362306a36Sopenharmony_ci else /* UAC_VERSION_2 */ 169462306a36Sopenharmony_ci cval->val_type = ctl_info->type_uac2 >= 0 ? 169562306a36Sopenharmony_ci ctl_info->type_uac2 : ctl_info->type; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci if (ctl_mask == 0) { 169862306a36Sopenharmony_ci cval->channels = 1; /* master channel */ 169962306a36Sopenharmony_ci cval->master_readonly = readonly_mask; 170062306a36Sopenharmony_ci } else { 170162306a36Sopenharmony_ci int i, c = 0; 170262306a36Sopenharmony_ci for (i = 0; i < 16; i++) 170362306a36Sopenharmony_ci if (ctl_mask & (1 << i)) 170462306a36Sopenharmony_ci c++; 170562306a36Sopenharmony_ci cval->channels = c; 170662306a36Sopenharmony_ci cval->ch_readonly = readonly_mask; 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci /* 171062306a36Sopenharmony_ci * If all channels in the mask are marked read-only, make the control 171162306a36Sopenharmony_ci * read-only. snd_usb_set_cur_mix_value() will check the mask again and won't 171262306a36Sopenharmony_ci * issue write commands to read-only channels. 171362306a36Sopenharmony_ci */ 171462306a36Sopenharmony_ci if (cval->channels == readonly_mask) 171562306a36Sopenharmony_ci kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval); 171662306a36Sopenharmony_ci else 171762306a36Sopenharmony_ci kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (!kctl) { 172062306a36Sopenharmony_ci usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); 172162306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 172262306a36Sopenharmony_ci return; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci kctl->private_free = snd_usb_mixer_elem_free; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); 172762306a36Sopenharmony_ci mapped_name = len != 0; 172862306a36Sopenharmony_ci if (!len && nameid) 172962306a36Sopenharmony_ci len = snd_usb_copy_string_desc(mixer->chip, nameid, 173062306a36Sopenharmony_ci kctl->id.name, sizeof(kctl->id.name)); 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci switch (control) { 173362306a36Sopenharmony_ci case UAC_FU_MUTE: 173462306a36Sopenharmony_ci case UAC_FU_VOLUME: 173562306a36Sopenharmony_ci /* 173662306a36Sopenharmony_ci * determine the control name. the rule is: 173762306a36Sopenharmony_ci * - if a name id is given in descriptor, use it. 173862306a36Sopenharmony_ci * - if the connected input can be determined, then use the name 173962306a36Sopenharmony_ci * of terminal type. 174062306a36Sopenharmony_ci * - if the connected output can be determined, use it. 174162306a36Sopenharmony_ci * - otherwise, anonymous name. 174262306a36Sopenharmony_ci */ 174362306a36Sopenharmony_ci if (!len) { 174462306a36Sopenharmony_ci if (iterm) 174562306a36Sopenharmony_ci len = get_term_name(mixer->chip, iterm, 174662306a36Sopenharmony_ci kctl->id.name, 174762306a36Sopenharmony_ci sizeof(kctl->id.name), 1); 174862306a36Sopenharmony_ci if (!len && oterm) 174962306a36Sopenharmony_ci len = get_term_name(mixer->chip, oterm, 175062306a36Sopenharmony_ci kctl->id.name, 175162306a36Sopenharmony_ci sizeof(kctl->id.name), 1); 175262306a36Sopenharmony_ci if (!len) 175362306a36Sopenharmony_ci snprintf(kctl->id.name, sizeof(kctl->id.name), 175462306a36Sopenharmony_ci "Feature %d", unitid); 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if (!mapped_name) 175862306a36Sopenharmony_ci check_no_speaker_on_headset(kctl, mixer->chip->card); 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci /* 176162306a36Sopenharmony_ci * determine the stream direction: 176262306a36Sopenharmony_ci * if the connected output is USB stream, then it's likely a 176362306a36Sopenharmony_ci * capture stream. otherwise it should be playback (hopefully :) 176462306a36Sopenharmony_ci */ 176562306a36Sopenharmony_ci if (!mapped_name && oterm && !(oterm->type >> 16)) { 176662306a36Sopenharmony_ci if ((oterm->type & 0xff00) == 0x0100) 176762306a36Sopenharmony_ci append_ctl_name(kctl, " Capture"); 176862306a36Sopenharmony_ci else 176962306a36Sopenharmony_ci append_ctl_name(kctl, " Playback"); 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci append_ctl_name(kctl, control == UAC_FU_MUTE ? 177262306a36Sopenharmony_ci " Switch" : " Volume"); 177362306a36Sopenharmony_ci break; 177462306a36Sopenharmony_ci default: 177562306a36Sopenharmony_ci if (!len) 177662306a36Sopenharmony_ci strscpy(kctl->id.name, audio_feature_info[control-1].name, 177762306a36Sopenharmony_ci sizeof(kctl->id.name)); 177862306a36Sopenharmony_ci break; 177962306a36Sopenharmony_ci } 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci /* get min/max values */ 178262306a36Sopenharmony_ci get_min_max_with_quirks(cval, 0, kctl); 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci /* skip a bogus volume range */ 178562306a36Sopenharmony_ci if (cval->max <= cval->min) { 178662306a36Sopenharmony_ci usb_audio_dbg(mixer->chip, 178762306a36Sopenharmony_ci "[%d] FU [%s] skipped due to invalid volume\n", 178862306a36Sopenharmony_ci cval->head.id, kctl->id.name); 178962306a36Sopenharmony_ci snd_ctl_free_one(kctl); 179062306a36Sopenharmony_ci return; 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci if (control == UAC_FU_VOLUME) { 179562306a36Sopenharmony_ci check_mapped_dB(map, cval); 179662306a36Sopenharmony_ci if (cval->dBmin < cval->dBmax || !cval->initialized) { 179762306a36Sopenharmony_ci kctl->tlv.c = snd_usb_mixer_vol_tlv; 179862306a36Sopenharmony_ci kctl->vd[0].access |= 179962306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ | 180062306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci } 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci range = (cval->max - cval->min) / cval->res; 180762306a36Sopenharmony_ci /* 180862306a36Sopenharmony_ci * Are there devices with volume range more than 255? I use a bit more 180962306a36Sopenharmony_ci * to be sure. 384 is a resolution magic number found on Logitech 181062306a36Sopenharmony_ci * devices. It will definitively catch all buggy Logitech devices. 181162306a36Sopenharmony_ci */ 181262306a36Sopenharmony_ci if (range > 384) { 181362306a36Sopenharmony_ci usb_audio_warn(mixer->chip, 181462306a36Sopenharmony_ci "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.", 181562306a36Sopenharmony_ci range); 181662306a36Sopenharmony_ci usb_audio_warn(mixer->chip, 181762306a36Sopenharmony_ci "[%d] FU [%s] ch = %d, val = %d/%d/%d", 181862306a36Sopenharmony_ci cval->head.id, kctl->id.name, cval->channels, 181962306a36Sopenharmony_ci cval->min, cval->max, cval->res); 182062306a36Sopenharmony_ci } 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", 182362306a36Sopenharmony_ci cval->head.id, kctl->id.name, cval->channels, 182462306a36Sopenharmony_ci cval->min, cval->max, cval->res); 182562306a36Sopenharmony_ci snd_usb_mixer_add_control(&cval->head, kctl); 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_cistatic void build_feature_ctl(struct mixer_build *state, void *raw_desc, 182962306a36Sopenharmony_ci unsigned int ctl_mask, int control, 183062306a36Sopenharmony_ci struct usb_audio_term *iterm, int unitid, 183162306a36Sopenharmony_ci int readonly_mask) 183262306a36Sopenharmony_ci{ 183362306a36Sopenharmony_ci struct uac_feature_unit_descriptor *desc = raw_desc; 183462306a36Sopenharmony_ci int nameid = uac_feature_unit_iFeature(desc); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci __build_feature_ctl(state->mixer, state->map, ctl_mask, control, 183762306a36Sopenharmony_ci iterm, &state->oterm, unitid, nameid, readonly_mask); 183862306a36Sopenharmony_ci} 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_cistatic void build_feature_ctl_badd(struct usb_mixer_interface *mixer, 184162306a36Sopenharmony_ci unsigned int ctl_mask, int control, int unitid, 184262306a36Sopenharmony_ci const struct usbmix_name_map *badd_map) 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci __build_feature_ctl(mixer, badd_map, ctl_mask, control, 184562306a36Sopenharmony_ci NULL, NULL, unitid, 0, 0); 184662306a36Sopenharmony_ci} 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_cistatic void get_connector_control_name(struct usb_mixer_interface *mixer, 184962306a36Sopenharmony_ci struct usb_audio_term *term, 185062306a36Sopenharmony_ci bool is_input, char *name, int name_size) 185162306a36Sopenharmony_ci{ 185262306a36Sopenharmony_ci int name_len = get_term_name(mixer->chip, term, name, name_size, 0); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci if (name_len == 0) 185562306a36Sopenharmony_ci strscpy(name, "Unknown", name_size); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci /* 185862306a36Sopenharmony_ci * sound/core/ctljack.c has a convention of naming jack controls 185962306a36Sopenharmony_ci * by ending in " Jack". Make it slightly more useful by 186062306a36Sopenharmony_ci * indicating Input or Output after the terminal name. 186162306a36Sopenharmony_ci */ 186262306a36Sopenharmony_ci if (is_input) 186362306a36Sopenharmony_ci strlcat(name, " - Input Jack", name_size); 186462306a36Sopenharmony_ci else 186562306a36Sopenharmony_ci strlcat(name, " - Output Jack", name_size); 186662306a36Sopenharmony_ci} 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci/* get connector value to "wake up" the USB audio */ 186962306a36Sopenharmony_cistatic int connector_mixer_resume(struct usb_mixer_elem_list *list) 187062306a36Sopenharmony_ci{ 187162306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci get_connector_value(cval, NULL, NULL); 187462306a36Sopenharmony_ci return 0; 187562306a36Sopenharmony_ci} 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci/* Build a mixer control for a UAC connector control (jack-detect) */ 187862306a36Sopenharmony_cistatic void build_connector_control(struct usb_mixer_interface *mixer, 187962306a36Sopenharmony_ci const struct usbmix_name_map *imap, 188062306a36Sopenharmony_ci struct usb_audio_term *term, bool is_input) 188162306a36Sopenharmony_ci{ 188262306a36Sopenharmony_ci struct snd_kcontrol *kctl; 188362306a36Sopenharmony_ci struct usb_mixer_elem_info *cval; 188462306a36Sopenharmony_ci const struct usbmix_name_map *map; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci map = find_map(imap, term->id, 0); 188762306a36Sopenharmony_ci if (check_ignored_ctl(map)) 188862306a36Sopenharmony_ci return; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci cval = kzalloc(sizeof(*cval), GFP_KERNEL); 189162306a36Sopenharmony_ci if (!cval) 189262306a36Sopenharmony_ci return; 189362306a36Sopenharmony_ci snd_usb_mixer_elem_init_std(&cval->head, mixer, term->id); 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci /* set up a specific resume callback */ 189662306a36Sopenharmony_ci cval->head.resume = connector_mixer_resume; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci /* 189962306a36Sopenharmony_ci * UAC2: The first byte from reading the UAC2_TE_CONNECTOR control returns the 190062306a36Sopenharmony_ci * number of channels connected. 190162306a36Sopenharmony_ci * 190262306a36Sopenharmony_ci * UAC3: The first byte specifies size of bitmap for the inserted controls. The 190362306a36Sopenharmony_ci * following byte(s) specifies which connectors are inserted. 190462306a36Sopenharmony_ci * 190562306a36Sopenharmony_ci * This boolean ctl will simply report if any channels are connected 190662306a36Sopenharmony_ci * or not. 190762306a36Sopenharmony_ci */ 190862306a36Sopenharmony_ci if (mixer->protocol == UAC_VERSION_2) 190962306a36Sopenharmony_ci cval->control = UAC2_TE_CONNECTOR; 191062306a36Sopenharmony_ci else /* UAC_VERSION_3 */ 191162306a36Sopenharmony_ci cval->control = UAC3_TE_INSERTION; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci cval->val_type = USB_MIXER_BOOLEAN; 191462306a36Sopenharmony_ci cval->channels = 1; /* report true if any channel is connected */ 191562306a36Sopenharmony_ci cval->min = 0; 191662306a36Sopenharmony_ci cval->max = 1; 191762306a36Sopenharmony_ci kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval); 191862306a36Sopenharmony_ci if (!kctl) { 191962306a36Sopenharmony_ci usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); 192062306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 192162306a36Sopenharmony_ci return; 192262306a36Sopenharmony_ci } 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) 192562306a36Sopenharmony_ci strlcat(kctl->id.name, " Jack", sizeof(kctl->id.name)); 192662306a36Sopenharmony_ci else 192762306a36Sopenharmony_ci get_connector_control_name(mixer, term, is_input, kctl->id.name, 192862306a36Sopenharmony_ci sizeof(kctl->id.name)); 192962306a36Sopenharmony_ci kctl->private_free = snd_usb_mixer_elem_free; 193062306a36Sopenharmony_ci snd_usb_mixer_add_control(&cval->head, kctl); 193162306a36Sopenharmony_ci} 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_cistatic int parse_clock_source_unit(struct mixer_build *state, int unitid, 193462306a36Sopenharmony_ci void *_ftr) 193562306a36Sopenharmony_ci{ 193662306a36Sopenharmony_ci struct uac_clock_source_descriptor *hdr = _ftr; 193762306a36Sopenharmony_ci struct usb_mixer_elem_info *cval; 193862306a36Sopenharmony_ci struct snd_kcontrol *kctl; 193962306a36Sopenharmony_ci int ret; 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci if (state->mixer->protocol != UAC_VERSION_2) 194262306a36Sopenharmony_ci return -EINVAL; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci /* 194562306a36Sopenharmony_ci * The only property of this unit we are interested in is the 194662306a36Sopenharmony_ci * clock source validity. If that isn't readable, just bail out. 194762306a36Sopenharmony_ci */ 194862306a36Sopenharmony_ci if (!uac_v2v3_control_is_readable(hdr->bmControls, 194962306a36Sopenharmony_ci UAC2_CS_CONTROL_CLOCK_VALID)) 195062306a36Sopenharmony_ci return 0; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci cval = kzalloc(sizeof(*cval), GFP_KERNEL); 195362306a36Sopenharmony_ci if (!cval) 195462306a36Sopenharmony_ci return -ENOMEM; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci snd_usb_mixer_elem_init_std(&cval->head, state->mixer, hdr->bClockID); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci cval->min = 0; 195962306a36Sopenharmony_ci cval->max = 1; 196062306a36Sopenharmony_ci cval->channels = 1; 196162306a36Sopenharmony_ci cval->val_type = USB_MIXER_BOOLEAN; 196262306a36Sopenharmony_ci cval->control = UAC2_CS_CONTROL_CLOCK_VALID; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci cval->master_readonly = 1; 196562306a36Sopenharmony_ci /* From UAC2 5.2.5.1.2 "Only the get request is supported." */ 196662306a36Sopenharmony_ci kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval); 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci if (!kctl) { 196962306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 197062306a36Sopenharmony_ci return -ENOMEM; 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci kctl->private_free = snd_usb_mixer_elem_free; 197462306a36Sopenharmony_ci ret = snd_usb_copy_string_desc(state->chip, hdr->iClockSource, 197562306a36Sopenharmony_ci kctl->id.name, sizeof(kctl->id.name)); 197662306a36Sopenharmony_ci if (ret > 0) 197762306a36Sopenharmony_ci append_ctl_name(kctl, " Validity"); 197862306a36Sopenharmony_ci else 197962306a36Sopenharmony_ci snprintf(kctl->id.name, sizeof(kctl->id.name), 198062306a36Sopenharmony_ci "Clock Source %d Validity", hdr->bClockID); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci return snd_usb_mixer_add_control(&cval->head, kctl); 198362306a36Sopenharmony_ci} 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci/* 198662306a36Sopenharmony_ci * parse a feature unit 198762306a36Sopenharmony_ci * 198862306a36Sopenharmony_ci * most of controls are defined here. 198962306a36Sopenharmony_ci */ 199062306a36Sopenharmony_cistatic int parse_audio_feature_unit(struct mixer_build *state, int unitid, 199162306a36Sopenharmony_ci void *_ftr) 199262306a36Sopenharmony_ci{ 199362306a36Sopenharmony_ci int channels, i, j; 199462306a36Sopenharmony_ci struct usb_audio_term iterm; 199562306a36Sopenharmony_ci unsigned int master_bits; 199662306a36Sopenharmony_ci int err, csize; 199762306a36Sopenharmony_ci struct uac_feature_unit_descriptor *hdr = _ftr; 199862306a36Sopenharmony_ci __u8 *bmaControls; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci if (state->mixer->protocol == UAC_VERSION_1) { 200162306a36Sopenharmony_ci csize = hdr->bControlSize; 200262306a36Sopenharmony_ci channels = (hdr->bLength - 7) / csize - 1; 200362306a36Sopenharmony_ci bmaControls = hdr->bmaControls; 200462306a36Sopenharmony_ci } else if (state->mixer->protocol == UAC_VERSION_2) { 200562306a36Sopenharmony_ci struct uac2_feature_unit_descriptor *ftr = _ftr; 200662306a36Sopenharmony_ci csize = 4; 200762306a36Sopenharmony_ci channels = (hdr->bLength - 6) / 4 - 1; 200862306a36Sopenharmony_ci bmaControls = ftr->bmaControls; 200962306a36Sopenharmony_ci } else { /* UAC_VERSION_3 */ 201062306a36Sopenharmony_ci struct uac3_feature_unit_descriptor *ftr = _ftr; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci csize = 4; 201362306a36Sopenharmony_ci channels = (ftr->bLength - 7) / 4 - 1; 201462306a36Sopenharmony_ci bmaControls = ftr->bmaControls; 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci /* parse the source unit */ 201862306a36Sopenharmony_ci err = parse_audio_unit(state, hdr->bSourceID); 201962306a36Sopenharmony_ci if (err < 0) 202062306a36Sopenharmony_ci return err; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci /* determine the input source type and name */ 202362306a36Sopenharmony_ci err = check_input_term(state, hdr->bSourceID, &iterm); 202462306a36Sopenharmony_ci if (err < 0) 202562306a36Sopenharmony_ci return err; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci master_bits = snd_usb_combine_bytes(bmaControls, csize); 202862306a36Sopenharmony_ci /* master configuration quirks */ 202962306a36Sopenharmony_ci switch (state->chip->usb_id) { 203062306a36Sopenharmony_ci case USB_ID(0x08bb, 0x2702): 203162306a36Sopenharmony_ci usb_audio_info(state->chip, 203262306a36Sopenharmony_ci "usbmixer: master volume quirk for PCM2702 chip\n"); 203362306a36Sopenharmony_ci /* disable non-functional volume control */ 203462306a36Sopenharmony_ci master_bits &= ~UAC_CONTROL_BIT(UAC_FU_VOLUME); 203562306a36Sopenharmony_ci break; 203662306a36Sopenharmony_ci case USB_ID(0x1130, 0xf211): 203762306a36Sopenharmony_ci usb_audio_info(state->chip, 203862306a36Sopenharmony_ci "usbmixer: volume control quirk for Tenx TP6911 Audio Headset\n"); 203962306a36Sopenharmony_ci /* disable non-functional volume control */ 204062306a36Sopenharmony_ci channels = 0; 204162306a36Sopenharmony_ci break; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci } 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci if (state->mixer->protocol == UAC_VERSION_1) { 204662306a36Sopenharmony_ci /* check all control types */ 204762306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 204862306a36Sopenharmony_ci unsigned int ch_bits = 0; 204962306a36Sopenharmony_ci int control = audio_feature_info[i].control; 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci for (j = 0; j < channels; j++) { 205262306a36Sopenharmony_ci unsigned int mask; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci mask = snd_usb_combine_bytes(bmaControls + 205562306a36Sopenharmony_ci csize * (j+1), csize); 205662306a36Sopenharmony_ci if (mask & (1 << i)) 205762306a36Sopenharmony_ci ch_bits |= (1 << j); 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci /* audio class v1 controls are never read-only */ 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci /* 206262306a36Sopenharmony_ci * The first channel must be set 206362306a36Sopenharmony_ci * (for ease of programming). 206462306a36Sopenharmony_ci */ 206562306a36Sopenharmony_ci if (ch_bits & 1) 206662306a36Sopenharmony_ci build_feature_ctl(state, _ftr, ch_bits, control, 206762306a36Sopenharmony_ci &iterm, unitid, 0); 206862306a36Sopenharmony_ci if (master_bits & (1 << i)) 206962306a36Sopenharmony_ci build_feature_ctl(state, _ftr, 0, control, 207062306a36Sopenharmony_ci &iterm, unitid, 0); 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci } else { /* UAC_VERSION_2/3 */ 207362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) { 207462306a36Sopenharmony_ci unsigned int ch_bits = 0; 207562306a36Sopenharmony_ci unsigned int ch_read_only = 0; 207662306a36Sopenharmony_ci int control = audio_feature_info[i].control; 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci for (j = 0; j < channels; j++) { 207962306a36Sopenharmony_ci unsigned int mask; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci mask = snd_usb_combine_bytes(bmaControls + 208262306a36Sopenharmony_ci csize * (j+1), csize); 208362306a36Sopenharmony_ci if (uac_v2v3_control_is_readable(mask, control)) { 208462306a36Sopenharmony_ci ch_bits |= (1 << j); 208562306a36Sopenharmony_ci if (!uac_v2v3_control_is_writeable(mask, control)) 208662306a36Sopenharmony_ci ch_read_only |= (1 << j); 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci } 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci /* 209162306a36Sopenharmony_ci * NOTE: build_feature_ctl() will mark the control 209262306a36Sopenharmony_ci * read-only if all channels are marked read-only in 209362306a36Sopenharmony_ci * the descriptors. Otherwise, the control will be 209462306a36Sopenharmony_ci * reported as writeable, but the driver will not 209562306a36Sopenharmony_ci * actually issue a write command for read-only 209662306a36Sopenharmony_ci * channels. 209762306a36Sopenharmony_ci */ 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci /* 210062306a36Sopenharmony_ci * The first channel must be set 210162306a36Sopenharmony_ci * (for ease of programming). 210262306a36Sopenharmony_ci */ 210362306a36Sopenharmony_ci if (ch_bits & 1) 210462306a36Sopenharmony_ci build_feature_ctl(state, _ftr, ch_bits, control, 210562306a36Sopenharmony_ci &iterm, unitid, ch_read_only); 210662306a36Sopenharmony_ci if (uac_v2v3_control_is_readable(master_bits, control)) 210762306a36Sopenharmony_ci build_feature_ctl(state, _ftr, 0, control, 210862306a36Sopenharmony_ci &iterm, unitid, 210962306a36Sopenharmony_ci !uac_v2v3_control_is_writeable(master_bits, 211062306a36Sopenharmony_ci control)); 211162306a36Sopenharmony_ci } 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci return 0; 211562306a36Sopenharmony_ci} 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci/* 211862306a36Sopenharmony_ci * Mixer Unit 211962306a36Sopenharmony_ci */ 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci/* check whether the given in/out overflows bmMixerControls matrix */ 212262306a36Sopenharmony_cistatic bool mixer_bitmap_overflow(struct uac_mixer_unit_descriptor *desc, 212362306a36Sopenharmony_ci int protocol, int num_ins, int num_outs) 212462306a36Sopenharmony_ci{ 212562306a36Sopenharmony_ci u8 *hdr = (u8 *)desc; 212662306a36Sopenharmony_ci u8 *c = uac_mixer_unit_bmControls(desc, protocol); 212762306a36Sopenharmony_ci size_t rest; /* remaining bytes after bmMixerControls */ 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci switch (protocol) { 213062306a36Sopenharmony_ci case UAC_VERSION_1: 213162306a36Sopenharmony_ci default: 213262306a36Sopenharmony_ci rest = 1; /* iMixer */ 213362306a36Sopenharmony_ci break; 213462306a36Sopenharmony_ci case UAC_VERSION_2: 213562306a36Sopenharmony_ci rest = 2; /* bmControls + iMixer */ 213662306a36Sopenharmony_ci break; 213762306a36Sopenharmony_ci case UAC_VERSION_3: 213862306a36Sopenharmony_ci rest = 6; /* bmControls + wMixerDescrStr */ 213962306a36Sopenharmony_ci break; 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci /* overflow? */ 214362306a36Sopenharmony_ci return c + (num_ins * num_outs + 7) / 8 + rest > hdr + hdr[0]; 214462306a36Sopenharmony_ci} 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci/* 214762306a36Sopenharmony_ci * build a mixer unit control 214862306a36Sopenharmony_ci * 214962306a36Sopenharmony_ci * the callbacks are identical with feature unit. 215062306a36Sopenharmony_ci * input channel number (zero based) is given in control field instead. 215162306a36Sopenharmony_ci */ 215262306a36Sopenharmony_cistatic void build_mixer_unit_ctl(struct mixer_build *state, 215362306a36Sopenharmony_ci struct uac_mixer_unit_descriptor *desc, 215462306a36Sopenharmony_ci int in_pin, int in_ch, int num_outs, 215562306a36Sopenharmony_ci int unitid, struct usb_audio_term *iterm) 215662306a36Sopenharmony_ci{ 215762306a36Sopenharmony_ci struct usb_mixer_elem_info *cval; 215862306a36Sopenharmony_ci unsigned int i, len; 215962306a36Sopenharmony_ci struct snd_kcontrol *kctl; 216062306a36Sopenharmony_ci const struct usbmix_name_map *map; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci map = find_map(state->map, unitid, 0); 216362306a36Sopenharmony_ci if (check_ignored_ctl(map)) 216462306a36Sopenharmony_ci return; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci cval = kzalloc(sizeof(*cval), GFP_KERNEL); 216762306a36Sopenharmony_ci if (!cval) 216862306a36Sopenharmony_ci return; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); 217162306a36Sopenharmony_ci cval->control = in_ch + 1; /* based on 1 */ 217262306a36Sopenharmony_ci cval->val_type = USB_MIXER_S16; 217362306a36Sopenharmony_ci for (i = 0; i < num_outs; i++) { 217462306a36Sopenharmony_ci __u8 *c = uac_mixer_unit_bmControls(desc, state->mixer->protocol); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci if (check_matrix_bitmap(c, in_ch, i, num_outs)) { 217762306a36Sopenharmony_ci cval->cmask |= (1 << i); 217862306a36Sopenharmony_ci cval->channels++; 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci } 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci /* get min/max values */ 218362306a36Sopenharmony_ci get_min_max(cval, 0); 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); 218662306a36Sopenharmony_ci if (!kctl) { 218762306a36Sopenharmony_ci usb_audio_err(state->chip, "cannot malloc kcontrol\n"); 218862306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 218962306a36Sopenharmony_ci return; 219062306a36Sopenharmony_ci } 219162306a36Sopenharmony_ci kctl->private_free = snd_usb_mixer_elem_free; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); 219462306a36Sopenharmony_ci if (!len) 219562306a36Sopenharmony_ci len = get_term_name(state->chip, iterm, kctl->id.name, 219662306a36Sopenharmony_ci sizeof(kctl->id.name), 0); 219762306a36Sopenharmony_ci if (!len) 219862306a36Sopenharmony_ci len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1); 219962306a36Sopenharmony_ci append_ctl_name(kctl, " Volume"); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci usb_audio_dbg(state->chip, "[%d] MU [%s] ch = %d, val = %d/%d\n", 220262306a36Sopenharmony_ci cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max); 220362306a36Sopenharmony_ci snd_usb_mixer_add_control(&cval->head, kctl); 220462306a36Sopenharmony_ci} 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_cistatic int parse_audio_input_terminal(struct mixer_build *state, int unitid, 220762306a36Sopenharmony_ci void *raw_desc) 220862306a36Sopenharmony_ci{ 220962306a36Sopenharmony_ci struct usb_audio_term iterm; 221062306a36Sopenharmony_ci unsigned int control, bmctls, term_id; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci if (state->mixer->protocol == UAC_VERSION_2) { 221362306a36Sopenharmony_ci struct uac2_input_terminal_descriptor *d_v2 = raw_desc; 221462306a36Sopenharmony_ci control = UAC2_TE_CONNECTOR; 221562306a36Sopenharmony_ci term_id = d_v2->bTerminalID; 221662306a36Sopenharmony_ci bmctls = le16_to_cpu(d_v2->bmControls); 221762306a36Sopenharmony_ci } else if (state->mixer->protocol == UAC_VERSION_3) { 221862306a36Sopenharmony_ci struct uac3_input_terminal_descriptor *d_v3 = raw_desc; 221962306a36Sopenharmony_ci control = UAC3_TE_INSERTION; 222062306a36Sopenharmony_ci term_id = d_v3->bTerminalID; 222162306a36Sopenharmony_ci bmctls = le32_to_cpu(d_v3->bmControls); 222262306a36Sopenharmony_ci } else { 222362306a36Sopenharmony_ci return 0; /* UAC1. No Insertion control */ 222462306a36Sopenharmony_ci } 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci check_input_term(state, term_id, &iterm); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci /* Check for jack detection. */ 222962306a36Sopenharmony_ci if ((iterm.type & 0xff00) != 0x0100 && 223062306a36Sopenharmony_ci uac_v2v3_control_is_readable(bmctls, control)) 223162306a36Sopenharmony_ci build_connector_control(state->mixer, state->map, &iterm, true); 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci return 0; 223462306a36Sopenharmony_ci} 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci/* 223762306a36Sopenharmony_ci * parse a mixer unit 223862306a36Sopenharmony_ci */ 223962306a36Sopenharmony_cistatic int parse_audio_mixer_unit(struct mixer_build *state, int unitid, 224062306a36Sopenharmony_ci void *raw_desc) 224162306a36Sopenharmony_ci{ 224262306a36Sopenharmony_ci struct uac_mixer_unit_descriptor *desc = raw_desc; 224362306a36Sopenharmony_ci struct usb_audio_term iterm; 224462306a36Sopenharmony_ci int input_pins, num_ins, num_outs; 224562306a36Sopenharmony_ci int pin, ich, err; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci err = uac_mixer_unit_get_channels(state, desc); 224862306a36Sopenharmony_ci if (err < 0) { 224962306a36Sopenharmony_ci usb_audio_err(state->chip, 225062306a36Sopenharmony_ci "invalid MIXER UNIT descriptor %d\n", 225162306a36Sopenharmony_ci unitid); 225262306a36Sopenharmony_ci return err; 225362306a36Sopenharmony_ci } 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci num_outs = err; 225662306a36Sopenharmony_ci input_pins = desc->bNrInPins; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci num_ins = 0; 225962306a36Sopenharmony_ci ich = 0; 226062306a36Sopenharmony_ci for (pin = 0; pin < input_pins; pin++) { 226162306a36Sopenharmony_ci err = parse_audio_unit(state, desc->baSourceID[pin]); 226262306a36Sopenharmony_ci if (err < 0) 226362306a36Sopenharmony_ci continue; 226462306a36Sopenharmony_ci /* no bmControls field (e.g. Maya44) -> ignore */ 226562306a36Sopenharmony_ci if (!num_outs) 226662306a36Sopenharmony_ci continue; 226762306a36Sopenharmony_ci err = check_input_term(state, desc->baSourceID[pin], &iterm); 226862306a36Sopenharmony_ci if (err < 0) 226962306a36Sopenharmony_ci return err; 227062306a36Sopenharmony_ci num_ins += iterm.channels; 227162306a36Sopenharmony_ci if (mixer_bitmap_overflow(desc, state->mixer->protocol, 227262306a36Sopenharmony_ci num_ins, num_outs)) 227362306a36Sopenharmony_ci break; 227462306a36Sopenharmony_ci for (; ich < num_ins; ich++) { 227562306a36Sopenharmony_ci int och, ich_has_controls = 0; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci for (och = 0; och < num_outs; och++) { 227862306a36Sopenharmony_ci __u8 *c = uac_mixer_unit_bmControls(desc, 227962306a36Sopenharmony_ci state->mixer->protocol); 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci if (check_matrix_bitmap(c, ich, och, num_outs)) { 228262306a36Sopenharmony_ci ich_has_controls = 1; 228362306a36Sopenharmony_ci break; 228462306a36Sopenharmony_ci } 228562306a36Sopenharmony_ci } 228662306a36Sopenharmony_ci if (ich_has_controls) 228762306a36Sopenharmony_ci build_mixer_unit_ctl(state, desc, pin, ich, num_outs, 228862306a36Sopenharmony_ci unitid, &iterm); 228962306a36Sopenharmony_ci } 229062306a36Sopenharmony_ci } 229162306a36Sopenharmony_ci return 0; 229262306a36Sopenharmony_ci} 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci/* 229562306a36Sopenharmony_ci * Processing Unit / Extension Unit 229662306a36Sopenharmony_ci */ 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci/* get callback for processing/extension unit */ 229962306a36Sopenharmony_cistatic int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, 230062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 230162306a36Sopenharmony_ci{ 230262306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 230362306a36Sopenharmony_ci int err, val; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci err = get_cur_ctl_value(cval, cval->control << 8, &val); 230662306a36Sopenharmony_ci if (err < 0) { 230762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = cval->min; 230862306a36Sopenharmony_ci return filter_error(cval, err); 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci val = get_relative_value(cval, val); 231162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = val; 231262306a36Sopenharmony_ci return 0; 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci/* put callback for processing/extension unit */ 231662306a36Sopenharmony_cistatic int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, 231762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 231862306a36Sopenharmony_ci{ 231962306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 232062306a36Sopenharmony_ci int val, oval, err; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci err = get_cur_ctl_value(cval, cval->control << 8, &oval); 232362306a36Sopenharmony_ci if (err < 0) 232462306a36Sopenharmony_ci return filter_error(cval, err); 232562306a36Sopenharmony_ci val = ucontrol->value.integer.value[0]; 232662306a36Sopenharmony_ci val = get_abs_value(cval, val); 232762306a36Sopenharmony_ci if (val != oval) { 232862306a36Sopenharmony_ci set_cur_ctl_value(cval, cval->control << 8, val); 232962306a36Sopenharmony_ci return 1; 233062306a36Sopenharmony_ci } 233162306a36Sopenharmony_ci return 0; 233262306a36Sopenharmony_ci} 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci/* alsa control interface for processing/extension unit */ 233562306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_procunit_ctl = { 233662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 233762306a36Sopenharmony_ci .name = "", /* will be filled later */ 233862306a36Sopenharmony_ci .info = mixer_ctl_feature_info, 233962306a36Sopenharmony_ci .get = mixer_ctl_procunit_get, 234062306a36Sopenharmony_ci .put = mixer_ctl_procunit_put, 234162306a36Sopenharmony_ci}; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci/* 234462306a36Sopenharmony_ci * predefined data for processing units 234562306a36Sopenharmony_ci */ 234662306a36Sopenharmony_cistruct procunit_value_info { 234762306a36Sopenharmony_ci int control; 234862306a36Sopenharmony_ci const char *suffix; 234962306a36Sopenharmony_ci int val_type; 235062306a36Sopenharmony_ci int min_value; 235162306a36Sopenharmony_ci}; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_cistruct procunit_info { 235462306a36Sopenharmony_ci int type; 235562306a36Sopenharmony_ci char *name; 235662306a36Sopenharmony_ci const struct procunit_value_info *values; 235762306a36Sopenharmony_ci}; 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_cistatic const struct procunit_value_info undefined_proc_info[] = { 236062306a36Sopenharmony_ci { 0x00, "Control Undefined", 0 }, 236162306a36Sopenharmony_ci { 0 } 236262306a36Sopenharmony_ci}; 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_cistatic const struct procunit_value_info updown_proc_info[] = { 236562306a36Sopenharmony_ci { UAC_UD_ENABLE, "Switch", USB_MIXER_BOOLEAN }, 236662306a36Sopenharmony_ci { UAC_UD_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 }, 236762306a36Sopenharmony_ci { 0 } 236862306a36Sopenharmony_ci}; 236962306a36Sopenharmony_cistatic const struct procunit_value_info prologic_proc_info[] = { 237062306a36Sopenharmony_ci { UAC_DP_ENABLE, "Switch", USB_MIXER_BOOLEAN }, 237162306a36Sopenharmony_ci { UAC_DP_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 }, 237262306a36Sopenharmony_ci { 0 } 237362306a36Sopenharmony_ci}; 237462306a36Sopenharmony_cistatic const struct procunit_value_info threed_enh_proc_info[] = { 237562306a36Sopenharmony_ci { UAC_3D_ENABLE, "Switch", USB_MIXER_BOOLEAN }, 237662306a36Sopenharmony_ci { UAC_3D_SPACE, "Spaciousness", USB_MIXER_U8 }, 237762306a36Sopenharmony_ci { 0 } 237862306a36Sopenharmony_ci}; 237962306a36Sopenharmony_cistatic const struct procunit_value_info reverb_proc_info[] = { 238062306a36Sopenharmony_ci { UAC_REVERB_ENABLE, "Switch", USB_MIXER_BOOLEAN }, 238162306a36Sopenharmony_ci { UAC_REVERB_LEVEL, "Level", USB_MIXER_U8 }, 238262306a36Sopenharmony_ci { UAC_REVERB_TIME, "Time", USB_MIXER_U16 }, 238362306a36Sopenharmony_ci { UAC_REVERB_FEEDBACK, "Feedback", USB_MIXER_U8 }, 238462306a36Sopenharmony_ci { 0 } 238562306a36Sopenharmony_ci}; 238662306a36Sopenharmony_cistatic const struct procunit_value_info chorus_proc_info[] = { 238762306a36Sopenharmony_ci { UAC_CHORUS_ENABLE, "Switch", USB_MIXER_BOOLEAN }, 238862306a36Sopenharmony_ci { UAC_CHORUS_LEVEL, "Level", USB_MIXER_U8 }, 238962306a36Sopenharmony_ci { UAC_CHORUS_RATE, "Rate", USB_MIXER_U16 }, 239062306a36Sopenharmony_ci { UAC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 }, 239162306a36Sopenharmony_ci { 0 } 239262306a36Sopenharmony_ci}; 239362306a36Sopenharmony_cistatic const struct procunit_value_info dcr_proc_info[] = { 239462306a36Sopenharmony_ci { UAC_DCR_ENABLE, "Switch", USB_MIXER_BOOLEAN }, 239562306a36Sopenharmony_ci { UAC_DCR_RATE, "Ratio", USB_MIXER_U16 }, 239662306a36Sopenharmony_ci { UAC_DCR_MAXAMPL, "Max Amp", USB_MIXER_S16 }, 239762306a36Sopenharmony_ci { UAC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 }, 239862306a36Sopenharmony_ci { UAC_DCR_ATTACK_TIME, "Attack Time", USB_MIXER_U16 }, 239962306a36Sopenharmony_ci { UAC_DCR_RELEASE_TIME, "Release Time", USB_MIXER_U16 }, 240062306a36Sopenharmony_ci { 0 } 240162306a36Sopenharmony_ci}; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_cistatic const struct procunit_info procunits[] = { 240462306a36Sopenharmony_ci { UAC_PROCESS_UP_DOWNMIX, "Up Down", updown_proc_info }, 240562306a36Sopenharmony_ci { UAC_PROCESS_DOLBY_PROLOGIC, "Dolby Prologic", prologic_proc_info }, 240662306a36Sopenharmony_ci { UAC_PROCESS_STEREO_EXTENDER, "3D Stereo Extender", threed_enh_proc_info }, 240762306a36Sopenharmony_ci { UAC_PROCESS_REVERB, "Reverb", reverb_proc_info }, 240862306a36Sopenharmony_ci { UAC_PROCESS_CHORUS, "Chorus", chorus_proc_info }, 240962306a36Sopenharmony_ci { UAC_PROCESS_DYN_RANGE_COMP, "DCR", dcr_proc_info }, 241062306a36Sopenharmony_ci { 0 }, 241162306a36Sopenharmony_ci}; 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_cistatic const struct procunit_value_info uac3_updown_proc_info[] = { 241462306a36Sopenharmony_ci { UAC3_UD_MODE_SELECT, "Mode Select", USB_MIXER_U8, 1 }, 241562306a36Sopenharmony_ci { 0 } 241662306a36Sopenharmony_ci}; 241762306a36Sopenharmony_cistatic const struct procunit_value_info uac3_stereo_ext_proc_info[] = { 241862306a36Sopenharmony_ci { UAC3_EXT_WIDTH_CONTROL, "Width Control", USB_MIXER_U8 }, 241962306a36Sopenharmony_ci { 0 } 242062306a36Sopenharmony_ci}; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_cistatic const struct procunit_info uac3_procunits[] = { 242362306a36Sopenharmony_ci { UAC3_PROCESS_UP_DOWNMIX, "Up Down", uac3_updown_proc_info }, 242462306a36Sopenharmony_ci { UAC3_PROCESS_STEREO_EXTENDER, "3D Stereo Extender", uac3_stereo_ext_proc_info }, 242562306a36Sopenharmony_ci { UAC3_PROCESS_MULTI_FUNCTION, "Multi-Function", undefined_proc_info }, 242662306a36Sopenharmony_ci { 0 }, 242762306a36Sopenharmony_ci}; 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci/* 243062306a36Sopenharmony_ci * predefined data for extension units 243162306a36Sopenharmony_ci */ 243262306a36Sopenharmony_cistatic const struct procunit_value_info clock_rate_xu_info[] = { 243362306a36Sopenharmony_ci { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 }, 243462306a36Sopenharmony_ci { 0 } 243562306a36Sopenharmony_ci}; 243662306a36Sopenharmony_cistatic const struct procunit_value_info clock_source_xu_info[] = { 243762306a36Sopenharmony_ci { USB_XU_CLOCK_SOURCE_SELECTOR, "External", USB_MIXER_BOOLEAN }, 243862306a36Sopenharmony_ci { 0 } 243962306a36Sopenharmony_ci}; 244062306a36Sopenharmony_cistatic const struct procunit_value_info spdif_format_xu_info[] = { 244162306a36Sopenharmony_ci { USB_XU_DIGITAL_FORMAT_SELECTOR, "SPDIF/AC3", USB_MIXER_BOOLEAN }, 244262306a36Sopenharmony_ci { 0 } 244362306a36Sopenharmony_ci}; 244462306a36Sopenharmony_cistatic const struct procunit_value_info soft_limit_xu_info[] = { 244562306a36Sopenharmony_ci { USB_XU_SOFT_LIMIT_SELECTOR, " ", USB_MIXER_BOOLEAN }, 244662306a36Sopenharmony_ci { 0 } 244762306a36Sopenharmony_ci}; 244862306a36Sopenharmony_cistatic const struct procunit_info extunits[] = { 244962306a36Sopenharmony_ci { USB_XU_CLOCK_RATE, "Clock rate", clock_rate_xu_info }, 245062306a36Sopenharmony_ci { USB_XU_CLOCK_SOURCE, "DigitalIn CLK source", clock_source_xu_info }, 245162306a36Sopenharmony_ci { USB_XU_DIGITAL_IO_STATUS, "DigitalOut format:", spdif_format_xu_info }, 245262306a36Sopenharmony_ci { USB_XU_DEVICE_OPTIONS, "AnalogueIn Soft Limit", soft_limit_xu_info }, 245362306a36Sopenharmony_ci { 0 } 245462306a36Sopenharmony_ci}; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci/* 245762306a36Sopenharmony_ci * build a processing/extension unit 245862306a36Sopenharmony_ci */ 245962306a36Sopenharmony_cistatic int build_audio_procunit(struct mixer_build *state, int unitid, 246062306a36Sopenharmony_ci void *raw_desc, const struct procunit_info *list, 246162306a36Sopenharmony_ci bool extension_unit) 246262306a36Sopenharmony_ci{ 246362306a36Sopenharmony_ci struct uac_processing_unit_descriptor *desc = raw_desc; 246462306a36Sopenharmony_ci int num_ins; 246562306a36Sopenharmony_ci struct usb_mixer_elem_info *cval; 246662306a36Sopenharmony_ci struct snd_kcontrol *kctl; 246762306a36Sopenharmony_ci int i, err, nameid, type, len, val; 246862306a36Sopenharmony_ci const struct procunit_info *info; 246962306a36Sopenharmony_ci const struct procunit_value_info *valinfo; 247062306a36Sopenharmony_ci const struct usbmix_name_map *map; 247162306a36Sopenharmony_ci static const struct procunit_value_info default_value_info[] = { 247262306a36Sopenharmony_ci { 0x01, "Switch", USB_MIXER_BOOLEAN }, 247362306a36Sopenharmony_ci { 0 } 247462306a36Sopenharmony_ci }; 247562306a36Sopenharmony_ci static const struct procunit_info default_info = { 247662306a36Sopenharmony_ci 0, NULL, default_value_info 247762306a36Sopenharmony_ci }; 247862306a36Sopenharmony_ci const char *name = extension_unit ? 247962306a36Sopenharmony_ci "Extension Unit" : "Processing Unit"; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci num_ins = desc->bNrInPins; 248262306a36Sopenharmony_ci for (i = 0; i < num_ins; i++) { 248362306a36Sopenharmony_ci err = parse_audio_unit(state, desc->baSourceID[i]); 248462306a36Sopenharmony_ci if (err < 0) 248562306a36Sopenharmony_ci return err; 248662306a36Sopenharmony_ci } 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci type = le16_to_cpu(desc->wProcessType); 248962306a36Sopenharmony_ci for (info = list; info && info->type; info++) 249062306a36Sopenharmony_ci if (info->type == type) 249162306a36Sopenharmony_ci break; 249262306a36Sopenharmony_ci if (!info || !info->type) 249362306a36Sopenharmony_ci info = &default_info; 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci for (valinfo = info->values; valinfo->control; valinfo++) { 249662306a36Sopenharmony_ci __u8 *controls = uac_processing_unit_bmControls(desc, state->mixer->protocol); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci if (state->mixer->protocol == UAC_VERSION_1) { 249962306a36Sopenharmony_ci if (!(controls[valinfo->control / 8] & 250062306a36Sopenharmony_ci (1 << ((valinfo->control % 8) - 1)))) 250162306a36Sopenharmony_ci continue; 250262306a36Sopenharmony_ci } else { /* UAC_VERSION_2/3 */ 250362306a36Sopenharmony_ci if (!uac_v2v3_control_is_readable(controls[valinfo->control / 8], 250462306a36Sopenharmony_ci valinfo->control)) 250562306a36Sopenharmony_ci continue; 250662306a36Sopenharmony_ci } 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci map = find_map(state->map, unitid, valinfo->control); 250962306a36Sopenharmony_ci if (check_ignored_ctl(map)) 251062306a36Sopenharmony_ci continue; 251162306a36Sopenharmony_ci cval = kzalloc(sizeof(*cval), GFP_KERNEL); 251262306a36Sopenharmony_ci if (!cval) 251362306a36Sopenharmony_ci return -ENOMEM; 251462306a36Sopenharmony_ci snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); 251562306a36Sopenharmony_ci cval->control = valinfo->control; 251662306a36Sopenharmony_ci cval->val_type = valinfo->val_type; 251762306a36Sopenharmony_ci cval->channels = 1; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci if (state->mixer->protocol > UAC_VERSION_1 && 252062306a36Sopenharmony_ci !uac_v2v3_control_is_writeable(controls[valinfo->control / 8], 252162306a36Sopenharmony_ci valinfo->control)) 252262306a36Sopenharmony_ci cval->master_readonly = 1; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci /* get min/max values */ 252562306a36Sopenharmony_ci switch (type) { 252662306a36Sopenharmony_ci case UAC_PROCESS_UP_DOWNMIX: { 252762306a36Sopenharmony_ci bool mode_sel = false; 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci switch (state->mixer->protocol) { 253062306a36Sopenharmony_ci case UAC_VERSION_1: 253162306a36Sopenharmony_ci case UAC_VERSION_2: 253262306a36Sopenharmony_ci default: 253362306a36Sopenharmony_ci if (cval->control == UAC_UD_MODE_SELECT) 253462306a36Sopenharmony_ci mode_sel = true; 253562306a36Sopenharmony_ci break; 253662306a36Sopenharmony_ci case UAC_VERSION_3: 253762306a36Sopenharmony_ci if (cval->control == UAC3_UD_MODE_SELECT) 253862306a36Sopenharmony_ci mode_sel = true; 253962306a36Sopenharmony_ci break; 254062306a36Sopenharmony_ci } 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci if (mode_sel) { 254362306a36Sopenharmony_ci __u8 *control_spec = uac_processing_unit_specific(desc, 254462306a36Sopenharmony_ci state->mixer->protocol); 254562306a36Sopenharmony_ci cval->min = 1; 254662306a36Sopenharmony_ci cval->max = control_spec[0]; 254762306a36Sopenharmony_ci cval->res = 1; 254862306a36Sopenharmony_ci cval->initialized = 1; 254962306a36Sopenharmony_ci break; 255062306a36Sopenharmony_ci } 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci get_min_max(cval, valinfo->min_value); 255362306a36Sopenharmony_ci break; 255462306a36Sopenharmony_ci } 255562306a36Sopenharmony_ci case USB_XU_CLOCK_RATE: 255662306a36Sopenharmony_ci /* 255762306a36Sopenharmony_ci * E-Mu USB 0404/0202/TrackerPre/0204 255862306a36Sopenharmony_ci * samplerate control quirk 255962306a36Sopenharmony_ci */ 256062306a36Sopenharmony_ci cval->min = 0; 256162306a36Sopenharmony_ci cval->max = 5; 256262306a36Sopenharmony_ci cval->res = 1; 256362306a36Sopenharmony_ci cval->initialized = 1; 256462306a36Sopenharmony_ci break; 256562306a36Sopenharmony_ci default: 256662306a36Sopenharmony_ci get_min_max(cval, valinfo->min_value); 256762306a36Sopenharmony_ci break; 256862306a36Sopenharmony_ci } 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci err = get_cur_ctl_value(cval, cval->control << 8, &val); 257162306a36Sopenharmony_ci if (err < 0) { 257262306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 257362306a36Sopenharmony_ci return -EINVAL; 257462306a36Sopenharmony_ci } 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); 257762306a36Sopenharmony_ci if (!kctl) { 257862306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 257962306a36Sopenharmony_ci return -ENOMEM; 258062306a36Sopenharmony_ci } 258162306a36Sopenharmony_ci kctl->private_free = snd_usb_mixer_elem_free; 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) { 258462306a36Sopenharmony_ci /* nothing */ ; 258562306a36Sopenharmony_ci } else if (info->name) { 258662306a36Sopenharmony_ci strscpy(kctl->id.name, info->name, sizeof(kctl->id.name)); 258762306a36Sopenharmony_ci } else { 258862306a36Sopenharmony_ci if (extension_unit) 258962306a36Sopenharmony_ci nameid = uac_extension_unit_iExtension(desc, state->mixer->protocol); 259062306a36Sopenharmony_ci else 259162306a36Sopenharmony_ci nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); 259262306a36Sopenharmony_ci len = 0; 259362306a36Sopenharmony_ci if (nameid) 259462306a36Sopenharmony_ci len = snd_usb_copy_string_desc(state->chip, 259562306a36Sopenharmony_ci nameid, 259662306a36Sopenharmony_ci kctl->id.name, 259762306a36Sopenharmony_ci sizeof(kctl->id.name)); 259862306a36Sopenharmony_ci if (!len) 259962306a36Sopenharmony_ci strscpy(kctl->id.name, name, sizeof(kctl->id.name)); 260062306a36Sopenharmony_ci } 260162306a36Sopenharmony_ci append_ctl_name(kctl, " "); 260262306a36Sopenharmony_ci append_ctl_name(kctl, valinfo->suffix); 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci usb_audio_dbg(state->chip, 260562306a36Sopenharmony_ci "[%d] PU [%s] ch = %d, val = %d/%d\n", 260662306a36Sopenharmony_ci cval->head.id, kctl->id.name, cval->channels, 260762306a36Sopenharmony_ci cval->min, cval->max); 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci err = snd_usb_mixer_add_control(&cval->head, kctl); 261062306a36Sopenharmony_ci if (err < 0) 261162306a36Sopenharmony_ci return err; 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci return 0; 261462306a36Sopenharmony_ci} 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_cistatic int parse_audio_processing_unit(struct mixer_build *state, int unitid, 261762306a36Sopenharmony_ci void *raw_desc) 261862306a36Sopenharmony_ci{ 261962306a36Sopenharmony_ci switch (state->mixer->protocol) { 262062306a36Sopenharmony_ci case UAC_VERSION_1: 262162306a36Sopenharmony_ci case UAC_VERSION_2: 262262306a36Sopenharmony_ci default: 262362306a36Sopenharmony_ci return build_audio_procunit(state, unitid, raw_desc, 262462306a36Sopenharmony_ci procunits, false); 262562306a36Sopenharmony_ci case UAC_VERSION_3: 262662306a36Sopenharmony_ci return build_audio_procunit(state, unitid, raw_desc, 262762306a36Sopenharmony_ci uac3_procunits, false); 262862306a36Sopenharmony_ci } 262962306a36Sopenharmony_ci} 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_cistatic int parse_audio_extension_unit(struct mixer_build *state, int unitid, 263262306a36Sopenharmony_ci void *raw_desc) 263362306a36Sopenharmony_ci{ 263462306a36Sopenharmony_ci /* 263562306a36Sopenharmony_ci * Note that we parse extension units with processing unit descriptors. 263662306a36Sopenharmony_ci * That's ok as the layout is the same. 263762306a36Sopenharmony_ci */ 263862306a36Sopenharmony_ci return build_audio_procunit(state, unitid, raw_desc, extunits, true); 263962306a36Sopenharmony_ci} 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci/* 264262306a36Sopenharmony_ci * Selector Unit 264362306a36Sopenharmony_ci */ 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci/* 264662306a36Sopenharmony_ci * info callback for selector unit 264762306a36Sopenharmony_ci * use an enumerator type for routing 264862306a36Sopenharmony_ci */ 264962306a36Sopenharmony_cistatic int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, 265062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 265162306a36Sopenharmony_ci{ 265262306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 265362306a36Sopenharmony_ci const char **itemlist = (const char **)kcontrol->private_value; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci if (snd_BUG_ON(!itemlist)) 265662306a36Sopenharmony_ci return -EINVAL; 265762306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, cval->max, itemlist); 265862306a36Sopenharmony_ci} 265962306a36Sopenharmony_ci 266062306a36Sopenharmony_ci/* get callback for selector unit */ 266162306a36Sopenharmony_cistatic int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, 266262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 266362306a36Sopenharmony_ci{ 266462306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 266562306a36Sopenharmony_ci int val, err; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci err = get_cur_ctl_value(cval, cval->control << 8, &val); 266862306a36Sopenharmony_ci if (err < 0) { 266962306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 267062306a36Sopenharmony_ci return filter_error(cval, err); 267162306a36Sopenharmony_ci } 267262306a36Sopenharmony_ci val = get_relative_value(cval, val); 267362306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = val; 267462306a36Sopenharmony_ci return 0; 267562306a36Sopenharmony_ci} 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci/* put callback for selector unit */ 267862306a36Sopenharmony_cistatic int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, 267962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 268062306a36Sopenharmony_ci{ 268162306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kcontrol->private_data; 268262306a36Sopenharmony_ci int val, oval, err; 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci err = get_cur_ctl_value(cval, cval->control << 8, &oval); 268562306a36Sopenharmony_ci if (err < 0) 268662306a36Sopenharmony_ci return filter_error(cval, err); 268762306a36Sopenharmony_ci val = ucontrol->value.enumerated.item[0]; 268862306a36Sopenharmony_ci val = get_abs_value(cval, val); 268962306a36Sopenharmony_ci if (val != oval) { 269062306a36Sopenharmony_ci set_cur_ctl_value(cval, cval->control << 8, val); 269162306a36Sopenharmony_ci return 1; 269262306a36Sopenharmony_ci } 269362306a36Sopenharmony_ci return 0; 269462306a36Sopenharmony_ci} 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci/* alsa control interface for selector unit */ 269762306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_selectunit_ctl = { 269862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 269962306a36Sopenharmony_ci .name = "", /* will be filled later */ 270062306a36Sopenharmony_ci .info = mixer_ctl_selector_info, 270162306a36Sopenharmony_ci .get = mixer_ctl_selector_get, 270262306a36Sopenharmony_ci .put = mixer_ctl_selector_put, 270362306a36Sopenharmony_ci}; 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci/* 270662306a36Sopenharmony_ci * private free callback. 270762306a36Sopenharmony_ci * free both private_data and private_value 270862306a36Sopenharmony_ci */ 270962306a36Sopenharmony_cistatic void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl) 271062306a36Sopenharmony_ci{ 271162306a36Sopenharmony_ci int i, num_ins = 0; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci if (kctl->private_data) { 271462306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = kctl->private_data; 271562306a36Sopenharmony_ci num_ins = cval->max; 271662306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 271762306a36Sopenharmony_ci kctl->private_data = NULL; 271862306a36Sopenharmony_ci } 271962306a36Sopenharmony_ci if (kctl->private_value) { 272062306a36Sopenharmony_ci char **itemlist = (char **)kctl->private_value; 272162306a36Sopenharmony_ci for (i = 0; i < num_ins; i++) 272262306a36Sopenharmony_ci kfree(itemlist[i]); 272362306a36Sopenharmony_ci kfree(itemlist); 272462306a36Sopenharmony_ci kctl->private_value = 0; 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci} 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci/* 272962306a36Sopenharmony_ci * parse a selector unit 273062306a36Sopenharmony_ci */ 273162306a36Sopenharmony_cistatic int parse_audio_selector_unit(struct mixer_build *state, int unitid, 273262306a36Sopenharmony_ci void *raw_desc) 273362306a36Sopenharmony_ci{ 273462306a36Sopenharmony_ci struct uac_selector_unit_descriptor *desc = raw_desc; 273562306a36Sopenharmony_ci unsigned int i, nameid, len; 273662306a36Sopenharmony_ci int err; 273762306a36Sopenharmony_ci struct usb_mixer_elem_info *cval; 273862306a36Sopenharmony_ci struct snd_kcontrol *kctl; 273962306a36Sopenharmony_ci const struct usbmix_name_map *map; 274062306a36Sopenharmony_ci char **namelist; 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci for (i = 0; i < desc->bNrInPins; i++) { 274362306a36Sopenharmony_ci err = parse_audio_unit(state, desc->baSourceID[i]); 274462306a36Sopenharmony_ci if (err < 0) 274562306a36Sopenharmony_ci return err; 274662306a36Sopenharmony_ci } 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci if (desc->bNrInPins == 1) /* only one ? nonsense! */ 274962306a36Sopenharmony_ci return 0; 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci map = find_map(state->map, unitid, 0); 275262306a36Sopenharmony_ci if (check_ignored_ctl(map)) 275362306a36Sopenharmony_ci return 0; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci cval = kzalloc(sizeof(*cval), GFP_KERNEL); 275662306a36Sopenharmony_ci if (!cval) 275762306a36Sopenharmony_ci return -ENOMEM; 275862306a36Sopenharmony_ci snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); 275962306a36Sopenharmony_ci cval->val_type = USB_MIXER_U8; 276062306a36Sopenharmony_ci cval->channels = 1; 276162306a36Sopenharmony_ci cval->min = 1; 276262306a36Sopenharmony_ci cval->max = desc->bNrInPins; 276362306a36Sopenharmony_ci cval->res = 1; 276462306a36Sopenharmony_ci cval->initialized = 1; 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci switch (state->mixer->protocol) { 276762306a36Sopenharmony_ci case UAC_VERSION_1: 276862306a36Sopenharmony_ci default: 276962306a36Sopenharmony_ci cval->control = 0; 277062306a36Sopenharmony_ci break; 277162306a36Sopenharmony_ci case UAC_VERSION_2: 277262306a36Sopenharmony_ci case UAC_VERSION_3: 277362306a36Sopenharmony_ci if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR || 277462306a36Sopenharmony_ci desc->bDescriptorSubtype == UAC3_CLOCK_SELECTOR) 277562306a36Sopenharmony_ci cval->control = UAC2_CX_CLOCK_SELECTOR; 277662306a36Sopenharmony_ci else /* UAC2/3_SELECTOR_UNIT */ 277762306a36Sopenharmony_ci cval->control = UAC2_SU_SELECTOR; 277862306a36Sopenharmony_ci break; 277962306a36Sopenharmony_ci } 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci namelist = kcalloc(desc->bNrInPins, sizeof(char *), GFP_KERNEL); 278262306a36Sopenharmony_ci if (!namelist) { 278362306a36Sopenharmony_ci err = -ENOMEM; 278462306a36Sopenharmony_ci goto error_cval; 278562306a36Sopenharmony_ci } 278662306a36Sopenharmony_ci#define MAX_ITEM_NAME_LEN 64 278762306a36Sopenharmony_ci for (i = 0; i < desc->bNrInPins; i++) { 278862306a36Sopenharmony_ci struct usb_audio_term iterm; 278962306a36Sopenharmony_ci namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); 279062306a36Sopenharmony_ci if (!namelist[i]) { 279162306a36Sopenharmony_ci err = -ENOMEM; 279262306a36Sopenharmony_ci goto error_name; 279362306a36Sopenharmony_ci } 279462306a36Sopenharmony_ci len = check_mapped_selector_name(state, unitid, i, namelist[i], 279562306a36Sopenharmony_ci MAX_ITEM_NAME_LEN); 279662306a36Sopenharmony_ci if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0) 279762306a36Sopenharmony_ci len = get_term_name(state->chip, &iterm, namelist[i], 279862306a36Sopenharmony_ci MAX_ITEM_NAME_LEN, 0); 279962306a36Sopenharmony_ci if (! len) 280062306a36Sopenharmony_ci sprintf(namelist[i], "Input %u", i); 280162306a36Sopenharmony_ci } 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); 280462306a36Sopenharmony_ci if (! kctl) { 280562306a36Sopenharmony_ci usb_audio_err(state->chip, "cannot malloc kcontrol\n"); 280662306a36Sopenharmony_ci err = -ENOMEM; 280762306a36Sopenharmony_ci goto error_name; 280862306a36Sopenharmony_ci } 280962306a36Sopenharmony_ci kctl->private_value = (unsigned long)namelist; 281062306a36Sopenharmony_ci kctl->private_free = usb_mixer_selector_elem_free; 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci /* check the static mapping table at first */ 281362306a36Sopenharmony_ci len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); 281462306a36Sopenharmony_ci if (!len) { 281562306a36Sopenharmony_ci /* no mapping ? */ 281662306a36Sopenharmony_ci switch (state->mixer->protocol) { 281762306a36Sopenharmony_ci case UAC_VERSION_1: 281862306a36Sopenharmony_ci case UAC_VERSION_2: 281962306a36Sopenharmony_ci default: 282062306a36Sopenharmony_ci /* if iSelector is given, use it */ 282162306a36Sopenharmony_ci nameid = uac_selector_unit_iSelector(desc); 282262306a36Sopenharmony_ci if (nameid) 282362306a36Sopenharmony_ci len = snd_usb_copy_string_desc(state->chip, 282462306a36Sopenharmony_ci nameid, kctl->id.name, 282562306a36Sopenharmony_ci sizeof(kctl->id.name)); 282662306a36Sopenharmony_ci break; 282762306a36Sopenharmony_ci case UAC_VERSION_3: 282862306a36Sopenharmony_ci /* TODO: Class-Specific strings not yet supported */ 282962306a36Sopenharmony_ci break; 283062306a36Sopenharmony_ci } 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci /* ... or pick up the terminal name at next */ 283362306a36Sopenharmony_ci if (!len) 283462306a36Sopenharmony_ci len = get_term_name(state->chip, &state->oterm, 283562306a36Sopenharmony_ci kctl->id.name, sizeof(kctl->id.name), 0); 283662306a36Sopenharmony_ci /* ... or use the fixed string "USB" as the last resort */ 283762306a36Sopenharmony_ci if (!len) 283862306a36Sopenharmony_ci strscpy(kctl->id.name, "USB", sizeof(kctl->id.name)); 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci /* and add the proper suffix */ 284162306a36Sopenharmony_ci if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR || 284262306a36Sopenharmony_ci desc->bDescriptorSubtype == UAC3_CLOCK_SELECTOR) 284362306a36Sopenharmony_ci append_ctl_name(kctl, " Clock Source"); 284462306a36Sopenharmony_ci else if ((state->oterm.type & 0xff00) == 0x0100) 284562306a36Sopenharmony_ci append_ctl_name(kctl, " Capture Source"); 284662306a36Sopenharmony_ci else 284762306a36Sopenharmony_ci append_ctl_name(kctl, " Playback Source"); 284862306a36Sopenharmony_ci } 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n", 285162306a36Sopenharmony_ci cval->head.id, kctl->id.name, desc->bNrInPins); 285262306a36Sopenharmony_ci return snd_usb_mixer_add_control(&cval->head, kctl); 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci error_name: 285562306a36Sopenharmony_ci for (i = 0; i < desc->bNrInPins; i++) 285662306a36Sopenharmony_ci kfree(namelist[i]); 285762306a36Sopenharmony_ci kfree(namelist); 285862306a36Sopenharmony_ci error_cval: 285962306a36Sopenharmony_ci usb_mixer_elem_info_free(cval); 286062306a36Sopenharmony_ci return err; 286162306a36Sopenharmony_ci} 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci/* 286462306a36Sopenharmony_ci * parse an audio unit recursively 286562306a36Sopenharmony_ci */ 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_cistatic int parse_audio_unit(struct mixer_build *state, int unitid) 286862306a36Sopenharmony_ci{ 286962306a36Sopenharmony_ci unsigned char *p1; 287062306a36Sopenharmony_ci int protocol = state->mixer->protocol; 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci if (test_and_set_bit(unitid, state->unitbitmap)) 287362306a36Sopenharmony_ci return 0; /* the unit already visited */ 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci p1 = find_audio_control_unit(state, unitid); 287662306a36Sopenharmony_ci if (!p1) { 287762306a36Sopenharmony_ci usb_audio_err(state->chip, "unit %d not found!\n", unitid); 287862306a36Sopenharmony_ci return -EINVAL; 287962306a36Sopenharmony_ci } 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci if (!snd_usb_validate_audio_desc(p1, protocol)) { 288262306a36Sopenharmony_ci usb_audio_dbg(state->chip, "invalid unit %d\n", unitid); 288362306a36Sopenharmony_ci return 0; /* skip invalid unit */ 288462306a36Sopenharmony_ci } 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ci switch (PTYPE(protocol, p1[2])) { 288762306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL): 288862306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL): 288962306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL): 289062306a36Sopenharmony_ci return parse_audio_input_terminal(state, unitid, p1); 289162306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT): 289262306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT): 289362306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT): 289462306a36Sopenharmony_ci return parse_audio_mixer_unit(state, unitid, p1); 289562306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE): 289662306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE): 289762306a36Sopenharmony_ci return parse_clock_source_unit(state, unitid, p1); 289862306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT): 289962306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT): 290062306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT): 290162306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR): 290262306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR): 290362306a36Sopenharmony_ci return parse_audio_selector_unit(state, unitid, p1); 290462306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT): 290562306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT): 290662306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT): 290762306a36Sopenharmony_ci return parse_audio_feature_unit(state, unitid, p1); 290862306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT): 290962306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2): 291062306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT): 291162306a36Sopenharmony_ci return parse_audio_processing_unit(state, unitid, p1); 291262306a36Sopenharmony_ci case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT): 291362306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2): 291462306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT): 291562306a36Sopenharmony_ci return parse_audio_extension_unit(state, unitid, p1); 291662306a36Sopenharmony_ci case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT): 291762306a36Sopenharmony_ci case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT): 291862306a36Sopenharmony_ci return 0; /* FIXME - effect units not implemented yet */ 291962306a36Sopenharmony_ci default: 292062306a36Sopenharmony_ci usb_audio_err(state->chip, 292162306a36Sopenharmony_ci "unit %u: unexpected type 0x%02x\n", 292262306a36Sopenharmony_ci unitid, p1[2]); 292362306a36Sopenharmony_ci return -EINVAL; 292462306a36Sopenharmony_ci } 292562306a36Sopenharmony_ci} 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_cistatic void snd_usb_mixer_free(struct usb_mixer_interface *mixer) 292862306a36Sopenharmony_ci{ 292962306a36Sopenharmony_ci /* kill pending URBs */ 293062306a36Sopenharmony_ci snd_usb_mixer_disconnect(mixer); 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci kfree(mixer->id_elems); 293362306a36Sopenharmony_ci if (mixer->urb) { 293462306a36Sopenharmony_ci kfree(mixer->urb->transfer_buffer); 293562306a36Sopenharmony_ci usb_free_urb(mixer->urb); 293662306a36Sopenharmony_ci } 293762306a36Sopenharmony_ci usb_free_urb(mixer->rc_urb); 293862306a36Sopenharmony_ci kfree(mixer->rc_setup_packet); 293962306a36Sopenharmony_ci kfree(mixer); 294062306a36Sopenharmony_ci} 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_cistatic int snd_usb_mixer_dev_free(struct snd_device *device) 294362306a36Sopenharmony_ci{ 294462306a36Sopenharmony_ci struct usb_mixer_interface *mixer = device->device_data; 294562306a36Sopenharmony_ci snd_usb_mixer_free(mixer); 294662306a36Sopenharmony_ci return 0; 294762306a36Sopenharmony_ci} 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci/* UAC3 predefined channels configuration */ 295062306a36Sopenharmony_cistruct uac3_badd_profile { 295162306a36Sopenharmony_ci int subclass; 295262306a36Sopenharmony_ci const char *name; 295362306a36Sopenharmony_ci int c_chmask; /* capture channels mask */ 295462306a36Sopenharmony_ci int p_chmask; /* playback channels mask */ 295562306a36Sopenharmony_ci int st_chmask; /* side tone mixing channel mask */ 295662306a36Sopenharmony_ci}; 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_cistatic const struct uac3_badd_profile uac3_badd_profiles[] = { 295962306a36Sopenharmony_ci { 296062306a36Sopenharmony_ci /* 296162306a36Sopenharmony_ci * BAIF, BAOF or combination of both 296262306a36Sopenharmony_ci * IN: Mono or Stereo cfg, Mono alt possible 296362306a36Sopenharmony_ci * OUT: Mono or Stereo cfg, Mono alt possible 296462306a36Sopenharmony_ci */ 296562306a36Sopenharmony_ci .subclass = UAC3_FUNCTION_SUBCLASS_GENERIC_IO, 296662306a36Sopenharmony_ci .name = "GENERIC IO", 296762306a36Sopenharmony_ci .c_chmask = -1, /* dynamic channels */ 296862306a36Sopenharmony_ci .p_chmask = -1, /* dynamic channels */ 296962306a36Sopenharmony_ci }, 297062306a36Sopenharmony_ci { 297162306a36Sopenharmony_ci /* BAOF; Stereo only cfg, Mono alt possible */ 297262306a36Sopenharmony_ci .subclass = UAC3_FUNCTION_SUBCLASS_HEADPHONE, 297362306a36Sopenharmony_ci .name = "HEADPHONE", 297462306a36Sopenharmony_ci .p_chmask = 3, 297562306a36Sopenharmony_ci }, 297662306a36Sopenharmony_ci { 297762306a36Sopenharmony_ci /* BAOF; Mono or Stereo cfg, Mono alt possible */ 297862306a36Sopenharmony_ci .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKER, 297962306a36Sopenharmony_ci .name = "SPEAKER", 298062306a36Sopenharmony_ci .p_chmask = -1, /* dynamic channels */ 298162306a36Sopenharmony_ci }, 298262306a36Sopenharmony_ci { 298362306a36Sopenharmony_ci /* BAIF; Mono or Stereo cfg, Mono alt possible */ 298462306a36Sopenharmony_ci .subclass = UAC3_FUNCTION_SUBCLASS_MICROPHONE, 298562306a36Sopenharmony_ci .name = "MICROPHONE", 298662306a36Sopenharmony_ci .c_chmask = -1, /* dynamic channels */ 298762306a36Sopenharmony_ci }, 298862306a36Sopenharmony_ci { 298962306a36Sopenharmony_ci /* 299062306a36Sopenharmony_ci * BAIOF topology 299162306a36Sopenharmony_ci * IN: Mono only 299262306a36Sopenharmony_ci * OUT: Mono or Stereo cfg, Mono alt possible 299362306a36Sopenharmony_ci */ 299462306a36Sopenharmony_ci .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET, 299562306a36Sopenharmony_ci .name = "HEADSET", 299662306a36Sopenharmony_ci .c_chmask = 1, 299762306a36Sopenharmony_ci .p_chmask = -1, /* dynamic channels */ 299862306a36Sopenharmony_ci .st_chmask = 1, 299962306a36Sopenharmony_ci }, 300062306a36Sopenharmony_ci { 300162306a36Sopenharmony_ci /* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */ 300262306a36Sopenharmony_ci .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER, 300362306a36Sopenharmony_ci .name = "HEADSET ADAPTER", 300462306a36Sopenharmony_ci .c_chmask = 1, 300562306a36Sopenharmony_ci .p_chmask = 3, 300662306a36Sopenharmony_ci .st_chmask = 1, 300762306a36Sopenharmony_ci }, 300862306a36Sopenharmony_ci { 300962306a36Sopenharmony_ci /* BAIF + BAOF; IN: Mono only; OUT: Mono only */ 301062306a36Sopenharmony_ci .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE, 301162306a36Sopenharmony_ci .name = "SPEAKERPHONE", 301262306a36Sopenharmony_ci .c_chmask = 1, 301362306a36Sopenharmony_ci .p_chmask = 1, 301462306a36Sopenharmony_ci }, 301562306a36Sopenharmony_ci { 0 } /* terminator */ 301662306a36Sopenharmony_ci}; 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_cistatic bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer, 301962306a36Sopenharmony_ci const struct uac3_badd_profile *f, 302062306a36Sopenharmony_ci int c_chmask, int p_chmask) 302162306a36Sopenharmony_ci{ 302262306a36Sopenharmony_ci /* 302362306a36Sopenharmony_ci * If both playback/capture channels are dynamic, make sure 302462306a36Sopenharmony_ci * at least one channel is present 302562306a36Sopenharmony_ci */ 302662306a36Sopenharmony_ci if (f->c_chmask < 0 && f->p_chmask < 0) { 302762306a36Sopenharmony_ci if (!c_chmask && !p_chmask) { 302862306a36Sopenharmony_ci usb_audio_warn(mixer->chip, "BAAD %s: no channels?", 302962306a36Sopenharmony_ci f->name); 303062306a36Sopenharmony_ci return false; 303162306a36Sopenharmony_ci } 303262306a36Sopenharmony_ci return true; 303362306a36Sopenharmony_ci } 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci if ((f->c_chmask < 0 && !c_chmask) || 303662306a36Sopenharmony_ci (f->c_chmask >= 0 && f->c_chmask != c_chmask)) { 303762306a36Sopenharmony_ci usb_audio_warn(mixer->chip, "BAAD %s c_chmask mismatch", 303862306a36Sopenharmony_ci f->name); 303962306a36Sopenharmony_ci return false; 304062306a36Sopenharmony_ci } 304162306a36Sopenharmony_ci if ((f->p_chmask < 0 && !p_chmask) || 304262306a36Sopenharmony_ci (f->p_chmask >= 0 && f->p_chmask != p_chmask)) { 304362306a36Sopenharmony_ci usb_audio_warn(mixer->chip, "BAAD %s p_chmask mismatch", 304462306a36Sopenharmony_ci f->name); 304562306a36Sopenharmony_ci return false; 304662306a36Sopenharmony_ci } 304762306a36Sopenharmony_ci return true; 304862306a36Sopenharmony_ci} 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci/* 305162306a36Sopenharmony_ci * create mixer controls for UAC3 BADD profiles 305262306a36Sopenharmony_ci * 305362306a36Sopenharmony_ci * UAC3 BADD device doesn't contain CS descriptors thus we will guess everything 305462306a36Sopenharmony_ci * 305562306a36Sopenharmony_ci * BADD device may contain Mixer Unit, which doesn't have any controls, skip it 305662306a36Sopenharmony_ci */ 305762306a36Sopenharmony_cistatic int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer, 305862306a36Sopenharmony_ci int ctrlif) 305962306a36Sopenharmony_ci{ 306062306a36Sopenharmony_ci struct usb_device *dev = mixer->chip->dev; 306162306a36Sopenharmony_ci struct usb_interface_assoc_descriptor *assoc; 306262306a36Sopenharmony_ci int badd_profile = mixer->chip->badd_profile; 306362306a36Sopenharmony_ci const struct uac3_badd_profile *f; 306462306a36Sopenharmony_ci const struct usbmix_ctl_map *map; 306562306a36Sopenharmony_ci int p_chmask = 0, c_chmask = 0, st_chmask = 0; 306662306a36Sopenharmony_ci int i; 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc; 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ci /* Detect BADD capture/playback channels from AS EP descriptors */ 307162306a36Sopenharmony_ci for (i = 0; i < assoc->bInterfaceCount; i++) { 307262306a36Sopenharmony_ci int intf = assoc->bFirstInterface + i; 307362306a36Sopenharmony_ci 307462306a36Sopenharmony_ci struct usb_interface *iface; 307562306a36Sopenharmony_ci struct usb_host_interface *alts; 307662306a36Sopenharmony_ci struct usb_interface_descriptor *altsd; 307762306a36Sopenharmony_ci unsigned int maxpacksize; 307862306a36Sopenharmony_ci char dir_in; 307962306a36Sopenharmony_ci int chmask, num; 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci if (intf == ctrlif) 308262306a36Sopenharmony_ci continue; 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ci iface = usb_ifnum_to_if(dev, intf); 308562306a36Sopenharmony_ci if (!iface) 308662306a36Sopenharmony_ci continue; 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci num = iface->num_altsetting; 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci if (num < 2) 309162306a36Sopenharmony_ci return -EINVAL; 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci /* 309462306a36Sopenharmony_ci * The number of Channels in an AudioStreaming interface 309562306a36Sopenharmony_ci * and the audio sample bit resolution (16 bits or 24 309662306a36Sopenharmony_ci * bits) can be derived from the wMaxPacketSize field in 309762306a36Sopenharmony_ci * the Standard AS Audio Data Endpoint descriptor in 309862306a36Sopenharmony_ci * Alternate Setting 1 309962306a36Sopenharmony_ci */ 310062306a36Sopenharmony_ci alts = &iface->altsetting[1]; 310162306a36Sopenharmony_ci altsd = get_iface_desc(alts); 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci if (altsd->bNumEndpoints < 1) 310462306a36Sopenharmony_ci return -EINVAL; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci /* check direction */ 310762306a36Sopenharmony_ci dir_in = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN); 310862306a36Sopenharmony_ci maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci switch (maxpacksize) { 311162306a36Sopenharmony_ci default: 311262306a36Sopenharmony_ci usb_audio_err(mixer->chip, 311362306a36Sopenharmony_ci "incorrect wMaxPacketSize 0x%x for BADD profile\n", 311462306a36Sopenharmony_ci maxpacksize); 311562306a36Sopenharmony_ci return -EINVAL; 311662306a36Sopenharmony_ci case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: 311762306a36Sopenharmony_ci case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: 311862306a36Sopenharmony_ci case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: 311962306a36Sopenharmony_ci case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24: 312062306a36Sopenharmony_ci chmask = 1; 312162306a36Sopenharmony_ci break; 312262306a36Sopenharmony_ci case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: 312362306a36Sopenharmony_ci case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: 312462306a36Sopenharmony_ci case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: 312562306a36Sopenharmony_ci case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: 312662306a36Sopenharmony_ci chmask = 3; 312762306a36Sopenharmony_ci break; 312862306a36Sopenharmony_ci } 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci if (dir_in) 313162306a36Sopenharmony_ci c_chmask = chmask; 313262306a36Sopenharmony_ci else 313362306a36Sopenharmony_ci p_chmask = chmask; 313462306a36Sopenharmony_ci } 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci usb_audio_dbg(mixer->chip, 313762306a36Sopenharmony_ci "UAC3 BADD profile 0x%x: detected c_chmask=%d p_chmask=%d\n", 313862306a36Sopenharmony_ci badd_profile, c_chmask, p_chmask); 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci /* check the mapping table */ 314162306a36Sopenharmony_ci for (map = uac3_badd_usbmix_ctl_maps; map->id; map++) { 314262306a36Sopenharmony_ci if (map->id == badd_profile) 314362306a36Sopenharmony_ci break; 314462306a36Sopenharmony_ci } 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci if (!map->id) 314762306a36Sopenharmony_ci return -EINVAL; 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci for (f = uac3_badd_profiles; f->name; f++) { 315062306a36Sopenharmony_ci if (badd_profile == f->subclass) 315162306a36Sopenharmony_ci break; 315262306a36Sopenharmony_ci } 315362306a36Sopenharmony_ci if (!f->name) 315462306a36Sopenharmony_ci return -EINVAL; 315562306a36Sopenharmony_ci if (!uac3_badd_func_has_valid_channels(mixer, f, c_chmask, p_chmask)) 315662306a36Sopenharmony_ci return -EINVAL; 315762306a36Sopenharmony_ci st_chmask = f->st_chmask; 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_ci /* Playback */ 316062306a36Sopenharmony_ci if (p_chmask) { 316162306a36Sopenharmony_ci /* Master channel, always writable */ 316262306a36Sopenharmony_ci build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, 316362306a36Sopenharmony_ci UAC3_BADD_FU_ID2, map->map); 316462306a36Sopenharmony_ci /* Mono/Stereo volume channels, always writable */ 316562306a36Sopenharmony_ci build_feature_ctl_badd(mixer, p_chmask, UAC_FU_VOLUME, 316662306a36Sopenharmony_ci UAC3_BADD_FU_ID2, map->map); 316762306a36Sopenharmony_ci } 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci /* Capture */ 317062306a36Sopenharmony_ci if (c_chmask) { 317162306a36Sopenharmony_ci /* Master channel, always writable */ 317262306a36Sopenharmony_ci build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, 317362306a36Sopenharmony_ci UAC3_BADD_FU_ID5, map->map); 317462306a36Sopenharmony_ci /* Mono/Stereo volume channels, always writable */ 317562306a36Sopenharmony_ci build_feature_ctl_badd(mixer, c_chmask, UAC_FU_VOLUME, 317662306a36Sopenharmony_ci UAC3_BADD_FU_ID5, map->map); 317762306a36Sopenharmony_ci } 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci /* Side tone-mixing */ 318062306a36Sopenharmony_ci if (st_chmask) { 318162306a36Sopenharmony_ci /* Master channel, always writable */ 318262306a36Sopenharmony_ci build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, 318362306a36Sopenharmony_ci UAC3_BADD_FU_ID7, map->map); 318462306a36Sopenharmony_ci /* Mono volume channel, always writable */ 318562306a36Sopenharmony_ci build_feature_ctl_badd(mixer, 1, UAC_FU_VOLUME, 318662306a36Sopenharmony_ci UAC3_BADD_FU_ID7, map->map); 318762306a36Sopenharmony_ci } 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci /* Insertion Control */ 319062306a36Sopenharmony_ci if (f->subclass == UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER) { 319162306a36Sopenharmony_ci struct usb_audio_term iterm, oterm; 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci /* Input Term - Insertion control */ 319462306a36Sopenharmony_ci memset(&iterm, 0, sizeof(iterm)); 319562306a36Sopenharmony_ci iterm.id = UAC3_BADD_IT_ID4; 319662306a36Sopenharmony_ci iterm.type = UAC_BIDIR_TERMINAL_HEADSET; 319762306a36Sopenharmony_ci build_connector_control(mixer, map->map, &iterm, true); 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci /* Output Term - Insertion control */ 320062306a36Sopenharmony_ci memset(&oterm, 0, sizeof(oterm)); 320162306a36Sopenharmony_ci oterm.id = UAC3_BADD_OT_ID3; 320262306a36Sopenharmony_ci oterm.type = UAC_BIDIR_TERMINAL_HEADSET; 320362306a36Sopenharmony_ci build_connector_control(mixer, map->map, &oterm, false); 320462306a36Sopenharmony_ci } 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_ci return 0; 320762306a36Sopenharmony_ci} 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_ci/* 321062306a36Sopenharmony_ci * create mixer controls 321162306a36Sopenharmony_ci * 321262306a36Sopenharmony_ci * walk through all UAC_OUTPUT_TERMINAL descriptors to search for mixers 321362306a36Sopenharmony_ci */ 321462306a36Sopenharmony_cistatic int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) 321562306a36Sopenharmony_ci{ 321662306a36Sopenharmony_ci struct mixer_build state; 321762306a36Sopenharmony_ci int err; 321862306a36Sopenharmony_ci const struct usbmix_ctl_map *map; 321962306a36Sopenharmony_ci void *p; 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci memset(&state, 0, sizeof(state)); 322262306a36Sopenharmony_ci state.chip = mixer->chip; 322362306a36Sopenharmony_ci state.mixer = mixer; 322462306a36Sopenharmony_ci state.buffer = mixer->hostif->extra; 322562306a36Sopenharmony_ci state.buflen = mixer->hostif->extralen; 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci /* check the mapping table */ 322862306a36Sopenharmony_ci for (map = usbmix_ctl_maps; map->id; map++) { 322962306a36Sopenharmony_ci if (map->id == state.chip->usb_id) { 323062306a36Sopenharmony_ci state.map = map->map; 323162306a36Sopenharmony_ci state.selector_map = map->selector_map; 323262306a36Sopenharmony_ci mixer->connector_map = map->connector_map; 323362306a36Sopenharmony_ci break; 323462306a36Sopenharmony_ci } 323562306a36Sopenharmony_ci } 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci p = NULL; 323862306a36Sopenharmony_ci while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, 323962306a36Sopenharmony_ci mixer->hostif->extralen, 324062306a36Sopenharmony_ci p, UAC_OUTPUT_TERMINAL)) != NULL) { 324162306a36Sopenharmony_ci if (!snd_usb_validate_audio_desc(p, mixer->protocol)) 324262306a36Sopenharmony_ci continue; /* skip invalid descriptor */ 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci if (mixer->protocol == UAC_VERSION_1) { 324562306a36Sopenharmony_ci struct uac1_output_terminal_descriptor *desc = p; 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci /* mark terminal ID as visited */ 324862306a36Sopenharmony_ci set_bit(desc->bTerminalID, state.unitbitmap); 324962306a36Sopenharmony_ci state.oterm.id = desc->bTerminalID; 325062306a36Sopenharmony_ci state.oterm.type = le16_to_cpu(desc->wTerminalType); 325162306a36Sopenharmony_ci state.oterm.name = desc->iTerminal; 325262306a36Sopenharmony_ci err = parse_audio_unit(&state, desc->bSourceID); 325362306a36Sopenharmony_ci if (err < 0 && err != -EINVAL) 325462306a36Sopenharmony_ci return err; 325562306a36Sopenharmony_ci } else if (mixer->protocol == UAC_VERSION_2) { 325662306a36Sopenharmony_ci struct uac2_output_terminal_descriptor *desc = p; 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci /* mark terminal ID as visited */ 325962306a36Sopenharmony_ci set_bit(desc->bTerminalID, state.unitbitmap); 326062306a36Sopenharmony_ci state.oterm.id = desc->bTerminalID; 326162306a36Sopenharmony_ci state.oterm.type = le16_to_cpu(desc->wTerminalType); 326262306a36Sopenharmony_ci state.oterm.name = desc->iTerminal; 326362306a36Sopenharmony_ci err = parse_audio_unit(&state, desc->bSourceID); 326462306a36Sopenharmony_ci if (err < 0 && err != -EINVAL) 326562306a36Sopenharmony_ci return err; 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci /* 326862306a36Sopenharmony_ci * For UAC2, use the same approach to also add the 326962306a36Sopenharmony_ci * clock selectors 327062306a36Sopenharmony_ci */ 327162306a36Sopenharmony_ci err = parse_audio_unit(&state, desc->bCSourceID); 327262306a36Sopenharmony_ci if (err < 0 && err != -EINVAL) 327362306a36Sopenharmony_ci return err; 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci if ((state.oterm.type & 0xff00) != 0x0100 && 327662306a36Sopenharmony_ci uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls), 327762306a36Sopenharmony_ci UAC2_TE_CONNECTOR)) { 327862306a36Sopenharmony_ci build_connector_control(state.mixer, state.map, 327962306a36Sopenharmony_ci &state.oterm, false); 328062306a36Sopenharmony_ci } 328162306a36Sopenharmony_ci } else { /* UAC_VERSION_3 */ 328262306a36Sopenharmony_ci struct uac3_output_terminal_descriptor *desc = p; 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci /* mark terminal ID as visited */ 328562306a36Sopenharmony_ci set_bit(desc->bTerminalID, state.unitbitmap); 328662306a36Sopenharmony_ci state.oterm.id = desc->bTerminalID; 328762306a36Sopenharmony_ci state.oterm.type = le16_to_cpu(desc->wTerminalType); 328862306a36Sopenharmony_ci state.oterm.name = le16_to_cpu(desc->wTerminalDescrStr); 328962306a36Sopenharmony_ci err = parse_audio_unit(&state, desc->bSourceID); 329062306a36Sopenharmony_ci if (err < 0 && err != -EINVAL) 329162306a36Sopenharmony_ci return err; 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci /* 329462306a36Sopenharmony_ci * For UAC3, use the same approach to also add the 329562306a36Sopenharmony_ci * clock selectors 329662306a36Sopenharmony_ci */ 329762306a36Sopenharmony_ci err = parse_audio_unit(&state, desc->bCSourceID); 329862306a36Sopenharmony_ci if (err < 0 && err != -EINVAL) 329962306a36Sopenharmony_ci return err; 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci if ((state.oterm.type & 0xff00) != 0x0100 && 330262306a36Sopenharmony_ci uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls), 330362306a36Sopenharmony_ci UAC3_TE_INSERTION)) { 330462306a36Sopenharmony_ci build_connector_control(state.mixer, state.map, 330562306a36Sopenharmony_ci &state.oterm, false); 330662306a36Sopenharmony_ci } 330762306a36Sopenharmony_ci } 330862306a36Sopenharmony_ci } 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci return 0; 331162306a36Sopenharmony_ci} 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_cistatic int delegate_notify(struct usb_mixer_interface *mixer, int unitid, 331462306a36Sopenharmony_ci u8 *control, u8 *channel) 331562306a36Sopenharmony_ci{ 331662306a36Sopenharmony_ci const struct usbmix_connector_map *map = mixer->connector_map; 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci if (!map) 331962306a36Sopenharmony_ci return unitid; 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_ci for (; map->id; map++) { 332262306a36Sopenharmony_ci if (map->id == unitid) { 332362306a36Sopenharmony_ci if (control && map->control) 332462306a36Sopenharmony_ci *control = map->control; 332562306a36Sopenharmony_ci if (channel && map->channel) 332662306a36Sopenharmony_ci *channel = map->channel; 332762306a36Sopenharmony_ci return map->delegated_id; 332862306a36Sopenharmony_ci } 332962306a36Sopenharmony_ci } 333062306a36Sopenharmony_ci return unitid; 333162306a36Sopenharmony_ci} 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_civoid snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) 333462306a36Sopenharmony_ci{ 333562306a36Sopenharmony_ci struct usb_mixer_elem_list *list; 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci unitid = delegate_notify(mixer, unitid, NULL, NULL); 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci for_each_mixer_elem(list, mixer, unitid) { 334062306a36Sopenharmony_ci struct usb_mixer_elem_info *info; 334162306a36Sopenharmony_ci 334262306a36Sopenharmony_ci if (!list->is_std_info) 334362306a36Sopenharmony_ci continue; 334462306a36Sopenharmony_ci info = mixer_elem_list_to_info(list); 334562306a36Sopenharmony_ci /* invalidate cache, so the value is read from the device */ 334662306a36Sopenharmony_ci info->cached = 0; 334762306a36Sopenharmony_ci snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, 334862306a36Sopenharmony_ci &list->kctl->id); 334962306a36Sopenharmony_ci } 335062306a36Sopenharmony_ci} 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_cistatic void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, 335362306a36Sopenharmony_ci struct usb_mixer_elem_list *list) 335462306a36Sopenharmony_ci{ 335562306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); 335662306a36Sopenharmony_ci static const char * const val_types[] = { 335762306a36Sopenharmony_ci [USB_MIXER_BOOLEAN] = "BOOLEAN", 335862306a36Sopenharmony_ci [USB_MIXER_INV_BOOLEAN] = "INV_BOOLEAN", 335962306a36Sopenharmony_ci [USB_MIXER_S8] = "S8", 336062306a36Sopenharmony_ci [USB_MIXER_U8] = "U8", 336162306a36Sopenharmony_ci [USB_MIXER_S16] = "S16", 336262306a36Sopenharmony_ci [USB_MIXER_U16] = "U16", 336362306a36Sopenharmony_ci [USB_MIXER_S32] = "S32", 336462306a36Sopenharmony_ci [USB_MIXER_U32] = "U32", 336562306a36Sopenharmony_ci [USB_MIXER_BESPOKEN] = "BESPOKEN", 336662306a36Sopenharmony_ci }; 336762306a36Sopenharmony_ci snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " 336862306a36Sopenharmony_ci "channels=%i, type=\"%s\"\n", cval->head.id, 336962306a36Sopenharmony_ci cval->control, cval->cmask, cval->channels, 337062306a36Sopenharmony_ci val_types[cval->val_type]); 337162306a36Sopenharmony_ci snd_iprintf(buffer, " Volume: min=%i, max=%i, dBmin=%i, dBmax=%i\n", 337262306a36Sopenharmony_ci cval->min, cval->max, cval->dBmin, cval->dBmax); 337362306a36Sopenharmony_ci} 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_cistatic void snd_usb_mixer_proc_read(struct snd_info_entry *entry, 337662306a36Sopenharmony_ci struct snd_info_buffer *buffer) 337762306a36Sopenharmony_ci{ 337862306a36Sopenharmony_ci struct snd_usb_audio *chip = entry->private_data; 337962306a36Sopenharmony_ci struct usb_mixer_interface *mixer; 338062306a36Sopenharmony_ci struct usb_mixer_elem_list *list; 338162306a36Sopenharmony_ci int unitid; 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_ci list_for_each_entry(mixer, &chip->mixer_list, list) { 338462306a36Sopenharmony_ci snd_iprintf(buffer, 338562306a36Sopenharmony_ci "USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n", 338662306a36Sopenharmony_ci chip->usb_id, mixer_ctrl_intf(mixer), 338762306a36Sopenharmony_ci mixer->ignore_ctl_error); 338862306a36Sopenharmony_ci snd_iprintf(buffer, "Card: %s\n", chip->card->longname); 338962306a36Sopenharmony_ci for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { 339062306a36Sopenharmony_ci for_each_mixer_elem(list, mixer, unitid) { 339162306a36Sopenharmony_ci snd_iprintf(buffer, " Unit: %i\n", list->id); 339262306a36Sopenharmony_ci if (list->kctl) 339362306a36Sopenharmony_ci snd_iprintf(buffer, 339462306a36Sopenharmony_ci " Control: name=\"%s\", index=%i\n", 339562306a36Sopenharmony_ci list->kctl->id.name, 339662306a36Sopenharmony_ci list->kctl->id.index); 339762306a36Sopenharmony_ci if (list->dump) 339862306a36Sopenharmony_ci list->dump(buffer, list); 339962306a36Sopenharmony_ci } 340062306a36Sopenharmony_ci } 340162306a36Sopenharmony_ci } 340262306a36Sopenharmony_ci} 340362306a36Sopenharmony_ci 340462306a36Sopenharmony_cistatic void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, 340562306a36Sopenharmony_ci int attribute, int value, int index) 340662306a36Sopenharmony_ci{ 340762306a36Sopenharmony_ci struct usb_mixer_elem_list *list; 340862306a36Sopenharmony_ci __u8 unitid = (index >> 8) & 0xff; 340962306a36Sopenharmony_ci __u8 control = (value >> 8) & 0xff; 341062306a36Sopenharmony_ci __u8 channel = value & 0xff; 341162306a36Sopenharmony_ci unsigned int count = 0; 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_ci if (channel >= MAX_CHANNELS) { 341462306a36Sopenharmony_ci usb_audio_dbg(mixer->chip, 341562306a36Sopenharmony_ci "%s(): bogus channel number %d\n", 341662306a36Sopenharmony_ci __func__, channel); 341762306a36Sopenharmony_ci return; 341862306a36Sopenharmony_ci } 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci unitid = delegate_notify(mixer, unitid, &control, &channel); 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci for_each_mixer_elem(list, mixer, unitid) 342362306a36Sopenharmony_ci count++; 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci if (count == 0) 342662306a36Sopenharmony_ci return; 342762306a36Sopenharmony_ci 342862306a36Sopenharmony_ci for_each_mixer_elem(list, mixer, unitid) { 342962306a36Sopenharmony_ci struct usb_mixer_elem_info *info; 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci if (!list->kctl) 343262306a36Sopenharmony_ci continue; 343362306a36Sopenharmony_ci if (!list->is_std_info) 343462306a36Sopenharmony_ci continue; 343562306a36Sopenharmony_ci 343662306a36Sopenharmony_ci info = mixer_elem_list_to_info(list); 343762306a36Sopenharmony_ci if (count > 1 && info->control != control) 343862306a36Sopenharmony_ci continue; 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci switch (attribute) { 344162306a36Sopenharmony_ci case UAC2_CS_CUR: 344262306a36Sopenharmony_ci /* invalidate cache, so the value is read from the device */ 344362306a36Sopenharmony_ci if (channel) 344462306a36Sopenharmony_ci info->cached &= ~(1 << channel); 344562306a36Sopenharmony_ci else /* master channel */ 344662306a36Sopenharmony_ci info->cached = 0; 344762306a36Sopenharmony_ci 344862306a36Sopenharmony_ci snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, 344962306a36Sopenharmony_ci &info->head.kctl->id); 345062306a36Sopenharmony_ci break; 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci case UAC2_CS_RANGE: 345362306a36Sopenharmony_ci /* TODO */ 345462306a36Sopenharmony_ci break; 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci case UAC2_CS_MEM: 345762306a36Sopenharmony_ci /* TODO */ 345862306a36Sopenharmony_ci break; 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci default: 346162306a36Sopenharmony_ci usb_audio_dbg(mixer->chip, 346262306a36Sopenharmony_ci "unknown attribute %d in interrupt\n", 346362306a36Sopenharmony_ci attribute); 346462306a36Sopenharmony_ci break; 346562306a36Sopenharmony_ci } /* switch */ 346662306a36Sopenharmony_ci } 346762306a36Sopenharmony_ci} 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_cistatic void snd_usb_mixer_interrupt(struct urb *urb) 347062306a36Sopenharmony_ci{ 347162306a36Sopenharmony_ci struct usb_mixer_interface *mixer = urb->context; 347262306a36Sopenharmony_ci int len = urb->actual_length; 347362306a36Sopenharmony_ci int ustatus = urb->status; 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci if (ustatus != 0) 347662306a36Sopenharmony_ci goto requeue; 347762306a36Sopenharmony_ci 347862306a36Sopenharmony_ci if (mixer->protocol == UAC_VERSION_1) { 347962306a36Sopenharmony_ci struct uac1_status_word *status; 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ci for (status = urb->transfer_buffer; 348262306a36Sopenharmony_ci len >= sizeof(*status); 348362306a36Sopenharmony_ci len -= sizeof(*status), status++) { 348462306a36Sopenharmony_ci dev_dbg(&urb->dev->dev, "status interrupt: %02x %02x\n", 348562306a36Sopenharmony_ci status->bStatusType, 348662306a36Sopenharmony_ci status->bOriginator); 348762306a36Sopenharmony_ci 348862306a36Sopenharmony_ci /* ignore any notifications not from the control interface */ 348962306a36Sopenharmony_ci if ((status->bStatusType & UAC1_STATUS_TYPE_ORIG_MASK) != 349062306a36Sopenharmony_ci UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF) 349162306a36Sopenharmony_ci continue; 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci if (status->bStatusType & UAC1_STATUS_TYPE_MEM_CHANGED) 349462306a36Sopenharmony_ci snd_usb_mixer_rc_memory_change(mixer, status->bOriginator); 349562306a36Sopenharmony_ci else 349662306a36Sopenharmony_ci snd_usb_mixer_notify_id(mixer, status->bOriginator); 349762306a36Sopenharmony_ci } 349862306a36Sopenharmony_ci } else { /* UAC_VERSION_2 */ 349962306a36Sopenharmony_ci struct uac2_interrupt_data_msg *msg; 350062306a36Sopenharmony_ci 350162306a36Sopenharmony_ci for (msg = urb->transfer_buffer; 350262306a36Sopenharmony_ci len >= sizeof(*msg); 350362306a36Sopenharmony_ci len -= sizeof(*msg), msg++) { 350462306a36Sopenharmony_ci /* drop vendor specific and endpoint requests */ 350562306a36Sopenharmony_ci if ((msg->bInfo & UAC2_INTERRUPT_DATA_MSG_VENDOR) || 350662306a36Sopenharmony_ci (msg->bInfo & UAC2_INTERRUPT_DATA_MSG_EP)) 350762306a36Sopenharmony_ci continue; 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci snd_usb_mixer_interrupt_v2(mixer, msg->bAttribute, 351062306a36Sopenharmony_ci le16_to_cpu(msg->wValue), 351162306a36Sopenharmony_ci le16_to_cpu(msg->wIndex)); 351262306a36Sopenharmony_ci } 351362306a36Sopenharmony_ci } 351462306a36Sopenharmony_ci 351562306a36Sopenharmony_cirequeue: 351662306a36Sopenharmony_ci if (ustatus != -ENOENT && 351762306a36Sopenharmony_ci ustatus != -ECONNRESET && 351862306a36Sopenharmony_ci ustatus != -ESHUTDOWN) { 351962306a36Sopenharmony_ci urb->dev = mixer->chip->dev; 352062306a36Sopenharmony_ci usb_submit_urb(urb, GFP_ATOMIC); 352162306a36Sopenharmony_ci } 352262306a36Sopenharmony_ci} 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci/* create the handler for the optional status interrupt endpoint */ 352562306a36Sopenharmony_cistatic int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) 352662306a36Sopenharmony_ci{ 352762306a36Sopenharmony_ci struct usb_endpoint_descriptor *ep; 352862306a36Sopenharmony_ci void *transfer_buffer; 352962306a36Sopenharmony_ci int buffer_length; 353062306a36Sopenharmony_ci unsigned int epnum; 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci /* we need one interrupt input endpoint */ 353362306a36Sopenharmony_ci if (get_iface_desc(mixer->hostif)->bNumEndpoints < 1) 353462306a36Sopenharmony_ci return 0; 353562306a36Sopenharmony_ci ep = get_endpoint(mixer->hostif, 0); 353662306a36Sopenharmony_ci if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_int(ep)) 353762306a36Sopenharmony_ci return 0; 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_ci epnum = usb_endpoint_num(ep); 354062306a36Sopenharmony_ci buffer_length = le16_to_cpu(ep->wMaxPacketSize); 354162306a36Sopenharmony_ci transfer_buffer = kmalloc(buffer_length, GFP_KERNEL); 354262306a36Sopenharmony_ci if (!transfer_buffer) 354362306a36Sopenharmony_ci return -ENOMEM; 354462306a36Sopenharmony_ci mixer->urb = usb_alloc_urb(0, GFP_KERNEL); 354562306a36Sopenharmony_ci if (!mixer->urb) { 354662306a36Sopenharmony_ci kfree(transfer_buffer); 354762306a36Sopenharmony_ci return -ENOMEM; 354862306a36Sopenharmony_ci } 354962306a36Sopenharmony_ci usb_fill_int_urb(mixer->urb, mixer->chip->dev, 355062306a36Sopenharmony_ci usb_rcvintpipe(mixer->chip->dev, epnum), 355162306a36Sopenharmony_ci transfer_buffer, buffer_length, 355262306a36Sopenharmony_ci snd_usb_mixer_interrupt, mixer, ep->bInterval); 355362306a36Sopenharmony_ci usb_submit_urb(mixer->urb, GFP_KERNEL); 355462306a36Sopenharmony_ci return 0; 355562306a36Sopenharmony_ci} 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_ciint snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif) 355862306a36Sopenharmony_ci{ 355962306a36Sopenharmony_ci static const struct snd_device_ops dev_ops = { 356062306a36Sopenharmony_ci .dev_free = snd_usb_mixer_dev_free 356162306a36Sopenharmony_ci }; 356262306a36Sopenharmony_ci struct usb_mixer_interface *mixer; 356362306a36Sopenharmony_ci int err; 356462306a36Sopenharmony_ci 356562306a36Sopenharmony_ci strcpy(chip->card->mixername, "USB Mixer"); 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); 356862306a36Sopenharmony_ci if (!mixer) 356962306a36Sopenharmony_ci return -ENOMEM; 357062306a36Sopenharmony_ci mixer->chip = chip; 357162306a36Sopenharmony_ci mixer->ignore_ctl_error = !!(chip->quirk_flags & QUIRK_FLAG_IGNORE_CTL_ERROR); 357262306a36Sopenharmony_ci mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems), 357362306a36Sopenharmony_ci GFP_KERNEL); 357462306a36Sopenharmony_ci if (!mixer->id_elems) { 357562306a36Sopenharmony_ci kfree(mixer); 357662306a36Sopenharmony_ci return -ENOMEM; 357762306a36Sopenharmony_ci } 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_ci mixer->hostif = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0]; 358062306a36Sopenharmony_ci switch (get_iface_desc(mixer->hostif)->bInterfaceProtocol) { 358162306a36Sopenharmony_ci case UAC_VERSION_1: 358262306a36Sopenharmony_ci default: 358362306a36Sopenharmony_ci mixer->protocol = UAC_VERSION_1; 358462306a36Sopenharmony_ci break; 358562306a36Sopenharmony_ci case UAC_VERSION_2: 358662306a36Sopenharmony_ci mixer->protocol = UAC_VERSION_2; 358762306a36Sopenharmony_ci break; 358862306a36Sopenharmony_ci case UAC_VERSION_3: 358962306a36Sopenharmony_ci mixer->protocol = UAC_VERSION_3; 359062306a36Sopenharmony_ci break; 359162306a36Sopenharmony_ci } 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_ci if (mixer->protocol == UAC_VERSION_3 && 359462306a36Sopenharmony_ci chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { 359562306a36Sopenharmony_ci err = snd_usb_mixer_controls_badd(mixer, ctrlif); 359662306a36Sopenharmony_ci if (err < 0) 359762306a36Sopenharmony_ci goto _error; 359862306a36Sopenharmony_ci } else { 359962306a36Sopenharmony_ci err = snd_usb_mixer_controls(mixer); 360062306a36Sopenharmony_ci if (err < 0) 360162306a36Sopenharmony_ci goto _error; 360262306a36Sopenharmony_ci } 360362306a36Sopenharmony_ci 360462306a36Sopenharmony_ci err = snd_usb_mixer_status_create(mixer); 360562306a36Sopenharmony_ci if (err < 0) 360662306a36Sopenharmony_ci goto _error; 360762306a36Sopenharmony_ci 360862306a36Sopenharmony_ci err = snd_usb_mixer_apply_create_quirk(mixer); 360962306a36Sopenharmony_ci if (err < 0) 361062306a36Sopenharmony_ci goto _error; 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci err = snd_device_new(chip->card, SNDRV_DEV_CODEC, mixer, &dev_ops); 361362306a36Sopenharmony_ci if (err < 0) 361462306a36Sopenharmony_ci goto _error; 361562306a36Sopenharmony_ci 361662306a36Sopenharmony_ci if (list_empty(&chip->mixer_list)) 361762306a36Sopenharmony_ci snd_card_ro_proc_new(chip->card, "usbmixer", chip, 361862306a36Sopenharmony_ci snd_usb_mixer_proc_read); 361962306a36Sopenharmony_ci 362062306a36Sopenharmony_ci list_add(&mixer->list, &chip->mixer_list); 362162306a36Sopenharmony_ci return 0; 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci_error: 362462306a36Sopenharmony_ci snd_usb_mixer_free(mixer); 362562306a36Sopenharmony_ci return err; 362662306a36Sopenharmony_ci} 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_civoid snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) 362962306a36Sopenharmony_ci{ 363062306a36Sopenharmony_ci if (mixer->disconnected) 363162306a36Sopenharmony_ci return; 363262306a36Sopenharmony_ci if (mixer->urb) 363362306a36Sopenharmony_ci usb_kill_urb(mixer->urb); 363462306a36Sopenharmony_ci if (mixer->rc_urb) 363562306a36Sopenharmony_ci usb_kill_urb(mixer->rc_urb); 363662306a36Sopenharmony_ci if (mixer->private_free) 363762306a36Sopenharmony_ci mixer->private_free(mixer); 363862306a36Sopenharmony_ci mixer->disconnected = true; 363962306a36Sopenharmony_ci} 364062306a36Sopenharmony_ci 364162306a36Sopenharmony_ci/* stop any bus activity of a mixer */ 364262306a36Sopenharmony_cistatic void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer) 364362306a36Sopenharmony_ci{ 364462306a36Sopenharmony_ci usb_kill_urb(mixer->urb); 364562306a36Sopenharmony_ci usb_kill_urb(mixer->rc_urb); 364662306a36Sopenharmony_ci} 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_cistatic int snd_usb_mixer_activate(struct usb_mixer_interface *mixer) 364962306a36Sopenharmony_ci{ 365062306a36Sopenharmony_ci int err; 365162306a36Sopenharmony_ci 365262306a36Sopenharmony_ci if (mixer->urb) { 365362306a36Sopenharmony_ci err = usb_submit_urb(mixer->urb, GFP_NOIO); 365462306a36Sopenharmony_ci if (err < 0) 365562306a36Sopenharmony_ci return err; 365662306a36Sopenharmony_ci } 365762306a36Sopenharmony_ci 365862306a36Sopenharmony_ci return 0; 365962306a36Sopenharmony_ci} 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ciint snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) 366262306a36Sopenharmony_ci{ 366362306a36Sopenharmony_ci snd_usb_mixer_inactivate(mixer); 366462306a36Sopenharmony_ci if (mixer->private_suspend) 366562306a36Sopenharmony_ci mixer->private_suspend(mixer); 366662306a36Sopenharmony_ci return 0; 366762306a36Sopenharmony_ci} 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_cistatic int restore_mixer_value(struct usb_mixer_elem_list *list) 367062306a36Sopenharmony_ci{ 367162306a36Sopenharmony_ci struct usb_mixer_elem_info *cval = mixer_elem_list_to_info(list); 367262306a36Sopenharmony_ci int c, err, idx; 367362306a36Sopenharmony_ci 367462306a36Sopenharmony_ci if (cval->val_type == USB_MIXER_BESPOKEN) 367562306a36Sopenharmony_ci return 0; 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci if (cval->cmask) { 367862306a36Sopenharmony_ci idx = 0; 367962306a36Sopenharmony_ci for (c = 0; c < MAX_CHANNELS; c++) { 368062306a36Sopenharmony_ci if (!(cval->cmask & (1 << c))) 368162306a36Sopenharmony_ci continue; 368262306a36Sopenharmony_ci if (cval->cached & (1 << (c + 1))) { 368362306a36Sopenharmony_ci err = snd_usb_set_cur_mix_value(cval, c + 1, idx, 368462306a36Sopenharmony_ci cval->cache_val[idx]); 368562306a36Sopenharmony_ci if (err < 0) 368662306a36Sopenharmony_ci break; 368762306a36Sopenharmony_ci } 368862306a36Sopenharmony_ci idx++; 368962306a36Sopenharmony_ci } 369062306a36Sopenharmony_ci } else { 369162306a36Sopenharmony_ci /* master */ 369262306a36Sopenharmony_ci if (cval->cached) 369362306a36Sopenharmony_ci snd_usb_set_cur_mix_value(cval, 0, 0, *cval->cache_val); 369462306a36Sopenharmony_ci } 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci return 0; 369762306a36Sopenharmony_ci} 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_ciint snd_usb_mixer_resume(struct usb_mixer_interface *mixer) 370062306a36Sopenharmony_ci{ 370162306a36Sopenharmony_ci struct usb_mixer_elem_list *list; 370262306a36Sopenharmony_ci int id, err; 370362306a36Sopenharmony_ci 370462306a36Sopenharmony_ci /* restore cached mixer values */ 370562306a36Sopenharmony_ci for (id = 0; id < MAX_ID_ELEMS; id++) { 370662306a36Sopenharmony_ci for_each_mixer_elem(list, mixer, id) { 370762306a36Sopenharmony_ci if (list->resume) { 370862306a36Sopenharmony_ci err = list->resume(list); 370962306a36Sopenharmony_ci if (err < 0) 371062306a36Sopenharmony_ci return err; 371162306a36Sopenharmony_ci } 371262306a36Sopenharmony_ci } 371362306a36Sopenharmony_ci } 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci snd_usb_mixer_resume_quirk(mixer); 371662306a36Sopenharmony_ci 371762306a36Sopenharmony_ci return snd_usb_mixer_activate(mixer); 371862306a36Sopenharmony_ci} 371962306a36Sopenharmony_ci 372062306a36Sopenharmony_civoid snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, 372162306a36Sopenharmony_ci struct usb_mixer_interface *mixer, 372262306a36Sopenharmony_ci int unitid) 372362306a36Sopenharmony_ci{ 372462306a36Sopenharmony_ci list->mixer = mixer; 372562306a36Sopenharmony_ci list->id = unitid; 372662306a36Sopenharmony_ci list->dump = snd_usb_mixer_dump_cval; 372762306a36Sopenharmony_ci list->resume = restore_mixer_value; 372862306a36Sopenharmony_ci} 3729