Commit f96f11d2 authored by Jaroslav Kysela's avatar Jaroslav Kysela

ALSA update

  - USB driver update
    - better extdigi support (mixer)
    - cleanups in the audio code
parent 13e242be
/* include/version.h. Generated automatically by configure. */ /* include/version.h. Generated automatically by configure. */
#define CONFIG_SND_VERSION "0.9.0rc3" #define CONFIG_SND_VERSION "0.9.0rc3"
#define CONFIG_SND_DATE " (Fri Oct 04 13:09:13 2002 UTC)" #define CONFIG_SND_DATE " (Sun Oct 13 15:15:37 2002 UTC)"
...@@ -957,6 +957,19 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) ...@@ -957,6 +957,19 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
if (subs->interface >= 0 && subs->interface != fmt->iface) { if (subs->interface >= 0 && subs->interface != fmt->iface) {
usb_set_interface(subs->dev, subs->interface, 0); usb_set_interface(subs->dev, subs->interface, 0);
subs->interface = -1; subs->interface = -1;
subs->format = 0;
}
/* set interface */
if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) {
if (usb_set_interface(dev, fmt->iface, fmt->altset_idx) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
dev->devnum, fmt->iface, fmt->altsetting);
return -EIO;
}
snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altset_idx);
subs->interface = fmt->iface;
subs->format = fmt->altset_idx;
} }
/* create a data pipe */ /* create a data pipe */
...@@ -965,7 +978,6 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) ...@@ -965,7 +978,6 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
subs->datapipe = usb_sndisocpipe(dev, ep); subs->datapipe = usb_sndisocpipe(dev, ep);
else else
subs->datapipe = usb_rcvisocpipe(dev, ep); subs->datapipe = usb_rcvisocpipe(dev, ep);
subs->format = fmt->altset_idx;
subs->syncpipe = subs->syncinterval = 0; subs->syncpipe = subs->syncinterval = 0;
subs->maxpacksize = alts->endpoint[0].wMaxPacketSize; subs->maxpacksize = alts->endpoint[0].wMaxPacketSize;
subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize); subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
...@@ -998,15 +1010,6 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) ...@@ -998,15 +1010,6 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
subs->syncinterval = alts->endpoint[1].bRefresh; subs->syncinterval = alts->endpoint[1].bRefresh;
} }
/* set interface */
if (usb_set_interface(dev, fmt->iface, fmt->altset_idx) < 0) {
snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
dev->devnum, fmt->iface, fmt->altsetting);
return -EIO;
}
snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altset_idx);
subs->interface = fmt->iface;
ep = alts->endpoint[0].bEndpointAddress; ep = alts->endpoint[0].bEndpointAddress;
/* if endpoint has pitch control, enable it */ /* if endpoint has pitch control, enable it */
if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) {
...@@ -1171,6 +1174,7 @@ static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction, ...@@ -1171,6 +1174,7 @@ static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction,
snd_usb_substream_t *subs = &as->substream[direction]; snd_usb_substream_t *subs = &as->substream[direction];
subs->interface = -1; subs->interface = -1;
subs->format = 0;
runtime->hw = *hw; runtime->hw = *hw;
runtime->private_data = subs; runtime->private_data = subs;
subs->pcm_substream = substream; subs->pcm_substream = substream;
...@@ -1601,6 +1605,10 @@ static int parse_audio_format_type(struct usb_device *dev, int iface_no, int alt ...@@ -1601,6 +1605,10 @@ static int parse_audio_format_type(struct usb_device *dev, int iface_no, int alt
/* FIXME: correct endianess and sign? */ /* FIXME: correct endianess and sign? */
pcm_format = -1; pcm_format = -1;
switch (format) { switch (format) {
case 0: /* some devices don't define this correctly... */
snd_printd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
dev->devnum, iface_no, altno);
/* fall-through */
case USB_AUDIO_FORMAT_PCM: case USB_AUDIO_FORMAT_PCM:
/* check the format byte size */ /* check the format byte size */
switch (fmt[6]) { switch (fmt[6]) {
......
...@@ -55,6 +55,8 @@ struct usb_audio_term { ...@@ -55,6 +55,8 @@ struct usb_audio_term {
int name; int name;
}; };
struct usbmix_name_map;
struct usb_mixer_build { struct usb_mixer_build {
snd_usb_audio_t *chip; snd_usb_audio_t *chip;
unsigned char *buffer; unsigned char *buffer;
...@@ -62,6 +64,7 @@ struct usb_mixer_build { ...@@ -62,6 +64,7 @@ struct usb_mixer_build {
unsigned int ctrlif; unsigned int ctrlif;
DECLARE_BITMAP(unitbitmap, 32*32); DECLARE_BITMAP(unitbitmap, 32*32);
usb_audio_term_t oterm; usb_audio_term_t oterm;
const struct usbmix_name_map *map;
}; };
struct usb_mixer_elem_info { struct usb_mixer_elem_info {
...@@ -78,7 +81,8 @@ struct usb_mixer_elem_info { ...@@ -78,7 +81,8 @@ struct usb_mixer_elem_info {
enum { enum {
USB_FEATURE_MUTE = 0, USB_FEATURE_NONE = 0,
USB_FEATURE_MUTE = 1,
USB_FEATURE_VOLUME, USB_FEATURE_VOLUME,
USB_FEATURE_BASS, USB_FEATURE_BASS,
USB_FEATURE_MID, USB_FEATURE_MID,
...@@ -99,9 +103,71 @@ enum { ...@@ -99,9 +103,71 @@ enum {
USB_MIXER_U16, USB_MIXER_U16,
}; };
enum {
USB_PROC_UPDOWN = 1,
USB_PROC_UPDOWN_SWITCH = 1,
USB_PROC_UPDOWN_MODE_SEL = 2,
USB_PROC_PROLOGIC = 2,
USB_PROC_PROLOGIC_SWITCH = 1,
USB_PROC_PROLOGIC_MODE_SEL = 2,
USB_PROC_3DENH = 3,
USB_PROC_3DENH_SWITCH = 1,
USB_PROC_3DENH_SPACE = 2,
USB_PROC_REVERB = 4,
USB_PROC_REVERB_SWITCH = 1,
USB_PROC_REVERB_LEVEL = 2,
USB_PROC_REVERB_TIME = 3,
USB_PROC_REVERB_DELAY = 4,
USB_PROC_CHORUS = 5,
USB_PROC_CHORUS_SWITCH = 1,
USB_PROC_CHORUS_LEVEL = 2,
USB_PROC_CHORUS_RATE = 3,
USB_PROC_CHORUS_DEPTH = 4,
USB_PROC_DCR = 6,
USB_PROC_DCR_SWITCH = 1,
USB_PROC_DCR_RATIO = 2,
USB_PROC_DCR_MAX_AMP = 3,
USB_PROC_DCR_THRESHOLD = 4,
USB_PROC_DCR_ATTACK = 5,
USB_PROC_DCR_RELEASE = 6,
};
#define MAX_CHANNELS 10 /* max logical channels */ #define MAX_CHANNELS 10 /* max logical channels */
/*
* manual mapping of mixer names
* if the mixer topology is too complicated and the parsed names are
* ambiguous, add the entries in usbmixer_maps.c.
*/
#include "usbmixer_maps.c"
/* get the mapped name if the unit matches */
static int check_mapped_name(mixer_build_t *state, int unitid, int control, char *buf, int buflen)
{
const struct usbmix_name_map *p;
if (! state->map)
return 0;
for (p = state->map; p->id; p++) {
if (p->id == unitid &&
(! control || ! p->control || control == p->control)) {
buflen--;
strncpy(buf, p->name, buflen);
buf[buflen] = 0;
return strlen(buf);
}
}
return 0;
}
/* /*
* find an audio control unit with the given unit id * find an audio control unit with the given unit id
*/ */
...@@ -227,7 +293,7 @@ static int get_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int *value ...@@ -227,7 +293,7 @@ static int get_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int *value
/* channel = 0: master, 1 = first channel */ /* channel = 0: master, 1 = first channel */
inline static int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value) inline static int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value)
{ {
return get_ctl_value(cval, GET_CUR, ((cval->control + 1) << 8) | channel, value); return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value);
} }
/* /*
...@@ -256,7 +322,7 @@ static int set_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int value) ...@@ -256,7 +322,7 @@ static int set_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int value)
inline static int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value) inline static int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value)
{ {
return set_ctl_value(cval, SET_CUR, ((cval->control + 1) << 8) | channel, value); return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value);
} }
...@@ -291,7 +357,7 @@ static int add_control_to_empty(snd_card_t *card, snd_kcontrol_t *kctl) ...@@ -291,7 +357,7 @@ static int add_control_to_empty(snd_card_t *card, snd_kcontrol_t *kctl)
while (snd_ctl_find_id(card, &kctl->id)) while (snd_ctl_find_id(card, &kctl->id))
kctl->id.index++; kctl->id.index++;
if ((err = snd_ctl_add(card, kctl)) < 0) { if ((err = snd_ctl_add(card, kctl)) < 0) {
snd_printk(KERN_ERR "cannot add control\n"); snd_printd(KERN_ERR "cannot add control (err = %d)\n", err);
snd_ctl_free_one(kctl); snd_ctl_free_one(kctl);
} }
return err; return err;
...@@ -509,9 +575,9 @@ static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t ...@@ -509,9 +575,9 @@ static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t
break; break;
} }
} }
if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 || if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0) { get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, cval->control); snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, cval->control, cval->id);
return -EINVAL; return -EINVAL;
} }
cval->initialized = 1; cval->initialized = 1;
...@@ -534,7 +600,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t ...@@ -534,7 +600,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
if (cval->cmask & (1 << c)) { if (cval->cmask & (1 << c)) {
err = get_cur_mix_value(cval, c + 1, &val); err = get_cur_mix_value(cval, c + 1, &val);
if (err < 0) { if (err < 0) {
printk("cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err);
return err; return err;
} }
val = get_relative_value(cval, val); val = get_relative_value(cval, val);
...@@ -546,7 +612,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t ...@@ -546,7 +612,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
/* master channel */ /* master channel */
err = get_cur_mix_value(cval, 0, &val); err = get_cur_mix_value(cval, 0, &val);
if (err < 0) { if (err < 0) {
printk("cannot get current value for control %d master ch: err = %d\n", cval->control, err); snd_printd(KERN_ERR "cannot get current value for control %d master ch: err = %d\n", cval->control, err);
return err; return err;
} }
val = get_relative_value(cval, val); val = get_relative_value(cval, val);
...@@ -610,12 +676,14 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, ...@@ -610,12 +676,14 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
unsigned int ctl_mask, int control, unsigned int ctl_mask, int control,
usb_audio_term_t *iterm, int unitid) usb_audio_term_t *iterm, int unitid)
{ {
int len = 0; int len = 0, mapped_name = 0;
int nameid = desc[desc[0] - 1]; int nameid = desc[desc[0] - 1];
snd_kcontrol_t *kctl; snd_kcontrol_t *kctl;
usb_mixer_elem_info_t *cval; usb_mixer_elem_info_t *cval;
int minchn = 0; int minchn = 0;
control++; /* change from zero-based to 1-based value */
if (control == USB_FEATURE_GEQ) { if (control == USB_FEATURE_GEQ) {
/* FIXME: not supported yet */ /* FIXME: not supported yet */
return; return;
...@@ -623,7 +691,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, ...@@ -623,7 +691,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
if (! cval) { if (! cval) {
snd_printk(KERN_ERR "cannot malloc kcontrol"); snd_printk(KERN_ERR "cannot malloc kcontrol\n");
return; return;
} }
cval->chip = state->chip; cval->chip = state->chip;
...@@ -631,7 +699,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, ...@@ -631,7 +699,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
cval->id = unitid; cval->id = unitid;
cval->control = control; cval->control = control;
cval->cmask = ctl_mask; cval->cmask = ctl_mask;
cval->val_type = audio_feature_info[control].type; cval->val_type = audio_feature_info[control-1].type;
if (ctl_mask == 0) if (ctl_mask == 0)
cval->channels = 1; /* master channel */ cval->channels = 1; /* master channel */
else { else {
...@@ -651,22 +719,24 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, ...@@ -651,22 +719,24 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
cval->max = 1; cval->max = 1;
cval->initialized = 1; cval->initialized = 1;
} else { } else {
if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 || if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0) get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0)
snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, control); snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, control, unitid);
else else
cval->initialized = 1; cval->initialized = 1;
} }
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
if (! kctl) { if (! kctl) {
snd_printk(KERN_ERR "cannot malloc kcontrol"); snd_printk(KERN_ERR "cannot malloc kcontrol\n");
snd_magic_kfree(cval); snd_magic_kfree(cval);
return; return;
} }
kctl->private_free = usb_mixer_elem_free; kctl->private_free = usb_mixer_elem_free;
if (nameid) len = check_mapped_name(state, unitid, control, 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) { switch (control) {
...@@ -679,7 +749,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, ...@@ -679,7 +749,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
* - if the connected output can be determined, use it. * - if the connected output can be determined, use it.
* - otherwise, anonymous name. * - otherwise, anonymous name.
*/ */
if (! nameid) { if (! len) {
len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1); len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1);
if (! len) if (! len)
len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1); len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1);
...@@ -690,21 +760,26 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, ...@@ -690,21 +760,26 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
* if the connected output is USB stream, then it's likely a * if the connected output is USB stream, then it's likely a
* capture stream. otherwise it should be playback (hopefully :) * capture stream. otherwise it should be playback (hopefully :)
*/ */
if (! (state->oterm.type >> 16)) { if (! mapped_name && ! (state->oterm.type >> 16)) {
if ((state->oterm.type & 0xff00) == 0x0100) { if ((state->oterm.type & 0xff00) == 0x0100) {
if (len + 8 < sizeof(kctl->id.name)) {
strcpy(kctl->id.name + len, " Capture"); strcpy(kctl->id.name + len, " Capture");
len += 8; len += 8;
}
} else { } else {
if (len + 9 < sizeof(kctl->id.name)) {
strcpy(kctl->id.name + len, " Playback"); strcpy(kctl->id.name + len, " Playback");
len += 9; len += 9;
} }
} }
}
if (len + 7 < sizeof(kctl->id.name))
strcpy(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume"); strcpy(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume");
break; break;
default: default:
if (! nameid) if (! len)
strcpy(kctl->id.name, audio_feature_info[control].name); strcpy(kctl->id.name, audio_feature_info[control-1].name);
break; break;
} }
...@@ -797,7 +872,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, ...@@ -797,7 +872,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc,
cval->chip = state->chip; cval->chip = state->chip;
cval->ctrlif = state->ctrlif; cval->ctrlif = state->ctrlif;
cval->id = unitid; cval->id = unitid;
cval->control = in_ch; cval->control = in_ch + 1; /* based on 1 */
cval->val_type = USB_MIXER_S16; cval->val_type = USB_MIXER_S16;
for (i = 0; i < num_outs; i++) { for (i = 0; i < num_outs; i++) {
if (check_matrix_bitmap(desc + 9 + num_ins, in_ch, i, num_outs)) { if (check_matrix_bitmap(desc + 9 + num_ins, in_ch, i, num_outs)) {
...@@ -809,23 +884,26 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, ...@@ -809,23 +884,26 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc,
} }
/* get min/max values */ /* get min/max values */
if (get_ctl_value(cval, GET_MAX, ((in_ch+1) << 8) | minchn, &cval->max) < 0 || if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
get_ctl_value(cval, GET_MIN, ((in_ch+1) << 8) | minchn, &cval->min) < 0) get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0)
snd_printk(KERN_ERR "cannot get min/max values for mixer\n"); snd_printd(KERN_ERR "cannot get min/max values for mixer (id %d)\n", unitid);
else else
cval->initialized = 1; cval->initialized = 1;
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
if (! kctl) { if (! kctl) {
snd_printk(KERN_ERR "cannot malloc kcontrol"); snd_printk(KERN_ERR "cannot malloc kcontrol\n");
snd_magic_kfree(cval); snd_magic_kfree(cval);
return; return;
} }
kctl->private_free = usb_mixer_elem_free; kctl->private_free = usb_mixer_elem_free;
len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
if (! len)
len = get_term_name(state, &iterm, kctl->id.name, sizeof(kctl->id.name), 0); len = get_term_name(state, &iterm, kctl->id.name, sizeof(kctl->id.name), 0);
if (! len) if (! len)
len = sprintf(kctl->id.name, "Mixer Source %d", in_ch); len = sprintf(kctl->id.name, "Mixer Source %d", in_ch);
if (len + 7 < sizeof(kctl->id.name))
strcpy(kctl->id.name + len, " Volume"); strcpy(kctl->id.name + len, " Volume");
snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n", snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n",
...@@ -917,51 +995,51 @@ struct procunit_info { ...@@ -917,51 +995,51 @@ struct procunit_info {
}; };
static struct procunit_value_info updown_proc_info[] = { static struct procunit_value_info updown_proc_info[] = {
{ 0x01, "Switch", USB_MIXER_BOOLEAN }, { USB_PROC_UPDOWN_SWITCH, "Switch", USB_MIXER_BOOLEAN },
{ 0x02, "Mode Select", USB_MIXER_U8 }, { USB_PROC_UPDOWN_MODE_SEL, "Mode Select", USB_MIXER_U8 },
{ 0 } { 0 }
}; };
static struct procunit_value_info prologic_proc_info[] = { static struct procunit_value_info prologic_proc_info[] = {
{ 0x01, "Switch", USB_MIXER_BOOLEAN }, { USB_PROC_PROLOGIC_SWITCH, "Switch", USB_MIXER_BOOLEAN },
{ 0x02, "Mode Select", USB_MIXER_U8 }, { USB_PROC_PROLOGIC_MODE_SEL, "Mode Select", USB_MIXER_U8 },
{ 0 } { 0 }
}; };
static struct procunit_value_info threed_enh_proc_info[] = { static struct procunit_value_info threed_enh_proc_info[] = {
{ 0x01, "Switch", USB_MIXER_BOOLEAN }, { USB_PROC_3DENH_SWITCH, "Switch", USB_MIXER_BOOLEAN },
{ 0x02, "Spaciousness", USB_MIXER_U8 }, { USB_PROC_3DENH_SPACE, "Spaciousness", USB_MIXER_U8 },
{ 0 } { 0 }
}; };
static struct procunit_value_info reverb_proc_info[] = { static struct procunit_value_info reverb_proc_info[] = {
{ 0x01, "Switch", USB_MIXER_BOOLEAN }, { USB_PROC_REVERB_SWITCH, "Switch", USB_MIXER_BOOLEAN },
{ 0x02, "Level", USB_MIXER_U8 }, { USB_PROC_REVERB_LEVEL, "Level", USB_MIXER_U8 },
{ 0x03, "Time", USB_MIXER_U16 }, { USB_PROC_REVERB_TIME, "Time", USB_MIXER_U16 },
{ 0x04, "Delay", USB_MIXER_U8 }, { USB_PROC_REVERB_DELAY, "Delay", USB_MIXER_U8 },
{ 0 } { 0 }
}; };
static struct procunit_value_info chorus_proc_info[] = { static struct procunit_value_info chorus_proc_info[] = {
{ 0x01, "Switch", USB_MIXER_BOOLEAN }, { USB_PROC_CHORUS_SWITCH, "Switch", USB_MIXER_BOOLEAN },
{ 0x02, "Level", USB_MIXER_U8 }, { USB_PROC_CHORUS_LEVEL, "Level", USB_MIXER_U8 },
{ 0x03, "Rate", USB_MIXER_U16 }, { USB_PROC_CHORUS_RATE, "Rate", USB_MIXER_U16 },
{ 0x04, "Depth", USB_MIXER_U16 }, { USB_PROC_CHORUS_DEPTH, "Depth", USB_MIXER_U16 },
{ 0 } { 0 }
}; };
static struct procunit_value_info dcr_proc_info[] = { static struct procunit_value_info dcr_proc_info[] = {
{ 0x01, "Switch", USB_MIXER_BOOLEAN }, { USB_PROC_DCR_SWITCH, "Switch", USB_MIXER_BOOLEAN },
{ 0x02, "Ratio", USB_MIXER_U16 }, { USB_PROC_DCR_RATIO, "Ratio", USB_MIXER_U16 },
{ 0x03, "Max Amp", USB_MIXER_S16 }, { USB_PROC_DCR_MAX_AMP, "Max Amp", USB_MIXER_S16 },
{ 0x04, "Threshold", USB_MIXER_S16 }, { USB_PROC_DCR_THRESHOLD, "Threshold", USB_MIXER_S16 },
{ 0x05, "Attack Time", USB_MIXER_U16 }, { USB_PROC_DCR_ATTACK, "Attack Time", USB_MIXER_U16 },
{ 0x06, "Release Time", USB_MIXER_U16 }, { USB_PROC_DCR_RELEASE, "Release Time", USB_MIXER_U16 },
{ 0 } { 0 }
}; };
static struct procunit_info procunits[] = { static struct procunit_info procunits[] = {
{ 0x01, "Up Down", updown_proc_info }, { USB_PROC_UPDOWN, "Up Down", updown_proc_info },
{ 0x02, "Dolby Prologic", prologic_proc_info }, { USB_PROC_PROLOGIC, "Dolby Prologic", prologic_proc_info },
{ 0x03, "3D Stereo Extender", threed_enh_proc_info }, { USB_PROC_3DENH, "3D Stereo Extender", threed_enh_proc_info },
{ 0x04, "Reverb", reverb_proc_info }, { USB_PROC_REVERB, "Reverb", reverb_proc_info },
{ 0x05, "Chorus", chorus_proc_info }, { USB_PROC_CHORUS, "Chorus", chorus_proc_info },
{ 0x06, "DCR", dcr_proc_info }, { USB_PROC_DCR, "DCR", dcr_proc_info },
{ 0 }, { 0 },
}; };
...@@ -973,7 +1051,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char ...@@ -973,7 +1051,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
int num_ins = dsc[6]; int num_ins = dsc[6];
usb_mixer_elem_info_t *cval; usb_mixer_elem_info_t *cval;
snd_kcontrol_t *kctl; snd_kcontrol_t *kctl;
int i, err, nameid, type; int i, err, nameid, type, len;
struct procunit_info *info; struct procunit_info *info;
struct procunit_value_info *valinfo; struct procunit_value_info *valinfo;
static struct procunit_value_info default_value_info[] = { static struct procunit_value_info default_value_info[] = {
...@@ -985,7 +1063,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char ...@@ -985,7 +1063,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
}; };
if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) { if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) {
snd_printk(KERN_ERR "invalid %s descriptor %d\n", name, unitid); snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid);
return -EINVAL; return -EINVAL;
} }
...@@ -1010,7 +1088,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char ...@@ -1010,7 +1088,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
continue; continue;
cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
if (! cval) { if (! cval) {
snd_printk(KERN_ERR "cannot malloc kcontrol"); snd_printk(KERN_ERR "cannot malloc kcontrol\n");
return -ENOMEM; return -ENOMEM;
} }
cval->chip = state->chip; cval->chip = state->chip;
...@@ -1023,29 +1101,39 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char ...@@ -1023,29 +1101,39 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
/* get min/max values */ /* get min/max values */
if (get_ctl_value(cval, GET_MAX, cval->control, &cval->max) < 0 || if (get_ctl_value(cval, GET_MAX, cval->control, &cval->max) < 0 ||
get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0) get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0)
snd_printk(KERN_ERR "cannot get min/max values for proc/ext unit\n"); snd_printd(KERN_ERR "cannot get min/max values for proc/ext control=%d, id=%d\n", cval->control, unitid);
else if (cval->max <= cval->min)
snd_printd(KERN_ERR "invalid min/max values (%d/%d) for proc/ext unit control=%d, id=%d\n", cval->min, cval->max, cval->control, unitid);
else else
cval->initialized = 1; cval->initialized = 1;
kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
if (! kctl) { if (! kctl) {
snd_printk(KERN_ERR "cannot malloc kcontrol"); snd_printk(KERN_ERR "cannot malloc kcontrol\n");
snd_magic_kfree(cval); snd_magic_kfree(cval);
return -ENOMEM; return -ENOMEM;
} }
kctl->private_free = usb_mixer_elem_free; kctl->private_free = usb_mixer_elem_free;
if (info->name) if (check_mapped_name(state, unitid, cval->control, kctl->id.name, sizeof(kctl->id.name)))
sprintf(kctl->id.name, "%s %s", info->name, valinfo->suffix); ;
else if (info->name)
strcpy(kctl->id.name, info->name);
else { else {
nameid = dsc[12 + num_ins + dsc[11 + num_ins]]; nameid = dsc[12 + num_ins + dsc[11 + num_ins]];
if (nameid) { len = 0;
int len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); if (nameid)
strcpy(kctl->id.name + len, valinfo->suffix); len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
} else if (! len) {
sprintf(kctl->id.name, "%s %s", name, valinfo->suffix); strncpy(kctl->id.name, name, sizeof(kctl->id.name) - 1);
kctl->id.name[sizeof(kctl->id.name)-1] = 0;
}
}
len = strlen(kctl->id.name);
if (len + sizeof(valinfo->suffix) + 1 < sizeof(kctl->id.name)) {
kctl->id.name[len] = ' ';
strcpy(kctl->id.name + len + 1, valinfo->suffix);
} }
snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n", snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n",
cval->id, kctl->id.name, cval->channels, cval->min, cval->max); cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
if ((err = add_control_to_empty(state->chip->card, kctl)) < 0) if ((err = add_control_to_empty(state->chip->card, kctl)) < 0)
...@@ -1158,7 +1246,7 @@ static void usb_mixer_selector_elem_free(snd_kcontrol_t *kctl) ...@@ -1158,7 +1246,7 @@ static void usb_mixer_selector_elem_free(snd_kcontrol_t *kctl)
static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned char *desc) static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned char *desc)
{ {
int num_ins = desc[4]; int num_ins = desc[4];
int i, err, nameid; int i, err, nameid, len;
usb_mixer_elem_info_t *cval; usb_mixer_elem_info_t *cval;
snd_kcontrol_t *kctl; snd_kcontrol_t *kctl;
char **namelist; char **namelist;
...@@ -1175,7 +1263,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned ...@@ -1175,7 +1263,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
if (! cval) { if (! cval) {
snd_printk(KERN_ERR "cannot malloc kcontrol"); snd_printk(KERN_ERR "cannot malloc kcontrol\n");
return -ENOMEM; return -ENOMEM;
} }
cval->chip = state->chip; cval->chip = state->chip;
...@@ -1196,7 +1284,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned ...@@ -1196,7 +1284,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
#define MAX_ITEM_NAME_LEN 64 #define MAX_ITEM_NAME_LEN 64
for (i = 0; i < num_ins; i++) { for (i = 0; i < num_ins; i++) {
usb_audio_term_t iterm; usb_audio_term_t iterm;
int len = 0; len = 0;
namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL); namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
if (! namelist[i]) { if (! namelist[i]) {
snd_printk(KERN_ERR "cannot malloc\n"); snd_printk(KERN_ERR "cannot malloc\n");
...@@ -1214,7 +1302,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned ...@@ -1214,7 +1302,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
if (! kctl) { if (! kctl) {
snd_printk(KERN_ERR "cannot malloc kcontrol"); snd_printk(KERN_ERR "cannot malloc kcontrol\n");
snd_magic_kfree(cval); snd_magic_kfree(cval);
return -ENOMEM; return -ENOMEM;
} }
...@@ -1222,18 +1310,24 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned ...@@ -1222,18 +1310,24 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
kctl->private_free = usb_mixer_selector_elem_free; kctl->private_free = usb_mixer_selector_elem_free;
nameid = desc[desc[0] - 1]; nameid = desc[desc[0] - 1];
if (nameid) len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
if (len)
;
else if (nameid)
snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name)); snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
else { else {
int len = get_term_name(state, &state->oterm, len = get_term_name(state, &state->oterm,
kctl->id.name, sizeof(kctl->id.name), 0); kctl->id.name, sizeof(kctl->id.name), 0);
if (! len) if (! len)
len = sprintf(kctl->id.name, "USB"); len = sprintf(kctl->id.name, "USB");
if ((state->oterm.type & 0xff00) == 0x0100) if ((state->oterm.type & 0xff00) == 0x0100) {
if (len + 15 < sizeof(kctl->id.name))
strcpy(kctl->id.name + len, " Capture Source"); strcpy(kctl->id.name + len, " Capture Source");
else } else {
if (len + 16 < sizeof(kctl->id.name))
strcpy(kctl->id.name + len, " Playback Source"); strcpy(kctl->id.name + len, " Playback Source");
} }
}
snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n", snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
cval->id, kctl->id.name, num_ins); cval->id, kctl->id.name, num_ins);
...@@ -1290,6 +1384,8 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffe ...@@ -1290,6 +1384,8 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffe
unsigned char *desc; unsigned char *desc;
mixer_build_t state; mixer_build_t state;
int err; int err;
const struct usbmix_ctl_map *map;
struct usb_device_descriptor *dev = &chip->dev->descriptor;
strcpy(chip->card->mixername, "USB Mixer"); strcpy(chip->card->mixername, "USB Mixer");
...@@ -1298,6 +1394,15 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffe ...@@ -1298,6 +1394,15 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffe
state.buffer = buffer; state.buffer = buffer;
state.buflen = buflen; state.buflen = buflen;
state.ctrlif = ctrlif; state.ctrlif = ctrlif;
/* check the mapping table */
for (map = usbmix_ctl_maps; map->vendor; map++) {
if (map->vendor == dev->idVendor && map->product == dev->idProduct) {
state.map = map->map;
break;
}
}
desc = NULL; desc = NULL;
while ((desc = snd_usb_find_csint_desc(buffer, buflen, desc, OUTPUT_TERMINAL, ctrlif, -1)) != NULL) { while ((desc = snd_usb_find_csint_desc(buffer, buflen, desc, OUTPUT_TERMINAL, ctrlif, -1)) != NULL) {
if (desc[0] < 9) if (desc[0] < 9)
......
/*
* Additional mixer mapping
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
struct usbmix_name_map {
int id;
const char *name;
int control;
};
struct usbmix_ctl_map {
int vendor;
int product;
const struct usbmix_name_map *map;
};
/*
* USB control mappers for SB Exitigy
*/
/*
* Topology of SB Extigy (see on the wide screen :)
USB_IN[1] --->FU[2]------------------------------+->MU[16]-->PE[17]-+->FU[18]--+->EU[27]--+->EU[21]-->FU[22]--+->FU[23] > Dig_OUT[24]
^ | | | |
USB_IN[3] -+->SU[5]-->FU[6]--+->MU[14] ->PE[15]->+ | | | +->FU[25] > Dig_OUT[26]
^ ^ | | | |
Dig_IN[4] -+ | | | | +->FU[28]---------------------> Spk_OUT[19]
| | | |
Lin-IN[7] -+-->FU[8]---------+ | | +----------------------------------------> Hph_OUT[20]
| | |
Mic-IN[9] --+->FU[10]----------------------------+ |
|| |
|| +----------------------------------------------------+
VV V
++--+->SU[11]-->FU[12] --------------------------------------------------------------------------------------> USB_OUT[13]
*/
static struct usbmix_name_map extigy_map[] = {
/* 1: IT pcm */
{ 2, "PCM Playback" }, /* FU */
/* 3: IT pcm */
/* 4: IT digital in */
{ 5, "Digital In Playback Source" }, /* SU */
{ 6, "Digital In" }, /* FU */
/* 7: IT line */
{ 8, "Line Playback" }, /* FU */
/* 9: IT mic */
{ 10, "Mic Playback" }, /* FU */
{ 11, "Capture Source" }, /* SU */
{ 12, "Capture" }, /* FU */
/* 13: OT pcm capture */
/* 14: MU (w/o controls) */
/* 15: PE (3D enh) */
/* 16: MU (w/o controls) */
/* 17: PE (updown) */ /* FIXME: what control? */
{ 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */
{ 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */
{ 18, "Master Playback" }, /* FU; others */
/* 19: OT speaker */
/* 20: OT headphone */
{ 21, "Digital Out Extension" }, /* EU */ /* FIXME: what? */
{ 22, "Digital Out Playback" }, /* FU */
{ 23, "Digital Out1 Playback" }, /* FU */ /* FIXME: corresponds to 24 */
/* 24: OT digital out */
{ 25, "Digital Out2 Playback" }, /* FU */ /* FIXME: corresponds to 26 */
/* 26: OT digital out */
{ 27, "Output Extension" }, /* EU */ /* FIXME: what? */
/* 28: FU (mute) */
{ 0 } /* terminator */
};
/*
* Control map entries
*/
static struct usbmix_ctl_map usbmix_ctl_maps[] = {
{ 0x41e, 0x3000, extigy_map },
{ 0 } /* terminator */
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment