diff options
Diffstat (limited to 'sound/usb')
-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 | }; |