diff options
| -rw-r--r-- | sound/usb/usbmixer.c | 125 | ||||
| -rw-r--r-- | sound/usb/usbmixer_maps.c | 23 |
2 files changed, 93 insertions, 55 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) |
diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c index 77c35885e21c..79e903a60862 100644 --- a/sound/usb/usbmixer_maps.c +++ b/sound/usb/usbmixer_maps.c | |||
| @@ -19,11 +19,16 @@ | |||
| 19 | * | 19 | * |
| 20 | */ | 20 | */ |
| 21 | 21 | ||
| 22 | struct usbmix_dB_map { | ||
| 23 | u32 min; | ||
| 24 | u32 max; | ||
| 25 | }; | ||
| 22 | 26 | ||
| 23 | struct usbmix_name_map { | 27 | struct usbmix_name_map { |
| 24 | int id; | 28 | int id; |
| 25 | const char *name; | 29 | const char *name; |
| 26 | int control; | 30 | int control; |
| 31 | struct usbmix_dB_map *dB; | ||
| 27 | }; | 32 | }; |
| 28 | 33 | ||
| 29 | struct usbmix_selector_map { | 34 | struct usbmix_selector_map { |
| @@ -72,7 +77,7 @@ static struct usbmix_name_map extigy_map[] = { | |||
| 72 | { 8, "Line Playback" }, /* FU */ | 77 | { 8, "Line Playback" }, /* FU */ |
| 73 | /* 9: IT mic */ | 78 | /* 9: IT mic */ |
| 74 | { 10, "Mic Playback" }, /* FU */ | 79 | { 10, "Mic Playback" }, /* FU */ |
| 75 | { 11, "Capture Input Source" }, /* SU */ | 80 | { 11, "Capture Source" }, /* SU */ |
| 76 | { 12, "Capture" }, /* FU */ | 81 | { 12, "Capture" }, /* FU */ |
| 77 | /* 13: OT pcm capture */ | 82 | /* 13: OT pcm capture */ |
| 78 | /* 14: MU (w/o controls) */ | 83 | /* 14: MU (w/o controls) */ |
| @@ -102,6 +107,9 @@ static struct usbmix_name_map extigy_map[] = { | |||
| 102 | * e.g. no Master and fake PCM volume | 107 | * e.g. no Master and fake PCM volume |
| 103 | * Pavel Mihaylov <bin@bash.info> | 108 | * Pavel Mihaylov <bin@bash.info> |
| 104 | */ | 109 | */ |
| 110 | static struct usbmix_dB_map mp3plus_dB_1 = {-4781, 0}; /* just guess */ | ||
| 111 | static struct usbmix_dB_map mp3plus_dB_2 = {-1781, 618}; /* just guess */ | ||
| 112 | |||
| 105 | static struct usbmix_name_map mp3plus_map[] = { | 113 | static struct usbmix_name_map mp3plus_map[] = { |
| 106 | /* 1: IT pcm */ | 114 | /* 1: IT pcm */ |
| 107 | /* 2: IT mic */ | 115 | /* 2: IT mic */ |
| @@ -110,16 +118,19 @@ static struct usbmix_name_map mp3plus_map[] = { | |||
| 110 | /* 5: OT digital out */ | 118 | /* 5: OT digital out */ |
| 111 | /* 6: OT speaker */ | 119 | /* 6: OT speaker */ |
| 112 | /* 7: OT pcm capture */ | 120 | /* 7: OT pcm capture */ |
| 113 | { 8, "Capture Input Source" }, /* FU, default PCM Capture Source */ | 121 | { 8, "Capture Source" }, /* FU, default PCM Capture Source */ |
| 114 | /* (Mic, Input 1 = Line input, Input 2 = Optical input) */ | 122 | /* (Mic, Input 1 = Line input, Input 2 = Optical input) */ |
| 115 | { 9, "Master Playback" }, /* FU, default Speaker 1 */ | 123 | { 9, "Master Playback" }, /* FU, default Speaker 1 */ |
| 116 | /* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */ | 124 | /* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */ |
| 117 | /* { 10, "Mic Capture", 2 }, */ /* FU, Mic Capture */ | 125 | { 10, /* "Mic Capture", */ NULL, 2, .dB = &mp3plus_dB_2 }, |
| 126 | /* FU, Mic Capture */ | ||
| 118 | { 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */ | 127 | { 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */ |
| 119 | { 11, "Line Capture" }, /* FU, default PCM Capture */ | 128 | { 11, "Line Capture", .dB = &mp3plus_dB_2 }, |
| 129 | /* FU, default PCM Capture */ | ||
| 120 | { 12, "Digital In Playback" }, /* FU, default PCM 1 */ | 130 | { 12, "Digital In Playback" }, /* FU, default PCM 1 */ |
| 121 | /* { 13, "Mic Playback" }, */ /* FU, default Mic Playback */ | 131 | { 13, /* "Mic Playback", */ .dB = &mp3plus_dB_1 }, |
| 122 | { 14, "Line Playback" }, /* FU, default Speaker */ | 132 | /* FU, default Mic Playback */ |
| 133 | { 14, "Line Playback", .dB = &mp3plus_dB_1 }, /* FU, default Speaker */ | ||
| 123 | /* 15: MU */ | 134 | /* 15: MU */ |
| 124 | { 0 } /* terminator */ | 135 | { 0 } /* terminator */ |
| 125 | }; | 136 | }; |
