linux/sound/usb/mixer.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *   (Tentative) USB Audio Driver for ALSA
 *
 *   Mixer control part
 *
 *   Copyright (c) 2002 by Takashi Iwai <[email protected]>
 *
 *   Many codes borrowed from audio.c by
 *	    Alan Cox ([email protected])
 *	    Thomas Sailer ([email protected])
 */

/*
 * TODOs, for both the mixer and the streaming interfaces:
 *
 *  - support for UAC2 effect units
 *  - support for graphical equalizers
 *  - RANGE and MEM set commands (UAC2)
 *  - RANGE and MEM interrupt dispatchers (UAC2)
 *  - audio channel clustering (UAC2)
 *  - audio sample rate converter units (UAC2)
 *  - proper handling of clock multipliers (UAC2)
 *  - dispatch clock change notifications (UAC2)
 *  	- stop PCM streams which use a clock that became invalid
 *  	- stop PCM streams which use a clock selector that has changed
 *  	- parse available sample rates again when clock sources changed
 */

#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/usb/audio-v3.h>

#include <sound/core.h>
#include <sound/control.h>
#include <sound/hwdep.h>
#include <sound/info.h>
#include <sound/tlv.h>

#include "usbaudio.h"
#include "mixer.h"
#include "helper.h"
#include "mixer_quirks.h"
#include "power.h"

#define MAX_ID_ELEMS

struct usb_audio_term {};

struct usbmix_name_map;

struct mixer_build {};

/*E-mu 0202/0404/0204 eXtension Unit(XU) control*/
enum {};
enum {};

/*
 * manual mapping of mixer names
 * if the mixer topology is too complicated and the parsed names are
 * ambiguous, add the entries in usbmixer_maps.c.
 */
#include "mixer_maps.c"

static const struct usbmix_name_map *
find_map(const struct usbmix_name_map *p, int unitid, int control)
{}

/* get the mapped name if the unit matches */
static int
check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen)
{}

/* ignore the error value if ignore_ctl_error flag is set */
#define filter_error(cval, err)

/* check whether the control should be ignored */
static inline int
check_ignored_ctl(const struct usbmix_name_map *p)
{}

/* dB mapping */
static inline void check_mapped_dB(const struct usbmix_name_map *p,
				   struct usb_mixer_elem_info *cval)
{}

/* get the mapped selector source name */
static int check_mapped_selector_name(struct mixer_build *state, int unitid,
				      int index, char *buf, int buflen)
{}

/*
 * find an audio control unit with the given unit id
 */
static void *find_audio_control_unit(struct mixer_build *state,
				     unsigned char unit)
{}

/*
 * copy a string with the given id
 */
static int snd_usb_copy_string_desc(struct snd_usb_audio *chip,
				    int index, char *buf, int maxlen)
{}

/*
 * convert from the byte/word on usb descriptor to the zero-based integer
 */
static int convert_signed_value(struct usb_mixer_elem_info *cval, int val)
{}

/*
 * convert from the zero-based int to the byte/word for usb descriptor
 */
static int convert_bytes_value(struct usb_mixer_elem_info *cval, int val)
{}

static int get_relative_value(struct usb_mixer_elem_info *cval, int val)
{}

static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
{}

static int uac2_ctl_value_size(int val_type)
{}


/*
 * retrieve a mixer value
 */

static inline int mixer_ctrl_intf(struct usb_mixer_interface *mixer)
{}

static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
			    int validx, int *value_ret)
{}

static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
			    int validx, int *value_ret)
{}

static int get_ctl_value(struct usb_mixer_elem_info *cval, int request,
			 int validx, int *value_ret)
{}

static int get_cur_ctl_value(struct usb_mixer_elem_info *cval,
			     int validx, int *value)
{}

/* channel = 0: master, 1 = first channel */
static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval,
				  int channel, int *value)
{}

int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
			     int channel, int index, int *value)
{}

/*
 * set a mixer value
 */

int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
				int request, int validx, int value_set)
{}

static int set_cur_ctl_value(struct usb_mixer_elem_info *cval,
			     int validx, int value)
{}

