[alsa-devel] [FT C400,v2,RFC 0/8] M-Audio Fast Track C400
Hi,
The following patches adds support for the M-Audio Fast Track C400. I recently bought this device and I was able to get this work thanks to the discussion on the alsa-devel list.[1] Thanks everyone!
This series applies against the latest mainline tree, 3.7-rc4 (HEAD 3d70f8c617a436c7146ecb81df2265b4626dfe89 )
This is v2 of the series. It adds patches #3 and #8.
* 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.
* Audio in/out seems to work good in v2, mainly because the playback is an implicit feedback endpoint, and it is now configured with the capture endpoint as its sync-buddy (patch #8).
* Also needed to correct the calculation for implicit feedback, because of the different channel numbers between the two endpoints on the C400 (6 playback - implicit feedback, 4 capture) (patch #3).
* 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.
* Patches #6 and #7 are not ready to go in. Patch #6 employs a crude hack and misuses cmask. Patch #7 follows suit. I'd appreciate some hints on how to do this properly. (It's theoretically possible to use cmask properly, but cmasks must be a 64 bits type, because we have 10 input * 6 output = 60 paths).
Grant, I mentioned just now that I get distorted playback. This was caused by me using an lower bandwidth altsetting (2), which I tried since it gives only 2 playback channels, figuring it might help latency somehow. I now reverted back to the original alsetting (1) and I seem to get a stable latency and I can't hear anything unusual (yet?). jack_delay seems to be happy as well.
Cheers, Eldad
Cc: Grant Diffey gdiffey@gmail.com Cc: Chris Cavey chris-alsa@rauros.net Signed-off-by: Eldad Zack eldad@fogrefinery.com
Eldad Zack (8): usb-audio: skip UAC2 EFFECT_UNIT usb-audio: parameterize FTU effect unit control usb-audio: use sender stride for implicit feedback usb-audio: M-Audio Fast Track C400 quirks table usb-audio: Fast Track C400 mixer ranges usb-audio: Fast Track C400 mixer quirks usb-audio: add remaining C400 controls usb-audio: FT C400 sync playback EP to capture EP
sound/usb/endpoint.c | 9 ++- sound/usb/mixer.c | 55 +++++++++++++- sound/usb/mixer_quirks.c | 196 ++++++++++++++++++++++++++++++++++++++++++++-- sound/usb/pcm.c | 13 +++ sound/usb/quirks-table.h | 71 +++++++++++++++++ 5 files changed, 332 insertions(+), 12 deletions(-)
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 | 11 ++++++++++- 1 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 298070e..9bbc981 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -719,8 +719,17 @@ 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) { + /* Differnt structure. 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 ae2b714..6807b97 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -621,11 +621,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, @@ -660,9 +662,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; @@ -684,6 +685,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) @@ -714,10 +717,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; @@ -735,6 +736,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); @@ -779,7 +783,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, @@ -802,6 +807,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); @@ -960,9 +967,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;
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 7f78c6d..e2317da 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1021,15 +1021,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; }
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 */ {
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 9bbc981..5cddc0b 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -800,6 +800,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) {
This patch adds a specific mixer quirk for the M-Audio Fast Track C400 and creates volume controls. This is a hack to accomodate the unit ID semantics used by the device. I'd appreciate some hints on how to properly implement this because of the model used by this device (m*n).
Signed-off-by: Eldad Zack eldad@fogrefinery.com --- sound/usb/mixer.c | 8 ++++++++ sound/usb/mixer_quirks.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 5cddc0b..2fdfc8a 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -323,6 +323,10 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v int idx = 0, ret, size; __u8 bRequest;
+ /* Fast Track C400 Hack */ + if (cval->mixer->chip->usb_id == USB_ID(0x0763, 0x2030)) + validx = (validx & 0xff00) | cval->cmask; /* FIXME */ + if (request == UAC_GET_CUR) { bRequest = UAC2_CS_CUR; size = sizeof(__u16); @@ -432,6 +436,10 @@ 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;
+ /* Fast Track C400 Hack */ + if (cval->mixer->chip->usb_id == USB_ID(0x0763, 0x2030)) + validx = (validx & 0xff00) | cval->cmask; /* FIXME */ + 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_quirks.c b/sound/usb/mixer_quirks.c index 6807b97..f8ff7f6 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1013,6 +1013,48 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, } }
+/* M-Audio Fast Track C400 */ +static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer) +{ + char name[64]; + unsigned int cmask; + 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), + "DOut%d-AOut%d Playback Volume", + chan + 1, out + 1); + } else { + snprintf(name, sizeof(name), + "AIn%d-DOut%d Playback Volume", + chan - 5, out + 1); + } + cmask = out + chan * 6; /* FIXME: abuse of cmask. Sorry about that. */ + 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_mixer(struct usb_mixer_interface *mixer) +{ + int err = 0; + err = snd_c400_create_vol_ctls(mixer); + + return err; +} + /* * The mixer units for Ebox-44 are corrupt, and even where they * are valid they presents mono controls as L and R channels of @@ -1115,6 +1157,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);
Add the remaining controls for the M-Audio Fast Track C400: * 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 | 130 +++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 128 insertions(+), 2 deletions(-)
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index f8ff7f6..7d35d78 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1014,6 +1014,7 @@ 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]; @@ -1047,12 +1048,137 @@ static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer) 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; /* FIXME: abuse of cmask. Sorry about that. */ + 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] = { 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]; /* FIXME: abuse of cmask. Sorry about that. */ + 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_mixer(struct usb_mixer_interface *mixer) { - int err = 0; + int err; + err = snd_c400_create_vol_ctls(mixer); + if (err < 0) + return err;
- 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 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 37428f7..f35c5a4 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 (1)
-
Eldad Zack