summaryrefslogtreecommitdiff
path: root/sound/usb/mixer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/mixer.c')
-rw-r--r--sound/usb/mixer.c200
1 files changed, 105 insertions, 95 deletions
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 14e1455ca9de..41650d5b93b7 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -136,6 +136,10 @@ check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen)
return strlcpy(buf, p->name, buflen);
}
+/* ignore the error value if ignore_ctl_error flag is set */
+#define filter_error(cval, err) \
+ ((cval)->head.mixer->ignore_ctl_error ? 0 : (err))
+
/* check whether the control should be ignored */
static inline int
check_ignored_ctl(const struct usbmix_name_map *p)
@@ -286,13 +290,13 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
int validx, int *value_ret)
{
- struct snd_usb_audio *chip = cval->mixer->chip;
+ struct snd_usb_audio *chip = cval->head.mixer->chip;
unsigned char buf[2];
int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
int timeout = 10;
int idx = 0, err;
- err = snd_usb_autoresume(cval->mixer->chip);
+ err = snd_usb_autoresume(chip);
if (err < 0)
return -EIO;
@@ -300,7 +304,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
while (timeout-- > 0) {
if (chip->shutdown)
break;
- idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
+ idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
validx, idx, buf, val_len) >= val_len) {
@@ -316,14 +320,14 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request,
out:
up_read(&chip->shutdown_rwsem);
- snd_usb_autosuspend(cval->mixer->chip);
+ snd_usb_autosuspend(chip);
return err;
}
static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
int validx, int *value_ret)
{
- struct snd_usb_audio *chip = cval->mixer->chip;
+ struct snd_usb_audio *chip = cval->head.mixer->chip;
unsigned char buf[2 + 3 * sizeof(__u16)]; /* enough space for one range */
unsigned char *val;
int idx = 0, ret, size;
@@ -347,7 +351,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
if (chip->shutdown) {
ret = -ENODEV;
} else {
- idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
+ idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
validx, idx, buf, size);
@@ -392,7 +396,7 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request,
{
validx += cval->idx_off;
- return (cval->mixer->protocol == UAC_VERSION_1) ?
+ return (cval->head.mixer->protocol == UAC_VERSION_1) ?
get_ctl_value_v1(cval, request, validx, value_ret) :
get_ctl_value_v2(cval, request, validx, value_ret);
}
@@ -423,8 +427,8 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
}
err = get_cur_mix_raw(cval, channel, value);
if (err < 0) {
- if (!cval->mixer->ignore_ctl_error)
- usb_audio_dbg(cval->mixer->chip,
+ if (!cval->head.mixer->ignore_ctl_error)
+ usb_audio_dbg(cval->head.mixer->chip,
"cannot get current value for control %d ch %d: err = %d\n",
cval->control, channel, err);
return err;
@@ -441,13 +445,13 @@ int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval,
int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
int request, int validx, int value_set)
{
- struct snd_usb_audio *chip = cval->mixer->chip;
+ struct snd_usb_audio *chip = cval->head.mixer->chip;
unsigned char buf[2];
int idx = 0, val_len, err, timeout = 10;
validx += cval->idx_off;
- if (cval->mixer->protocol == UAC_VERSION_1) {
+ if (cval->head.mixer->protocol == UAC_VERSION_1) {
val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
} else { /* UAC_VERSION_2 */
/* audio class v2 controls are always 2 bytes in size */
@@ -472,7 +476,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
while (timeout-- > 0) {
if (chip->shutdown)
break;
- idx = snd_usb_ctrl_intf(chip) | (cval->id << 8);
+ idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8);
if (snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), request,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
@@ -506,7 +510,7 @@ int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
cval->ch_readonly & (1 << (channel - 1));
if (read_only) {
- usb_audio_dbg(cval->mixer->chip,
+ usb_audio_dbg(cval->head.mixer->chip,
"%s(): channel %d of control %d is read_only\n",
__func__, channel, cval->control);
return 0;
@@ -565,10 +569,10 @@ static int check_matrix_bitmap(unsigned char *bmap,
* if failed, give up and free the control instance.
*/
-int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
+int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list,
struct snd_kcontrol *kctl)
{
- struct usb_mixer_elem_info *cval = kctl->private_data;
+ struct usb_mixer_interface *mixer = list->mixer;
int err;
while (snd_ctl_find_id(mixer->chip->card, &kctl->id))
@@ -578,9 +582,9 @@ int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
err);
return err;
}
- cval->elem_id = &kctl->id;
- cval->next_id_elem = mixer->id_elems[cval->id];
- mixer->id_elems[cval->id] = cval;
+ list->kctl = kctl;
+ list->next_id_elem = mixer->id_elems[list->id];
+ mixer->id_elems[list->id] = list;
return 0;
}
@@ -829,7 +833,7 @@ void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl)
static void volume_control_quirks(struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
{
- struct snd_usb_audio *chip = cval->mixer->chip;
+ struct snd_usb_audio *chip = cval->head.mixer->chip;
switch (chip->usb_id) {
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
@@ -954,10 +958,10 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
}
if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
- usb_audio_err(cval->mixer->chip,
+ usb_audio_err(cval->head.mixer->chip,
"%d:%d: cannot get min/max values for control %d (id %d)\n",
- cval->id, snd_usb_ctrl_intf(cval->mixer->chip),
- cval->control, cval->id);
+ cval->head.id, snd_usb_ctrl_intf(cval->head.mixer->chip),
+ cval->control, cval->head.id);
return -EINVAL;
}
if (get_ctl_value(cval, UAC_GET_RES,
@@ -1061,7 +1065,7 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol,
kcontrol->vd[0].access &=
~(SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
- snd_ctl_notify(cval->mixer->chip->card,
+ snd_ctl_notify(cval->head.mixer->chip->card,
SNDRV_CTL_EVENT_MASK_INFO,
&kcontrol->id);
}
@@ -1088,7 +1092,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol,
continue;
err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val);
if (err < 0)
- return cval->mixer->ignore_ctl_error ? 0 : err;
+ return filter_error(cval, err);
val = get_relative_value(cval, val);
ucontrol->value.integer.value[cnt] = val;
cnt++;
@@ -1098,7 +1102,7 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol,
/* master channel */
err = snd_usb_get_cur_mix_value(cval, 0, 0, &val);
if (err < 0)
- return cval->mixer->ignore_ctl_error ? 0 : err;
+ return filter_error(cval, err);
val = get_relative_value(cval, val);
ucontrol->value.integer.value[0] = val;
}
@@ -1120,7 +1124,7 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
continue;
err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval);
if (err < 0)
- return cval->mixer->ignore_ctl_error ? 0 : err;
+ return filter_error(cval, err);
val = ucontrol->value.integer.value[cnt];
val = get_abs_value(cval, val);
if (oval != val) {
@@ -1133,7 +1137,7 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol,
/* master channel */
err = snd_usb_get_cur_mix_value(cval, 0, 0, &oval);
if (err < 0)
- return cval->mixer->ignore_ctl_error ? 0 : err;
+ return filter_error(cval, err);
val = ucontrol->value.integer.value[0];
val = get_abs_value(cval, val);
if (val != oval) {
@@ -1231,8 +1235,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return;
- cval->mixer = state->mixer;
- cval->id = unitid;
+ snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
cval->control = control;
cval->cmask = ctl_mask;
cval->val_type = audio_feature_info[control-1].type;
@@ -1343,14 +1346,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
range);
usb_audio_warn(state->chip,
"[%d] FU [%s] ch = %d, val = %d/%d/%d",
- cval->id, kctl->id.name, cval->channels,
+ cval->head.id, kctl->id.name, cval->channels,
cval->min, cval->max, cval->res);
}
usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
- cval->id, kctl->id.name, cval->channels,
+ cval->head.id, kctl->id.name, cval->channels,
cval->min, cval->max, cval->res);
- snd_usb_mixer_add_control(state->mixer, kctl);
+ snd_usb_mixer_add_control(&cval->head, kctl);
}
/*
@@ -1524,8 +1527,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
if (!cval)
return;
- cval->mixer = state->mixer;
- cval->id = unitid;
+ snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
cval->control = in_ch + 1; /* based on 1 */
cval->val_type = USB_MIXER_S16;
for (i = 0; i < num_outs; i++) {
@@ -1557,8 +1559,8 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
append_ctl_name(kctl, " Volume");
usb_audio_dbg(state->chip, "[%d] MU [%s] ch = %d, val = %d/%d\n",
- cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
- snd_usb_mixer_add_control(state->mixer, kctl);
+ cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max);
+ snd_usb_mixer_add_control(&cval->head, kctl);
}
/*
@@ -1628,12 +1630,10 @@ static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol,
int err, val;
err = get_cur_ctl_value(cval, cval->control << 8, &val);
- if (err < 0 && cval->mixer->ignore_ctl_error) {
+ if (err < 0) {
ucontrol->value.integer.value[0] = cval->min;
- return 0;
+ return filter_error(cval, err);
}
- if (err < 0)
- return err;
val = get_relative_value(cval, val);
ucontrol->value.integer.value[0] = val;
return 0;
@@ -1647,11 +1647,8 @@ static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol,
int val, oval, err;
err = get_cur_ctl_value(cval, cval->control << 8, &oval);
- if (err < 0) {
- if (cval->mixer->ignore_ctl_error)
- return 0;
- return err;
- }
+ if (err < 0)
+ return filter_error(cval, err);
val = ucontrol->value.integer.value[0];
val = get_abs_value(cval, val);
if (val != oval) {
@@ -1813,8 +1810,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return -ENOMEM;
- cval->mixer = state->mixer;
- cval->id = unitid;
+ snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
cval->control = valinfo->control;
cval->val_type = valinfo->val_type;
cval->channels = 1;
@@ -1867,10 +1863,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
usb_audio_dbg(state->chip,
"[%d] PU [%s] ch = %d, val = %d/%d\n",
- cval->id, kctl->id.name, cval->channels,
+ cval->head.id, kctl->id.name, cval->channels,
cval->min, cval->max);
- err = snd_usb_mixer_add_control(state->mixer, kctl);
+ err = snd_usb_mixer_add_control(&cval->head, kctl);
if (err < 0)
return err;
}
@@ -1923,11 +1919,8 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol,
err = get_cur_ctl_value(cval, cval->control << 8, &val);
if (err < 0) {
- if (cval->mixer->ignore_ctl_error) {
- ucontrol->value.enumerated.item[0] = 0;
- return 0;
- }
- return err;
+ ucontrol->value.enumerated.item[0] = 0;
+ return filter_error(cval, err);
}
val = get_relative_value(cval, val);
ucontrol->value.enumerated.item[0] = val;
@@ -1942,11 +1935,8 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol,
int val, oval, err;
err = get_cur_ctl_value(cval, cval->control << 8, &oval);
- if (err < 0) {
- if (cval->mixer->ignore_ctl_error)
- return 0;
- return err;
- }
+ if (err < 0)
+ return filter_error(cval, err);
val = ucontrol->value.enumerated.item[0];
val = get_abs_value(cval, val);
if (val != oval) {
@@ -2023,8 +2013,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return -ENOMEM;
- cval->mixer = state->mixer;
- cval->id = unitid;
+ snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid);
cval->val_type = USB_MIXER_U8;
cval->channels = 1;
cval->min = 1;
@@ -2032,10 +2021,11 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
cval->res = 1;
cval->initialized = 1;
- if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
- cval->control = UAC2_CX_CLOCK_SELECTOR;
- else
+ if (state->mixer->protocol == UAC_VERSION_1)
cval->control = 0;
+ else /* UAC_VERSION_2 */
+ cval->control = (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR) ?
+ UAC2_CX_CLOCK_SELECTOR : UAC2_SU_SELECTOR;
namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
if (!namelist) {
@@ -2094,11 +2084,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
}
usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n",
- cval->id, kctl->id.name, desc->bNrInPins);
- if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0)
- return err;
-
- return 0;
+ cval->head.id, kctl->id.name, desc->bNrInPins);
+ return snd_usb_mixer_add_control(&cval->head, kctl);
}
/*
@@ -2243,25 +2230,21 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
{
- struct usb_mixer_elem_info *info;
+ struct usb_mixer_elem_list *list;
- for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem)
+ for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem)
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- info->elem_id);
+ &list->kctl->id);
}
static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer,
- int unitid,
- struct usb_mixer_elem_info *cval)
+ struct usb_mixer_elem_list *list)
{
+ struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list;
static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN",
"S8", "U8", "S16", "U16"};
- snd_iprintf(buffer, " Unit: %i\n", unitid);
- if (cval->elem_id)
- snd_iprintf(buffer, " Control: name=\"%s\", index=%i\n",
- cval->elem_id->name, cval->elem_id->index);
snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, "
- "channels=%i, type=\"%s\"\n", cval->id,
+ "channels=%i, type=\"%s\"\n", cval->head.id,
cval->control, cval->cmask, cval->channels,
val_types[cval->val_type]);
snd_iprintf(buffer, " Volume: min=%i, max=%i, dBmin=%i, dBmax=%i\n",
@@ -2273,7 +2256,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
{
struct snd_usb_audio *chip = entry->private_data;
struct usb_mixer_interface *mixer;
- struct usb_mixer_elem_info *cval;
+ struct usb_mixer_elem_list *list;
int unitid;
list_for_each_entry(mixer, &chip->mixer_list, list) {
@@ -2283,9 +2266,17 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
mixer->ignore_ctl_error);
snd_iprintf(buffer, "Card: %s\n", chip->card->longname);
for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) {
- for (cval = mixer->id_elems[unitid]; cval;
- cval = cval->next_id_elem)
- snd_usb_mixer_dump_cval(buffer, unitid, cval);
+ for (list = mixer->id_elems[unitid]; list;
+ list = list->next_id_elem) {
+ snd_iprintf(buffer, " Unit: %i\n", list->id);
+ if (list->kctl)
+ snd_iprintf(buffer,
+ " Control: name=\"%s\", index=%i\n",
+ list->kctl->id.name,
+ list->kctl->id.index);
+ if (list->dump)
+ list->dump(buffer, list);
+ }
}
}
}
@@ -2293,7 +2284,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
int attribute, int value, int index)
{
- struct usb_mixer_elem_info *info;
+ struct usb_mixer_elem_list *list;
__u8 unitid = (index >> 8) & 0xff;
__u8 control = (value >> 8) & 0xff;
__u8 channel = value & 0xff;
@@ -2305,7 +2296,13 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
return;
}
- for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) {
+ for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
+ struct usb_mixer_elem_info *info;
+
+ if (!list->kctl)
+ continue;
+
+ info = (struct usb_mixer_elem_info *)list;
if (info->control != control)
continue;
@@ -2318,7 +2315,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
info->cached = 0;
snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- info->elem_id);
+ &info->head.kctl->id);
break;
case UAC2_CS_RANGE:
@@ -2516,8 +2513,9 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer)
return 0;
}
-static int restore_mixer_value(struct usb_mixer_elem_info *cval)
+static int restore_mixer_value(struct usb_mixer_elem_list *list)
{
+ struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list;
int c, err, idx;
if (cval->cmask) {
@@ -2547,19 +2545,19 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval)
int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume)
{
- struct usb_mixer_elem_info *cval;
+ struct usb_mixer_elem_list *list;
int id, err;
- /* FIXME: any mixer quirks? */
-
if (reset_resume) {
/* restore cached mixer values */
for (id = 0; id < MAX_ID_ELEMS; id++) {
- for (cval = mixer->id_elems[id]; cval;
- cval = cval->next_id_elem) {
- err = restore_mixer_value(cval);
- if (err < 0)
- return err;
+ for (list = mixer->id_elems[id]; list;
+ list = list->next_id_elem) {
+ if (list->resume) {
+ err = list->resume(list);
+ if (err < 0)
+ return err;
+ }
}
}
}
@@ -2567,3 +2565,15 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume)
return snd_usb_mixer_activate(mixer);
}
#endif
+
+void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list,
+ struct usb_mixer_interface *mixer,
+ int unitid)
+{
+ list->mixer = mixer;
+ list->id = unitid;
+ list->dump = snd_usb_mixer_dump_cval;
+#ifdef CONFIG_PM
+ list->resume = restore_mixer_value;
+#endif
+}