int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
			     int index, int value)
{}

/*
 * TLV callback for mixer volume controls
 */
int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
			 unsigned int size, unsigned int __user *_tlv)
{}

/*
 * parser routines begin here...
 */

static int parse_audio_unit(struct mixer_build *state, int unitid);


/*
 * check if the input/output channel routing is enabled on the given bitmap.
 * used for mixer unit parser
 */
static int check_matrix_bitmap(unsigned char *bmap,
			       int ich, int och, int num_outs)
{}

/*
 * add an alsa control element
 * search and increment the index until an empty slot is found.
 *
 * if failed, give up and free the control instance.
 */

int snd_usb_mixer_add_list(struct usb_mixer_elem_list *list,
			   struct snd_kcontrol *kctl,
			   bool is_std_info)
{}

/*
 * get a terminal name string
 */

static struct iterm_name_combo {} iterm_names[] =;

static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iterm,
			 unsigned char *name, int maxlen, int term_only)
{}

/*
 * Get logical cluster information for UAC3 devices.
 */
static int get_cluster_channels_v3(struct mixer_build *state, unsigned int cluster_id)
{}

/*
 * Get number of channels for a Mixer Unit.
 */
static int uac_mixer_unit_get_channels(struct mixer_build *state,
				       struct uac_mixer_unit_descriptor *desc)
{}

/*
 * Parse Input Terminal Unit
 */
static int __check_input_term(struct mixer_build *state, int id,
			      struct usb_audio_term *term);

static int parse_term_uac1_iterm_unit(struct mixer_build *state,
				      struct usb_audio_term *term,
				      void *p1, int id)
{}

static int parse_term_uac2_iterm_unit(struct mixer_build *state,
				      struct usb_audio_term *term,
				      void *p1, int id)
{}

static int parse_term_uac3_iterm_unit(struct mixer_build *state,
				      struct usb_audio_term *term,
				      void *p1, int id)
{}

static int parse_term_mixer_unit(struct mixer_build *state,
				 struct usb_audio_term *term,
				 void *p1, int id)
{}

static int parse_term_selector_unit(struct mixer_build *state,
				    struct usb_audio_term *term,
				    void *p1, int id)
{}

static int parse_term_proc_unit(struct mixer_build *state,
				struct usb_audio_term *term,
				void *p1, int id, int vtype)
{}

static int parse_term_effect_unit(struct mixer_build *state,
				  struct usb_audio_term *term,
				  void *p1, int id)
{}

static int parse_term_uac2_clock_source(struct mixer_build *state,
					struct usb_audio_term *term,
					void *p1, int id)
{}

static int parse_term_uac3_clock_source(struct mixer_build *state,
					struct usb_audio_term *term,
					void *p1, int id)
{}

#define PTYPE(a, b)

/*
 * parse the source unit recursively until it reaches to a terminal
 * or a branched unit.
 */
static int __check_input_term(struct mixer_build *state, int id,
			      struct usb_audio_term *term)
{}


static int check_input_term(struct mixer_build *state, int id,
			    struct usb_audio_term *term)
{}

/*
 * Feature Unit
 */

/* feature unit control information */
struct usb_feature_control_info {};

static const struct usb_feature_control_info audio_feature_info[] =;

static void usb_mixer_elem_info_free(struct usb_mixer_elem_info *cval)
{}

/* private_free callback */
void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl)
{}

/*
 * interface to ALSA control for feature/mixer units
 */

/* volume control quirks */
static void volume_control_quirks(struct usb_mixer_elem_info *cval,
				  struct snd_kcontrol *kctl)
{}

/* forcibly initialize the current mixer value; if GET_CUR fails, set to
 * the minimum as default
 */
static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
{}

/*
 * retrieve the minimum and maximum values for the specified control
 */
static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
				   int default_min, struct snd_kcontrol *kctl)
{}

#define get_min_max(cval, def)

/* get a feature/mixer unit info */
static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_info *uinfo)
{}

/* get the current value from feature/mixer unit */
static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_value *ucontrol)
{}

