ALSA: usb-audio: Add resume support for FTU controls

A few FTU mixer controls have the own value handling, so they have to
be rewritten to follow the support for resume callbacks.  This ended
up in a fair amount of refactoring.  Its own struct is now removed,
instead the values are embedded in kctl private_value totally.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2014-11-18 17:37:40 +01:00
parent da6d276957
commit 0b4e9cfcef
1 changed files with 58 additions and 144 deletions

View File

@ -892,14 +892,6 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
/* M-Audio FastTrack Ultra quirks */ /* M-Audio FastTrack Ultra quirks */
/* FTU Effect switch (also used by C400/C600) */ /* FTU Effect switch (also used by C400/C600) */
struct snd_ftu_eff_switch_priv_val {
struct usb_mixer_interface *mixer;
int cached_value;
int is_cached;
int bUnitID;
int validx;
};
static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
@ -911,138 +903,77 @@ static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
} }
static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
struct snd_ctl_elem_value *ucontrol) struct snd_kcontrol *kctl)
{ {
struct snd_usb_audio *chip; struct usb_device *dev = mixer->chip->dev;
struct usb_mixer_interface *mixer; unsigned int pval = kctl->private_value;
struct snd_ftu_eff_switch_priv_val *pval;
int err; int err;
unsigned char value[2]; unsigned char value[2];
int id, validx;
const int val_len = 2;
value[0] = 0x00; value[0] = 0x00;
value[1] = 0x00; value[1] = 0x00;
pval = (struct snd_ftu_eff_switch_priv_val *) err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
kctl->private_value; USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
pval & 0xff00,
if (pval->is_cached) { snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8),
ucontrol->value.enumerated.item[0] = pval->cached_value; value, 2);
return 0;
}
mixer = (struct usb_mixer_interface *) pval->mixer;
if (snd_BUG_ON(!mixer))
return -EINVAL;
chip = (struct snd_usb_audio *) mixer->chip;
if (snd_BUG_ON(!chip))
return -EINVAL;
id = pval->bUnitID;
validx = pval->validx;
down_read(&mixer->chip->shutdown_rwsem);
if (mixer->chip->shutdown)
err = -ENODEV;
else
err = snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
value, val_len);
up_read(&mixer->chip->shutdown_rwsem);
if (err < 0) if (err < 0)
return err; return err;
ucontrol->value.enumerated.item[0] = value[0]; kctl->private_value |= value[0] << 24;
pval->cached_value = value[0];
pval->is_cached = 1;
return 0; return 0;
} }
static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol)
{
ucontrol->value.enumerated.item[0] = kctl->private_value >> 24;
return 0;
}
static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list)
{
struct snd_usb_audio *chip = list->mixer->chip;
unsigned int pval = list->kctl->private_value;
unsigned char value[2];
int err;
value[0] = pval >> 24;
value[1] = 0;
down_read(&chip->shutdown_rwsem);
if (chip->shutdown)
err = -ENODEV;
else
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0),
UAC_SET_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
pval & 0xff00,
snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8),
value, 2);
up_read(&chip->shutdown_rwsem);
return err;
}
static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_usb_audio *chip; struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl);
struct snd_ftu_eff_switch_priv_val *pval; unsigned int pval = list->kctl->private_value;
int cur_val, err, new_val;
struct usb_mixer_interface *mixer; cur_val = pval >> 24;
int changed, cur_val, err, new_val;
unsigned char value[2];
int id, validx;
const int val_len = 2;
changed = 0;
pval = (struct snd_ftu_eff_switch_priv_val *)
kctl->private_value;
cur_val = pval->cached_value;
new_val = ucontrol->value.enumerated.item[0]; new_val = ucontrol->value.enumerated.item[0];
if (cur_val == new_val)
return 0;
mixer = (struct usb_mixer_interface *) pval->mixer; kctl->private_value &= ~(0xff << 24);
if (snd_BUG_ON(!mixer)) kctl->private_value |= new_val << 24;
return -EINVAL; err = snd_ftu_eff_switch_update(list);
return err < 0 ? err : 1;
chip = (struct snd_usb_audio *) mixer->chip;
if (snd_BUG_ON(!chip))
return -EINVAL;
id = pval->bUnitID;
validx = pval->validx;
if (!pval->is_cached) {
/* Read current value */
down_read(&mixer->chip->shutdown_rwsem);
if (mixer->chip->shutdown)
err = -ENODEV;
else
err = snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
value, val_len);
up_read(&mixer->chip->shutdown_rwsem);
if (err < 0)
return err;
cur_val = value[0];
pval->cached_value = cur_val;
pval->is_cached = 1;
}
/* update value if needed */
if (cur_val != new_val) {
value[0] = new_val;
value[1] = 0;
down_read(&mixer->chip->shutdown_rwsem);
if (mixer->chip->shutdown)
err = -ENODEV;
else
err = snd_usb_ctl_msg(chip->dev,
usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
value, val_len);
up_read(&mixer->chip->shutdown_rwsem);
if (err < 0)
return err;
pval->cached_value = new_val;
pval->is_cached = 1;
changed = 1;
}
return changed;
}
static void kctl_private_value_free(struct snd_kcontrol *kctl)
{
kfree((void *)kctl->private_value);
} }
static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
@ -1057,33 +988,16 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
.get = snd_ftu_eff_switch_get, .get = snd_ftu_eff_switch_get,
.put = snd_ftu_eff_switch_put .put = snd_ftu_eff_switch_put
}; };
struct usb_mixer_elem_list *list;
int err; int err;
struct snd_kcontrol *kctl;
struct snd_ftu_eff_switch_priv_val *pval;
pval = kzalloc(sizeof(*pval), GFP_KERNEL); err = add_single_ctl_with_resume(mixer, bUnitID,
if (!pval) snd_ftu_eff_switch_update,
return -ENOMEM; &template, &list);
pval->cached_value = 0;
pval->is_cached = 0;
pval->mixer = mixer;
pval->bUnitID = bUnitID;
pval->validx = validx;
template.private_value = (unsigned long) pval;
kctl = snd_ctl_new1(&template, mixer->chip);
if (!kctl) {
kfree(pval);
return -ENOMEM;
}
kctl->private_free = kctl_private_value_free;
err = snd_ctl_add(mixer->chip->card, kctl);
if (err < 0) if (err < 0)
return err; return err;
list->kctl->private_value = (validx << 8) | bUnitID;
snd_ftu_eff_switch_init(mixer, list->kctl);
return 0; return 0;
} }