diff options
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/usbmixer.c | 122 |
1 files changed, 70 insertions, 52 deletions
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 00397c8..c07b3f8 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -110,6 +110,8 @@ struct mixer_build { const struct usbmix_selector_map *selector_map; }; +#define MAX_CHANNELS 10 /* max logical channels */ + struct usb_mixer_elem_info { struct usb_mixer_interface *mixer; struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ @@ -120,6 +122,8 @@ struct usb_mixer_elem_info { int channels; int val_type; int min, max, res; + int cached; + int cache_val[MAX_CHANNELS]; u8 initialized; }; @@ -181,8 +185,6 @@ enum { USB_PROC_DCR_RELEASE = 6, }; -#define MAX_CHANNELS 10 /* max logical channels */ - /* * manual mapping of mixer names @@ -376,11 +378,35 @@ static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int * } /* channel = 0: master, 1 = first channel */ -static inline int get_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int *value) +static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval, + int channel, int *value) { return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value); } +static int get_cur_mix_value(struct usb_mixer_elem_info *cval, + int channel, int index, int *value) +{ + int err; + + if (cval->cached & (1 << channel)) { + *value = cval->cache_val[index]; + return 0; + } + err = get_cur_mix_raw(cval, channel, value); + if (err < 0) { + if (!cval->mixer->ignore_ctl_error) + snd_printd(KERN_ERR "cannot get current value for " + "control %d ch %d: err = %d\n", + cval->control, channel, err); + return err; + } + cval->cached |= 1 << channel; + cval->cache_val[index] = *value; + return 0; +} + + /* * set a mixer value */ @@ -412,9 +438,17 @@ static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int v return set_ctl_value(cval, SET_CUR, validx, value); } -static inline int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int value) +static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, + int index, int value) { - return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value); + int err; + err = set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, + value); + if (err < 0) + return err; + cval->cached |= 1 << channel; + cval->cache_val[index] = value; + return 0; } /* @@ -718,7 +752,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) if (cval->min + cval->res < cval->max) { int last_valid_res = cval->res; int saved, test, check; - get_cur_mix_value(cval, minchn, &saved); + get_cur_mix_raw(cval, minchn, &saved); for (;;) { test = saved; if (test < cval->max) @@ -726,8 +760,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) else test -= cval->res; if (test < cval->min || test > cval->max || - set_cur_mix_value(cval, minchn, test) || - get_cur_mix_value(cval, minchn, &check)) { + set_cur_mix_value(cval, minchn, 0, test) || + get_cur_mix_raw(cval, minchn, &check)) { cval->res = last_valid_res; break; } @@ -735,7 +769,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) break; cval->res *= 2; } - set_cur_mix_value(cval, minchn, saved); + set_cur_mix_value(cval, minchn, 0, saved); } cval->initialized = 1; @@ -775,35 +809,25 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct usb_mixer_elem_info *cval = kcontrol->private_data; int c, cnt, val, err; + ucontrol->value.integer.value[0] = cval->min; if (cval->cmask) { cnt = 0; for (c = 0; c < MAX_CHANNELS; c++) { - if (cval->cmask & (1 << c)) { - err = get_cur_mix_value(cval, c + 1, &val); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) { - ucontrol->value.integer.value[0] = cval->min; - return 0; - } - snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); - return err; - } - val = get_relative_value(cval, val); - ucontrol->value.integer.value[cnt] = val; - cnt++; - } + if (!(cval->cmask & (1 << c))) + continue; + err = get_cur_mix_value(cval, c + 1, cnt, &val); + if (err < 0) + return cval->mixer->ignore_ctl_error ? 0 : err; + val = get_relative_value(cval, val); + ucontrol->value.integer.value[cnt] = val; + cnt++; } + return 0; } else { /* master channel */ - err = get_cur_mix_value(cval, 0, &val); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) { - ucontrol->value.integer.value[0] = cval->min; - return 0; - } - snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err); - return err; - } + err = get_cur_mix_value(cval, 0, 0, &val); + if (err < 0) + return cval->mixer->ignore_ctl_error ? 0 : err; val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; } @@ -820,34 +844,28 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e if (cval->cmask) { cnt = 0; for (c = 0; c < MAX_CHANNELS; c++) { - if (cval->cmask & (1 << c)) { - err = get_cur_mix_value(cval, c + 1, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } - val = ucontrol->value.integer.value[cnt]; - val = get_abs_value(cval, val); - if (oval != val) { - set_cur_mix_value(cval, c + 1, val); - changed = 1; - } - get_cur_mix_value(cval, c + 1, &val); - cnt++; + if (!(cval->cmask & (1 << c))) + continue; + err = get_cur_mix_value(cval, c + 1, cnt, &oval); + if (err < 0) + return cval->mixer->ignore_ctl_error ? 0 : err; + val = ucontrol->value.integer.value[cnt]; + val = get_abs_value(cval, val); + if (oval != val) { + set_cur_mix_value(cval, c + 1, cnt, val); + changed = 1; } + cnt++; } } else { /* master channel */ - err = get_cur_mix_value(cval, 0, &oval); - if (err < 0 && cval->mixer->ignore_ctl_error) - return 0; + err = get_cur_mix_value(cval, 0, 0, &oval); if (err < 0) - return err; + return cval->mixer->ignore_ctl_error ? 0 : err; val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { - set_cur_mix_value(cval, 0, val); + set_cur_mix_value(cval, 0, 0, val); changed = 1; } } |