/* put the current value to feature/mixer unit */
static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_value *ucontrol)
{}

/* get the boolean value from the master channel of a UAC control */
static int mixer_ctl_master_bool_get(struct snd_kcontrol *kcontrol,
				     struct snd_ctl_elem_value *ucontrol)
{}

static int get_connector_value(struct usb_mixer_elem_info *cval,
			       char *name, int *val)
{}

/* get the connectors status and report it as boolean type */
static int mixer_ctl_connector_get(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_value *ucontrol)
{}

static const struct snd_kcontrol_new usb_feature_unit_ctl =;

/* the read-only variant */
static const struct snd_kcontrol_new usb_feature_unit_ctl_ro =;

/*
 * A control which shows the boolean value from reading a UAC control on
 * the master channel.
 */
static const struct snd_kcontrol_new usb_bool_master_control_ctl_ro =;

static const struct snd_kcontrol_new usb_connector_ctl_ro =;

/*
 * This symbol is exported in order to allow the mixer quirks to
 * hook up to the standard feature unit control mechanism
 */
const struct snd_kcontrol_new *snd_usb_feature_unit_ctl =;

/*
 * build a feature control
 */
static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
{}

/*
 * A lot of headsets/headphones have a "Speaker" mixer. Make sure we
 * rename it to "Headphone". We determine if something is a headphone
 * similar to how udev determines form factor.
 */
static void check_no_speaker_on_headset(struct snd_kcontrol *kctl,
					struct snd_card *card)
{}

static const struct usb_feature_control_info *get_feature_control_info(int control)
{}

static void __build_feature_ctl(struct usb_mixer_interface *mixer,
				const struct usbmix_name_map *imap,
				unsigned int ctl_mask, int control,
				struct usb_audio_term *iterm,
				struct usb_audio_term *oterm,
				int unitid, int nameid, int readonly_mask)
{}

static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
			      unsigned int ctl_mask, int control,
			      struct usb_audio_term *iterm, int unitid,
			      int readonly_mask)
{}

static void build_feature_ctl_badd(struct usb_mixer_interface *mixer,
			      unsigned int ctl_mask, int control, int unitid,
			      const struct usbmix_name_map *badd_map)
{}

static void get_connector_control_name(struct usb_mixer_interface *mixer,
				       struct usb_audio_term *term,
				       bool is_input, char *name, int name_size)
{}

/* get connector value to "wake up" the USB audio */
static int connector_mixer_resume(struct usb_mixer_elem_list *list)
{}

/* Build a mixer control for a UAC connector control (jack-detect) */
static void build_connector_control(struct usb_mixer_interface *mixer,
				    const struct usbmix_name_map *imap,
				    struct usb_audio_term *term, bool is_input)
{}

static int parse_clock_source_unit(struct mixer_build *state, int unitid,
				   void *_ftr)
{}

/*
 * parse a feature unit
 *
 * most of controls are defined here.
 */
static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
				    void *_ftr)
{}

/*
 * Mixer Unit
 */

/* check whether the given in/out overflows bmMixerControls matrix */
static bool mixer_bitmap_overflow(struct uac_mixer_unit_descriptor *desc,
				  int protocol, int num_ins, int num_outs)
{}

/*
 * build a mixer unit control
 *
 * the callbacks are identical with feature unit.
 * input channel number (zero based) is given in control field instead.
 */
static void build_mixer_unit_ctl(struct mixer_build *state,
				 struct uac_mixer_unit_descriptor *desc,
				 int in_pin, int in_ch, int num_outs,
				 int unitid, struct usb_audio_term *iterm)
{}

static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
				      void *raw_desc)
{}

/*
 * parse a mixer unit
 */
static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
				  void *raw_desc)
{}

/*
 * Processing Unit / Extension Unit
 */

/* get callback for processing/extension unit */
static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_value *ucontrol)
{}

/* put callback for processing/extension unit */
static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_value *ucontrol)
{}

/* alsa control interface for processing/extension unit */
static const struct snd_kcontrol_new mixer_procunit_ctl =;

