diff options
author | Jaroslav Kysela <perex@perex.cz> | 2010-02-11 11:50:44 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2010-02-11 12:00:16 -0500 |
commit | c3a3e040f01457d2ea4f199f75ca205401001a3b (patch) | |
tree | 38ba87d579c874ff68f55317ef468f56127cdc87 /sound/usb/usbmixer.c | |
parent | d5e1ca05f758fec2845a97fd7aa1eeca91c51a21 (diff) |
ALSA: usbmixer - add possibility to remap dB values
USB devices tends to represent dB ranges in different way than ALSA expects.
Add possibility to override these values and add guessed values for
SoundBlaster MP3+.
Also rename 'Capture Input Source' control to 'Capture Source' for
SoundBlaster MP3+ and Extigy.
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/usb/usbmixer.c')
-rw-r--r-- | sound/usb/usbmixer.c | 125 |
1 files changed, 76 insertions, 49 deletions
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index c998220b99c6..c72ad0c82581 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c | |||
@@ -123,6 +123,7 @@ struct usb_mixer_elem_info { | |||
123 | int channels; | 123 | int channels; |
124 | int val_type; | 124 | int val_type; |
125 | int min, max, res; | 125 | int min, max, res; |
126 | int dBmin, dBmax; | ||
126 | int cached; | 127 | int cached; |
127 | int cache_val[MAX_CHANNELS]; | 128 | int cache_val[MAX_CHANNELS]; |
128 | u8 initialized; | 129 | u8 initialized; |
@@ -194,42 +195,50 @@ enum { | |||
194 | */ | 195 | */ |
195 | #include "usbmixer_maps.c" | 196 | #include "usbmixer_maps.c" |
196 | 197 | ||
197 | /* get the mapped name if the unit matches */ | 198 | static const struct usbmix_name_map * |
198 | static int check_mapped_name(struct mixer_build *state, int unitid, int control, char *buf, int buflen) | 199 | find_map(struct mixer_build *state, int unitid, int control) |
199 | { | 200 | { |
200 | const struct usbmix_name_map *p; | 201 | const struct usbmix_name_map *p = state->map; |
201 | 202 | ||
202 | if (! state->map) | 203 | if (!p) |
203 | return 0; | 204 | return NULL; |
204 | 205 | ||
205 | for (p = state->map; p->id; p++) { | 206 | for (p = state->map; p->id; p++) { |
206 | if (p->id == unitid && p->name && | 207 | if (p->id == unitid && |
207 | (! control || ! p->control || control == p->control)) { | 208 | (!control || !p->control || control == p->control)) |
208 | buflen--; | 209 | return p; |
209 | return strlcpy(buf, p->name, buflen); | ||
210 | } | ||
211 | } | 210 | } |
212 | return 0; | 211 | return NULL; |
213 | } | 212 | } |
214 | 213 | ||
215 | /* check whether the control should be ignored */ | 214 | /* get the mapped name if the unit matches */ |
216 | static int check_ignored_ctl(struct mixer_build *state, int unitid, int control) | 215 | static int |
216 | check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen) | ||
217 | { | 217 | { |
218 | const struct usbmix_name_map *p; | 218 | if (!p || !p->name) |
219 | return 0; | ||
219 | 220 | ||
220 | if (! state->map) | 221 | buflen--; |
222 | return strlcpy(buf, p->name, buflen); | ||
223 | } | ||
224 | |||
225 | /* check whether the control should be ignored */ | ||
226 | static inline int | ||
227 | check_ignored_ctl(const struct usbmix_name_map *p) | ||
228 | { | ||
229 | if (!p || p->name || p->dB) | ||
221 | return 0; | 230 | return 0; |
222 | for (p = state->map; p->id; p++) { | 231 | return 1; |
223 | if (p->id == unitid && ! p->name && | 232 | } |
224 | (! control || ! p->control || control == p->control)) { | 233 | |
225 | /* | 234 | /* dB mapping */ |
226 | printk(KERN_DEBUG "ignored control %d:%d\n", | 235 | static inline void check_mapped_dB(const struct usbmix_name_map *p, |
227 | unitid, control); | 236 | struct usb_mixer_elem_info *cval) |
228 | */ | 237 | { |
229 | return 1; | 238 | if (p && p->dB) { |
230 | } | 239 | cval->dBmin = p->dB->min; |
240 | cval->dBmax = p->dB->max; | ||
231 | } | 241 | } |
232 | return 0; | ||
233 | } | 242 | } |
234 | 243 | ||
235 | /* get the mapped selector source name */ | 244 | /* get the mapped selector source name */ |
@@ -466,20 +475,8 @@ static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, | |||
466 | 475 | ||
467 | if (size < sizeof(scale)) | 476 | if (size < sizeof(scale)) |
468 | return -ENOMEM; | 477 | return -ENOMEM; |
469 | /* USB descriptions contain the dB scale in 1/256 dB unit | 478 | scale[2] = cval->dBmin; |
470 | * while ALSA TLV contains in 1/100 dB unit | 479 | scale[3] = cval->dBmax; |
471 | */ | ||
472 | scale[2] = (convert_signed_value(cval, cval->min) * 100) / 256; | ||
473 | scale[3] = (convert_signed_value(cval, cval->max) * 100) / 256; | ||
474 | if (scale[3] <= scale[2]) { | ||
475 | /* something is wrong; assume it's either from/to 0dB */ | ||
476 | if (scale[2] < 0) | ||
477 | scale[3] = 0; | ||
478 | else if (scale[2] > 0) | ||
479 | scale[2] = 0; | ||
480 | else /* totally crap, return an error */ | ||
481 | return -EINVAL; | ||
482 | } | ||
483 | if (copy_to_user(_tlv, scale, sizeof(scale))) | 480 | if (copy_to_user(_tlv, scale, sizeof(scale))) |
484 | return -EFAULT; | 481 | return -EFAULT; |
485 | return 0; | 482 | return 0; |
@@ -720,6 +717,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) | |||
720 | cval->min = default_min; | 717 | cval->min = default_min; |
721 | cval->max = cval->min + 1; | 718 | cval->max = cval->min + 1; |
722 | cval->res = 1; | 719 | cval->res = 1; |
720 | cval->dBmin = cval->dBmax = 0; | ||
723 | 721 | ||
724 | if (cval->val_type == USB_MIXER_BOOLEAN || | 722 | if (cval->val_type == USB_MIXER_BOOLEAN || |
725 | cval->val_type == USB_MIXER_INV_BOOLEAN) { | 723 | cval->val_type == USB_MIXER_INV_BOOLEAN) { |
@@ -787,6 +785,24 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min) | |||
787 | 785 | ||
788 | cval->initialized = 1; | 786 | cval->initialized = 1; |
789 | } | 787 | } |
788 | |||
789 | /* USB descriptions contain the dB scale in 1/256 dB unit | ||
790 | * while ALSA TLV contains in 1/100 dB unit | ||
791 | */ | ||
792 | cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256; | ||
793 | cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256; | ||
794 | if (cval->dBmin > cval->dBmax) { | ||
795 | /* something is wrong; assume it's either from/to 0dB */ | ||
796 | if (cval->dBmin < 0) | ||
797 | cval->dBmax = 0; | ||
798 | else if (cval->dBmin > 0) | ||
799 | cval->dBmin = 0; | ||
800 | if (cval->dBmin > cval->dBmax) { | ||
801 | /* totally crap, return an error */ | ||
802 | return -EINVAL; | ||
803 | } | ||
804 | } | ||
805 | |||
790 | return 0; | 806 | return 0; |
791 | } | 807 | } |
792 | 808 | ||
@@ -912,6 +928,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, | |||
912 | int nameid = desc[desc[0] - 1]; | 928 | int nameid = desc[desc[0] - 1]; |
913 | struct snd_kcontrol *kctl; | 929 | struct snd_kcontrol *kctl; |
914 | struct usb_mixer_elem_info *cval; | 930 | struct usb_mixer_elem_info *cval; |
931 | const struct usbmix_name_map *map; | ||
915 | 932 | ||
916 | control++; /* change from zero-based to 1-based value */ | 933 | control++; /* change from zero-based to 1-based value */ |
917 | 934 | ||
@@ -920,7 +937,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, | |||
920 | return; | 937 | return; |
921 | } | 938 | } |
922 | 939 | ||
923 | if (check_ignored_ctl(state, unitid, control)) | 940 | map = find_map(state, unitid, control); |
941 | if (check_ignored_ctl(map)) | ||
924 | return; | 942 | return; |
925 | 943 | ||
926 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); | 944 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); |
@@ -954,10 +972,11 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, | |||
954 | } | 972 | } |
955 | kctl->private_free = usb_mixer_elem_free; | 973 | kctl->private_free = usb_mixer_elem_free; |
956 | 974 | ||
957 | len = check_mapped_name(state, unitid, control, kctl->id.name, sizeof(kctl->id.name)); | 975 | len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); |
958 | mapped_name = len != 0; | 976 | mapped_name = len != 0; |
959 | if (! len && nameid) | 977 | if (! len && nameid) |
960 | len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); | 978 | len = snd_usb_copy_string_desc(state, nameid, |
979 | kctl->id.name, sizeof(kctl->id.name)); | ||
961 | 980 | ||
962 | switch (control) { | 981 | switch (control) { |
963 | case USB_FEATURE_MUTE: | 982 | case USB_FEATURE_MUTE: |
@@ -995,6 +1014,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc, | |||
995 | kctl->vd[0].access |= | 1014 | kctl->vd[0].access |= |
996 | SNDRV_CTL_ELEM_ACCESS_TLV_READ | | 1015 | SNDRV_CTL_ELEM_ACCESS_TLV_READ | |
997 | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; | 1016 | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; |
1017 | check_mapped_dB(map, cval); | ||
998 | } | 1018 | } |
999 | break; | 1019 | break; |
1000 | 1020 | ||
@@ -1122,8 +1142,10 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, | |||
1122 | unsigned int num_outs = desc[5 + input_pins]; | 1142 | unsigned int num_outs = desc[5 + input_pins]; |
1123 | unsigned int i, len; | 1143 | unsigned int i, len; |
1124 | struct snd_kcontrol *kctl; | 1144 | struct snd_kcontrol *kctl; |
1145 | const struct usbmix_name_map *map; | ||
1125 | 1146 | ||
1126 | if (check_ignored_ctl(state, unitid, 0)) | 1147 | map = find_map(state, unitid, 0); |
1148 | if (check_ignored_ctl(map)) | ||
1127 | return; | 1149 | return; |
1128 | 1150 | ||
1129 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); | 1151 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); |
@@ -1152,7 +1174,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc, | |||
1152 | } | 1174 | } |
1153 | kctl->private_free = usb_mixer_elem_free; | 1175 | kctl->private_free = usb_mixer_elem_free; |
1154 | 1176 | ||
1155 | len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name)); | 1177 | len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); |
1156 | if (! len) | 1178 | if (! len) |
1157 | len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0); | 1179 | len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0); |
1158 | if (! len) | 1180 | if (! len) |
@@ -1342,6 +1364,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned | |||
1342 | int i, err, nameid, type, len; | 1364 | int i, err, nameid, type, len; |
1343 | struct procunit_info *info; | 1365 | struct procunit_info *info; |
1344 | struct procunit_value_info *valinfo; | 1366 | struct procunit_value_info *valinfo; |
1367 | const struct usbmix_name_map *map; | ||
1345 | static struct procunit_value_info default_value_info[] = { | 1368 | static struct procunit_value_info default_value_info[] = { |
1346 | { 0x01, "Switch", USB_MIXER_BOOLEAN }, | 1369 | { 0x01, "Switch", USB_MIXER_BOOLEAN }, |
1347 | { 0 } | 1370 | { 0 } |
@@ -1371,7 +1394,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned | |||
1371 | /* FIXME: bitmap might be longer than 8bit */ | 1394 | /* FIXME: bitmap might be longer than 8bit */ |
1372 | if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1)))) | 1395 | if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1)))) |
1373 | continue; | 1396 | continue; |
1374 | if (check_ignored_ctl(state, unitid, valinfo->control)) | 1397 | map = find_map(state, unitid, valinfo->control); |
1398 | if (check_ignored_ctl(map)) | ||
1375 | continue; | 1399 | continue; |
1376 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); | 1400 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); |
1377 | if (! cval) { | 1401 | if (! cval) { |
@@ -1402,8 +1426,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned | |||
1402 | } | 1426 | } |
1403 | kctl->private_free = usb_mixer_elem_free; | 1427 | kctl->private_free = usb_mixer_elem_free; |
1404 | 1428 | ||
1405 | if (check_mapped_name(state, unitid, cval->control, kctl->id.name, sizeof(kctl->id.name))) | 1429 | if (check_mapped_name(map, kctl->id.name, |
1406 | ; | 1430 | sizeof(kctl->id.name))) |
1431 | /* nothing */ ; | ||
1407 | else if (info->name) | 1432 | else if (info->name) |
1408 | strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); | 1433 | strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); |
1409 | else { | 1434 | else { |
@@ -1542,6 +1567,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi | |||
1542 | int err; | 1567 | int err; |
1543 | struct usb_mixer_elem_info *cval; | 1568 | struct usb_mixer_elem_info *cval; |
1544 | struct snd_kcontrol *kctl; | 1569 | struct snd_kcontrol *kctl; |
1570 | const struct usbmix_name_map *map; | ||
1545 | char **namelist; | 1571 | char **namelist; |
1546 | 1572 | ||
1547 | if (! num_ins || desc[0] < 5 + num_ins) { | 1573 | if (! num_ins || desc[0] < 5 + num_ins) { |
@@ -1557,7 +1583,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi | |||
1557 | if (num_ins == 1) /* only one ? nonsense! */ | 1583 | if (num_ins == 1) /* only one ? nonsense! */ |
1558 | return 0; | 1584 | return 0; |
1559 | 1585 | ||
1560 | if (check_ignored_ctl(state, unitid, 0)) | 1586 | map = find_map(state, unitid, 0); |
1587 | if (check_ignored_ctl(map)) | ||
1561 | return 0; | 1588 | return 0; |
1562 | 1589 | ||
1563 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); | 1590 | cval = kzalloc(sizeof(*cval), GFP_KERNEL); |
@@ -1612,7 +1639,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi | |||
1612 | kctl->private_free = usb_mixer_selector_elem_free; | 1639 | kctl->private_free = usb_mixer_selector_elem_free; |
1613 | 1640 | ||
1614 | nameid = desc[desc[0] - 1]; | 1641 | nameid = desc[desc[0] - 1]; |
1615 | len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name)); | 1642 | len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); |
1616 | if (len) | 1643 | if (len) |
1617 | ; | 1644 | ; |
1618 | else if (nameid) | 1645 | else if (nameid) |