[alsa-devel] [FT C400,PATCH RFC,v3 0/9] M-Audio Fast Track C400
Hi,
The following patches adds support for the M-Audio Fast Track C400. This is version 3 of the patch series.
v2: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-November/056750.ht...
This series applies against the latest mainline tree, 3.7-rc7 (HEAD 2844a48706e54ddda4a04269dba4250b42f449de )
* First of all, everything works: mixer controls, effects control, playback and capture (with the correct sampling frequency set). Note that the device doesn't really have a master control or left/right pan. It's just "emulated" in the software.
* To deal with the mixer channel IDs, limited by cmask bit length, I introduced an offset value. This allows to get rid of the shorehorning of the IDs into cmask and device-referencing in the low-level functions. Is this a good approach?
* Audio in/out seems to work good in this series, mainly because the playback is an implicit feedback endpoint, and it is now configured with the capture endpoint as its sync-buddy. Also needed to correct the calculation, because of the different channel numbers between the two endpoints on the C400 (6 playback - implicit feedback, 4 capture).
* The clock sources are still named "Unit 129" (0x81, internal) and "Unit 130" (0x82, SPDIF). I'll fix this later. I also plan to fix the channel names (i.e., instead of "AIn3", use "SPDIF In L").
* I've touched one FTU mixer creation function. I'd appreciate if someone can test this with the FTU to make sure I didn't break something. (I haven't gotten any feedback on that one yet)
Cheers, Eldad
Signed-off-by: Eldad Zack eldad@fogrefinery.com
Eldad Zack (9): usb-audio: replace hardcoded value with const usb-audio: use sender stride for implicit feedback usb-audio: add control index offset usb-audio: skip UAC2 EFFECT_UNIT usb-audio: parameterize FTU effect unit control usb-audio: M-Audio Fast Track C400 quirks table usb-audio: Fast Track C400 mixer ranges usb-audio: Fast Track C400 mixer controls usb-audio: FT C400 sync playback EP to capture EP
sound/usb/endpoint.c | 9 ++- sound/usb/mixer.c | 53 +++++++++++- sound/usb/mixer.h | 1 + sound/usb/mixer_quirks.c | 216 ++++++++++++++++++++++++++++++++++++++++++++-- sound/usb/pcm.c | 15 +++- sound/usb/quirks-table.h | 71 +++++++++++++++ 6 files changed, 351 insertions(+), 14 deletions(-)
In this context, 0x01 is USB_ENDPOINT_XFER_ISOC.
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/pcm.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ef6fa24..ff8cbbf 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -381,7 +381,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* ... and check descriptor size before accessing bSynchAddress because there is a version of the SB Audigy 2 NX firmware lacking the audio fields in the endpoint descriptors */ - if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 || + if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && get_endpoint(alts, 1)->bSynchAddress != 0 && !implicit_fb)) {
For implicit feedback endpoints, the number of bytes for each packet is matched by the corresponding synchronizing endpoint. The size is calculated by taking the actual size and dividing it by the stride - currently by the endpoint's stride, but we should use the synchronization source's stride. This is evident when the number of channels differ between the synchornization source and the implictly fed-back endpoint, as with M-Audio Fast Track C400 - the synchonization source (capture) has 4 channels, while the implicit feedback mode endpoint has 6.
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/endpoint.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 34de6f2..72e711c 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1034,15 +1034,18 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, /* * Iterate through the inbound packet and prepare the lengths * for the output packet. The OUT packet we are about to send - * will have the same amount of payload bytes than the IN - * packet we just received. + * will have the same amount of payload bytes per stride as the + * IN packet we just received. Since the actual size is scaled + * by the stride, use the sender stride to calculate the length + * in case the number of channels differ between the implicitly + * fed-back endpoint and the synchronizing endpoint. */
out_packet->packets = in_ctx->packets; for (i = 0; i < in_ctx->packets; i++) { if (urb->iso_frame_desc[i].status == 0) out_packet->packet_size[i] = - urb->iso_frame_desc[i].actual_length / ep->stride; + urb->iso_frame_desc[i].actual_length / sender->stride; else out_packet->packet_size[i] = 0; }
Currently, channel IDs exceeding 31 (0x1f) cannot be used. The channel ID is derived from the cmask. Extending cmask to a 64-bit type would only allow it to go up to 63 (0x2f). Some devices have channel IDs exceeding that as well. To address that, add an offset to the mixer element which is then accounted for in the UAC set/get functions.
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/mixer.c | 4 ++++ sound/usb/mixer.h | 1 + sound/usb/mixer_quirks.c | 16 +++++++++++++++- 3 files changed, 20 insertions(+), 1 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 298070e..b0fc6ae 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -382,6 +382,8 @@ error:
static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { + validx += cval->idx_off; + return (cval->mixer->protocol == UAC_VERSION_1) ? get_ctl_value_v1(cval, request, validx, value_ret) : get_ctl_value_v2(cval, request, validx, value_ret); @@ -432,6 +434,8 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, unsigned char buf[2]; int idx = 0, val_len, err, timeout = 10;
+ validx += cval->idx_off; + if (cval->mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; } else { /* UAC_VERSION_2 */ diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index a7f3d45..aab80df 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -43,6 +43,7 @@ struct usb_mixer_elem_info { unsigned int id; unsigned int control; /* CS or ICN (high byte) */ unsigned int cmask; /* channel mask bitmap: 0 = master */ + unsigned int idx_off; /* Control index offset */ unsigned int ch_readonly; unsigned int master_readonly; int channels; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index ae2b714..4199b97 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -63,11 +63,12 @@ static void usb_mixer_elem_free(struct snd_kcontrol *kctl) * Since there doesn't seem to be a devices that needs a multichannel * version, we keep it mono for simplicity. */ -static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, +static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, unsigned int unitid, unsigned int control, unsigned int cmask, int val_type, + unsigned int idx_off, const char *name, snd_kcontrol_tlv_rw_t *tlv_callback) { @@ -85,6 +86,7 @@ static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, cval->channels = 1; cval->control = control; cval->cmask = cmask; + cval->idx_off = idx_off;
/* get_min_max() is called only for integer volumes later, * so provide a short-cut for booleans */ @@ -120,6 +122,18 @@ static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, return 0; }
+static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, + unsigned int unitid, + unsigned int control, + unsigned int cmask, + int val_type, + const char *name, + snd_kcontrol_tlv_rw_t *tlv_callback) +{ + return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask, + val_type, 0 /* Offset */, name, tlv_callback); +} + /* * Create a set of standard UAC controls from a table */
Current code mishandles the case where the device is a UAC2 and the bDescriptorSubtype is a UAC2 Effect Unit (0x07). It tries to parse it as a Processing Unit (which is similar to two other UAC1 units with overlapping subtypes), but since the structure is different (See: 4.7.2.10, 4.7.2.11 in UAC2 standard), the parsing is done incorrectly and prevents the device from initializing. For now, just ignore the unit.
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/mixer.c | 13 ++++++++++++- 1 files changed, 12 insertions(+), 1 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index b0fc6ae..4eacbe2 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -723,8 +723,19 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_ return 0; } case UAC1_PROCESSING_UNIT: - case UAC1_EXTENSION_UNIT: { + case UAC1_EXTENSION_UNIT: + /* UAC2_PROCESSING_UNIT_V2 */ + /* UAC2_EFFECT_UNIT */ { struct uac_processing_unit_descriptor *d = p1; + + if (state->mixer->protocol == UAC_VERSION_2 && + hdr[2] == UAC2_EFFECT_UNIT) { + /* UAC2/UAC1 unit IDs overlap here in an + * uncompatible way. Ignore this unit for now. + */ + return 0; + } + if (d->bNrInPins) { id = d->baSourceID[0]; break; /* continue to parse */
Adds the unit ID and the control as parameters to the creation of the effect unit control for the M-Audio Fast Track Ultra. This allows the code to be shared with other devices that use different unit ID and control, such as the M-Audio Fast Track C400.
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/mixer_quirks.c | 24 ++++++++++++++++-------- 1 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 4199b97..a614dab 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -635,11 +635,13 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, }
/* M-Audio FastTrack Ultra quirks */ -/* FTU Effect switch */ +/* FTU Effect switch (also used by C400) */ struct snd_ftu_eff_switch_priv_val { struct usb_mixer_interface *mixer; int cached_value; int is_cached; + int bUnitID; + int validx; };
static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, @@ -674,9 +676,8 @@ static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, struct snd_ftu_eff_switch_priv_val *pval; int err; unsigned char value[2]; + int id, validx;
- const int id = 6; - const int validx = 1; const int val_len = 2;
value[0] = 0x00; @@ -698,6 +699,8 @@ static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, if (snd_BUG_ON(!chip)) return -EINVAL;
+ id = pval->bUnitID; + validx = pval->validx;
down_read(&mixer->chip->shutdown_rwsem); if (mixer->chip->shutdown) @@ -728,10 +731,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, struct usb_mixer_interface *mixer; int changed, cur_val, err, new_val; unsigned char value[2]; + int id, validx;
- - const int id = 6; - const int validx = 1; const int val_len = 2;
changed = 0; @@ -749,6 +750,9 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, if (snd_BUG_ON(!chip)) return -EINVAL;
+ id = pval->bUnitID; + validx = pval->validx; + if (!pval->is_cached) { /* Read current value */ down_read(&mixer->chip->shutdown_rwsem); @@ -793,7 +797,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, return changed; }
-static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer) +static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, + int validx, int bUnitID) { static struct snd_kcontrol_new template = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -816,6 +821,8 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer) pval->cached_value = 0; pval->is_cached = 0; pval->mixer = mixer; + pval->bUnitID = bUnitID; + pval->validx = validx;
template.private_value = (unsigned long) pval; kctl = snd_ctl_new1(&template, mixer->chip); @@ -974,9 +981,10 @@ static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer) if (err < 0) return err;
- err = snd_ftu_create_effect_switch(mixer); + err = snd_ftu_create_effect_switch(mixer, 1, 6); if (err < 0) return err; + err = snd_ftu_create_effect_volume_ctl(mixer); if (err < 0) return err;
Adds a quirks table for the M-Audio Fast Track C400.
Based on the following patch from the alsa-devel list: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-May/051676.html
See also: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-April/051219.html
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/quirks-table.h | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 71 insertions(+), 0 deletions(-)
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 88d8ceb..7ec7b7f 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2296,6 +2296,77 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{ + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2030), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + /* .vendor_name = "M-Audio", */ + /* .product_name = "Fast Track C400", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = &(const struct snd_usb_audio_quirk[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_MIXER, + }, + /* Playback */ + { + .ifnum = 2, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 6, + .iface = 2, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x01, + .ep_attr = 0x09, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 4, + .rate_table = (unsigned int[]) { + 44100, 48000, 88200, 96000 + }, + .clock = 0x81, + } + }, + /* Capture */ + { + .ifnum = 3, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 4, + .iface = 3, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x81, + .ep_attr = 0x05, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 4, + .rate_table = (unsigned int[]) { + 44100, 48000, 88200, 96000 + }, + .clock = 0x81, + } + }, + /* MIDI */ + { + .ifnum = -1 /* Interface = 4 */ + } + } + } +},
/* Casio devices */ {
Eldad Zack wrote:
Adds a quirks table for the M-Audio Fast Track C400.
+++ b/sound/usb/quirks-table.h @@ -2296,6 +2296,77 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{
- USB_DEVICE_VENDOR_SPEC(0x0763, 0x2030),
The table should be sorted.
Regards, Clemens
On Tue, 27 Nov 2012, Clemens Ladisch wrote:
Eldad Zack wrote:
Adds a quirks table for the M-Audio Fast Track C400.
+++ b/sound/usb/quirks-table.h @@ -2296,6 +2296,77 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{
- USB_DEVICE_VENDOR_SPEC(0x0763, 0x2030),
The table should be sorted.
Thanks! Fixed locally (I will post a v4 later).
Cheers, Eldad
At Tue, 27 Nov 2012 17:44:01 +0100 (CET), Eldad Zack wrote:
On Tue, 27 Nov 2012, Clemens Ladisch wrote:
Eldad Zack wrote:
Adds a quirks table for the M-Audio Fast Track C400.
+++ b/sound/usb/quirks-table.h @@ -2296,6 +2296,77 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +{
- USB_DEVICE_VENDOR_SPEC(0x0763, 0x2030),
The table should be sorted.
Thanks! Fixed locally (I will post a v4 later).
It'd be helpful if you can post in this week for merging to 3.8 kernel. Otherwise I'll have to postpone the merge for 3.9.
thanks,
Takashi
Add ranges for various Fast Track C400 controls, as observed while using the vendor's mixer control software (res values are an estimation).
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/mixer.c | 36 ++++++++++++++++++++++++++++++++++++ 1 files changed, 36 insertions(+), 0 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 4eacbe2..1ae0ff0 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -806,6 +806,42 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl) { switch (cval->mixer->chip->usb_id) { + case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + if (strcmp(kctl->id.name, "Effect Duration") == 0) { + snd_printk(KERN_INFO + "usb-audio: set quirk for C400 Effect Duration\n"); + cval->min = 0x0000; + cval->max = 0xffff; + cval->res = 0x00e6; + break; + } + if (strcmp(kctl->id.name, "Effect Volume") == 0 || + strcmp(kctl->id.name, "Effect Feedback Volume") == 0) { + snd_printk(KERN_INFO + "usb-audio: set quirks for C400 Effect Feedback/Volume\n"); + cval->min = 0x00; + cval->max = 0xff; + break; + } + if (strstr(kctl->id.name, "Effect Return") != NULL) { + snd_printk(KERN_INFO + "usb-audio: set quirks for C400 %s\n", kctl->id.name); + cval->min = 0xb706; + cval->max = 0xff7b; + cval->res = 0x0073; + break; + } + if ((strstr(kctl->id.name, "Playback Volume") != NULL) || + (strstr(kctl->id.name, "Effect Send") != NULL)) { + snd_printk(KERN_INFO + "usb-audio: set quirk for Fast Track C400 %s\n", + kctl->id.name); + cval->min = 0xb5fb; /* -73 dB = 0xb6ff */ + cval->max = 0xfcfe; + cval->res = 0x0073; + } + break; + case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ if (strcmp(kctl->id.name, "Effect Duration") == 0) {
Eldad Zack wrote:
Add ranges for various Fast Track C400 controls, as observed while using the vendor's mixer control software (res values are an estimation).
snd_printk(KERN_INFO
"usb-audio: set quirk for C400 Effect Duration\n");
snd_printk(KERN_INFO
"usb-audio: set quirks for C400 Effect Feedback/Volume\n");
snd_printk(KERN_INFO
"usb-audio: set quirks for C400 %s\n", kctl->id.name);
snd_printk(KERN_INFO
"usb-audio: set quirk for Fast Track C400 %s\n",
kctl->id.name);
Are these messages really needed?
Regards, Clemens
On Tue, 27 Nov 2012, Clemens Ladisch wrote:
Eldad Zack wrote:
Add ranges for various Fast Track C400 controls, as observed while using the vendor's mixer control software (res values are an estimation).
snd_printk(KERN_INFO
"usb-audio: set quirk for C400 Effect Duration\n");
snd_printk(KERN_INFO
"usb-audio: set quirks for C400 Effect Feedback/Volume\n");
snd_printk(KERN_INFO
"usb-audio: set quirks for C400 %s\n", kctl->id.name);
snd_printk(KERN_INFO
"usb-audio: set quirk for Fast Track C400 %s\n",
kctl->id.name);
Are these messages really needed?
I followed the example of ftu code and didn't give it much thought, but now that I think about it, they are just logspam. I'll remove it for v4. Thanks!
Cheers, Eldad
Add a mixer quirks for the M-Audio Fast Track C400 and create the following:
* Volume controls * Effect Type (reusing FTU controls) * Effect Volume * Effect Send/Return * Effect Program * Effect Feedback
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/mixer_quirks.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 176 insertions(+), 0 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index a614dab..05404e5 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1027,6 +1027,178 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, } }
+/* M-Audio Fast Track C400 */ +/* C400 volume controls, this control needs a volume quirk, see mixer.c */ +static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer) +{ + char name[64]; + unsigned int cmask, offset; + int out, chan, err; + + const unsigned int id = 0x40; + const int val_type = USB_MIXER_S16; + const int control = 1; + + for (chan = 0; chan < 10; chan++) { + for (out = 0; out < 6; out++) { + if (chan < 6) { + snprintf(name, sizeof(name), + "PCM%d-Out%d Playback Volume", + chan + 1, out + 1); + } else { + snprintf(name, sizeof(name), + "In%d-Out%d Playback Volume", + chan - 5, out + 1); + } + + cmask = (out == 0) ? 0 : 1 << (out - 1); + offset = chan * 6; + err = snd_create_std_mono_ctl_offset(mixer, id, control, + cmask, val_type, offset, name, + &snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + } + + return 0; +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Volume"; + const unsigned int id = 0x43; + const int val_type = USB_MIXER_U8; + const unsigned int control = 3; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, snd_usb_mixer_vol_tlv); +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Duration"; + const unsigned int id = 0x43; + const int val_type = USB_MIXER_S16; + const unsigned int control = 4; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, snd_usb_mixer_vol_tlv); +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Feedback Volume"; + const unsigned int id = 0x43; + const int val_type = USB_MIXER_U8; + const unsigned int control = 5; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, NULL); +} + +static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer) +{ + char name[64]; + unsigned int cmask; + int chan, err; + + const unsigned int id = 0x42; + const int val_type = USB_MIXER_S16; + const int control = 1; + + for (chan = 0; chan < 10; chan++) { + if (chan < 6) { + snprintf(name, sizeof(name), + "Effect Send DOut%d", + chan + 1); + } else { + snprintf(name, sizeof(name), + "Effect Send AIn%d", + chan - 5); + } + + cmask = (chan == 0) ? 0 : 1 << (chan - 1); + err = snd_create_std_mono_ctl(mixer, id, control, + cmask, val_type, name, + &snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + + return 0; +} + +static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer) +{ + char name[64]; + unsigned int cmask; + int chan, err; + + const unsigned int id = 0x40; + const int val_type = USB_MIXER_S16; + const int control = 1; + const int chan_id[6] = { 0, 7, 2, 9, 4, 0xb }; + const unsigned int offset = 0x3c; + /* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */ + + for (chan = 0; chan < 6; chan++) { + snprintf(name, sizeof(name), + "Effect Return %d", + chan + 1); + + cmask = (chan_id[chan] == 0) ? 0 : 1 << (chan_id[chan] - 1); + err = snd_create_std_mono_ctl_offset(mixer, id, control, + cmask, val_type, offset, name, + &snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + + return 0; +} + +static int snd_c400_create_mixer(struct usb_mixer_interface *mixer) +{ + int err; + + err = snd_c400_create_vol_ctls(mixer); + if (err < 0) + return err; + + err = snd_c400_create_effect_vol_ctls(mixer); + if (err < 0) + return err; + + err = snd_c400_create_effect_ret_vol_ctls(mixer); + if (err < 0) + return err; + + err = snd_ftu_create_effect_switch(mixer, 2, 0x43); + if (err < 0) + return err; + + err = snd_c400_create_effect_volume_ctl(mixer); + if (err < 0) + return err; + + err = snd_c400_create_effect_duration_ctl(mixer); + if (err < 0) + return err; + + err = snd_c400_create_effect_feedback_ctl(mixer); + if (err < 0) + return err; + + return 0; +} + /* * The mixer units for Ebox-44 are corrupt, and even where they * are valid they presents mono controls as L and R channels of @@ -1129,6 +1301,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_ftu_create_mixer(mixer); break;
+ case USB_ID(0x0763, 0x2030): /* M-Audio C400 */ + err = snd_c400_create_mixer(mixer); + break; + case USB_ID(0x0b05, 0x1739): case USB_ID(0x0b05, 0x1743): err = snd_xonar_u1_controls_create(mixer);
The playback endpoint uses implicit feedback mode, similiar to the M-Audio FTU. Like with the FTU, we need to associate the sync pipe ourselves.
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/pcm.c | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ff8cbbf..4813cdc 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -372,6 +372,19 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) alts = &iface->altsetting[1]; goto add_sync_ep; } + break; + case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + if (is_playback) { + implicit_fb = 1; + ep = 0x81; + iface = usb_ifnum_to_if(dev, 3); + + if (!iface || iface->num_altsetting == 0) + return -EINVAL; + + alts = &iface->altsetting[1]; + goto add_sync_ep; + } }
if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
participants (3)
-
Clemens Ladisch
-
Eldad Zack
-
Takashi Iwai