/*
 * predefined data for processing units
 */
struct procunit_value_info {};

struct procunit_info {};

static const struct procunit_value_info undefined_proc_info[] =;

static const struct procunit_value_info updown_proc_info[] =;
static const struct procunit_value_info prologic_proc_info[] =;
static const struct procunit_value_info threed_enh_proc_info[] =;
static const struct procunit_value_info reverb_proc_info[] =;
static const struct procunit_value_info chorus_proc_info[] =;
static const struct procunit_value_info dcr_proc_info[] =;

static const struct procunit_info procunits[] =;

static const struct procunit_value_info uac3_updown_proc_info[] =;
static const struct procunit_value_info uac3_stereo_ext_proc_info[] =;

static const struct procunit_info uac3_procunits[] =;

/*
 * predefined data for extension units
 */
static const struct procunit_value_info clock_rate_xu_info[] =;
static const struct procunit_value_info clock_source_xu_info[] =;
static const struct procunit_value_info spdif_format_xu_info[] =;
static const struct procunit_value_info soft_limit_xu_info[] =;
static const struct procunit_info extunits[] =;

/*
 * build a processing/extension unit
 */
static int build_audio_procunit(struct mixer_build *state, int unitid,
				void *raw_desc, const struct procunit_info *list,
				bool extension_unit)
{}

static int parse_audio_processing_unit(struct mixer_build *state, int unitid,
				       void *raw_desc)
{}

static int parse_audio_extension_unit(struct mixer_build *state, int unitid,
				      void *raw_desc)
{}

/*
 * Selector Unit
 */

/*
 * info callback for selector unit
 * use an enumerator type for routing
 */
static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol,
				   struct snd_ctl_elem_info *uinfo)
{}

/* get callback for selector unit */
static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_value *ucontrol)
{}

/* put callback for selector unit */
static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_value *ucontrol)
{}

/* alsa control interface for selector unit */
static const struct snd_kcontrol_new mixer_selectunit_ctl =;

/*
 * private free callback.
 * free both private_data and private_value
 */
static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
{}

/*
 * parse a selector unit
 */
static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
				     void *raw_desc)
{}

/*
 * parse an audio unit recursively
 */

static int parse_audio_unit(struct mixer_build *state, int unitid)
{}

static void snd_usb_mixer_free(struct usb_mixer_interface *mixer)
{}

static int snd_usb_mixer_dev_free(struct snd_device *device)
{}

/* UAC3 predefined channels configuration */
struct uac3_badd_profile {};

static const struct uac3_badd_profile uac3_badd_profiles[] =;

static bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer,
					      const struct uac3_badd_profile *f,
					      int c_chmask, int p_chmask)
{}

/*
 * create mixer controls for UAC3 BADD profiles
 *
 * UAC3 BADD device doesn't contain CS descriptors thus we will guess everything
 *
 * BADD device may contain Mixer Unit, which doesn't have any controls, skip it
 */
static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer,
				       int ctrlif)
{}

/*
 * create mixer controls
 *
 * walk through all UAC_OUTPUT_TERMINAL descriptors to search for mixers
 */
static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
{}

static int delegate_notify(struct usb_mixer_interface *mixer, int unitid,
			   u8 *control, u8 *channel)
{}

void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
{}

static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer,
				    struct usb_mixer_elem_list *list)
{}

static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
				    struct snd_info_buffer *buffer)
{}

static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
				       int attribute, int value, int index)
{}

static void snd_usb_mixer_interrupt(struct urb *urb)
{}

/* create the handler for the optional status interrupt endpoint */
static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
{}

int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)
{}

void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer)
{}

/* stop any bus activity of a mixer */
static void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer)
{}

static int snd_usb_mixer_activate(struct usb_mixer_interface *mixer)
{}

int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer)
{}

static int restore_mixer_value(struct usb_mixer_elem_list *list)
{}

int snd_usb_mixer_resume(struct usb_mixer_interface *mixer)
{}

void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
				 struct usb_mixer_interface *mixer,
				 int unitid)
{}