Skip to content

Commit

Permalink
ALSA: usbmixer - add possibility to remap dB values
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jaroslav Kysela committed Feb 11, 2010
1 parent d5e1ca0 commit c3a3e04
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 55 deletions.
125 changes: 76 additions & 49 deletions sound/usb/usbmixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ struct usb_mixer_elem_info {
int channels;
int val_type;
int min, max, res;
int dBmin, dBmax;
int cached;
int cache_val[MAX_CHANNELS];
u8 initialized;
Expand Down Expand Up @@ -194,42 +195,50 @@ enum {
*/
#include "usbmixer_maps.c"

/* get the mapped name if the unit matches */
static int check_mapped_name(struct mixer_build *state, int unitid, int control, char *buf, int buflen)
static const struct usbmix_name_map *
find_map(struct mixer_build *state, int unitid, int control)
{
const struct usbmix_name_map *p;
const struct usbmix_name_map *p = state->map;

if (! state->map)
return 0;
if (!p)
return NULL;

for (p = state->map; p->id; p++) {
if (p->id == unitid && p->name &&
(! control || ! p->control || control == p->control)) {
buflen--;
return strlcpy(buf, p->name, buflen);
}
if (p->id == unitid &&
(!control || !p->control || control == p->control))
return p;
}
return 0;
return NULL;
}

/* check whether the control should be ignored */
static int check_ignored_ctl(struct mixer_build *state, int unitid, int control)
/* get the mapped name if the unit matches */
static int
check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen)
{
const struct usbmix_name_map *p;
if (!p || !p->name)
return 0;

if (! state->map)
buflen--;
return strlcpy(buf, p->name, buflen);
}

/* check whether the control should be ignored */
static inline int
check_ignored_ctl(const struct usbmix_name_map *p)
{
if (!p || p->name || p->dB)
return 0;
for (p = state->map; p->id; p++) {
if (p->id == unitid && ! p->name &&
(! control || ! p->control || control == p->control)) {
/*
printk(KERN_DEBUG "ignored control %d:%d\n",
unitid, control);
*/
return 1;
}
return 1;
}

/* dB mapping */
static inline void check_mapped_dB(const struct usbmix_name_map *p,
struct usb_mixer_elem_info *cval)
{
if (p && p->dB) {
cval->dBmin = p->dB->min;
cval->dBmax = p->dB->max;
}
return 0;
}

/* get the mapped selector source name */
Expand Down Expand Up @@ -466,20 +475,8 @@ static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,

if (size < sizeof(scale))
return -ENOMEM;
/* USB descriptions contain the dB scale in 1/256 dB unit
* while ALSA TLV contains in 1/100 dB unit
*/
scale[2] = (convert_signed_value(cval, cval->min) * 100) / 256;
scale[3] = (convert_signed_value(cval, cval->max) * 100) / 256;
if (scale[3] <= scale[2]) {
/* something is wrong; assume it's either from/to 0dB */
if (scale[2] < 0)
scale[3] = 0;
else if (scale[2] > 0)
scale[2] = 0;
else /* totally crap, return an error */
return -EINVAL;
}
scale[2] = cval->dBmin;
scale[3] = cval->dBmax;
if (copy_to_user(_tlv, scale, sizeof(scale)))
return -EFAULT;
return 0;
Expand Down Expand Up @@ -720,6 +717,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
cval->min = default_min;
cval->max = cval->min + 1;
cval->res = 1;
cval->dBmin = cval->dBmax = 0;

if (cval->val_type == USB_MIXER_BOOLEAN ||
cval->val_type == USB_MIXER_INV_BOOLEAN) {
Expand Down Expand Up @@ -787,6 +785,24 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)

cval->initialized = 1;
}

/* USB descriptions contain the dB scale in 1/256 dB unit
* while ALSA TLV contains in 1/100 dB unit
*/
cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256;
cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256;
if (cval->dBmin > cval->dBmax) {
/* something is wrong; assume it's either from/to 0dB */
if (cval->dBmin < 0)
cval->dBmax = 0;
else if (cval->dBmin > 0)
cval->dBmin = 0;
if (cval->dBmin > cval->dBmax) {
/* totally crap, return an error */
return -EINVAL;
}
}

return 0;
}

Expand Down Expand Up @@ -912,6 +928,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
int nameid = desc[desc[0] - 1];
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;

control++; /* change from zero-based to 1-based value */

Expand All @@ -920,7 +937,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
return;
}

if (check_ignored_ctl(state, unitid, control))
map = find_map(state, unitid, control);
if (check_ignored_ctl(map))
return;

cval = kzalloc(sizeof(*cval), GFP_KERNEL);
Expand Down Expand Up @@ -954,10 +972,11 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
}
kctl->private_free = usb_mixer_elem_free;

len = check_mapped_name(state, unitid, control, kctl->id.name, sizeof(kctl->id.name));
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
mapped_name = len != 0;
if (! len && nameid)
len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
len = snd_usb_copy_string_desc(state, nameid,
kctl->id.name, sizeof(kctl->id.name));

switch (control) {
case USB_FEATURE_MUTE:
Expand Down Expand Up @@ -995,6 +1014,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
kctl->vd[0].access |=
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
check_mapped_dB(map, cval);
}
break;

