[alsa-devel] [PATCH 0/4] USB Audio Device Class 3.0 BADD profiles support
This patchset adds BADD profiles support from the USB Audio Device Class 3.0 spec [1].
BADD profile support is defined as mandatory feature of UAC3-compliant device, it should be implemented as a separate USB configuration.
Notable issue with BADD configuration is that it misses class-specific descriptors (and it's mandatory as per spec), so host should guess them from BADD profile number and parameters of endpoints (type, number of endpoints and max packet size)
This patchset adds support of all known/existing BADD profiles from the UAC3 specification.
It's an alternative implementation comparing to [2], and doesn't build usb descriptors on the host but instead initializes alsa-usb structures with known parameters, so we don't need to keep whole class-specific descriptors in the driver since we anyway need to have BADD-specific logic.
I've picked one Jorge's UAC1 patch-improvement and updated it to v4.17 wich contais recently introduced header's sanity checks.
Remaining part is to add interrupt endpoint support so we will be able to detect jack insertion in the Headset Adapter profile.
This has been tested on ARM and x86-64 machines with custom UAC3 gadget which I'll post later to linux-usb
Comments and testing are welcome.
[1] http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip [2] https://www.spinics.net/lists/alsa-devel/msg71614.html
Jorge Sanjuan (1): ALSA: usb: Only get AudioControl header for UAC1 class.
Ruslan Bilovol (3): ALSA: usb: stream: refactor audio interface parsing include: usb: audio-v3: add BADD-specific values ALSA: usb: add UAC3 BADD profiles support
include/linux/usb/audio-v3.h | 26 ++ sound/usb/card.c | 53 ++-- sound/usb/clock.c | 9 +- sound/usb/mixer.c | 313 +++++++++++++++++++- sound/usb/mixer_maps.c | 65 +++++ sound/usb/stream.c | 678 +++++++++++++++++++++++++------------------ sound/usb/usbaudio.h | 2 + 7 files changed, 836 insertions(+), 310 deletions(-)
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support.
Move class-specific parts to separate functions which now produce audioformat structure that is ready to be fed to snd_usb_add_audio_stream().
This also broke Blue Microphones workaround (which relies on audioformat decoded from previous altsetting) into two parts: prepare quirk flag analyzing previous altsetting then use it with current altsetting.
Signed-off-by: Ruslan Bilovol ruslan.bilovol@gmail.com --- sound/usb/stream.c | 613 +++++++++++++++++++++++++++++------------------------ 1 file changed, 334 insertions(+), 279 deletions(-)
diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 6a8f584..586d664 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -626,6 +626,319 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return NULL; }
+static struct audioformat * +snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int protocol, int iface_no, + int altno, int stream, int bm_quirk) +{ + struct usb_device *dev = chip->dev; + struct uac_format_type_i_continuous_descriptor *fmt; + unsigned int num_channels = 0, chconfig = 0; + struct audioformat *fp; + int clock = 0; + u64 format; + + /* get audio formats */ + if (protocol == UAC_VERSION_1) { + struct uac1_as_header_descriptor *as = + snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_AS_GENERAL); + struct uac_input_terminal_descriptor *iterm; + + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + return NULL; + } + + format = le16_to_cpu(as->wFormatTag); /* remember the format value */ + + iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (iterm) { + num_channels = iterm->bNrChannels; + chconfig = le16_to_cpu(iterm->wChannelConfig); + } + } else { /* UAC_VERSION_2 */ + struct uac2_input_terminal_descriptor *input_term; + struct uac2_output_terminal_descriptor *output_term; + struct uac2_as_header_descriptor *as = + snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_AS_GENERAL); + + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + return NULL; + } + + num_channels = as->bNrChannels; + format = le32_to_cpu(as->bmFormats); + chconfig = le32_to_cpu(as->bmChannelConfig); + + /* + * lookup the terminal associated to this interface + * to extract the clock + */ + input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (input_term) { + clock = input_term->bCSourceID; + if (!chconfig && (num_channels == input_term->bNrChannels)) + chconfig = le32_to_cpu(input_term->bmChannelConfig); + goto found_clock; + } + + output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (output_term) { + clock = output_term->bCSourceID; + goto found_clock; + } + + dev_err(&dev->dev, + "%u:%d : bogus bTerminalLink %d\n", + iface_no, altno, as->bTerminalLink); + return NULL; + } + +found_clock: + /* get format type */ + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_FORMAT_TYPE); + if (!fmt) { + dev_err(&dev->dev, + "%u:%d : no UAC_FORMAT_TYPE desc\n", + iface_no, altno); + return NULL; + } + if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) + || ((protocol == UAC_VERSION_2) && + (fmt->bLength < 6))) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_FORMAT_TYPE desc\n", + iface_no, altno); + return NULL; + } + + /* + * Blue Microphones workaround: The last altsetting is + * identical with the previous one, except for a larger + * packet size, but is actually a mislabeled two-channel + * setting; ignore it. + * + * Part 2: analyze quirk flag and format + */ + if (bm_quirk && fmt->bNrChannels == 1 && fmt->bSubframeSize == 2) + return NULL; + + fp = kzalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) + return ERR_PTR(-ENOMEM); + + fp->iface = iface_no; + fp->altsetting = altno; + fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; + fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = snd_usb_parse_datainterval(chip, alts); + fp->protocol = protocol; + fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + fp->channels = num_channels; + if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) + fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) + * (fp->maxpacksize & 0x7ff); + fp->attributes = parse_uac_endpoint_attributes(chip, alts, + protocol, iface_no); + fp->clock = clock; + INIT_LIST_HEAD(&fp->list); + + /* some quirks for attributes here */ + snd_usb_audioformat_attributes_quirk(chip, fp, stream); + + /* ok, let's parse further... */ + if (snd_usb_parse_audio_format(chip, fp, format, + fmt, stream) < 0) { + kfree(fp->rate_table); + kfree(fp); + return NULL; + } + + /* Create chmap */ + if (fp->channels != num_channels) + chconfig = 0; + + fp->chmap = convert_chmap(fp->channels, chconfig, protocol); + + return fp; +} + +static struct audioformat * +snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int iface_no, int altno, int stream) +{ + struct usb_device *dev = chip->dev; + 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_hc_descriptor_header hc_header; + struct snd_pcm_chmap_elem *chmap; + unsigned int num_channels; + struct audioformat *fp; + u16 cluster_id, wLength; + int clock = 0; + int err; + + as = snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_AS_GENERAL); + if (!as) { + dev_err(&dev->dev, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(&dev->dev, + "%u:%d : invalid UAC_AS_GENERAL desc\n", + iface_no, altno); + return NULL; + } + + cluster_id = le16_to_cpu(as->wClusterDescrID); + if (!cluster_id) { + dev_err(&dev->dev, + "%u:%d : no cluster descriptor\n", + iface_no, altno); + return NULL; + } + + /* + * Get number of channels and channel map through + * High Capability Cluster Descriptor + * + * First step: get High Capability header and + * read size of Cluster Descriptor + */ + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + cluster_id, + snd_usb_ctrl_intf(chip), + &hc_header, sizeof(hc_header)); + if (err < 0) + return ERR_PTR(err); + else if (err != sizeof(hc_header)) { + dev_err(&dev->dev, + "%u:%d : can't get High Capability descriptor\n", + iface_no, altno); + return ERR_PTR(-EIO); + } + + /* + * Second step: allocate needed amount of memory + * and request Cluster Descriptor + */ + wLength = le16_to_cpu(hc_header.wLength); + cluster = kzalloc(wLength, GFP_KERNEL); + if (!cluster) + return ERR_PTR(-ENOMEM); + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + cluster_id, + snd_usb_ctrl_intf(chip), + cluster, wLength); + if (err < 0) { + kfree(cluster); + return ERR_PTR(err); + } else if (err != wLength) { + dev_err(&dev->dev, + "%u:%d : can't get Cluster Descriptor\n", + iface_no, altno); + kfree(cluster); + return ERR_PTR(-EIO); + } + + num_channels = cluster->bNrChannels; + chmap = convert_chmap_v3(cluster); + kfree(cluster); + + /* + * lookup the terminal associated to this interface + * to extract the clock + */ + input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (input_term) { + clock = input_term->bCSourceID; + goto found_clock; + } + + output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (output_term) { + clock = output_term->bCSourceID; + goto found_clock; + } + + dev_err(&dev->dev, "%u:%d : bogus bTerminalLink %d\n", + iface_no, altno, as->bTerminalLink); + return NULL; + +found_clock: + fp = kzalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) + return ERR_PTR(-ENOMEM); + + fp->iface = iface_no; + fp->altsetting = altno; + fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; + fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; + fp->datainterval = snd_usb_parse_datainterval(chip, alts); + fp->protocol = UAC_VERSION_3; + fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + fp->channels = num_channels; + if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) + fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) + * (fp->maxpacksize & 0x7ff); + fp->attributes = parse_uac_endpoint_attributes(chip, alts, + UAC_VERSION_3, + iface_no); + fp->clock = clock; + fp->chmap = chmap; + INIT_LIST_HEAD(&fp->list); + + /* 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; +} + int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) { struct usb_device *dev; @@ -633,13 +946,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; int i, altno, err, stream; - u64 format = 0; - unsigned int num_channels = 0; struct audioformat *fp = NULL; - int num, protocol, clock = 0; - struct uac_format_type_i_continuous_descriptor *fmt = NULL; - struct snd_pcm_chmap_elem *chmap_v3 = NULL; - unsigned int chconfig; + int num, protocol;
dev = chip->dev;
@@ -688,303 +996,50 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) protocol <= 2) protocol = UAC_VERSION_1;
- chconfig = 0; - /* get audio formats */ switch (protocol) { default: dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n", iface_no, altno, protocol); protocol = UAC_VERSION_1; /* fall through */ - - case UAC_VERSION_1: { - struct uac1_as_header_descriptor *as = - snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); - struct uac_input_terminal_descriptor *iterm; - - if (!as) { - dev_err(&dev->dev, - "%u:%d : UAC_AS_GENERAL descriptor not found\n", - iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_AS_GENERAL desc\n", - iface_no, altno); - continue; - } - - format = le16_to_cpu(as->wFormatTag); /* remember the format value */ - - iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (iterm) { - num_channels = iterm->bNrChannels; - chconfig = le16_to_cpu(iterm->wChannelConfig); - } - - break; - } - + case UAC_VERSION_1: + /* fall through */ case UAC_VERSION_2: { - struct uac2_input_terminal_descriptor *input_term; - struct uac2_output_terminal_descriptor *output_term; - struct uac2_as_header_descriptor *as = - snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); - - if (!as) { - dev_err(&dev->dev, - "%u:%d : UAC_AS_GENERAL descriptor not found\n", - iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_AS_GENERAL desc\n", - iface_no, altno); - continue; - } - - num_channels = as->bNrChannels; - format = le32_to_cpu(as->bmFormats); - chconfig = le32_to_cpu(as->bmChannelConfig); - - /* lookup the terminal associated to this interface - * to extract the clock */ - input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (input_term) { - clock = input_term->bCSourceID; - if (!chconfig && (num_channels == input_term->bNrChannels)) - chconfig = le32_to_cpu(input_term->bmChannelConfig); - break; - } - - output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (output_term) { - clock = output_term->bCSourceID; - break; - } - - dev_err(&dev->dev, - "%u:%d : bogus bTerminalLink %d\n", - iface_no, altno, as->bTerminalLink); - continue; - } - - case UAC_VERSION_3: { - struct uac3_input_terminal_descriptor *input_term; - struct uac3_output_terminal_descriptor *output_term; - struct uac3_as_header_descriptor *as; - struct uac3_cluster_header_descriptor *cluster; - struct uac3_hc_descriptor_header hc_header; - u16 cluster_id, wLength; - - as = snd_usb_find_csint_desc(alts->extra, - alts->extralen, - NULL, UAC_AS_GENERAL); - - if (!as) { - dev_err(&dev->dev, - "%u:%d : UAC_AS_GENERAL descriptor not found\n", - iface_no, altno); - continue; - } - - if (as->bLength < sizeof(*as)) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_AS_GENERAL desc\n", - iface_no, altno); - continue; - } - - cluster_id = le16_to_cpu(as->wClusterDescrID); - if (!cluster_id) { - dev_err(&dev->dev, - "%u:%d : no cluster descriptor\n", - iface_no, altno); - continue; - } - - /* - * Get number of channels and channel map through - * High Capability Cluster Descriptor - * - * First step: get High Capability header and - * read size of Cluster Descriptor - */ - err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), - UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - cluster_id, - snd_usb_ctrl_intf(chip), - &hc_header, sizeof(hc_header)); - if (err < 0) - return err; - else if (err != sizeof(hc_header)) { - dev_err(&dev->dev, - "%u:%d : can't get High Capability descriptor\n", - iface_no, altno); - return -EIO; - } - - /* - * Second step: allocate needed amount of memory - * and request Cluster Descriptor - */ - wLength = le16_to_cpu(hc_header.wLength); - cluster = kzalloc(wLength, GFP_KERNEL); - if (!cluster) - return -ENOMEM; - err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), - UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - cluster_id, - snd_usb_ctrl_intf(chip), - cluster, wLength); - if (err < 0) { - kfree(cluster); - return err; - } else if (err != wLength) { - dev_err(&dev->dev, - "%u:%d : can't get Cluster Descriptor\n", - iface_no, altno); - kfree(cluster); - return -EIO; - } - - num_channels = cluster->bNrChannels; - chmap_v3 = convert_chmap_v3(cluster); - - kfree(cluster); - - format = le64_to_cpu(as->bmFormats); - - /* lookup the terminal associated to this interface - * to extract the clock */ - input_term = snd_usb_find_input_terminal_descriptor( - chip->ctrl_intf, - as->bTerminalLink); - - if (input_term) { - clock = input_term->bCSourceID; - break; - } - - output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, - as->bTerminalLink); - if (output_term) { - clock = output_term->bCSourceID; - break; - } - - dev_err(&dev->dev, - "%u:%d : bogus bTerminalLink %d\n", - iface_no, altno, as->bTerminalLink); - continue; - } - } - - if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { - /* get format type */ - fmt = snd_usb_find_csint_desc(alts->extra, - alts->extralen, - NULL, UAC_FORMAT_TYPE); - if (!fmt) { - dev_err(&dev->dev, - "%u:%d : no UAC_FORMAT_TYPE desc\n", - iface_no, altno); - continue; - } - if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) - || ((protocol == UAC_VERSION_2) && - (fmt->bLength < 6))) { - dev_err(&dev->dev, - "%u:%d : invalid UAC_FORMAT_TYPE desc\n", - iface_no, altno); - continue; - } + int bm_quirk = 0;
/* * Blue Microphones workaround: The last altsetting is * identical with the previous one, except for a larger * packet size, but is actually a mislabeled two-channel * setting; ignore it. + * + * Part 1: prepare quirk flag */ - if (fmt->bNrChannels == 1 && - fmt->bSubframeSize == 2 && - altno == 2 && num == 3 && + if (altno == 2 && num == 3 && fp && fp->altsetting == 1 && fp->channels == 1 && fp->formats == SNDRV_PCM_FMTBIT_S16_LE && protocol == UAC_VERSION_1 && le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == fp->maxpacksize * 2) - continue; + bm_quirk = 1; + + fp = snd_usb_get_audioformat_uac12(chip, alts, protocol, + iface_no, altno, + stream, bm_quirk); + break; + } + case UAC_VERSION_3: + fp = snd_usb_get_audioformat_uac3(chip, alts, + iface_no, altno, stream); + break; }
- fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (!fp) - return -ENOMEM; + continue; + else if (IS_ERR(fp)) + return PTR_ERR(fp);
- fp->iface = iface_no; - fp->altsetting = altno; fp->altset_idx = i; - fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; - fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; - fp->datainterval = snd_usb_parse_datainterval(chip, alts); - fp->protocol = protocol; - fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); - fp->channels = num_channels; - if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) - fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) - * (fp->maxpacksize & 0x7ff); - fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); - fp->clock = clock; - INIT_LIST_HEAD(&fp->list); - - /* some quirks for attributes here */ - snd_usb_audioformat_attributes_quirk(chip, fp, stream); - - /* ok, let's parse further... */ - if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { - if (snd_usb_parse_audio_format(chip, fp, format, - fmt, stream) < 0) { - kfree(fp->rate_table); - kfree(fp); - fp = NULL; - continue; - } - } else { - struct uac3_as_header_descriptor *as; - - as = snd_usb_find_csint_desc(alts->extra, - alts->extralen, - NULL, UAC_AS_GENERAL); - - if (snd_usb_parse_audio_format_v3(chip, fp, as, - stream) < 0) { - kfree(fp->rate_table); - kfree(fp); - fp = NULL; - continue; - } - } - - /* Create chmap */ - if (fp->channels != num_channels) - chconfig = 0; - - if (protocol == UAC_VERSION_3) - fp->chmap = chmap_v3; - else - fp->chmap = convert_chmap(fp->channels, chconfig, - protocol);
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); err = snd_usb_add_audio_stream(chip, stream, fp);
On Sat, 14 Apr 2018 00:24:23 +0200, Ruslan Bilovol wrote:
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support.
Move class-specific parts to separate functions which now produce audioformat structure that is ready to be fed to snd_usb_add_audio_stream().
This also broke Blue Microphones workaround (which relies on audioformat decoded from previous altsetting) into two parts: prepare quirk flag analyzing previous altsetting then use it with current altsetting.
Signed-off-by: Ruslan Bilovol ruslan.bilovol@gmail.com
Could you try to split this patch to two parts: one is a simple refactoring to move the code to snd_usb_get_audioformat_uac12(), and another to add snd_usb_get_audioformat_uac3().
In this way, we can see a problem more clearly if it's in the refactoring part.
thanks,
Takashi
On Thu, Apr 19, 2018 at 12:55 PM, Takashi Iwai tiwai@suse.de wrote:
On Sat, 14 Apr 2018 00:24:23 +0200, Ruslan Bilovol wrote:
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support.
Move class-specific parts to separate functions which now produce audioformat structure that is ready to be fed to snd_usb_add_audio_stream().
This also broke Blue Microphones workaround (which relies on audioformat decoded from previous altsetting) into two parts: prepare quirk flag analyzing previous altsetting then use it with current altsetting.
Signed-off-by: Ruslan Bilovol ruslan.bilovol@gmail.com
Could you try to split this patch to two parts: one is a simple refactoring to move the code to snd_usb_get_audioformat_uac12(), and another to add snd_usb_get_audioformat_uac3().
In this way, we can see a problem more clearly if it's in the refactoring part.
Sure, will do in in next patchset
Thanks, Ruslan
Add BADD-specific predefined values to audio-v3 so usb-audio in ALSA and UAC3 gadget can use them
Signed-off-by: Ruslan Bilovol ruslan.bilovol@gmail.com --- include/linux/usb/audio-v3.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h index a8959aa..38add1d 100644 --- a/include/linux/usb/audio-v3.h +++ b/include/linux/usb/audio-v3.h @@ -392,4 +392,30 @@ struct uac3_interrupt_data_msg { #define UAC3_AC_ACTIVE_INTERFACE_CONTROL 0x01 #define UAC3_AC_POWER_DOMAIN_CONTROL 0x02
+/* BADD predefined Unit/Terminal values */ +#define UAC3_BADD_IT_ID1 1 /* Input Terminal ID1: bTerminalID = 1 */ +#define UAC3_BADD_FU_ID2 2 /* Feature Unit ID2: bUnitID = 2 */ +#define UAC3_BADD_OT_ID3 3 /* Output Terminal ID3: bTerminalID = 3 */ +#define UAC3_BADD_IT_ID4 4 /* Input Terminal ID4: bTerminalID = 4 */ +#define UAC3_BADD_FU_ID5 5 /* Feature Unit ID5: bUnitID = 5 */ +#define UAC3_BADD_OT_ID6 6 /* Output Terminal ID6: bTerminalID = 6 */ +#define UAC3_BADD_FU_ID7 7 /* Feature Unit ID7: bUnitID = 7 */ +#define UAC3_BADD_MU_ID8 8 /* Mixer Unit ID8: bUnitID = 8 */ +#define UAC3_BADD_CS_ID9 9 /* Clock Source Entity ID9: bClockID = 9 */ +#define UAC3_BADD_PD_ID10 10 /* Power Domain ID10: bPowerDomainID = 10 */ +#define UAC3_BADD_PD_ID11 11 /* Power Domain ID11: bPowerDomainID = 11 */ + +/* BADD wMaxPacketSize of AS endpoints */ +#define UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16 0x0060 +#define UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16 0x0062 +#define UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24 0x0090 +#define UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24 0x0093 +#define UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16 0x00C0 +#define UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16 0x00C4 +#define UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24 0x0120 +#define UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24 0x0126 + +/* BADD sample rate is always fixed to 48kHz */ +#define UAC3_BADD_SAMPLING_RATE 48000 + #endif /* __LINUX_USB_AUDIO_V3_H */
From: Jorge Sanjuan jorge.sanjuan@codethink.co.uk
The control header needs to be read from buffer at this point only in the case of UAC1 protocol. Move it inside the switch case as other protocols such as the Basic Audio Device spec will have an empty buffer that is latter filled as inferred.
Signed-off-by: Jorge Sanjuan jorge.sanjuan@codethink.co.uk [Ruslan: updated with recently added sanity checks] Signed-off-by: Ruslan Bilovol ruslan.bilovol@gmail.com --- sound/usb/card.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-)
diff --git a/sound/usb/card.c b/sound/usb/card.c index 4a1c6bb..4d866bd 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -221,32 +221,13 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) struct usb_device *dev = chip->dev; struct usb_host_interface *host_iface; struct usb_interface_descriptor *altsd; - void *control_header; int i, protocol; - int rest_bytes;
/* find audiocontrol interface */ host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0]; - control_header = snd_usb_find_csint_desc(host_iface->extra, - host_iface->extralen, - NULL, UAC_HEADER); altsd = get_iface_desc(host_iface); protocol = altsd->bInterfaceProtocol;
- if (!control_header) { - dev_err(&dev->dev, "cannot find UAC_HEADER\n"); - return -EINVAL; - } - - rest_bytes = (void *)(host_iface->extra + host_iface->extralen) - - control_header; - - /* just to be sure -- this shouldn't hit at all */ - if (rest_bytes <= 0) { - dev_err(&dev->dev, "invalid control header\n"); - return -EINVAL; - } - switch (protocol) { default: dev_warn(&dev->dev, @@ -255,7 +236,25 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) /* fall through */
case UAC_VERSION_1: { - struct uac1_ac_header_descriptor *h1 = control_header; + struct uac1_ac_header_descriptor *h1; + int rest_bytes; + + h1 = snd_usb_find_csint_desc(host_iface->extra, + host_iface->extralen, + NULL, UAC_HEADER); + if (!h1) { + dev_err(&dev->dev, "cannot find UAC_HEADER\n"); + return -EINVAL; + } + + rest_bytes = (void *)(host_iface->extra + + host_iface->extralen) - (void *)h1; + + /* just to be sure -- this shouldn't hit at all */ + if (rest_bytes <= 0) { + dev_err(&dev->dev, "invalid control header\n"); + return -EINVAL; + }
if (rest_bytes < sizeof(*h1)) { dev_err(&dev->dev, "too short v1 buffer descriptor\n");
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 --- sound/usb/card.c | 14 +++ sound/usb/clock.c | 9 +- sound/usb/mixer.c | 313 +++++++++++++++++++++++++++++++++++++++++++++++-- sound/usb/mixer_maps.c | 65 ++++++++++ sound/usb/stream.c | 83 +++++++++++-- sound/usb/usbaudio.h | 2 + 6 files changed, 466 insertions(+), 20 deletions(-)
diff --git a/sound/usb/card.c b/sound/usb/card.c index 4d866bd..47ebc50 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 301ad61..e5c3b0d 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,6 +1331,76 @@ static struct usb_feature_control_info *get_feature_control_info(int control) return NULL; }
+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) +{ + struct usb_feature_control_info *ctl_info; + unsigned int len = 0; + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *cval; + const struct usbmix_name_map *map; + + map = find_map(badd_map, unitid, control); + if (!map) + return; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (!cval) + return; + snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); + cval->control = control; + cval->cmask = ctl_mask; + + ctl_info = get_feature_control_info(control); + if (!ctl_info) { + kfree(cval); + return; + } + cval->val_type = ctl_info->type; + + if (ctl_mask == 0) { + cval->channels = 1; /* master channel */ + } else { + int i, c = 0; + + for (i = 0; i < 2; i++) + if (ctl_mask & (1 << i)) + c++; + cval->channels = c; + } + + kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + + if (!kctl) { + usb_audio_err(mixer->chip, "cannot malloc kcontrol\n"); + kfree(cval); + return; + } + kctl->private_free = snd_usb_mixer_elem_free; + len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); + + append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume"); + + /* get min/max values */ + get_min_max_with_quirks(cval, 0, kctl); + + if (control == UAC_FU_VOLUME) { + check_mapped_dB(map, cval); + if (cval->dBmin < cval->dBmax || !cval->initialized) { + kctl->tlv.c = snd_usb_mixer_vol_tlv; + kctl->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } + } + + 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, @@ -1353,7 +1421,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, return; }
- map = find_map(state, unitid, control); + map = find_map(state->map, unitid, control); if (check_ignored_ctl(map)) return;
@@ -1806,7 +1874,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;
@@ -2105,7 +2173,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); @@ -2308,7 +2376,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;
@@ -2495,6 +2563,226 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) }
/* + * 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; + 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; + + if (intf != ctrlif) { + struct usb_interface *iface; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + unsigned int maxpacksize; + char dir_in; + int chmask, num; + + 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) { + mixer->ignore_ctl_error = map->ignore_ctl_error; + break; + } + } + + if (!map->id) + return -EINVAL; + + switch (badd_profile) { + default: + return -EINVAL; + case UAC3_FUNCTION_SUBCLASS_GENERIC_IO: + /* + * BAIF, BAOF or combination of both + * IN: Mono or Stereo cfg, Mono alt possible + * OUT: Mono or Stereo cfg, Mono alt possible + */ + /* c_chmask := DYNAMIC */ + /* p_chmask := DYNAMIC */ + if (!c_chmask && !p_chmask) { + usb_audio_err(mixer->chip, + "BADD GENERIC_IO profile: no channels?\n"); + return -EINVAL; + } + break; + case UAC3_FUNCTION_SUBCLASS_HEADPHONE: + /* BAOF; Stereo only cfg, Mono alt possible */ + if (p_chmask != 3) + usb_audio_warn(mixer->chip, + "BADD HEADPHONE p_chmask mismatch: expected 3 actual %d\n", + p_chmask); + p_chmask = 3; + break; + case UAC3_FUNCTION_SUBCLASS_SPEAKER: + /* BAOF; Mono or Stereo cfg, Mono alt possible */ + /* p_chmask := DYNAMIC */ + if (!p_chmask) { + usb_audio_err(mixer->chip, + "BADD SPEAKER profile: no playback channels?\n"); + return -EINVAL; + } + break; + case UAC3_FUNCTION_SUBCLASS_MICROPHONE: + /* BAIF; Mono or Stereo cfg, Mono alt possible */ + /* c_chmask := DYNAMIC */ + if (!c_chmask) { + usb_audio_err(mixer->chip, + "BADD MICROPHONE profile: no capture channels?\n"); + return -EINVAL; + } + break; + case UAC3_FUNCTION_SUBCLASS_HEADSET: + /* + * BAIOF + * IN: Mono only + * OUT: Mono or Stereo cfg, Mono alt possible + */ + if (c_chmask != 1) + usb_audio_warn(mixer->chip, + "BADD HEADSET c_chmask mismatch: expected 1 actual %d\n", + c_chmask); + c_chmask = 1; + st_chmask = 1; + /* p_chmask := DYNAMIC */ + if (!p_chmask) { + usb_audio_err(mixer->chip, + "BADD HEADSET profile: no playback channels?\n"); + return -EINVAL; + } + break; + case UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER: + /* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */ + if (c_chmask != 1) + usb_audio_warn(mixer->chip, + "BADD HEADSET_ADAPTER c_chmask mismatch: expected 1 actual %d\n", + c_chmask); + if (p_chmask != 3) + usb_audio_warn(mixer->chip, + "BADD HEADSET_ADAPTER p_chmask mismatch: expected 3 actual %d\n", + p_chmask); + c_chmask = 1; + st_chmask = 1; + p_chmask = 3; + break; + case UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE: + /* BAIF + BAOF; IN: Mono only; OUT: Mono only */ + if (c_chmask != 1) + usb_audio_warn(mixer->chip, + "BADD SPEAKERPHONE c_chmask mismatch: expected 1 actual %d\n", + c_chmask); + if (p_chmask != 1) + usb_audio_warn(mixer->chip, + "BADD SPEAKERPHONE p_chmask mismatch: expected 1 actual %d\n", + p_chmask); + c_chmask = 1; + p_chmask = 1; + break; + } + + /* 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 * * walk through all UAC_OUTPUT_TERMINAL descriptors to search for mixers @@ -2838,9 +3126,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; + }
snd_usb_mixer_apply_create_quirk(mixer);
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 9038b2e..13f03c8 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -482,3 +482,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, "Side Tone 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 586d664..ea5a13e0 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -798,15 +798,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) { @@ -922,18 +974,31 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) * (fp->maxpacksize & 0x7ff); - fp->attributes = parse_uac_endpoint_attributes(chip, alts, - UAC_VERSION_3, - iface_no); fp->clock = clock; fp->chmap = chmap; INIT_LIST_HEAD(&fp->list);
- /* 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 4d5c89a..1bb5e2c 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;
On 2018-04-13 23: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.
Right. I would have thought that, since BADD is a subset of UAC3, it may be simpler to fill the Class Specific descriptors buffer and let the UAC3 path intact as it would result in the same behavior (for UAC3 and BADD configs) without the need to add that much code to the mixer, which is already quite big.
In the patch I proposed [1], the Class Specific buffer is filled once with the BADD descriptors, which are already UAC3 compliant, so the driver would handle the rest in the same way it would do with an UAC3 configuration.
I will keep an eye on this as I'd need to do some work based on this instead.
[1] https://www.spinics.net/lists/alsa-devel/msg71617.html
Thanks,
Jorge
This patch adds support of all BADD profiles from the spec
Signed-off-by: Ruslan Bilovol ruslan.bilovol@gmail.com
sound/usb/card.c | 14 +++ sound/usb/clock.c | 9 +- sound/usb/mixer.c | 313 +++++++++++++++++++++++++++++++++++++++++++++++-- sound/usb/mixer_maps.c | 65 ++++++++++ sound/usb/stream.c | 83 +++++++++++-- sound/usb/usbaudio.h | 2 + 6 files changed, 466 insertions(+), 20 deletions(-)
diff --git a/sound/usb/card.c b/sound/usb/card.c index 4d866bd..47ebc50 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 301ad61..e5c3b0d 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,6 +1331,76 @@ static struct usb_feature_control_info *get_feature_control_info(int control) return NULL; }
+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)
+{
- struct usb_feature_control_info *ctl_info;
- unsigned int len = 0;
- struct snd_kcontrol *kctl;
- struct usb_mixer_elem_info *cval;
- const struct usbmix_name_map *map;
- map = find_map(badd_map, unitid, control);
- if (!map)
return;
- cval = kzalloc(sizeof(*cval), GFP_KERNEL);
- if (!cval)
return;
- snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
- cval->control = control;
- cval->cmask = ctl_mask;
- ctl_info = get_feature_control_info(control);
- if (!ctl_info) {
kfree(cval);
return;
- }
- cval->val_type = ctl_info->type;
- if (ctl_mask == 0) {
cval->channels = 1; /* master channel */
- } else {
int i, c = 0;
for (i = 0; i < 2; i++)
if (ctl_mask & (1 << i))
c++;
cval->channels = c;
- }
- kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
- if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
kfree(cval);
return;
- }
- kctl->private_free = snd_usb_mixer_elem_free;
- len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
- append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : "
Volume");
- /* get min/max values */
- get_min_max_with_quirks(cval, 0, kctl);
- if (control == UAC_FU_VOLUME) {
check_mapped_dB(map, cval);
if (cval->dBmin < cval->dBmax || !cval->initialized) {
kctl->tlv.c = snd_usb_mixer_vol_tlv;
kctl->vd[0].access |=
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
}
- }
- 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, @@ -1353,7 +1421,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, return; }
- map = find_map(state, unitid, control);
- map = find_map(state->map, unitid, control); if (check_ignored_ctl(map)) return;
@@ -1806,7 +1874,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;
@@ -2105,7 +2173,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);
if (check_ignored_ctl(map)) continue; cval = kzalloc(sizeof(*cval), GFP_KERNEL);map = find_map(state->map, unitid, valinfo->control);
@@ -2308,7 +2376,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;
@@ -2495,6 +2563,226 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) }
/*
- 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;
- 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;
if (intf != ctrlif) {
struct usb_interface *iface;
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
unsigned int maxpacksize;
char dir_in;
int chmask, num;
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) {
mixer->ignore_ctl_error = map->ignore_ctl_error;
break;
}
- }
- if (!map->id)
return -EINVAL;
- switch (badd_profile) {
- default:
return -EINVAL;
- case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
/*
* BAIF, BAOF or combination of both
* IN: Mono or Stereo cfg, Mono alt possible
* OUT: Mono or Stereo cfg, Mono alt possible
*/
/* c_chmask := DYNAMIC */
/* p_chmask := DYNAMIC */
if (!c_chmask && !p_chmask) {
usb_audio_err(mixer->chip,
"BADD GENERIC_IO profile: no channels?\n");
return -EINVAL;
}
break;
- case UAC3_FUNCTION_SUBCLASS_HEADPHONE:
/* BAOF; Stereo only cfg, Mono alt possible */
if (p_chmask != 3)
usb_audio_warn(mixer->chip,
"BADD HEADPHONE p_chmask mismatch: expected 3 actual %d\n",
p_chmask);
p_chmask = 3;
break;
- case UAC3_FUNCTION_SUBCLASS_SPEAKER:
/* BAOF; Mono or Stereo cfg, Mono alt possible */
/* p_chmask := DYNAMIC */
if (!p_chmask) {
usb_audio_err(mixer->chip,
"BADD SPEAKER profile: no playback channels?\n");
return -EINVAL;
}
break;
- case UAC3_FUNCTION_SUBCLASS_MICROPHONE:
/* BAIF; Mono or Stereo cfg, Mono alt possible */
/* c_chmask := DYNAMIC */
if (!c_chmask) {
usb_audio_err(mixer->chip,
"BADD MICROPHONE profile: no capture channels?\n");
return -EINVAL;
}
break;
- case UAC3_FUNCTION_SUBCLASS_HEADSET:
/*
* BAIOF
* IN: Mono only
* OUT: Mono or Stereo cfg, Mono alt possible
*/
if (c_chmask != 1)
usb_audio_warn(mixer->chip,
"BADD HEADSET c_chmask mismatch: expected 1 actual %d\n",
c_chmask);
c_chmask = 1;
st_chmask = 1;
/* p_chmask := DYNAMIC */
if (!p_chmask) {
usb_audio_err(mixer->chip,
"BADD HEADSET profile: no playback channels?\n");
return -EINVAL;
}
break;
- case UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER:
/* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt possible */
if (c_chmask != 1)
usb_audio_warn(mixer->chip,
"BADD HEADSET_ADAPTER c_chmask mismatch: expected 1 actual %d\n",
c_chmask);
if (p_chmask != 3)
usb_audio_warn(mixer->chip,
"BADD HEADSET_ADAPTER p_chmask mismatch: expected 3 actual %d\n",
p_chmask);
c_chmask = 1;
st_chmask = 1;
p_chmask = 3;
break;
- case UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE:
/* BAIF + BAOF; IN: Mono only; OUT: Mono only */
if (c_chmask != 1)
usb_audio_warn(mixer->chip,
"BADD SPEAKERPHONE c_chmask mismatch: expected 1 actual %d\n",
c_chmask);
if (p_chmask != 1)
usb_audio_warn(mixer->chip,
"BADD SPEAKERPHONE p_chmask mismatch: expected 1 actual %d\n",
p_chmask);
c_chmask = 1;
p_chmask = 1;
break;
- }
- /* 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
- walk through all UAC_OUTPUT_TERMINAL descriptors to search for
mixers @@ -2838,9 +3126,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;
}
snd_usb_mixer_apply_create_quirk(mixer);
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 9038b2e..13f03c8 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -482,3 +482,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, "Side Tone 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 586d664..ea5a13e0 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -798,15 +798,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) {
@@ -922,18 +974,31 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) * (fp->maxpacksize & 0x7ff);
fp->attributes = parse_uac_endpoint_attributes(chip, alts,
UAC_VERSION_3,
iface_no);
fp->clock = clock; fp->chmap = chmap; INIT_LIST_HEAD(&fp->list);
/* 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 4d5c89a..1bb5e2c 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;
On Sat, Apr 14, 2018 at 8:55 PM, Jorge Sanjuan jorge.sanjuan@codethink.co.uk wrote:
On 2018-04-13 23: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.
Right. I would have thought that, since BADD is a subset of UAC3, it may be simpler to fill the Class Specific descriptors buffer and let the UAC3 path intact as it would result in the same behavior (for UAC3 and BADD configs) without the need to add that much code to the mixer, which is already quite big.
In the patch I proposed [1], the Class Specific buffer is filled once with the BADD descriptors, which are already UAC3 compliant, so the driver would handle the rest in the same way it would do with an UAC3 configuration.
That was looking as a good idea to me as well when I seen patch [1] first time. However, after thinking a bit more, I realized that in mixer.c we just need to initialize from one to three feature units for any BADD profile. Mentioned Feature Units are simple and can have only Volume/Mute controls. We also have nothing to do with Mixer Unit (which exists in BAIOF topology) since it doesn't have any controls. Most of the code there is to just detect all possible combinations of channels, topologies (BAIF, BAOF, BAIOF, BAIF+BAOF) for BADD profiles, and to add some meaningful names for feature unit controls, which are missing in the descriptors. The only change in the mixer.c I'm unhappy with is to have separate build_feature_ctl_badd() function that is nothing else but simplified version of build_feature_ctl() function; but I already have an idea how to reuse original one for BADD case.
Changes to stream.c are very simple and straightforward, almost all values are common/predefined for all BADD profiles except channel numbers and sample size.
So as a boottom line direct changes to mixer/stream code seems more easy and understandable in this particular case rather than generating all needed class-specific desriptors. And we have all BADD profiles support in this quite short patch.
Thanks, Ruslan
I will keep an eye on this as I'd need to do some work based on this instead.
[1] https://www.spinics.net/lists/alsa-devel/msg71617.html
Thanks,
Jorge
This patch adds support of all BADD profiles from the spec
Signed-off-by: Ruslan Bilovol ruslan.bilovol@gmail.com
sound/usb/card.c | 14 +++ sound/usb/clock.c | 9 +- sound/usb/mixer.c | 313 +++++++++++++++++++++++++++++++++++++++++++++++-- sound/usb/mixer_maps.c | 65 ++++++++++ sound/usb/stream.c | 83 +++++++++++-- sound/usb/usbaudio.h | 2 + 6 files changed, 466 insertions(+), 20 deletions(-)
diff --git a/sound/usb/card.c b/sound/usb/card.c index 4d866bd..47ebc50 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 301ad61..e5c3b0d 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,6 +1331,76 @@ static struct usb_feature_control_info *get_feature_control_info(int control) return NULL; }
+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)
+{
struct usb_feature_control_info *ctl_info;
unsigned int len = 0;
struct snd_kcontrol *kctl;
struct usb_mixer_elem_info *cval;
const struct usbmix_name_map *map;
map = find_map(badd_map, unitid, control);
if (!map)
return;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return;
snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid);
cval->control = control;
cval->cmask = ctl_mask;
ctl_info = get_feature_control_info(control);
if (!ctl_info) {
kfree(cval);
return;
}
cval->val_type = ctl_info->type;
if (ctl_mask == 0) {
cval->channels = 1; /* master channel */
} else {
int i, c = 0;
for (i = 0; i < 2; i++)
if (ctl_mask & (1 << i))
c++;
cval->channels = c;
}
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
kfree(cval);
return;
}
kctl->private_free = snd_usb_mixer_elem_free;
len = check_mapped_name(map, kctl->id.name,
sizeof(kctl->id.name));
append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : "
Volume");
/* get min/max values */
get_min_max_with_quirks(cval, 0, kctl);
if (control == UAC_FU_VOLUME) {
check_mapped_dB(map, cval);
if (cval->dBmin < cval->dBmax || !cval->initialized) {
kctl->tlv.c = snd_usb_mixer_vol_tlv;
kctl->vd[0].access |=
SNDRV_CTL_ELEM_ACCESS_TLV_READ |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
}
}
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, @@ -1353,7 +1421,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, return; }
map = find_map(state, unitid, control);
map = find_map(state->map, unitid, control); if (check_ignored_ctl(map)) return;
@@ -1806,7 +1874,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;
@@ -2105,7 +2173,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);
@@ -2308,7 +2376,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;
@@ -2495,6 +2563,226 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) }
/*
- 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;
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;
if (intf != ctrlif) {
struct usb_interface *iface;
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
unsigned int maxpacksize;
char dir_in;
int chmask, num;
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) {
mixer->ignore_ctl_error = map->ignore_ctl_error;
break;
}
}
if (!map->id)
return -EINVAL;
switch (badd_profile) {
default:
return -EINVAL;
case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
/*
* BAIF, BAOF or combination of both
* IN: Mono or Stereo cfg, Mono alt possible
* OUT: Mono or Stereo cfg, Mono alt possible
*/
/* c_chmask := DYNAMIC */
/* p_chmask := DYNAMIC */
if (!c_chmask && !p_chmask) {
usb_audio_err(mixer->chip,
"BADD GENERIC_IO profile: no
channels?\n");
return -EINVAL;
}
break;
case UAC3_FUNCTION_SUBCLASS_HEADPHONE:
/* BAOF; Stereo only cfg, Mono alt possible */
if (p_chmask != 3)
usb_audio_warn(mixer->chip,
"BADD HEADPHONE p_chmask mismatch:
expected 3 actual %d\n",
p_chmask);
p_chmask = 3;
break;
case UAC3_FUNCTION_SUBCLASS_SPEAKER:
/* BAOF; Mono or Stereo cfg, Mono alt possible */
/* p_chmask := DYNAMIC */
if (!p_chmask) {
usb_audio_err(mixer->chip,
"BADD SPEAKER profile: no playback
channels?\n");
return -EINVAL;
}
break;
case UAC3_FUNCTION_SUBCLASS_MICROPHONE:
/* BAIF; Mono or Stereo cfg, Mono alt possible */
/* c_chmask := DYNAMIC */
if (!c_chmask) {
usb_audio_err(mixer->chip,
"BADD MICROPHONE profile: no capture
channels?\n");
return -EINVAL;
}
break;
case UAC3_FUNCTION_SUBCLASS_HEADSET:
/*
* BAIOF
* IN: Mono only
* OUT: Mono or Stereo cfg, Mono alt possible
*/
if (c_chmask != 1)
usb_audio_warn(mixer->chip,
"BADD HEADSET c_chmask mismatch: expected
1 actual %d\n",
c_chmask);
c_chmask = 1;
st_chmask = 1;
/* p_chmask := DYNAMIC */
if (!p_chmask) {
usb_audio_err(mixer->chip,
"BADD HEADSET profile: no playback
channels?\n");
return -EINVAL;
}
break;
case UAC3_FUNCTION_SUBCLASS_HEADSET_ADAPTER:
/* BAIOF; IN: Mono only; OUT: Stereo only, Mono alt
possible */
if (c_chmask != 1)
usb_audio_warn(mixer->chip,
"BADD HEADSET_ADAPTER c_chmask mismatch:
expected 1 actual %d\n",
c_chmask);
if (p_chmask != 3)
usb_audio_warn(mixer->chip,
"BADD HEADSET_ADAPTER p_chmask mismatch:
expected 3 actual %d\n",
p_chmask);
c_chmask = 1;
st_chmask = 1;
p_chmask = 3;
break;
case UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE:
/* BAIF + BAOF; IN: Mono only; OUT: Mono only */
if (c_chmask != 1)
usb_audio_warn(mixer->chip,
"BADD SPEAKERPHONE c_chmask mismatch:
expected 1 actual %d\n",
c_chmask);
if (p_chmask != 1)
usb_audio_warn(mixer->chip,
"BADD SPEAKERPHONE p_chmask mismatch:
expected 1 actual %d\n",
p_chmask);
c_chmask = 1;
p_chmask = 1;
break;
}
/* 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
- walk through all UAC_OUTPUT_TERMINAL descriptors to search for mixers
@@ -2838,9 +3126,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)) <
goto _error;
} else if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
(err = snd_usb_mixer_status_create(mixer)) < 0) { goto _error;
} snd_usb_mixer_apply_create_quirk(mixer);
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 9038b2e..13f03c8 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -482,3 +482,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, "Side Tone 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 586d664..ea5a13e0 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -798,15 +798,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) {
@@ -922,18 +974,31 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) * (fp->maxpacksize & 0x7ff);
fp->attributes = parse_uac_endpoint_attributes(chip, alts,
UAC_VERSION_3,
iface_no); fp->clock = clock; fp->chmap = chmap; INIT_LIST_HEAD(&fp->list);
/* 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) <
- {
kfree(fp->rate_table);
kfree(fp);
return NULL;
} } return fp;
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 4d5c89a..1bb5e2c 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;
Hi Ruslan,
Thank you for the patch! Perhaps something to improve:
url: https://github.com/0day-ci/linux/commits/Ruslan-Bilovol/USB-Audio-Device-Cla... base: https://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git for-next
smatch warnings: sound/usb/stream.c:964 snd_usb_get_audioformat_uac3() warn: possible memory leak of 'chmap'
# https://github.com/0day-ci/linux/commit/7e255927f3e697fce36dcd48f205a7997771... git remote add linux-review https://github.com/0day-ci/linux git remote update linux-review git checkout 7e255927f3e697fce36dcd48f205a7997771ed4d vim +/chmap +964 sound/usb/stream.c
9a2fe9b801 Ruslan Bilovol 2018-03-21 791 1145e3d13b Ruslan Bilovol 2018-04-14 792 static struct audioformat * 1145e3d13b Ruslan Bilovol 2018-04-14 793 snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, 1145e3d13b Ruslan Bilovol 2018-04-14 794 struct usb_host_interface *alts, 1145e3d13b Ruslan Bilovol 2018-04-14 795 int iface_no, int altno, int stream) 1145e3d13b Ruslan Bilovol 2018-04-14 796 { 1145e3d13b Ruslan Bilovol 2018-04-14 797 struct usb_device *dev = chip->dev; 9a2fe9b801 Ruslan Bilovol 2018-03-21 798 struct uac3_input_terminal_descriptor *input_term; 9a2fe9b801 Ruslan Bilovol 2018-03-21 799 struct uac3_output_terminal_descriptor *output_term; 9a2fe9b801 Ruslan Bilovol 2018-03-21 800 struct uac3_cluster_header_descriptor *cluster; 7e255927f3 Ruslan Bilovol 2018-04-14 801 struct uac3_as_header_descriptor *as = NULL; 9a2fe9b801 Ruslan Bilovol 2018-03-21 802 struct uac3_hc_descriptor_header hc_header; 1145e3d13b Ruslan Bilovol 2018-04-14 803 struct snd_pcm_chmap_elem *chmap; 7e255927f3 Ruslan Bilovol 2018-04-14 804 unsigned char badd_profile; 7e255927f3 Ruslan Bilovol 2018-04-14 805 u64 badd_formats = 0; 1145e3d13b Ruslan Bilovol 2018-04-14 806 unsigned int num_channels; 1145e3d13b Ruslan Bilovol 2018-04-14 807 struct audioformat *fp; 9a2fe9b801 Ruslan Bilovol 2018-03-21 808 u16 cluster_id, wLength; 1145e3d13b Ruslan Bilovol 2018-04-14 809 int clock = 0; 1145e3d13b Ruslan Bilovol 2018-04-14 810 int err; 9a2fe9b801 Ruslan Bilovol 2018-03-21 811 7e255927f3 Ruslan Bilovol 2018-04-14 812 badd_profile = chip->badd_profile; 7e255927f3 Ruslan Bilovol 2018-04-14 813 7e255927f3 Ruslan Bilovol 2018-04-14 814 if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { 7e255927f3 Ruslan Bilovol 2018-04-14 815 unsigned int maxpacksize = 7e255927f3 Ruslan Bilovol 2018-04-14 816 le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); 7e255927f3 Ruslan Bilovol 2018-04-14 817 7e255927f3 Ruslan Bilovol 2018-04-14 818 switch (maxpacksize) { 7e255927f3 Ruslan Bilovol 2018-04-14 819 default: 7e255927f3 Ruslan Bilovol 2018-04-14 820 dev_err(&dev->dev, 7e255927f3 Ruslan Bilovol 2018-04-14 821 "%u:%d : incorrect wMaxPacketSize for BADD profile\n", 7e255927f3 Ruslan Bilovol 2018-04-14 822 iface_no, altno); 7e255927f3 Ruslan Bilovol 2018-04-14 823 return NULL; 7e255927f3 Ruslan Bilovol 2018-04-14 824 case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16: 7e255927f3 Ruslan Bilovol 2018-04-14 825 case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_16: 7e255927f3 Ruslan Bilovol 2018-04-14 826 badd_formats = SNDRV_PCM_FMTBIT_S16_LE; 7e255927f3 Ruslan Bilovol 2018-04-14 827 num_channels = 1; 7e255927f3 Ruslan Bilovol 2018-04-14 828 break; 7e255927f3 Ruslan Bilovol 2018-04-14 829 case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24: 7e255927f3 Ruslan Bilovol 2018-04-14 830 case UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_24: 7e255927f3 Ruslan Bilovol 2018-04-14 831 badd_formats = SNDRV_PCM_FMTBIT_S24_3LE; 7e255927f3 Ruslan Bilovol 2018-04-14 832 num_channels = 1; 7e255927f3 Ruslan Bilovol 2018-04-14 833 break; 7e255927f3 Ruslan Bilovol 2018-04-14 834 case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16: 7e255927f3 Ruslan Bilovol 2018-04-14 835 case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_16: 7e255927f3 Ruslan Bilovol 2018-04-14 836 badd_formats = SNDRV_PCM_FMTBIT_S16_LE; 7e255927f3 Ruslan Bilovol 2018-04-14 837 num_channels = 2; 7e255927f3 Ruslan Bilovol 2018-04-14 838 break; 7e255927f3 Ruslan Bilovol 2018-04-14 839 case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_24: 7e255927f3 Ruslan Bilovol 2018-04-14 840 case UAC3_BADD_EP_MAXPSIZE_ASYNC_STEREO_24: 7e255927f3 Ruslan Bilovol 2018-04-14 841 badd_formats = SNDRV_PCM_FMTBIT_S24_3LE; 7e255927f3 Ruslan Bilovol 2018-04-14 842 num_channels = 2; 7e255927f3 Ruslan Bilovol 2018-04-14 843 break; 7e255927f3 Ruslan Bilovol 2018-04-14 844 } 7e255927f3 Ruslan Bilovol 2018-04-14 845 7e255927f3 Ruslan Bilovol 2018-04-14 846 chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); 7e255927f3 Ruslan Bilovol 2018-04-14 847 if (!chmap) 7e255927f3 Ruslan Bilovol 2018-04-14 848 return ERR_PTR(-ENOMEM); 7e255927f3 Ruslan Bilovol 2018-04-14 849 7e255927f3 Ruslan Bilovol 2018-04-14 850 if (num_channels == 1) { 7e255927f3 Ruslan Bilovol 2018-04-14 851 chmap->map[0] = SNDRV_CHMAP_MONO; 7e255927f3 Ruslan Bilovol 2018-04-14 852 } else { 7e255927f3 Ruslan Bilovol 2018-04-14 853 chmap->map[0] = SNDRV_CHMAP_FL; 7e255927f3 Ruslan Bilovol 2018-04-14 854 chmap->map[1] = SNDRV_CHMAP_FR; 7e255927f3 Ruslan Bilovol 2018-04-14 855 } 7e255927f3 Ruslan Bilovol 2018-04-14 856 7e255927f3 Ruslan Bilovol 2018-04-14 857 chmap->channels = num_channels; 7e255927f3 Ruslan Bilovol 2018-04-14 858 clock = UAC3_BADD_CS_ID9; 7e255927f3 Ruslan Bilovol 2018-04-14 859 goto found_clock; 7e255927f3 Ruslan Bilovol 2018-04-14 860 } 7e255927f3 Ruslan Bilovol 2018-04-14 861 1145e3d13b Ruslan Bilovol 2018-04-14 862 as = snd_usb_find_csint_desc(alts->extra, alts->extralen, 9a2fe9b801 Ruslan Bilovol 2018-03-21 863 NULL, UAC_AS_GENERAL); 9a2fe9b801 Ruslan Bilovol 2018-03-21 864 if (!as) { 9a2fe9b801 Ruslan Bilovol 2018-03-21 865 dev_err(&dev->dev, 9a2fe9b801 Ruslan Bilovol 2018-03-21 866 "%u:%d : UAC_AS_GENERAL descriptor not found\n", 9a2fe9b801 Ruslan Bilovol 2018-03-21 867 iface_no, altno); 1145e3d13b Ruslan Bilovol 2018-04-14 868 return NULL; e8e8babf56 Daniel Mack 2011-09-12 869 } e8e8babf56 Daniel Mack 2011-09-12 870 9a2fe9b801 Ruslan Bilovol 2018-03-21 871 if (as->bLength < sizeof(*as)) { 9a2fe9b801 Ruslan Bilovol 2018-03-21 872 dev_err(&dev->dev, 9a2fe9b801 Ruslan Bilovol 2018-03-21 873 "%u:%d : invalid UAC_AS_GENERAL desc\n", 9a2fe9b801 Ruslan Bilovol 2018-03-21 874 iface_no, altno); 1145e3d13b Ruslan Bilovol 2018-04-14 875 return NULL; 9a2fe9b801 Ruslan Bilovol 2018-03-21 876 } 9a2fe9b801 Ruslan Bilovol 2018-03-21 877 9a2fe9b801 Ruslan Bilovol 2018-03-21 878 cluster_id = le16_to_cpu(as->wClusterDescrID); 9a2fe9b801 Ruslan Bilovol 2018-03-21 879 if (!cluster_id) { 9a2fe9b801 Ruslan Bilovol 2018-03-21 880 dev_err(&dev->dev, 9a2fe9b801 Ruslan Bilovol 2018-03-21 881 "%u:%d : no cluster descriptor\n", 9a2fe9b801 Ruslan Bilovol 2018-03-21 882 iface_no, altno); 1145e3d13b Ruslan Bilovol 2018-04-14 883 return NULL; 9a2fe9b801 Ruslan Bilovol 2018-03-21 884 } 9a2fe9b801 Ruslan Bilovol 2018-03-21 885 9a2fe9b801 Ruslan Bilovol 2018-03-21 886 /* 9a2fe9b801 Ruslan Bilovol 2018-03-21 887 * Get number of channels and channel map through 9a2fe9b801 Ruslan Bilovol 2018-03-21 888 * High Capability Cluster Descriptor 9a2fe9b801 Ruslan Bilovol 2018-03-21 889 * 9a2fe9b801 Ruslan Bilovol 2018-03-21 890 * First step: get High Capability header and 9a2fe9b801 Ruslan Bilovol 2018-03-21 891 * read size of Cluster Descriptor 9a2fe9b801 Ruslan Bilovol 2018-03-21 892 */ 9a2fe9b801 Ruslan Bilovol 2018-03-21 893 err = snd_usb_ctl_msg(chip->dev, 9a2fe9b801 Ruslan Bilovol 2018-03-21 894 usb_rcvctrlpipe(chip->dev, 0), 9a2fe9b801 Ruslan Bilovol 2018-03-21 895 UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, 9a2fe9b801 Ruslan Bilovol 2018-03-21 896 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 9a2fe9b801 Ruslan Bilovol 2018-03-21 897 cluster_id, 9a2fe9b801 Ruslan Bilovol 2018-03-21 898 snd_usb_ctrl_intf(chip), 9a2fe9b801 Ruslan Bilovol 2018-03-21 899 &hc_header, sizeof(hc_header)); 9a2fe9b801 Ruslan Bilovol 2018-03-21 900 if (err < 0) 1145e3d13b Ruslan Bilovol 2018-04-14 901 return ERR_PTR(err); 9a2fe9b801 Ruslan Bilovol 2018-03-21 902 else if (err != sizeof(hc_header)) { 9a2fe9b801 Ruslan Bilovol 2018-03-21 903 dev_err(&dev->dev, 9a2fe9b801 Ruslan Bilovol 2018-03-21 904 "%u:%d : can't get High Capability descriptor\n", 9a2fe9b801 Ruslan Bilovol 2018-03-21 905 iface_no, altno); 1145e3d13b Ruslan Bilovol 2018-04-14 906 return ERR_PTR(-EIO); 9a2fe9b801 Ruslan Bilovol 2018-03-21 907 } 9a2fe9b801 Ruslan Bilovol 2018-03-21 908 9a2fe9b801 Ruslan Bilovol 2018-03-21 909 /* 9a2fe9b801 Ruslan Bilovol 2018-03-21 910 * Second step: allocate needed amount of memory 9a2fe9b801 Ruslan Bilovol 2018-03-21 911 * and request Cluster Descriptor 9a2fe9b801 Ruslan Bilovol 2018-03-21 912 */ 9a2fe9b801 Ruslan Bilovol 2018-03-21 913 wLength = le16_to_cpu(hc_header.wLength); 9a2fe9b801 Ruslan Bilovol 2018-03-21 914 cluster = kzalloc(wLength, GFP_KERNEL); 9a2fe9b801 Ruslan Bilovol 2018-03-21 915 if (!cluster) 1145e3d13b Ruslan Bilovol 2018-04-14 916 return ERR_PTR(-ENOMEM); 9a2fe9b801 Ruslan Bilovol 2018-03-21 917 err = snd_usb_ctl_msg(chip->dev, 9a2fe9b801 Ruslan Bilovol 2018-03-21 918 usb_rcvctrlpipe(chip->dev, 0), 9a2fe9b801 Ruslan Bilovol 2018-03-21 919 UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, 9a2fe9b801 Ruslan Bilovol 2018-03-21 920 USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, 9a2fe9b801 Ruslan Bilovol 2018-03-21 921 cluster_id, 9a2fe9b801 Ruslan Bilovol 2018-03-21 922 snd_usb_ctrl_intf(chip), 9a2fe9b801 Ruslan Bilovol 2018-03-21 923 cluster, wLength); 9a2fe9b801 Ruslan Bilovol 2018-03-21 924 if (err < 0) { 9a2fe9b801 Ruslan Bilovol 2018-03-21 925 kfree(cluster); 1145e3d13b Ruslan Bilovol 2018-04-14 926 return ERR_PTR(err); 9a2fe9b801 Ruslan Bilovol 2018-03-21 927 } else if (err != wLength) { 9a2fe9b801 Ruslan Bilovol 2018-03-21 928 dev_err(&dev->dev, 9a2fe9b801 Ruslan Bilovol 2018-03-21 929 "%u:%d : can't get Cluster Descriptor\n", 9a2fe9b801 Ruslan Bilovol 2018-03-21 930 iface_no, altno); 9a2fe9b801 Ruslan Bilovol 2018-03-21 931 kfree(cluster); 1145e3d13b Ruslan Bilovol 2018-04-14 932 return ERR_PTR(-EIO); 9a2fe9b801 Ruslan Bilovol 2018-03-21 933 } 9a2fe9b801 Ruslan Bilovol 2018-03-21 934 9a2fe9b801 Ruslan Bilovol 2018-03-21 935 num_channels = cluster->bNrChannels; 1145e3d13b Ruslan Bilovol 2018-04-14 936 chmap = convert_chmap_v3(cluster); 9a2fe9b801 Ruslan Bilovol 2018-03-21 937 kfree(cluster); 9a2fe9b801 Ruslan Bilovol 2018-03-21 938 1145e3d13b Ruslan Bilovol 2018-04-14 939 /* 1145e3d13b Ruslan Bilovol 2018-04-14 940 * lookup the terminal associated to this interface 1145e3d13b Ruslan Bilovol 2018-04-14 941 * to extract the clock 1145e3d13b Ruslan Bilovol 2018-04-14 942 */ 1145e3d13b Ruslan Bilovol 2018-04-14 943 input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, 9a2fe9b801 Ruslan Bilovol 2018-03-21 944 as->bTerminalLink); 9a2fe9b801 Ruslan Bilovol 2018-03-21 945 if (input_term) { 9a2fe9b801 Ruslan Bilovol 2018-03-21 946 clock = input_term->bCSourceID; 1145e3d13b Ruslan Bilovol 2018-04-14 947 goto found_clock; 9a2fe9b801 Ruslan Bilovol 2018-03-21 948 } 9a2fe9b801 Ruslan Bilovol 2018-03-21 949 9a2fe9b801 Ruslan Bilovol 2018-03-21 950 output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, 9a2fe9b801 Ruslan Bilovol 2018-03-21 951 as->bTerminalLink); 9a2fe9b801 Ruslan Bilovol 2018-03-21 952 if (output_term) { 9a2fe9b801 Ruslan Bilovol 2018-03-21 953 clock = output_term->bCSourceID; 1145e3d13b Ruslan Bilovol 2018-04-14 954 goto found_clock; 9a2fe9b801 Ruslan Bilovol 2018-03-21 955 } 9a2fe9b801 Ruslan Bilovol 2018-03-21 956 1145e3d13b Ruslan Bilovol 2018-04-14 957 dev_err(&dev->dev, "%u:%d : bogus bTerminalLink %d\n", 9a2fe9b801 Ruslan Bilovol 2018-03-21 958 iface_no, altno, as->bTerminalLink); 1145e3d13b Ruslan Bilovol 2018-04-14 959 return NULL; e8e8babf56 Daniel Mack 2011-09-12 960 1145e3d13b Ruslan Bilovol 2018-04-14 961 found_clock: e8e8babf56 Daniel Mack 2011-09-12 962 fp = kzalloc(sizeof(*fp), GFP_KERNEL); 9ecb2406de Markus Elfring 2017-08-11 963 if (!fp) 1145e3d13b Ruslan Bilovol 2018-04-14 @964 return ERR_PTR(-ENOMEM); e8e8babf56 Daniel Mack 2011-09-12 965 e8e8babf56 Daniel Mack 2011-09-12 966 fp->iface = iface_no; e8e8babf56 Daniel Mack 2011-09-12 967 fp->altsetting = altno; e8e8babf56 Daniel Mack 2011-09-12 968 fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; e8e8babf56 Daniel Mack 2011-09-12 969 fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; e8e8babf56 Daniel Mack 2011-09-12 970 fp->datainterval = snd_usb_parse_datainterval(chip, alts); 1145e3d13b Ruslan Bilovol 2018-04-14 971 fp->protocol = UAC_VERSION_3; e8e8babf56 Daniel Mack 2011-09-12 972 fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); e8e8babf56 Daniel Mack 2011-09-12 973 fp->channels = num_channels; e8e8babf56 Daniel Mack 2011-09-12 974 if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) e8e8babf56 Daniel Mack 2011-09-12 975 fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) e8e8babf56 Daniel Mack 2011-09-12 976 * (fp->maxpacksize & 0x7ff); e8e8babf56 Daniel Mack 2011-09-12 977 fp->clock = clock; 1145e3d13b Ruslan Bilovol 2018-04-14 978 fp->chmap = chmap; 836b34a935 Vladis Dronov 2016-03-31 979 INIT_LIST_HEAD(&fp->list); e8e8babf56 Daniel Mack 2011-09-12 980 7e255927f3 Ruslan Bilovol 2018-04-14 981 if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { 7e255927f3 Ruslan Bilovol 2018-04-14 982 fp->attributes = 0; /* No attributes */ 7e255927f3 Ruslan Bilovol 2018-04-14 983 7e255927f3 Ruslan Bilovol 2018-04-14 984 fp->fmt_type = UAC_FORMAT_TYPE_I; 7e255927f3 Ruslan Bilovol 2018-04-14 985 fp->formats = badd_formats; 7e255927f3 Ruslan Bilovol 2018-04-14 986 7e255927f3 Ruslan Bilovol 2018-04-14 987 fp->nr_rates = 0; /* SNDRV_PCM_RATE_CONTINUOUS */ 7e255927f3 Ruslan Bilovol 2018-04-14 988 fp->rate_min = UAC3_BADD_SAMPLING_RATE; 7e255927f3 Ruslan Bilovol 2018-04-14 989 fp->rate_max = UAC3_BADD_SAMPLING_RATE; 7e255927f3 Ruslan Bilovol 2018-04-14 990 fp->rates = SNDRV_PCM_RATE_CONTINUOUS; 7e255927f3 Ruslan Bilovol 2018-04-14 991 7e255927f3 Ruslan Bilovol 2018-04-14 992 } else { 7e255927f3 Ruslan Bilovol 2018-04-14 993 fp->attributes = parse_uac_endpoint_attributes(chip, alts, 7e255927f3 Ruslan Bilovol 2018-04-14 994 UAC_VERSION_3, 7e255927f3 Ruslan Bilovol 2018-04-14 995 iface_no); e8e8babf56 Daniel Mack 2011-09-12 996 /* ok, let's parse further... */ 1145e3d13b Ruslan Bilovol 2018-04-14 997 if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { e8e8babf56 Daniel Mack 2011-09-12 998 kfree(fp->rate_table); e8e8babf56 Daniel Mack 2011-09-12 999 kfree(fp); 1145e3d13b Ruslan Bilovol 2018-04-14 1000 return NULL; e8e8babf56 Daniel Mack 2011-09-12 1001 } 7e255927f3 Ruslan Bilovol 2018-04-14 1002 } 9a2fe9b801 Ruslan Bilovol 2018-03-21 1003 1145e3d13b Ruslan Bilovol 2018-04-14 1004 return fp; 1145e3d13b Ruslan Bilovol 2018-04-14 1005 } 9a2fe9b801 Ruslan Bilovol 2018-03-21 1006
--- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Sat, Apr 14, 2018 at 6:24 AM, Ruslan Bilovol ruslan.bilovol@gmail.com 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
sound/usb/card.c | 14 +++ sound/usb/clock.c | 9 +- sound/usb/mixer.c | 313 +++++++++++++++++++++++++++++++++++++++++++++++-- sound/usb/mixer_maps.c | 65 ++++++++++ sound/usb/stream.c | 83 +++++++++++-- sound/usb/usbaudio.h | 2 + 6 files changed, 466 insertions(+), 20 deletions(-)
--- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -482,3 +482,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, "Side Tone Mixing" },
Can you please call this "Sidetone"? This better matches other Sidetone control names in the sound tree and makes it compatible with existing Android userspace usage.
On Thu, Apr 19, 2018 at 12:42 PM, Andrew Chant achant@google.com wrote:
On Sat, Apr 14, 2018 at 6:24 AM, Ruslan Bilovol ruslan.bilovol@gmail.com 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
sound/usb/card.c | 14 +++ sound/usb/clock.c | 9 +- sound/usb/mixer.c | 313 +++++++++++++++++++++++++++++++++++++++++++++++-- sound/usb/mixer_maps.c | 65 ++++++++++ sound/usb/stream.c | 83 +++++++++++-- sound/usb/usbaudio.h | 2 + 6 files changed, 466 insertions(+), 20 deletions(-)
--- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -482,3 +482,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, "Side Tone Mixing" },
Can you please call this "Sidetone"? This better matches other Sidetone control names in the sound tree and makes it compatible with existing Android userspace usage.
I have no any objections, "Side Tone" was in UAC3 documentation, but "Sidetone" is OK too, so will change it
Thanks, Ruslan
On Sat, 14 Apr 2018 00:24:26 +0200, Ruslan Bilovol wrote:
+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)
+{
....
- kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
- if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
No need for error message after malloc failure. The kernel is already chatty about 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;
- 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;
if (intf != ctrlif) {
In this case, it's better to skip like
if (intf == ctrlif) continue;
so that we can save an indentation for the whole long block.
- switch (badd_profile) {
- default:
return -EINVAL;
- case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
/*
* BAIF, BAOF or combination of both
* IN: Mono or Stereo cfg, Mono alt possible
* OUT: Mono or Stereo cfg, Mono alt possible
*/
/* c_chmask := DYNAMIC */
/* p_chmask := DYNAMIC */
if (!c_chmask && !p_chmask) {
usb_audio_err(mixer->chip,
"BADD GENERIC_IO profile: no channels?\n");
return -EINVAL;
}
break;
Maybe we can simplify the whole switch/case with a table lookup. For example,
for (f = uac3_func_tables; f->name; f++) { if (badd_profile == f->subclass) break; } if (!f->name) return -EINVAL; if (!uac3_func_has_valid_channels(mixer, f, c_chmask, p_chmask)) return -EINVAL; st_chmask = f->st_chmask;
and in uac3_func_has_valid_channels(),
static bool uac3_func_has_valid_channels() { 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; }
thanks,
Takashi
On Thu, Apr 19, 2018 at 1:19 PM, Takashi Iwai tiwai@suse.de wrote:
On Sat, 14 Apr 2018 00:24:26 +0200, Ruslan Bilovol wrote:
+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)
+{
....
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
if (!kctl) {
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
No need for error message after malloc failure. The kernel is already chatty about it.
Okay. BTW, I'm trying to avoid separate badd variant of build_feature_ctl(), so this most probably will go away.
There are many existing places in usb-snd which can be cleaned up though.
+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;
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;
if (intf != ctrlif) {
In this case, it's better to skip like
if (intf == ctrlif) continue;
so that we can save an indentation for the whole long block.
Good point, will do it
switch (badd_profile) {
default:
return -EINVAL;
case UAC3_FUNCTION_SUBCLASS_GENERIC_IO:
/*
* BAIF, BAOF or combination of both
* IN: Mono or Stereo cfg, Mono alt possible
* OUT: Mono or Stereo cfg, Mono alt possible
*/
/* c_chmask := DYNAMIC */
/* p_chmask := DYNAMIC */
if (!c_chmask && !p_chmask) {
usb_audio_err(mixer->chip,
"BADD GENERIC_IO profile: no channels?\n");
return -EINVAL;
}
break;
Maybe we can simplify the whole switch/case with a table lookup.
Yes, it should be possible, I'll implent it that way
Thanks, Ruslan
For example,
for (f = uac3_func_tables; f->name; f++) { if (badd_profile == f->subclass) break; } if (!f->name) return -EINVAL; if (!uac3_func_has_valid_channels(mixer, f, c_chmask, p_chmask)) return -EINVAL; st_chmask = f->st_chmask;
and in uac3_func_has_valid_channels(),
static bool uac3_func_has_valid_channels() { 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; }
thanks,
Takashi
participants (5)
-
Andrew Chant
-
Jorge Sanjuan
-
kbuild test robot
-
Ruslan Bilovol
-
Takashi Iwai