Messages in this thread | | | Subject | Re: [PATCH v2 7/7] ALSA: usb: add UAC3 BADD profiles support | From | Jorge <> | Date | Fri, 11 May 2018 16:36:36 +0100 |
| |
On 04/05/18 02:24, Ruslan Bilovol wrote: > Recently released USB Audio Class 3.0 specification > contains BADD (Basic Audio Device Definition) document > which describes pre-defined UAC3 configurations. > > BADD support is mandatory for UAC3 devices, it should be > implemented as a separate USB device configuration. > As per BADD document, class-specific descriptors > shall not be included in the Device’s Configuration > descriptor ("inferred"), but host can guess them > from BADD profile number, number of endpoints and > their max packed sizes. > > This patch adds support of all BADD profiles from the spec > > Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com>
Tested-by: Jorge Sanjuan <jorge.sanjuan@codethink.co.uk>
> --- > sound/usb/card.c | 14 +++ > sound/usb/clock.c | 9 +- > sound/usb/mixer.c | 327 ++++++++++++++++++++++++++++++++++++++++++++----- > sound/usb/mixer_maps.c | 65 ++++++++++ > sound/usb/stream.c | 83 +++++++++++-- > sound/usb/usbaudio.h | 2 + > 6 files changed, 459 insertions(+), 41 deletions(-) > > diff --git a/sound/usb/card.c b/sound/usb/card.c > index 0d7a5d7..f6c3c1c 100644 > --- a/sound/usb/card.c > +++ b/sound/usb/card.c > @@ -307,6 +307,20 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) > return -EINVAL; > } > > + if (protocol == UAC_VERSION_3) { > + int badd = assoc->bFunctionSubClass; > + > + if (badd != UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 && > + (badd < UAC3_FUNCTION_SUBCLASS_GENERIC_IO || > + badd > UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE)) { > + dev_err(&dev->dev, > + "Unsupported UAC3 BADD profile\n"); > + return -EINVAL; > + } > + > + chip->badd_profile = badd; > + } > + > for (i = 0; i < assoc->bInterfaceCount; i++) { > int intf = assoc->bFirstInterface + i; > > diff --git a/sound/usb/clock.c b/sound/usb/clock.c > index 0b030d8..17673f3 100644 > --- a/sound/usb/clock.c > +++ b/sound/usb/clock.c > @@ -587,8 +587,15 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, > default: > return set_sample_rate_v1(chip, iface, alts, fmt, rate); > > - case UAC_VERSION_2: > case UAC_VERSION_3: > + if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { > + if (rate != UAC3_BADD_SAMPLING_RATE) > + return -ENXIO; > + else > + return 0; > + } > + /* fall through */ > + case UAC_VERSION_2: > return set_sample_rate_v2v3(chip, iface, alts, fmt, rate); > } > } > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index e280354..d98bc3f 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -112,14 +112,12 @@ enum { > #include "mixer_maps.c" > > static const struct usbmix_name_map * > -find_map(struct mixer_build *state, int unitid, int control) > +find_map(const struct usbmix_name_map *p, int unitid, int control) > { > - const struct usbmix_name_map *p = state->map; > - > if (!p) > return NULL; > > - for (p = state->map; p->id; p++) { > + for (; p->id; p++) { > if (p->id == unitid && > (!control || !p->control || control == p->control)) > return p; > @@ -1333,16 +1331,16 @@ static struct usb_feature_control_info *get_feature_control_info(int control) > return NULL; > } > > -static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > - unsigned int ctl_mask, int control, > - struct usb_audio_term *iterm, int unitid, > - int readonly_mask) > +static void __build_feature_ctl(struct usb_mixer_interface *mixer, > + const struct usbmix_name_map *imap, > + unsigned int ctl_mask, int control, > + struct usb_audio_term *iterm, > + struct usb_audio_term *oterm, > + int unitid, int nameid, int readonly_mask) > { > - struct uac_feature_unit_descriptor *desc = raw_desc; > struct usb_feature_control_info *ctl_info; > unsigned int len = 0; > int mapped_name = 0; > - int nameid = uac_feature_unit_iFeature(desc); > struct snd_kcontrol *kctl; > struct usb_mixer_elem_info *cval; > const struct usbmix_name_map *map; > @@ -1353,14 +1351,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > return; > } > > - map = find_map(state, unitid, control); > + map = find_map(imap, unitid, control); > if (check_ignored_ctl(map)) > return; > > cval = kzalloc(sizeof(*cval), GFP_KERNEL); > if (!cval) > return; > - snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); > + snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); > cval->control = control; > cval->cmask = ctl_mask; > > @@ -1369,7 +1367,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > kfree(cval); > return; > } > - if (state->mixer->protocol == UAC_VERSION_1) > + if (mixer->protocol == UAC_VERSION_1) > cval->val_type = ctl_info->type; > else /* UAC_VERSION_2 */ > cval->val_type = ctl_info->type_uac2 >= 0 ? > @@ -1398,7 +1396,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); > > if (!kctl) { > - usb_audio_err(state->chip, "cannot malloc kcontrol\n"); > + usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); > kfree(cval); > return; > } > @@ -1407,7 +1405,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > 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->chip, nameid, > + len = snd_usb_copy_string_desc(mixer->chip, nameid, > kctl->id.name, sizeof(kctl->id.name)); > > switch (control) { > @@ -1422,10 +1420,12 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > * - otherwise, anonymous name. > */ > if (!len) { > - len = get_term_name(state->chip, iterm, kctl->id.name, > - sizeof(kctl->id.name), 1); > - if (!len) > - len = get_term_name(state->chip, &state->oterm, > + if (iterm) > + len = get_term_name(mixer->chip, iterm, > + kctl->id.name, > + sizeof(kctl->id.name), 1); > + if (!len && oterm) > + len = get_term_name(mixer->chip, oterm, > kctl->id.name, > sizeof(kctl->id.name), 1); > if (!len) > @@ -1434,15 +1434,15 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > } > > if (!mapped_name) > - check_no_speaker_on_headset(kctl, state->mixer->chip->card); > + check_no_speaker_on_headset(kctl, mixer->chip->card); > > /* > * determine the stream direction: > * if the connected output is USB stream, then it's likely a > * capture stream. otherwise it should be playback (hopefully :) > */ > - if (!mapped_name && !(state->oterm.type >> 16)) { > - if ((state->oterm.type & 0xff00) == 0x0100) > + if (!mapped_name && oterm && !(oterm->type >> 16)) { > + if ((oterm->type & 0xff00) == 0x0100) > append_ctl_name(kctl, " Capture"); > else > append_ctl_name(kctl, " Playback"); > @@ -1470,7 +1470,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > } > } > > - snd_usb_mixer_fu_apply_quirk(state->mixer, cval, unitid, kctl); > + snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl); > > range = (cval->max - cval->min) / cval->res; > /* > @@ -1479,21 +1479,41 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > * devices. It will definitively catch all buggy Logitech devices. > */ > if (range > 384) { > - usb_audio_warn(state->chip, > + usb_audio_warn(mixer->chip, > "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.", > range); > - usb_audio_warn(state->chip, > + usb_audio_warn(mixer->chip, > "[%d] FU [%s] ch = %d, val = %d/%d/%d", > cval->head.id, kctl->id.name, cval->channels, > cval->min, cval->max, cval->res); > } > > - usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", > + usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", > cval->head.id, kctl->id.name, cval->channels, > cval->min, cval->max, cval->res); > snd_usb_mixer_add_control(&cval->head, kctl); > } > > +static void build_feature_ctl(struct mixer_build *state, void *raw_desc, > + unsigned int ctl_mask, int control, > + struct usb_audio_term *iterm, int unitid, > + int readonly_mask) > +{ > + struct uac_feature_unit_descriptor *desc = raw_desc; > + int nameid = uac_feature_unit_iFeature(desc); > + > + __build_feature_ctl(state->mixer, state->map, ctl_mask, control, > + iterm, &state->oterm, unitid, nameid, readonly_mask); > +} > + > +static void build_feature_ctl_badd(struct usb_mixer_interface *mixer, > + unsigned int ctl_mask, int control, int unitid, > + const struct usbmix_name_map *badd_map) > +{ > + __build_feature_ctl(mixer, badd_map, ctl_mask, control, > + NULL, NULL, unitid, 0, 0); > +} > + > static void get_connector_control_name(struct mixer_build *state, > struct usb_audio_term *term, > bool is_input, char *name, int name_size) > @@ -1807,7 +1827,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, > struct snd_kcontrol *kctl; > const struct usbmix_name_map *map; > > - map = find_map(state, unitid, 0); > + map = find_map(state->map, unitid, 0); > if (check_ignored_ctl(map)) > return; > > @@ -2106,7 +2126,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, > > if (!(controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1)))) > continue; > - map = find_map(state, unitid, valinfo->control); > + map = find_map(state->map, unitid, valinfo->control); > if (check_ignored_ctl(map)) > continue; > cval = kzalloc(sizeof(*cval), GFP_KERNEL); > @@ -2310,7 +2330,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, > if (desc->bNrInPins == 1) /* only one ? nonsense! */ > return 0; > > - map = find_map(state, unitid, 0); > + map = find_map(state->map, unitid, 0); > if (check_ignored_ctl(map)) > return 0; > > @@ -2497,6 +2517,246 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) > return 0; > } > > +/* UAC3 predefined channels configuration */ > +struct uac3_badd_profile { > + int subclass; > + const char *name; > + int c_chmask; /* capture channels mask */ > + int p_chmask; /* playback channels mask */ > + int st_chmask; /* side tone mixing channel mask */ > +}; > + > +static struct uac3_badd_profile uac3_badd_profiles[] = { > + { > + /* > + * BAIF, BAOF or combination of both > + * IN: Mono or Stereo cfg, Mono alt possible > + * OUT: Mono or Stereo cfg, Mono alt possible > + */ > + .subclass = UAC3_FUNCTION_SUBCLASS_GENERIC_IO, > + .name = "GENERIC IO", > + .c_chmask = -1, /* dynamic channels */ > + .p_chmask = -1, /* dynamic channels */ > + }, > + { > + /* BAOF; Stereo only cfg, Mono alt possible */ > + .subclass = UAC3_FUNCTION_SUBCLASS_HEADPHONE, > + .name = "HEADPHONE", > + .p_chmask = 3, > + }, > + { > + /* BAOF; Mono or Stereo cfg, Mono alt possible */ > + .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKER, > + .name = "SPEAKER", > + .p_chmask = -1, /* dynamic channels */ > + }, > + { > + /* BAIF; Mono or Stereo cfg, Mono alt possible */ > + .subclass = UAC3_FUNCTION_SUBCLASS_MICROPHONE, > + .name = "MICROPHONE", > + .c_chmask = -1, /* dynamic channels */ > + }, > + { > + /* > + * BAIOF topology > + * IN: Mono only > + * OUT: Mono or Stereo cfg, Mono alt possible > + */ > + .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET, > + .name = "HEADSET", > + .c_chmask = 1, > + .p_chmask = -1, /* dynamic channels */ > + .st_chmask = 1, > + }, > + { > + /* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */ > + .subclass = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER, > + .name = "HEADSET ADAPTER", > + .c_chmask = 1, > + .p_chmask = 3, > + .st_chmask = 1, > + }, > + { > + /* BAIF + BAOF; IN: Mono only; OUT: Mono only */ > + .subclass = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE, > + .name = "SPEAKERPHONE", > + .c_chmask = 1, > + .p_chmask = 1, > + }, > + { 0 } /* terminator */ > +}; > + > +static bool uac3_badd_func_has_valid_channels(struct usb_mixer_interface *mixer, > + struct uac3_badd_profile *f, > + int c_chmask, int p_chmask) > +{ > + /* > + * If both playback/capture channels are dynamic, make sure > + * at least one channel is present > + */ > + if (f->c_chmask < 0 && f->p_chmask < 0) { > + if (!c_chmask && !p_chmask) { > + usb_audio_warn(mixer->chip, "BAAD %s: no channels?", > + f->name); > + return false; > + } > + return true; > + } > + > + if ((f->c_chmask < 0 && !c_chmask) || > + (f->c_chmask >= 0 && f->c_chmask != c_chmask)) { > + usb_audio_warn(mixer->chip, "BAAD %s c_chmask mismatch", > + f->name); > + return false; > + } > + if ((f->p_chmask < 0 && !p_chmask) || > + (f->p_chmask >= 0 && f->p_chmask != p_chmask)) { > + usb_audio_warn(mixer->chip, "BAAD %s p_chmask mismatch", > + f->name); > + return false; > + } > + return true; > +} > + > +/* > + * create mixer controls for UAC3 BADD profiles > + * > + * UAC3 BADD device doesn't contain CS descriptors thus we will guess everything > + * > + * BADD device may contain Mixer Unit, which doesn't have any controls, skip it > + */ > +static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer, > + int ctrlif) > +{ > + struct usb_device *dev = mixer->chip->dev; > + struct usb_interface_assoc_descriptor *assoc; > + int badd_profile = mixer->chip->badd_profile; > + struct uac3_badd_profile *f; > + const struct usbmix_ctl_map *map; > + int p_chmask = 0, c_chmask = 0, st_chmask = 0; > + int i; > + > + assoc = usb_ifnum_to_if(dev, ctrlif)->intf_assoc; > + > + /* Detect BADD capture/playback channels from AS EP descriptors */ > + for (i = 0; i < assoc->bInterfaceCount; i++) { > + int intf = assoc->bFirstInterface + i; > + > + struct usb_interface *iface; > + struct usb_host_interface *alts; > + struct usb_interface_descriptor *altsd; > + unsigned int maxpacksize; > + char dir_in; > + int chmask, num; > + > + if (intf == ctrlif) > + continue; > + > + iface = usb_ifnum_to_if(dev, intf); > + num = iface->num_altsetting; > + > + if (num < 2) > + return -EINVAL; > + > + /* > + * The number of Channels in an AudioStreaming interface > + * and the audio sample bit resolution (16 bits or 24 > + * bits) can be derived from the wMaxPacketSize field in > + * the Standard AS Audio Data Endpoint descriptor in > + * Alternate Setting 1 > + */ > + alts = &iface->altsetting[1]; > + altsd = get_iface_desc(alts); > + > + if (altsd->bNumEndpoints < 1) > + return -EINVAL; > + > + /* check direction */ > + dir_in = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN); > + maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); > + > + switch (maxpacksize) { > + default: > + usb_audio_err(mixer->chip, > + "incorrect wMaxPacketSize 0x%x for BADD profile\n", > + maxpacksize); > + return -EINVAL; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: > + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24: > + chmask = 1; > + break; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: > + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: > + chmask = 3; > + break; > + } > + > + if (dir_in) > + c_chmask = chmask; > + else > + p_chmask = chmask; > + } > + > + usb_audio_dbg(mixer->chip, > + "UAC3 BADD profile 0x%x: detected c_chmask=%d p_chmask=%d\n", > + badd_profile, c_chmask, p_chmask); > + > + /* check the mapping table */ > + for (map = uac3_badd_usbmix_ctl_maps; map->id; map++) { > + if (map->id == badd_profile) > + break; > + } > + > + if (!map->id) > + return -EINVAL; > + > + for (f = uac3_badd_profiles; f->name; f++) { > + if (badd_profile == f->subclass) > + break; > + } > + if (!f->name) > + return -EINVAL; > + if (!uac3_badd_func_has_valid_channels(mixer, f, c_chmask, p_chmask)) > + return -EINVAL; > + st_chmask = f->st_chmask; > + > + /* Playback */ > + if (p_chmask) { > + /* Master channel, always writable */ > + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, > + UAC3_BADD_FU_ID2, map->map); > + /* Mono/Stereo volume channels, always writable */ > + build_feature_ctl_badd(mixer, p_chmask, UAC_FU_VOLUME, > + UAC3_BADD_FU_ID2, map->map); > + } > + > + /* Capture */ > + if (c_chmask) { > + /* Master channel, always writable */ > + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, > + UAC3_BADD_FU_ID5, map->map); > + /* Mono/Stereo volume channels, always writable */ > + build_feature_ctl_badd(mixer, c_chmask, UAC_FU_VOLUME, > + UAC3_BADD_FU_ID5, map->map); > + } > + > + /* Side tone-mixing */ > + if (st_chmask) { > + /* Master channel, always writable */ > + build_feature_ctl_badd(mixer, 0, UAC_FU_MUTE, > + UAC3_BADD_FU_ID7, map->map); > + /* Mono volume channel, always writable */ > + build_feature_ctl_badd(mixer, 1, UAC_FU_VOLUME, > + UAC3_BADD_FU_ID7, map->map); > + } > + > + return 0; > +} > + > /* > * create mixer controls > * > @@ -2883,9 +3143,14 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, > break; > } > > - if ((err = snd_usb_mixer_controls(mixer)) < 0 || > - (err = snd_usb_mixer_status_create(mixer)) < 0) > + if (mixer->protocol == UAC_VERSION_3 && > + chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { > + if ((err = snd_usb_mixer_controls_badd(mixer, ctrlif)) < 0) > + goto _error; > + } else if ((err = snd_usb_mixer_controls(mixer)) < 0 || > + (err = snd_usb_mixer_status_create(mixer)) < 0) { > goto _error; > + } > err = create_keep_iface_ctl(mixer); > if (err < 0) > goto _error; > diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c > index eaa03ac..71069e1 100644 > --- a/sound/usb/mixer_maps.c > +++ b/sound/usb/mixer_maps.c > @@ -485,3 +485,68 @@ struct usbmix_ctl_map { > { 0 } /* terminator */ > }; > > +/* > + * Control map entries for UAC3 BADD profiles > + */ > + > +static struct usbmix_name_map uac3_badd_generic_io_map[] = { > + { UAC3_BADD_FU_ID2, "Generic Out Playback" }, > + { UAC3_BADD_FU_ID5, "Generic In Capture" }, > + { 0 } /* terminator */ > +}; > +static struct usbmix_name_map uac3_badd_headphone_map[] = { > + { UAC3_BADD_FU_ID2, "Headphone Playback" }, > + { 0 } /* terminator */ > +}; > +static struct usbmix_name_map uac3_badd_speaker_map[] = { > + { UAC3_BADD_FU_ID2, "Speaker Playback" }, > + { 0 } /* terminator */ > +}; > +static struct usbmix_name_map uac3_badd_microphone_map[] = { > + { UAC3_BADD_FU_ID5, "Mic Capture" }, > + { 0 } /* terminator */ > +}; > +/* Covers also 'headset adapter' profile */ > +static struct usbmix_name_map uac3_badd_headset_map[] = { > + { UAC3_BADD_FU_ID2, "Headset Playback" }, > + { UAC3_BADD_FU_ID5, "Headset Capture" }, > + { UAC3_BADD_FU_ID7, "Sidetone Mixing" }, > + { 0 } /* terminator */ > +}; > +static struct usbmix_name_map uac3_badd_speakerphone_map[] = { > + { UAC3_BADD_FU_ID2, "Speaker Playback" }, > + { UAC3_BADD_FU_ID5, "Mic Capture" }, > + { 0 } /* terminator */ > +}; > + > +static struct usbmix_ctl_map uac3_badd_usbmix_ctl_maps[] = { > + { > + .id = UAC3_FUNCTION_SUBCLASS_GENERIC_IO, > + .map = uac3_badd_generic_io_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_HEADPHONE, > + .map = uac3_badd_headphone_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_SPEAKER, > + .map = uac3_badd_speaker_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_MICROPHONE, > + .map = uac3_badd_microphone_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_HEADSET, > + .map = uac3_badd_headset_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER, > + .map = uac3_badd_headset_map, > + }, > + { > + .id = UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE, > + .map = uac3_badd_speakerphone_map, > + }, > + { 0 } /* terminator */ > +}; > diff --git a/sound/usb/stream.c b/sound/usb/stream.c > index 764be07..de8bbb3 100644 > --- a/sound/usb/stream.c > +++ b/sound/usb/stream.c > @@ -817,15 +817,67 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, > struct uac3_input_terminal_descriptor *input_term; > struct uac3_output_terminal_descriptor *output_term; > struct uac3_cluster_header_descriptor *cluster; > - struct uac3_as_header_descriptor *as; > + struct uac3_as_header_descriptor *as = NULL; > struct uac3_hc_descriptor_header hc_header; > struct snd_pcm_chmap_elem *chmap; > + unsigned char badd_profile; > + u64 badd_formats = 0; > unsigned int num_channels; > struct audioformat *fp; > u16 cluster_id, wLength; > int clock = 0; > int err; > > + badd_profile = chip->badd_profile; > + > + if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { > + unsigned int maxpacksize = > + le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); > + > + switch (maxpacksize) { > + default: > + dev_err(&dev->dev, > + "%u:%d : incorrect wMaxPacketSize for BADD profile\n", > + iface_no, altno); > + return NULL; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: > + badd_formats = SNDRV_PCM_FMTBIT_S16_LE; > + num_channels = 1; > + break; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24: > + badd_formats = SNDRV_PCM_FMTBIT_S24_3LE; > + num_channels = 1; > + break; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: > + badd_formats = SNDRV_PCM_FMTBIT_S16_LE; > + num_channels = 2; > + break; > + case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: > + case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: > + badd_formats = SNDRV_PCM_FMTBIT_S24_3LE; > + num_channels = 2; > + break; > + } > + > + chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); > + if (!chmap) > + return ERR_PTR(-ENOMEM); > + > + if (num_channels == 1) { > + chmap->map[0] = SNDRV_CHMAP_MONO; > + } else { > + chmap->map[0] = SNDRV_CHMAP_FL; > + chmap->map[1] = SNDRV_CHMAP_FR; > + } > + > + chmap->channels = num_channels; > + clock = UAC3_BADD_CS_ID9; > + goto found_clock; > + } > + > as = snd_usb_find_csint_desc(alts->extra, alts->extralen, > NULL, UAC_AS_GENERAL); > if (!as) { > @@ -931,16 +983,29 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, > if (!fp) > return ERR_PTR(-ENOMEM); > > - fp->attributes = parse_uac_endpoint_attributes(chip, alts, > - UAC_VERSION_3, > - iface_no); > fp->chmap = chmap; > > - /* ok, let's parse further... */ > - if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { > - kfree(fp->rate_table); > - kfree(fp); > - return NULL; > + if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { > + fp->attributes = 0; /* No attributes */ > + > + fp->fmt_type = UAC_FORMAT_TYPE_I; > + fp->formats = badd_formats; > + > + fp->nr_rates = 0; /* SNDRV_PCM_RATE_CONTINUOUS */ > + fp->rate_min = UAC3_BADD_SAMPLING_RATE; > + fp->rate_max = UAC3_BADD_SAMPLING_RATE; > + fp->rates = SNDRV_PCM_RATE_CONTINUOUS; > + > + } else { > + fp->attributes = parse_uac_endpoint_attributes(chip, alts, > + UAC_VERSION_3, > + iface_no); > + /* ok, let's parse further... */ > + if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { > + kfree(fp->rate_table); > + kfree(fp); > + return NULL; > + } > } > > return fp; > diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h > index 1cb6b3e..7b28cbd 100644 > --- a/sound/usb/usbaudio.h > +++ b/sound/usb/usbaudio.h > @@ -49,6 +49,8 @@ struct snd_usb_audio { > int num_suspended_intf; > int sample_rate_read_error; > > + int badd_profile; /* UAC3 BADD profile */ > + > struct list_head pcm_list; /* list of pcm streams */ > struct list_head ep_list; /* list of audio-related endpoints */ > int pcm_devs; >
| |