Expand Down Expand Up @@ -1122,8 +1142,10 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
unsigned int num_outs = desc[5 + input_pins];
unsigned int i, len;
struct snd_kcontrol *kctl;
const struct usbmix_name_map *map;

if (check_ignored_ctl(state, unitid, 0))
map = find_map(state, unitid, 0);
if (check_ignored_ctl(map))
return;

cval = kzalloc(sizeof(*cval), GFP_KERNEL);
Expand Down Expand Up @@ -1152,7 +1174,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
}
kctl->private_free = usb_mixer_elem_free;

len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
if (! len)
len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0);
if (! len)
Expand Down Expand Up @@ -1342,6 +1364,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
int i, err, nameid, type, len;
struct procunit_info *info;
struct procunit_value_info *valinfo;
const struct usbmix_name_map *map;
static struct procunit_value_info default_value_info[] = {
{ 0x01, "Switch", USB_MIXER_BOOLEAN },
{ 0 }
Expand Down Expand Up @@ -1371,7 +1394,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
/* FIXME: bitmap might be longer than 8bit */
if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
continue;
if (check_ignored_ctl(state, unitid, valinfo->control))
map = find_map(state, unitid, valinfo->control);
if (check_ignored_ctl(map))
continue;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (! cval) {
Expand Down Expand Up @@ -1402,8 +1426,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
}
kctl->private_free = usb_mixer_elem_free;

if (check_mapped_name(state, unitid, cval->control, kctl->id.name, sizeof(kctl->id.name)))
;
if (check_mapped_name(map, kctl->id.name,
sizeof(kctl->id.name)))
/* nothing */ ;
else if (info->name)
strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
else {
Expand Down Expand Up @@ -1542,6 +1567,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
int err;
struct usb_mixer_elem_info *cval;
struct snd_kcontrol *kctl;
const struct usbmix_name_map *map;
char **namelist;

if (! num_ins || desc[0] < 5 + num_ins) {
Expand All @@ -1557,7 +1583,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
if (num_ins == 1) /* only one ? nonsense! */
return 0;

if (check_ignored_ctl(state, unitid, 0))
map = find_map(state, unitid, 0);
if (check_ignored_ctl(map))
return 0;

cval = kzalloc(sizeof(*cval), GFP_KERNEL);
Expand Down Expand Up @@ -1612,7 +1639,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
kctl->private_free = usb_mixer_selector_elem_free;

nameid = desc[desc[0] - 1];
len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
if (len)
;
else if (nameid)
Expand Down
23 changes: 17 additions & 6 deletions sound/usb/usbmixer_maps.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@
*
*/

struct usbmix_dB_map {
u32 min;
u32 max;
};

struct usbmix_name_map {
int id;
const char *name;
int control;
struct usbmix_dB_map *dB;
};

struct usbmix_selector_map {
Expand Down Expand Up @@ -72,7 +77,7 @@ static struct usbmix_name_map extigy_map[] = {
{ 8, "Line Playback" }, /* FU */
/* 9: IT mic */
{ 10, "Mic Playback" }, /* FU */
{ 11, "Capture Input Source" }, /* SU */
{ 11, "Capture Source" }, /* SU */
{ 12, "Capture" }, /* FU */
/* 13: OT pcm capture */
/* 14: MU (w/o controls) */
Expand Down Expand Up @@ -102,6 +107,9 @@ static struct usbmix_name_map extigy_map[] = {
* e.g. no Master and fake PCM volume
* Pavel Mihaylov <bin@bash.info>
*/
static struct usbmix_dB_map mp3plus_dB_1 = {-4781, 0}; /* just guess */
static struct usbmix_dB_map mp3plus_dB_2 = {-1781, 618}; /* just guess */

static struct usbmix_name_map mp3plus_map[] = {
/* 1: IT pcm */
/* 2: IT mic */
Expand All @@ -110,16 +118,19 @@ static struct usbmix_name_map mp3plus_map[] = {
/* 5: OT digital out */
/* 6: OT speaker */
/* 7: OT pcm capture */
{ 8, "Capture Input Source" }, /* FU, default PCM Capture Source */
{ 8, "Capture Source" }, /* FU, default PCM Capture Source */
/* (Mic, Input 1 = Line input, Input 2 = Optical input) */
{ 9, "Master Playback" }, /* FU, default Speaker 1 */
/* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */
/* { 10, "Mic Capture", 2 }, */ /* FU, Mic Capture */
{ 10, /* "Mic Capture", */ NULL, 2, .dB = &mp3plus_dB_2 },
/* FU, Mic Capture */
{ 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */
{ 11, "Line Capture" }, /* FU, default PCM Capture */
{ 11, "Line Capture", .dB = &mp3plus_dB_2 },
/* FU, default PCM Capture */
{ 12, "Digital In Playback" }, /* FU, default PCM 1 */
/* { 13, "Mic Playback" }, */ /* FU, default Mic Playback */
{ 14, "Line Playback" }, /* FU, default Speaker */
{ 13, /* "Mic Playback", */ .dB = &mp3plus_dB_1 },
/* FU, default Mic Playback */
{ 14, "Line Playback", .dB = &mp3plus_dB_1 }, /* FU, default Speaker */
/* 15: MU */
{ 0 } /* terminator */
};
Expand Down

0 comments on commit c3a3e04

Please sign in to comment.