Re: [PATCH 0/8] USB Audio Gadget part 2: Feedback endpoint, Volume/Mute support
Hi Johannes, On Mon, Mar 1, 2021 at 6:49 PM Johannes Freyberger wrote: > > Hi Ruslan, > > thanks for all your efforts to make the USB Audio Gadget work in Win10 using > UAC2. Meanwhile I managed to apply and compile your previous modifications > and now my Raspberry PI shows up in the Windows Device Manager as a valid > UAC2 audio device. Unfortunately it still doesn't work to transfer any audio > as it seems the audio endpoints or the topology is not working. Are you testing my previous version of the patches on some older kernel? Just for records - these two patch sets (part 1 and part 2) are based on Greg's usb-next branch (commit b5a12546e779d4f5586f58e60e0ef5070a833a64 which is based on v5.11-rc5 tag). I retested them today with a BBB board and it works fine under Win 10. Also I rebased these two patchsets today against latest Greg's usb-next branch which is now Linus's v5.12-rc1 tag and again it works fine under Win10 - both Volume/Mute controls and audio streaming. These patches have been tested previously on Raspberry PI 4 running v5.9 and v5.10 stable kernels. The only issues I've seen were because of Raspberry's DWC2 DMA issue in the driver that I described in this cover letter. However if you disable volume/mute controls, it won't affect you. > I checked it > with some tools and found one providing some information on the USB part > (it's called UVCview.exe and is part of the Windows Driver Kit). Here's the > output which I hope can give some hints on the problems still existing in > this driver: >From the output below I see UAC2 descriptors are completely screwed up (or UVCview.exe doesn't show them correctly). Windows is very strict to the descriptors and doesn't allow devices to start in case of any issues. So if it appears as a valid UAC2 device in Device Manager, most likely UVCview.exe doesn't decode UAC2 descriptors well. Could you please also apply these patches to the latest kernel (v5.12-rc1) and test? Thanks, Ruslan > > ---===>Device Information<===--- > English product name: "Linux USB Audio Gadget" > > ConnectionStatus: > Current Config Value: 0x01 -> Device Bus Speed: High > Device Address:0x0F > Open Pipes: 0 > *!*ERROR: No open pipes! > > ===>Device Descriptor<=== > bLength: 0x12 > bDescriptorType: 0x01 > bcdUSB: 0x0200 > bDeviceClass: 0xEF -> This is a Multi-interface > Function Code Device > bDeviceSubClass: 0x02 -> This is the Common Class Sub > Class > bDeviceProtocol: 0x01 -> This is the Interface > Association Descriptor protocol > bMaxPacketSize0: 0x40 = (64) Bytes > idVendor:0x1D6B = The Linux Foundation > idProduct: 0x0101 > bcdDevice: 0x0510 > iManufacturer: 0x01 > English (United States) "Linux 5.10.17-v7l-R3LAY_TEST+ with > fe98.usb" > iProduct: 0x02 > English (United States) "Linux USB Audio Gadget" > iSerialNumber: 0x00 > bNumConfigurations:0x01 > > ===>Configuration Descriptor<=== > bLength: 0x09 > bDescriptorType: 0x02 > wTotalLength:0x00E2 -> Validated > bNumInterfaces:0x03 > bConfigurationValue: 0x01 > iConfiguration:0x00 > bmAttributes: 0xC0 -> Bus Powered > MaxPower: 0x01 = 2 mA > > ===>IAD Descriptor<=== > bLength: 0x08 > bDescriptorType: 0x0B > bFirstInterface: 0x00 > bInterfaceCount: 0x03 > bFunctionClass:0x01 -> Audio Interface Class > bFunctionSubClass: 0x00 > *!*CAUTION:This appears to be an invalid bFunctionSubClass > bFunctionProtocol: 0x20 > iFunction: 0x04 > English (United States) "R3lay PI" > > ===>Interface Descriptor<=== > bLength: 0x09 > bDescriptorType: 0x04 > bInterfaceNumber: 0x00 > bAlternateSetting: 0x00 > bNumEndpoints: 0x00 > bInterfaceClass: 0x01 -> Audio Interface Class > bInterfaceSubClass:0x01 -> Audio Control Interface SubClass > bInterfaceProtocol:0x20 > CAUTION: This may be an invalid bInterfaceProtocol > iInterface:0x05 > English (United States) "Topology Control" > > ===>Audio Control Interface Header Descriptor<=== > bLength: 0x09 > bDescriptorType: 0x24 > bDescriptorSubtype:0x01 > bcdADC: 0x0200 >
[PATCH 8/8] usb: gadget: f_uac1: add volume and mute support
This adds bi-directional (host->device, device->host) volume/mute support to the f_uac1 driver by adding Feature Units and interrupt endpoint. Currently only master channel is supported. Volume and mute are configurable through configfs, by default volume has -100..0 dB range with 1 dB step. Similar to existing flexible endpoints configuration, Feature Unit won't be added to the topology if both mute and volume are not enabled, also interrupt endpoint isn't added to the device if no feature unit is present Signed-off-by: Ruslan Bilovol --- Documentation/ABI/testing/configfs-usb-gadget-uac1 | 10 + Documentation/usb/gadget-testing.rst | 26 +- drivers/usb/gadget/function/f_uac1.c | 674 - drivers/usb/gadget/function/u_uac1.h | 20 + 4 files changed, 700 insertions(+), 30 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac1 b/Documentation/ABI/testing/configfs-usb-gadget-uac1 index dc23fd7..dd647d4 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac1 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac1 @@ -8,9 +8,19 @@ Description: c_chmaskcapture channel mask c_srate capture sampling rate c_ssize capture sample size (bytes) + c_mute_present capture mute control enable + c_volume_presentcapture volume control enable + c_volume_mincapture volume control min value (in 1/256 dB) + c_volume_maxcapture volume control max value (in 1/256 dB) + c_volume_rescapture volume control resolution (in 1/256 dB) p_chmaskplayback channel mask p_srate playback sampling rate p_ssize playback sample size (bytes) + p_mute_present playback mute control enable + p_volume_presentplayback volume control enable + p_volume_minplayback volume control min value (in 1/256 dB) + p_volume_maxplayback volume control max value (in 1/256 dB) + p_volume_resplayback volume control resolution (in 1/256 dB) req_number the number of pre-allocated request for both capture and playback == === diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 26adb0b..f078fc9 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -914,14 +914,24 @@ The function name to use when creating the function directory is "uac1". The uac1 function provides these attributes in its function directory: == - c_chmask capture channel mask - c_sratecapture sampling rate - c_ssizecapture sample size (bytes) - p_chmask playback channel mask - p_srateplayback sampling rate - p_ssizeplayback sample size (bytes) - req_number the number of pre-allocated request for both capture - and playback + c_chmaskcapture channel mask + c_srate capture sampling rate + c_ssize capture sample size (bytes) + c_mute_present capture mute control enable + c_volume_presentcapture volume control enable + c_volume_mincapture volume control min value (in 1/256 dB) + c_volume_maxcapture volume control max value (in 1/256 dB) + c_volume_rescapture volume control resolution (in 1/256 dB) + p_chmaskplayback channel mask + p_srate playback sampling rate + p_ssize playback sample size (bytes) + p_mute_present playback mute control enable + p_volume_presentplayback volume control enable + p_volume_minplayback volume control min value (in 1/256 dB) + p_volume_maxplayback volume control max value (in 1/256 dB) + p_volume_resplayback volume control resolution (in 1/256 dB) + req_number the number of pre-allocated request for both capture + and playback == The attributes have sane default values. diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index d047075..3b3db1a 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -22,13 +22,26 @@ /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ #define UAC1_CHANNEL_MASK 0x0FFF +#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID) +#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID) + #define EPIN_EN(_opts) ((_opts)->p_chmask != 0) #define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) +#d
[PATCH 7/8] usb: gadget: f_uac2: add volume and mute support
This adds bi-directional (host->device, device->host) volume/mute support to the f_uac2 driver by adding Feature Units and interrupt endpoint. Currently only master channel is supported. Volume and mute are configurable through configfs, by default volume has -100..0 dB range with 1 dB step. Similar to existing flexible endpoints configuration, Feature Unit won't be added to the topology if both mute and volume are not enabled, also interrupt endpoint isn't added to the device if no feature unit is present Signed-off-by: Ruslan Bilovol --- Documentation/ABI/testing/configfs-usb-gadget-uac2 | 10 + Documentation/usb/gadget-testing.rst | 10 + drivers/usb/gadget/function/f_uac2.c | 640 +++-- drivers/usb/gadget/function/u_uac2.h | 21 + 4 files changed, 623 insertions(+), 58 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2 index e7e59d7..9242234 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac2 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2 @@ -9,7 +9,17 @@ Description: c_sratecapture sampling rate c_ssizecapture sample size (bytes) c_sync capture synchronization type (async/adaptive) + c_mute_present capture mute control enable + c_volume_presentcapture volume control enable + c_volume_mincapture volume control min value (in 1/256 dB) + c_volume_maxcapture volume control max value (in 1/256 dB) + c_volume_rescapture volume control resolution (in 1/256 dB) p_chmask playback channel mask p_srateplayback sampling rate p_ssizeplayback sample size (bytes) + p_mute_present playback mute control enable + p_volume_presentplayback volume control enable + p_volume_minplayback volume control min value (in 1/256 dB) + p_volume_maxplayback volume control max value (in 1/256 dB) + p_volume_resplayback volume control resolution (in 1/256 dB) = diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index f5a1266..26adb0b 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -729,9 +729,19 @@ The uac2 function provides these attributes in its function directory: c_srate capture sampling rate c_ssize capture sample size (bytes) c_sync capture synchronization type (async/adaptive) + c_mute_present capture mute control enable + c_volume_presentcapture volume control enable + c_volume_mincapture volume control min value (in 1/256 dB) + c_volume_maxcapture volume control max value (in 1/256 dB) + c_volume_rescapture volume control resolution (in 1/256 dB) p_chmaskplayback channel mask p_srate playback sampling rate p_ssize playback sample size (bytes) + p_mute_present playback mute control enable + p_volume_presentplayback volume control enable + p_volume_minplayback volume control min value (in 1/256 dB) + p_volume_maxplayback volume control max value (in 1/256 dB) + p_volume_resplayback volume control resolution (in 1/256 dB) req_number the number of pre-allocated request for both capture and playback === diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 91b22fb..57f6bcb6 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -5,6 +5,9 @@ * Copyright (C) 2011 *Yadwinder Singh (yadi.bra...@gmail.com) *Jaswinder Singh (jaswinder.si...@linaro.org) + * + * Copyright (C) 2020 + *Ruslan Bilovol (ruslan.bilo...@gmail.com) */ #include @@ -19,14 +22,16 @@ /* * The driver implements a simple UAC_2 topology. - * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture - * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN + * USB-OUT -> IT_1 -> FU -> OT_3 -> ALSA_Capture + * ALSA_Playback -> IT_2 -> FU -> OT_4 -> USB-IN * Capture and Playback sampling rates are independently * controlled by two clock sources : *CLK_5 := c_srate, and CLK_6 := p_srate */ #define USB_OUT_CLK_ID (out_clk_src_desc.bClockID) #define USB_IN_CLK_ID (in_clk_src_desc.bClockID) +#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID) +#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID) #define CONTROL_ABSENT 0 #define CONTROL_RDONLY 1 @@ -34,6 +39,8 @@ #define CLK_FREQ_CTRL 0 #define CLK_VLD_CTRL
[PATCH 4/8] usb: gadget: u_audio: add real feedback implementation
This adds interface between userspace and feedback endpoint to report real feedback frequency to the Host. Current implementation adds new userspace interface ALSA mixer control "PCM Feedback Frequency Hz" (similar to aloop driver's "PCM Rate Shift 10" mixer control) We allow +/-20% deviation of nominal sampling frequency, that usually is more than enough in real-world usecases Usage of this new control is easy to implement in existing userspace tools like alsaloop from alsa-utils. Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac2.c | 4 ++ drivers/usb/gadget/function/u_audio.c | 93 +++ drivers/usb/gadget/function/u_audio.h | 7 +++ 3 files changed, 104 insertions(+) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 72b42f8..91b22fb 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -506,6 +506,10 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, max_size_bw = num_channels(chmask) * ssize * ((srate / (factor / (1 << (ep_desc->bInterval - 1 + 1); + + if (!is_playback && (uac2_opts->c_sync == USB_ENDPOINT_SYNC_ASYNC)) + max_size_bw = max_size_bw * FBACK_FREQ_MAX / 100; + ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw, max_size_ep)); diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 8e74b54..b434e70 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "u_audio.h" @@ -598,12 +599,87 @@ void u_audio_stop_playback(struct g_audio *audio_dev) } EXPORT_SYMBOL_GPL(u_audio_stop_playback); +static int u_audio_rate_shift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + struct uac_params *params = _dev->params; + unsigned int ffback_min, ffback_max; + + ffback_min = params->c_srate * FBACK_FREQ_MIN / 100; + ffback_max = params->c_srate * FBACK_FREQ_MAX / 100; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ffback_min; + uinfo->value.integer.max = ffback_max; + uinfo->value.integer.step = 1; + return 0; +} + +static int u_audio_rate_shift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(>lock, flags); + ucontrol->value.integer.value[0] = prm->ffback; + spin_unlock_irqrestore(>lock, flags); + + return 0; +} + +static int u_audio_rate_shift_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + struct uac_params *params = _dev->params; + unsigned int val; + unsigned int ffback_min, ffback_max; + unsigned long flags; + int change = 0; + + ffback_min = params->c_srate * FBACK_FREQ_MIN / 100; + ffback_max = params->c_srate * FBACK_FREQ_MAX / 100; + + val = ucontrol->value.integer.value[0]; + if (val < ffback_min) + val = ffback_min; + if (val > ffback_max) + val = ffback_max; + + spin_lock_irqsave(>lock, flags); + if (prm->ffback != val) { + prm->ffback = val; + change = 1; + } + spin_unlock_irqrestore(>lock, flags); + + return change; +} + +static const struct snd_kcontrol_new u_audio_controls[] = { +{ + .iface =SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Feedback Frequency Hz", + .info = u_audio_rate_shift_info, + .get = u_audio_rate_shift_get, + .put = u_audio_rate_shift_put, +}, +}; + int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, const char *card_name) { struct snd_uac_chip *uac; struct snd_card *card; struct snd_pcm *pcm; + struct snd_kcontrol *kctl; struct uac_params *params; int p_chmask, c_chmask; int err; @@ -693,6 +769,23 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, snd_pcm_set_ops(pcm, SNDRV_PCM
[PATCH 6/8] usb: gadget: u_audio: add bi-directional volume and mute support
USB Audio Class 1/2 have ability to change device's volume and mute by USB Host through class-specific control requests. Device also can notify Host about volume/mute change on its side through optional interrupt endpoint. This patch adds Volume and Mute ALSA controls which can be used by user to send and receive notifications to/from the USB Host about Volume and Mute change. These params come from f_uac* so volume and mute controls will be created only if the function support and enable each explicitly Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/u_audio.c | 360 +- drivers/usb/gadget/function/u_audio.h | 22 +++ 2 files changed, 372 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index b434e70..f4b850b 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -12,11 +12,14 @@ *Jaswinder Singh (jaswinder.si...@linaro.org) */ +#include #include #include #include #include #include +#include +#include #include "u_audio.h" @@ -24,6 +27,12 @@ #define PRD_SIZE_MAX PAGE_SIZE #define MIN_PERIODS4 +enum { + UAC_FBACK_CTRL, + UAC_MUTE_CTRL, + UAC_VOLUME_CTRL, +}; + /* Runtime data params for one stream */ struct uac_rtd_params { struct snd_uac_chip *uac; /* parent chip */ @@ -44,6 +53,14 @@ struct uac_rtd_params { unsigned int ffback; /* Real frequency reported by feedback endpoint */ bool fb_ep_enabled; /* if the ep is enabled */ + /* Volume/Mute controls and their state */ + int fu_id; /* Feature Unit ID */ + struct snd_kcontrol *snd_kctl_volume; + struct snd_kcontrol *snd_kctl_mute; + s16 volume_min, volume_max, volume_res; + s16 volume; + int mute; + spinlock_t lock; /* lock for feedback and control transfers */ }; @@ -599,6 +616,102 @@ void u_audio_stop_playback(struct g_audio *audio_dev) } EXPORT_SYMBOL_GPL(u_audio_stop_playback); +int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + + if (playback) + prm = >p_prm; + else + prm = >c_prm; + + spin_lock_irqsave(>lock, flags); + *val = prm->volume; + spin_unlock_irqrestore(>lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_get_volume); + +int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + int change = 0; + + if (playback) + prm = >p_prm; + else + prm = >c_prm; + + spin_lock_irqsave(>lock, flags); + val = clamp(val, prm->volume_min, prm->volume_max); + if (prm->volume != val) { + prm->volume = val; + change = 1; + } + spin_unlock_irqrestore(>lock, flags); + + if (change) + snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, + >snd_kctl_volume->id); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_set_volume); + +int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + + if (playback) + prm = >p_prm; + else + prm = >c_prm; + + spin_lock_irqsave(>lock, flags); + *val = prm->mute; + spin_unlock_irqrestore(>lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_get_mute); + +int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + int change = 0; + int mute; + + if (playback) + prm = >p_prm; + else + prm = >c_prm; + + mute = val ? 1 : 0; + + spin_lock_irqsave(>lock, flags); + if (prm->mute != mute) { + prm->mute = mute; + change = 1; + } + spin_unlock_irqrestore(>lock, flags); + + if (change) + snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, + >snd_kctl_mute->id); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_set_mute); + static int u_audio_rate_shift_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -663,14 +776,157 @@ static int u_audio_rate_shift_put(struct snd_kcontrol *kcontrol, return change; } -static const struct snd_kcontrol_new u_audio_controls[] = { +static int u_a
[PATCH 5/8] usb: audio-v2: add ability to define feature unit descriptor
Similar to UAC1 spec, UAC2 feature unit descriptor has variable size. Current audio-v2 feature unit descriptor structure is used for parsing descriptors, but can't be used to define your own descriptor. Add a new macro similar to what audio v1 already has. Signed-off-by: Ruslan Bilovol --- include/linux/usb/audio-v2.h | 14 ++ 1 file changed, 14 insertions(+) diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index ead8c9a..8fc2abd 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -156,6 +156,20 @@ struct uac2_feature_unit_descriptor { __u8 bmaControls[]; /* variable length */ } __attribute__((packed)); +#define UAC2_DT_FEATURE_UNIT_SIZE(ch) (6 + ((ch) + 1) * 4) + +/* As above, but more useful for defining your own descriptors: */ +#define DECLARE_UAC2_FEATURE_UNIT_DESCRIPTOR(ch) \ +struct uac2_feature_unit_descriptor_##ch { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubtype; \ + __u8 bUnitID; \ + __u8 bSourceID;\ + __le32 bmaControls[ch + 1]; \ + __u8 iFeature; \ +} __packed + /* 4.7.2.10 Effect Unit Descriptor */ struct uac2_effect_unit_descriptor { -- 1.9.1
[PATCH 2/8] usb: gadget: f_uac2/u_audio: add feedback endpoint support
As per USB and UAC2 specs, asynchronous audio sink endpoint requires explicit synchronization mechanism (Isochronous Feedback Endpoint) Implement feedback companion endpoint for ISO OUT endpoint This patch adds all required infrastructure and USB requests handling for feedback endpoint. Syncrhonization itself is still dummy (feedback ep always reports 'nomimal frequency' e.g. no adjustement is needed). This satisfies hosts that require feedback endpoint (like Win10) and poll it periodically Actual synchronization mechanism should be implemented separately Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac2.c | 34 +++- drivers/usb/gadget/function/u_audio.c | 141 +- drivers/usb/gadget/function/u_audio.h | 3 + 3 files changed, 175 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index f868e38..6f7b7c1 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -240,7 +240,7 @@ enum { .bDescriptorType = USB_DT_INTERFACE, .bAlternateSetting = 1, - .bNumEndpoints = 1, + .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, .bInterfaceProtocol = UAC_VERSION_2, @@ -299,6 +299,27 @@ enum { .wLockDelay = 0, }; +/* STD AS ISO IN Feedback Endpoint */ +static struct usb_endpoint_descriptor fs_epin_fback_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK, + .wMaxPacketSize = cpu_to_le16(3), + .bInterval = 1, +}; + +static struct usb_endpoint_descriptor hs_epin_fback_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_USAGE_FEEDBACK, + .wMaxPacketSize = cpu_to_le16(4), + .bInterval = 4, +}; + + /* Audio Streaming IN Interface - Alt0 */ static struct usb_interface_descriptor std_as_in_if0_desc = { .bLength = sizeof std_as_in_if0_desc, @@ -395,6 +416,7 @@ enum { (struct usb_descriptor_header *)_out_fmt1_desc, (struct usb_descriptor_header *)_epout_desc, (struct usb_descriptor_header *)_iso_out_desc, + (struct usb_descriptor_header *)_epin_fback_desc, (struct usb_descriptor_header *)_as_in_if0_desc, (struct usb_descriptor_header *)_as_in_if1_desc, @@ -425,6 +447,7 @@ enum { (struct usb_descriptor_header *)_out_fmt1_desc, (struct usb_descriptor_header *)_epout_desc, (struct usb_descriptor_header *)_iso_out_desc, + (struct usb_descriptor_header *)_epin_fback_desc, (struct usb_descriptor_header *)_as_in_if0_desc, (struct usb_descriptor_header *)_as_in_if1_desc, @@ -562,6 +585,7 @@ static void setup_descriptor(struct f_uac2_opts *opts) fs_audio_desc[i++] = USBDHDR(_out_fmt1_desc); fs_audio_desc[i++] = USBDHDR(_epout_desc); fs_audio_desc[i++] = USBDHDR(_iso_out_desc); + fs_audio_desc[i++] = USBDHDR(_epin_fback_desc); } if (EPIN_EN(opts)) { fs_audio_desc[i++] = USBDHDR(_as_in_if0_desc); @@ -595,6 +619,7 @@ static void setup_descriptor(struct f_uac2_opts *opts) hs_audio_desc[i++] = USBDHDR(_out_fmt1_desc); hs_audio_desc[i++] = USBDHDR(_epout_desc); hs_audio_desc[i++] = USBDHDR(_iso_out_desc); + hs_audio_desc[i++] = USBDHDR(_epin_fback_desc); } if (EPIN_EN(opts)) { hs_audio_desc[i++] = USBDHDR(_as_in_if0_desc); @@ -757,6 +782,12 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return -ENODEV; } + agdev->in_ep_fback = usb_ep_autoconfig(gadget, + _epin_fback_desc); + if (!agdev->in_ep_fback) { + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); + return -ENODEV; + } } if (EPIN_EN(uac2_opts)) { @@ -775,6 +806,7 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) le16_to_cpu(hs_epout_desc.wMaxPacketSize)); hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; + hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress; hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; setup_descriptor(uac2_opts); diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 5fbceee..8e74b54 10064
[PATCH 1/8] usb: gadget: u_audio: convert to strscpy
Usage of strlcpy in Linux Kernel has been recently deprecated [1], convert driver to strscpy [1] https://lore.kernel.org/lkml/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=v6a6g1ouzcprm...@mail.gmail.com/ Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/u_audio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 265c4d8..5fbceee 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -549,15 +549,15 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, if (err < 0) goto snd_fail; - strlcpy(pcm->name, pcm_name, sizeof(pcm->name)); + strscpy(pcm->name, pcm_name, sizeof(pcm->name)); pcm->private_data = uac; uac->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, _pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, _pcm_ops); - strlcpy(card->driver, card_name, sizeof(card->driver)); - strlcpy(card->shortname, card_name, sizeof(card->shortname)); + strscpy(card->driver, card_name, sizeof(card->driver)); + strscpy(card->shortname, card_name, sizeof(card->shortname)); sprintf(card->longname, "%s %i", card_name, card->dev->id); snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, -- 1.9.1
[PATCH 3/8] usb: gadget: f_uac2: add adaptive sync support for capture
Current f_uac2 USB OUT (aka 'capture') synchronization implements 'ASYNC' scenario which means USB Gadget has it's own freerunning clock and can update Host about real clock frequency through feedback endpoint so Host can align number of samples sent to the USB gadget to prevent overruns/underruns In case if Gadget can has no it's internal clock and can consume audio samples at any rate (for example, on the Gadget side someone records audio directly to a file, or audio samples are played through an external DAC as soon as they arrive), UAC2 spec suggests 'ADAPTIVE' synchronization type. Change UAC2 driver to make it configurable through additional 'c_sync' configfs file. Default remains 'asynchronous' with possibility to switch it to 'adaptive' Signed-off-by: Ruslan Bilovol --- Documentation/ABI/testing/configfs-usb-gadget-uac2 | 1 + Documentation/usb/gadget-testing.rst | 1 + drivers/usb/gadget/function/f_uac2.c | 102 ++--- drivers/usb/gadget/function/u_uac2.h | 2 + 4 files changed, 95 insertions(+), 11 deletions(-) diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uac2 b/Documentation/ABI/testing/configfs-usb-gadget-uac2 index d4356c8..e7e59d7 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-uac2 +++ b/Documentation/ABI/testing/configfs-usb-gadget-uac2 @@ -8,6 +8,7 @@ Description: c_chmask capture channel mask c_sratecapture sampling rate c_ssizecapture sample size (bytes) + c_sync capture synchronization type (async/adaptive) p_chmask playback channel mask p_srateplayback sampling rate p_ssizeplayback sample size (bytes) diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 2085e7b..f5a1266 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -728,6 +728,7 @@ The uac2 function provides these attributes in its function directory: c_chmaskcapture channel mask c_srate capture sampling rate c_ssize capture sample size (bytes) + c_sync capture synchronization type (async/adaptive) p_chmaskplayback channel mask p_srate playback sampling rate p_ssize playback sample size (bytes) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 6f7b7c1..72b42f8 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -44,6 +44,7 @@ #define EPIN_EN(_opts) ((_opts)->p_chmask != 0) #define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) +#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC) struct f_uac2 { struct g_audio g_audio; @@ -240,7 +241,7 @@ enum { .bDescriptorType = USB_DT_INTERFACE, .bAlternateSetting = 1, - .bNumEndpoints = 2, + /* .bNumEndpoints = DYNAMIC */ .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, .bInterfaceProtocol = UAC_VERSION_2, @@ -273,7 +274,7 @@ enum { .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + /* .bmAttributes = = DYNAMIC */ /* .wMaxPacketSize = DYNAMIC */ .bInterval = 1, }; @@ -282,7 +283,7 @@ enum { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + /* .bmAttributes = = DYNAMIC */ /* .wMaxPacketSize = DYNAMIC */ .bInterval = 1, }; @@ -544,6 +545,8 @@ static void setup_descriptor(struct f_uac2_opts *opts) iad_desc.bInterfaceCount = 1; ac_hdr_desc.wTotalLength = cpu_to_le16(sizeof(ac_hdr_desc)); + std_as_out_if1_desc.bNumEndpoints = 1; + if (EPIN_EN(opts)) { u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength); @@ -561,6 +564,19 @@ static void setup_descriptor(struct f_uac2_opts *opts) len += sizeof(io_out_ot_desc); ac_hdr_desc.wTotalLength = cpu_to_le16(len); iad_desc.bInterfaceCount++; + + fs_epout_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC; + hs_epout_desc.bmAttributes = USB_ENDPOINT_XFER_ISOC; + if (EPOUT_FBACK_IN_EN(opts)) { + fs_epout_desc.bmAttributes |= USB_ENDPOINT_SYNC_ASYNC; + hs_epout_desc.bmAttributes |= USB_ENDPOINT_SYNC_ASYNC; + std_as_out_if1_desc.bNumEndpoints++; + } else { + fs_epout_desc.bmAttributes |= + USB_ENDPOINT_SYNC_ADAPTIVE; + hs_epout_desc.bmA
[PATCH 0/8] USB Audio Gadget part 2: Feedback endpoint, Volume/Mute support
This is extendend version of "UAC2 Feedback endpoint" patch set I've sent back in 2020 [1]. It is extended with bi-directional Volume/Mute controls support for both UAC1 and UAC2 gadgets. It fixes issues with enumeration in various operation systems because of Feedback endpoint implementation, yet also adds new Volume/Mute support which allows developers to control UAC1/2 Gadget's Volume/Mute from the Host and in the same way UAC1/2 Gadget can control Volume/Mute of the Host making it closer to a real USB Audio card. This patch set should be applied on top of USB Audio Gadget part 1 fixes/improvements that I've sent previously to the mailing list [2] UAC2 Feedback Endpoint support Current UAC2 gadget implements capture/sync paths as two USB ISO ASYNC endpoints (IN and OUT). This violates USB spec which says that ISO ASYNC OUT endpoint should have feedback companion endpoint. See USB2.0 spec "5.12.4.1 Synchronization Type": asynchronous sink provides explicit feedback (isochronous pipe). Interesting that for ISO ASYNC *IN* endpoint respective feedback isn't required since source provides implicit feedforward (data stream). While it's not an issue if UAC2 Gadget is connected to Linux host (Linux ignores missing feedback endpoint), with other hosts like Windows or MacOS the UAC2 Gadget isn't enumerated due to missing feedback endpoint. This patch series adds feedback endpoint support to UAC2 function, new control to UAC2 mixer which can be used by userspace tools (like alsaloop from alsa-utils) for updating feedback frequency reported to the host. This is useful for usecases when UAC2 Gadget's audio samples are played to another codec or audio card with its own internal freerunning clock so host can be notified that more/less samples are required. The alsaloop tool requires some (relatively small) modifications in order to start support driving feedback frequency through UAC2 mixer control. That change have been sent as a separate patch to ALSA community [3]. Also added ability to switch ISO ASYNC OUT endpoint into adaptive endpoint which doesn't require feedback endpoint (as per USB spec). UAC1/2 Volume/Mute controls support == Volume and Mute controls are traditionally presented in USB Audio cards that are available on the market, and Hosts usually expect they exist in the attached devices. However, Linux UAC gadget previosly didn't have such functinality which (depending on Host's operation system) was causing different behaviour: Linux hosts (usually) in this case can adjust volume by changing audio sample's amplitude that are sent to the gadget. Other hosts may stuck with maximum volume and can't adjust it for the Linux UAC gadget device. Volume/Mute controls support is quite useful feature. If implemented according to UAC1/2 spec, it allows to control volume in both directions: from the Host to Gadget and from Gadget to the Host. To do it, it's required to add new Feature Unit descriptor to existing UAC1/2 Gadget topology and add new interrupt endpoint so Volume/Mute changes can be reported to the Host. This has been tested with Linux/MacOS/Windows(7,10) hosts, by attaching alsamixer to the UAC1/2 ALSA card on Gadget side, and using available Volume/Mute control applications on the Hosts. An user can adjust Volume/Mute on the Host and see respective changes on the Gadget side. In the same way, an user can adjust Volume/Mute on the Gadget side and observe respective changes on the Host There is a known issue with Win7 which for some reason doesn't poll UAC1 interrupt endpoint causing Volume/Mute control not working in Gadget->Host direction Both features have been tested on BeagleBone Black and Raspberry PI 4 boards with Linux/MacOS/Windows(7,10) hosts. While on BeagleBone Black it works fine, Raspberry PI 4 DWC2 controller is affected by the DMA issue for control trasfers which I reported a while back in [4]. It is causing incorrect data provided by DWC2 UDC to UAC1/2 gadgets screwing up volume and mute control messages in the Host->Gadget direction. The hack is available (see [4]) but it leads to traces/issues with audio streaming. Patches reviews and testing on your HW is welcome as usual! [1] https://lore.kernel.org/linux-usb/1604794711-8661-1-git-send-email-ruslan.bilo...@gmail.com/ [2] https://lore.kernel.org/linux-usb/1614599375-8803-1-git-send-email-ruslan.bilo...@gmail.com/ [3] https://lore.kernel.org/linux-usb/1605220482-28487-1-git-send-email-ruslan.bilo...@gmail.com/ [4] https://lore.kernel.org/linux-usb/CAB=otbTVxa=ngwf4k1aycyypceyyrkc_1hysb_nhu6c9rmz...@mail.gmail.com/ Ruslan Bilovol (8): usb: gadget: u_audio: convert to strscpy usb: gadget: f_uac2/u_audio: add feedback endpoint support usb: gadget: f_uac2: add adaptive sync support for capture usb: gadget: u_audio: add real feedback implementation usb: audio-v2: add abil
[PATCH v2 4/5] usb: gadget: f_uac1: validate input parameters
Currently user can configure UAC1 function with parameters that violate UAC1 spec or are not supported by UAC1 gadget implementation. This can lead to incorrect behavior if such gadget is connected to the host - like enumeration failure or other issues depending on host's UAC1 driver implementation, bringing user to a long hours of debugging the issue. Instead of silently accept these parameters, throw an error if they are not valid. Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac1.c | 43 1 file changed, 43 insertions(+) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 560382e..e65f474 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -19,6 +19,9 @@ #include "u_audio.h" #include "u_uac1.h" +/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ +#define UAC1_CHANNEL_MASK 0x0FFF + struct f_uac1 { struct g_audio g_audio; u8 ac_intf, as_in_intf, as_out_intf; @@ -30,6 +33,11 @@ static inline struct f_uac1 *func_to_uac1(struct usb_function *f) return container_of(f, struct f_uac1, g_audio.func); } +static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) +{ + return container_of(audio->func.fi, struct f_uac1_opts, func_inst); +} + /* * DESCRIPTORS ... most are static, but strings and full * configuration descriptors are built on demand. @@ -505,11 +513,42 @@ static void f_audio_disable(struct usb_function *f) /*-*/ +static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) +{ + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + + if (!opts->p_chmask && !opts->c_chmask) { + dev_err(dev, "Error: no playback and capture channels\n"); + return -EINVAL; + } else if (opts->p_chmask & ~UAC1_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported playback channels mask\n"); + return -EINVAL; + } else if (opts->c_chmask & ~UAC1_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported capture channels mask\n"); + return -EINVAL; + } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) { + dev_err(dev, "Error: incorrect playback sample size\n"); + return -EINVAL; + } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) { + dev_err(dev, "Error: incorrect capture sample size\n"); + return -EINVAL; + } else if (!opts->p_srate) { + dev_err(dev, "Error: incorrect playback sampling rate\n"); + return -EINVAL; + } else if (!opts->c_srate) { + dev_err(dev, "Error: incorrect capture sampling rate\n"); + return -EINVAL; + } + + return 0; +} + /* audio function driver setup/binding */ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev*cdev = c->cdev; struct usb_gadget *gadget = cdev->gadget; + struct device *dev = >dev; struct f_uac1 *uac1 = func_to_uac1(f); struct g_audio *audio = func_to_g_audio(f); struct f_uac1_opts *audio_opts; @@ -519,6 +558,10 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) int rate; int status; + status = f_audio_validate_opts(audio, dev); + if (status) + return status; + audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); -- 1.9.1
[PATCH v2 5/5] usb: gadget: f_uac1: disable IN/OUT ep if unused
User can configure f_uac1 function via p_chmask/c_chmask whether uac1 shall support playback and/or capture, but it has only effect on the created ALSA device, but not on the USB descriptor. This patch adds playback/capture descriptors dependent on that parameter. It is similar to the same conversion done earlier for f_uac2 Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac1.c | 229 +-- 1 file changed, 163 insertions(+), 66 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index e65f474..d047075 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -22,6 +22,9 @@ /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ #define UAC1_CHANNEL_MASK 0x0FFF +#define EPIN_EN(_opts) ((_opts)->p_chmask != 0) +#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) + struct f_uac1 { struct g_audio g_audio; u8 ac_intf, as_in_intf, as_out_intf; @@ -50,11 +53,6 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN */ -#define F_AUDIO_AC_INTERFACE 0 -#define F_AUDIO_AS_OUT_INTERFACE 1 -#define F_AUDIO_AS_IN_INTERFACE2 -/* Number of streaming interfaces */ -#define F_AUDIO_NUM_INTERFACES 2 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { @@ -65,73 +63,47 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; -/* - * The number of AudioStreaming and MIDIStreaming interfaces - * in the Audio Interface Collection - */ -DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); - -#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) -/* 2 input terminals and 2 output terminals */ -#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ - + 2*UAC_DT_INPUT_TERMINAL_SIZE + 2*UAC_DT_OUTPUT_TERMINAL_SIZE) /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_2 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_LENGTH, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_HEADER, - .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(UAC_DT_TOTAL_LENGTH), - .bInCollection =F_AUDIO_NUM_INTERFACES, - .baInterfaceNr = { - /* Interface number of the AudioStream interfaces */ - [0] = 1, - [1] = 2, - } -}; +static struct uac1_ac_header_descriptor *ac_header_desc; -#define USB_OUT_IT_ID 1 static struct uac_input_terminal_descriptor usb_out_it_desc = { .bLength = UAC_DT_INPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID = USB_OUT_IT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType =cpu_to_le16(UAC_TERMINAL_STREAMING), .bAssocTerminal = 0, .wChannelConfig = cpu_to_le16(0x3), }; -#define IO_OUT_OT_ID 2 static struct uac1_output_terminal_descriptor io_out_ot_desc = { .bLength= UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType= USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID= IO_OUT_OT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER), .bAssocTerminal = 0, - .bSourceID = USB_OUT_IT_ID, + /* .bSourceID = DYNAMIC */ }; -#define IO_IN_IT_ID3 static struct uac_input_terminal_descriptor io_in_it_desc = { .bLength= UAC_DT_INPUT_TERMINAL_SIZE, .bDescriptorType= USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID= IO_IN_IT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE), .bAssocTerminal = 0, .wChannelConfig = cpu_to_le16(0x3), }; -#define USB_IN_OT_ID 4 static struct uac1_output_terminal_descriptor usb_in_ot_desc = { .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID = USB_IN_OT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType =cpu_to_le16(UAC_TERMINAL_STREAMING), .bAssocTerminal = 0, - .bSourceID =IO_IN_IT_ID, + /* .bSourceID = DYNAMIC */ }; /* B.4.1 Standard AS Interface Descriptor */ @@ -176,7
[PATCH v2 3/5] usb: gadget: f_uac2: validate input parameters
Currently user can configure UAC2 function with parameters that violate UAC2 spec or are not supported by UAC2 gadget implementation. This can lead to incorrect behavior if such gadget is connected to the host - like enumeration failure or other issues depending on host's UAC2 driver implementation, bringing user to a long hours of debugging the issue. Instead of silently accept these parameters, throw an error if they are not valid. Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac2.c | 39 ++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index c62cccb..f868e38 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -14,6 +14,9 @@ #include "u_audio.h" #include "u_uac2.h" +/* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */ +#define UAC2_CHANNEL_MASK 0x07FF + /* * The driver implements a simple UAC_2 topology. * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture @@ -604,6 +607,36 @@ static void setup_descriptor(struct f_uac2_opts *opts) hs_audio_desc[i] = NULL; } +static int afunc_validate_opts(struct g_audio *agdev, struct device *dev) +{ + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + + if (!opts->p_chmask && !opts->c_chmask) { + dev_err(dev, "Error: no playback and capture channels\n"); + return -EINVAL; + } else if (opts->p_chmask & ~UAC2_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported playback channels mask\n"); + return -EINVAL; + } else if (opts->c_chmask & ~UAC2_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported capture channels mask\n"); + return -EINVAL; + } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) { + dev_err(dev, "Error: incorrect playback sample size\n"); + return -EINVAL; + } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) { + dev_err(dev, "Error: incorrect capture sample size\n"); + return -EINVAL; + } else if (!opts->p_srate) { + dev_err(dev, "Error: incorrect playback sampling rate\n"); + return -EINVAL; + } else if (!opts->c_srate) { + dev_err(dev, "Error: incorrect capture sampling rate\n"); + return -EINVAL; + } + + return 0; +} + static int afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) { @@ -612,11 +645,13 @@ static void setup_descriptor(struct f_uac2_opts *opts) struct usb_composite_dev *cdev = cfg->cdev; struct usb_gadget *gadget = cdev->gadget; struct device *dev = >dev; - struct f_uac2_opts *uac2_opts; + struct f_uac2_opts *uac2_opts = g_audio_to_uac2_opts(agdev); struct usb_string *us; int ret; - uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); + ret = afunc_validate_opts(agdev, dev); + if (ret) + return ret; us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); if (IS_ERR(us)) -- 1.9.1
[PATCH v2 2/5] usb: gadget: f_uac1: stop playback on function disable
There is missing playback stop/cleanup in case of gadget's ->disable callback that happens on events like USB host resetting or gadget disconnection Fixes: 0591bc236015 ("usb: gadget: add f_uac1 variant based on a new u_audio api") Cc: # 4.13+ Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 00d3469..560382e 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -499,6 +499,7 @@ static void f_audio_disable(struct usb_function *f) uac1->as_out_alt = 0; uac1->as_in_alt = 0; + u_audio_stop_playback(>g_audio); u_audio_stop_capture(>g_audio); } -- 1.9.1
[PATCH v2 0/5] USB Audio Gadget part 1: misc fixes and improvements
Here are some bug fixes and improvements to USB Audio Gadget drivers which I made during my work on a new UAC features like feedback endpoint implementation and Volume/Mute controls. That's nice bugfixes/improvements that I expect get applied faster than coming new features thus sent them as 'part 1' The new UAC features will be sent as a separate patch set (aka 'part 2') v2: fixed possible NULL-ptr issue with input parameters validation Ruslan Bilovol (5): usb: gadget: f_uac2: always increase endpoint max_packet_size by one audio slot usb: gadget: f_uac1: stop playback on function disable usb: gadget: f_uac2: validate input parameters usb: gadget: f_uac1: validate input parameters usb: gadget: f_uac1: disable IN/OUT ep if unused drivers/usb/gadget/function/f_uac1.c | 273 ++- drivers/usb/gadget/function/f_uac2.c | 41 +- 2 files changed, 245 insertions(+), 69 deletions(-) -- 1.9.1
[PATCH v2 1/5] usb: gadget: f_uac2: always increase endpoint max_packet_size by one audio slot
As per UAC2 Audio Data Formats spec (2.3.1.1 USB Packets), if the sampling rate is a constant, the allowable variation of number of audio slots per virtual frame is +/- 1 audio slot. It means that endpoint should be able to accept/send +1 audio slot. Previous endpoint max_packet_size calculation code was adding sometimes +1 audio slot due to DIV_ROUND_UP behaviour which was rounding up to closest integer. However this doesn't work if the numbers are divisible. It had no any impact with Linux hosts which ignore this issue, but in case of more strict Windows it caused rejected enumeration Thus always add +1 audio slot to endpoint's max packet size Fixes: 913e4a90b6f9 ("usb: gadget: f_uac2: finalize wMaxPacketSize according to bandwidth") Cc: Peter Chen Cc: #v4.3+ Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 740cb64..c62cccb 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -478,7 +478,7 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, } max_size_bw = num_channels(chmask) * ssize * - DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))); + ((srate / (factor / (1 << (ep_desc->bInterval - 1 + 1); ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw, max_size_ep)); -- 1.9.1
[PATCH 5/5] usb: gadget: f_uac1: disable IN/OUT ep if unused
User can configure f_uac1 function via p_chmask/c_chmask whether uac1 shall support playback and/or capture, but it has only effect on the created ALSA device, but not on the USB descriptor. This patch adds playback/capture descriptors dependent on that parameter. It is similar to the same conversion done earlier for f_uac2 Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac1.c | 229 +-- 1 file changed, 163 insertions(+), 66 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index cce9478f..b535407 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -22,6 +22,9 @@ /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ #define UAC1_CHANNEL_MASK 0x0FFF +#define EPIN_EN(_opts) ((_opts)->p_chmask != 0) +#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) + struct f_uac1 { struct g_audio g_audio; u8 ac_intf, as_in_intf, as_out_intf; @@ -50,11 +53,6 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN */ -#define F_AUDIO_AC_INTERFACE 0 -#define F_AUDIO_AS_OUT_INTERFACE 1 -#define F_AUDIO_AS_IN_INTERFACE2 -/* Number of streaming interfaces */ -#define F_AUDIO_NUM_INTERFACES 2 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc = { @@ -65,73 +63,47 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; -/* - * The number of AudioStreaming and MIDIStreaming interfaces - * in the Audio Interface Collection - */ -DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); - -#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) -/* 2 input terminals and 2 output terminals */ -#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \ - + 2*UAC_DT_INPUT_TERMINAL_SIZE + 2*UAC_DT_OUTPUT_TERMINAL_SIZE) /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac1_ac_header_descriptor_2 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_LENGTH, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_HEADER, - .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(UAC_DT_TOTAL_LENGTH), - .bInCollection =F_AUDIO_NUM_INTERFACES, - .baInterfaceNr = { - /* Interface number of the AudioStream interfaces */ - [0] = 1, - [1] = 2, - } -}; +static struct uac1_ac_header_descriptor *ac_header_desc; -#define USB_OUT_IT_ID 1 static struct uac_input_terminal_descriptor usb_out_it_desc = { .bLength = UAC_DT_INPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID = USB_OUT_IT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType =cpu_to_le16(UAC_TERMINAL_STREAMING), .bAssocTerminal = 0, .wChannelConfig = cpu_to_le16(0x3), }; -#define IO_OUT_OT_ID 2 static struct uac1_output_terminal_descriptor io_out_ot_desc = { .bLength= UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType= USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID= IO_OUT_OT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER), .bAssocTerminal = 0, - .bSourceID = USB_OUT_IT_ID, + /* .bSourceID = DYNAMIC */ }; -#define IO_IN_IT_ID3 static struct uac_input_terminal_descriptor io_in_it_desc = { .bLength= UAC_DT_INPUT_TERMINAL_SIZE, .bDescriptorType= USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID= IO_IN_IT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE), .bAssocTerminal = 0, .wChannelConfig = cpu_to_le16(0x3), }; -#define USB_IN_OT_ID 4 static struct uac1_output_terminal_descriptor usb_in_ot_desc = { .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID = USB_IN_OT_ID, + /* .bTerminalID = DYNAMIC */ .wTerminalType =cpu_to_le16(UAC_TERMINAL_STREAMING), .bAssocTerminal = 0, - .bSourceID =IO_IN_IT_ID, + /* .bSourceID = DYNAMIC */ }; /* B.4.1 Standard AS Interface Descriptor */ @@
[PATCH 1/5] usb: gadget: f_uac2: always increase endpoint max_packet_size by one audio slot
As per UAC2 Audio Data Formats spec (2.3.1.1 USB Packets), if the sampling rate is a constant, the allowable variation of number of audio slots per virtual frame is +/- 1 audio slot. It means that endpoint should be able to accept/send +1 audio slot. Previous endpoint max_packet_size calculation code was adding sometimes +1 audio slot due to DIV_ROUND_UP behaviour which was rounding up to closest integer. However this doesn't work if the numbers are divisible. It had no any impact with Linux hosts which ignore this issue, but in case of more strict Windows it caused rejected enumeration Thus always add +1 audio slot to endpoint's max packet size Fixes: 913e4a90b6f9 ("usb: gadget: f_uac2: finalize wMaxPacketSize according to bandwidth") Cc: Peter Chen Cc: #v4.3+ Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 740cb64..c62cccb 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -478,7 +478,7 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, } max_size_bw = num_channels(chmask) * ssize * - DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))); + ((srate / (factor / (1 << (ep_desc->bInterval - 1 + 1); ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw, max_size_ep)); -- 1.9.1
[PATCH 0/5] USB Audio Gadget part 1: misc fixes and improvements
Here are some bug fixes and improvements to USB Audio Gadget drivers which I made during my work on a new UAC features like feedback endpoint implementation and Volume/Mute controls. The new UAC features will be sent as a separate patch set (aka 'part 2') on top of these changes later this week Ruslan Bilovol (5): usb: gadget: f_uac2: always increase endpoint max_packet_size by one audio slot usb: gadget: f_uac1: stop playback on function disable usb: gadget: f_uac2: validate input parameters usb: gadget: f_uac1: validate input parameters usb: gadget: f_uac1: disable IN/OUT ep if unused drivers/usb/gadget/function/f_uac1.c | 273 ++- drivers/usb/gadget/function/f_uac2.c | 42 +- 2 files changed, 246 insertions(+), 69 deletions(-) -- 1.9.1
[PATCH 4/5] usb: gadget: f_uac1: validate input parameters
Currently user can configure UAC1 function with parameters that violate UAC1 spec or are not supported by UAC1 gadget implementation. This can lead to incorrect behavior if such gadget is connected to the host - like enumeration failure or other issues depending on host's UAC1 driver implementation, bringing user to a long hours of debugging the issue. Instead of silently accept these parameters, throw an error if they are not valid. Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac1.c | 43 1 file changed, 43 insertions(+) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 560382e..cce9478f 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -19,6 +19,9 @@ #include "u_audio.h" #include "u_uac1.h" +/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ +#define UAC1_CHANNEL_MASK 0x0FFF + struct f_uac1 { struct g_audio g_audio; u8 ac_intf, as_in_intf, as_out_intf; @@ -30,6 +33,11 @@ static inline struct f_uac1 *func_to_uac1(struct usb_function *f) return container_of(f, struct f_uac1, g_audio.func); } +static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) +{ + return container_of(audio->func.fi, struct f_uac1_opts, func_inst); +} + /* * DESCRIPTORS ... most are static, but strings and full * configuration descriptors are built on demand. @@ -505,6 +513,37 @@ static void f_audio_disable(struct usb_function *f) /*-*/ +static int f_audio_validate_opts(struct g_audio *audio) +{ + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); + struct device *dev = >gadget->dev; + + if (!opts->p_chmask && !opts->c_chmask) { + dev_err(dev, "Error: no playback and capture channels\n"); + return -EINVAL; + } else if (opts->p_chmask & ~UAC1_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported playback channels mask\n"); + return -EINVAL; + } else if (opts->c_chmask & ~UAC1_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported capture channels mask\n"); + return -EINVAL; + } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) { + dev_err(dev, "Error: incorrect playback sample size\n"); + return -EINVAL; + } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) { + dev_err(dev, "Error: incorrect capture sample size\n"); + return -EINVAL; + } else if (!opts->p_srate) { + dev_err(dev, "Error: incorrect playback sampling rate\n"); + return -EINVAL; + } else if (!opts->c_srate) { + dev_err(dev, "Error: incorrect capture sampling rate\n"); + return -EINVAL; + } + + return 0; +} + /* audio function driver setup/binding */ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) { @@ -519,6 +558,10 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) int rate; int status; + status = f_audio_validate_opts(audio); + if (status) + return status; + audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); -- 1.9.1
[PATCH 3/5] usb: gadget: f_uac2: validate input parameters
Currently user can configure UAC2 function with parameters that violate UAC2 spec or are not supported by UAC2 gadget implementation. This can lead to incorrect behavior if such gadget is connected to the host - like enumeration failure or other issues depending on host's UAC2 driver implementation, bringing user to a long hours of debugging the issue. Instead of silently accept these parameters, throw an error if they are not valid. Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac2.c | 40 ++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index c62cccb..d0e50fc 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -14,6 +14,9 @@ #include "u_audio.h" #include "u_uac2.h" +/* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */ +#define UAC2_CHANNEL_MASK 0x07FF + /* * The driver implements a simple UAC_2 topology. * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture @@ -604,6 +607,37 @@ static void setup_descriptor(struct f_uac2_opts *opts) hs_audio_desc[i] = NULL; } +static int afunc_validate_opts(struct g_audio *agdev) +{ + struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev); + struct device *dev = >gadget->dev; + + if (!opts->p_chmask && !opts->c_chmask) { + dev_err(dev, "Error: no playback and capture channels\n"); + return -EINVAL; + } else if (opts->p_chmask & ~UAC2_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported playback channels mask\n"); + return -EINVAL; + } else if (opts->c_chmask & ~UAC2_CHANNEL_MASK) { + dev_err(dev, "Error: unsupported capture channels mask\n"); + return -EINVAL; + } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) { + dev_err(dev, "Error: incorrect playback sample size\n"); + return -EINVAL; + } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) { + dev_err(dev, "Error: incorrect capture sample size\n"); + return -EINVAL; + } else if (!opts->p_srate) { + dev_err(dev, "Error: incorrect playback sampling rate\n"); + return -EINVAL; + } else if (!opts->c_srate) { + dev_err(dev, "Error: incorrect capture sampling rate\n"); + return -EINVAL; + } + + return 0; +} + static int afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) { @@ -612,11 +646,13 @@ static void setup_descriptor(struct f_uac2_opts *opts) struct usb_composite_dev *cdev = cfg->cdev; struct usb_gadget *gadget = cdev->gadget; struct device *dev = >dev; - struct f_uac2_opts *uac2_opts; + struct f_uac2_opts *uac2_opts = g_audio_to_uac2_opts(agdev); struct usb_string *us; int ret; - uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); + ret = afunc_validate_opts(agdev); + if (ret) + return ret; us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); if (IS_ERR(us)) -- 1.9.1
[PATCH 2/5] usb: gadget: f_uac1: stop playback on function disable
There is missing playback stop/cleanup in case of gadget's ->disable callback that happens on events like USB host resetting or gadget disconnection Fixes: 0591bc236015 ("usb: gadget: add f_uac1 variant based on a new u_audio api") Cc: # 4.13+ Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/function/f_uac1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 00d3469..560382e 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -499,6 +499,7 @@ static void f_audio_disable(struct usb_function *f) uac1->as_out_alt = 0; uac1->as_in_alt = 0; + u_audio_stop_playback(>g_audio); u_audio_stop_capture(>g_audio); } -- 1.9.1
RPi4 DWC2 gadget doesn't copy data to a buffer in ep0 SETUP + DATA OUT transaction
Hi Minas and other USB experts, I'm currently developing new features for UAC1/UAC2 audio gadgets like Volume/Mute controls which use Control SETUP + DATA OUT transactions through ep0. While it works fine on BeagleBone black board with MUSB UDC, on Raspberry Pi 4 with DWC2 UDC there is an issue. I found that when DWC2 receives ep0 SETUP + DATA OUT transaction, it doesn't copy data transferred from the host to EP0 in DATA OUT stage to the usb_request->buf buffer. This buffer contains unchanged data from previous transactions. However, when I disable DMA for DWC2 controller (see the patch below) it starts to work as expected and correctly copies data transferred from the host in the DATA OUT stage, to the usb_request->buf buffer. So far I tested it on v5.9 kernel and v5.10.10 stable kernel both have the same issue. This issue is easily reproducible with RNDIS gadget which also uses ep0 SETUP + DATA OUT transactions for transferring RNDIS control messages. During enumeration of RNDIS gadget attached to Linux host, I see next messages for RPi4 DWC2 with DMA enabled: | ## RPi4 DWC2 DMA | [ 91.029881] rndis_msg_parser: unknown RNDIS message 0x0052033A len 4456526 | [ 91.029889] RNDIS command error -524, 24/24 In this case rndis_msg_parser can't parse messages from the host because they are sent through SETUP + DATA OUT transaction and DWC2 didn't copy that messages to the buffer, so buffer contains some garbage from previous transactions which can't be parsed. In case of BBB black or DWC2 with disabled DMA, it looks like: | ## BBB black | [ 32.867751] rndis_msg_parser: RNDIS_MSG_INIT | ## RPi4 DWC2 no DMA | [ 151.080724] rndis_msg_parser: RNDIS_MSG_INIT I also did a quick googling and found that same issue was recently reported for Raspberry pi OS: https://github.com/raspberrypi/Raspberry-Pi-OS-64bit/issues/127 I spent some time on debugging this issue, but without having DWC2 documentation and experience with DWC2 internals that's all that I found so far. Is this a known issue? Anybody debugging it? Any ideas? Thanks, Ruslan -8< >From ced7a3631d9800d04bcbcd756dac4583459fe48c Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Wed, 20 Jan 2021 00:27:52 +0200 Subject: [PATCH] usb: dwc2: workaround: disable DMA for gadget On Raspberry PI 4 it was observer that in case of control transfers with DATA phase from a host, the driver for some reason doesn't copy transferred data to the buffer, leaving previous data in it. With disabled DMA the issue isn't reproducible, thus temporarily disable it Signed-off-by: Ruslan Bilovol --- drivers/usb/dwc2/params.c | 8 1 file changed, 8 insertions(+) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 267543c..46c18af 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -357,7 +357,11 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg) { struct dwc2_hw_params *hw = >hw_params; struct dwc2_core_params *p = >params; +#if 0 bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH); +#else + bool dma_capable = 0; +#endif dwc2_set_param_otg_cap(hsotg); dwc2_set_param_phy_type(hsotg); @@ -651,7 +655,11 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg) { struct dwc2_hw_params *hw = >hw_params; struct dwc2_core_params *p = >params; +#if 0 bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH); +#else + bool dma_capable = 0; +#endif dwc2_check_param_otg_cap(hsotg); dwc2_check_param_phy_type(hsotg); -- 1.9.1
Re: [PATCH v2] usb: gadget: epautoconf: claim smallest endpoints first
On Tue, Jun 30, 2020 at 4:58 AM Peter Chen wrote: > > On 20-06-29 23:18:45, Ruslan Bilovol wrote: > > UDC hardware may have endpoints with different maxpacket > > size. Current endpoint matching code takes first matching > > endpoint from the list. > > > > It's always possible that gadget allocates endpoints for > > small transfers (maxpacket size) first, then larger ones. > > That works fine if all matching UDC endpoints have same > > maxpacket size or are big enough to serve that allocation. > > > > However, some UDCs have first endpoints in the list with > > bigger maxpacket size, whereas last endpoints are much > > smaller. In this case endpoint allocation will fail for > > the gadget (which allocates smaller endpoints first) on > > final endpoint allocations. > > > > To make endpoint allocation fair, pick up smallest > > matching endpoints first, leaving bigger ones for > > heavier applications. > > > > Signed-off-by: Ruslan Bilovol > > --- > > > > v2: rebased onto latest balbi/next branch > > > > drivers/usb/gadget/epautoconf.c | 23 ++- > > 1 file changed, 18 insertions(+), 5 deletions(-) > > > > diff --git a/drivers/usb/gadget/epautoconf.c > > b/drivers/usb/gadget/epautoconf.c > > index 1eb4fa2e623f..6c453b5d87bb 100644 > > --- a/drivers/usb/gadget/epautoconf.c > > +++ b/drivers/usb/gadget/epautoconf.c > > @@ -66,7 +66,7 @@ struct usb_ep *usb_ep_autoconfig_ss( > > struct usb_ss_ep_comp_descriptor *ep_comp > > ) > > { > > - struct usb_ep *ep; > > + struct usb_ep *ep, *ep_min = NULL; > > > > if (gadget->ops->match_ep) { > > ep = gadget->ops->match_ep(gadget, desc, ep_comp); > > @@ -74,14 +74,27 @@ struct usb_ep *usb_ep_autoconfig_ss( > > goto found_ep; > > } > > > > - /* Second, look at endpoints until an unclaimed one looks usable */ > > + /* > > + * Second, look at endpoints until an unclaimed one looks usable. > > + * Try to find one with smallest maxpacket limit, leaving larger > > + * endpoints for heavier applications > > + */ > > list_for_each_entry (ep, >ep_list, ep_list) { > > - if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp)) > > - goto found_ep; > > + if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp)) { > > + if (desc->wMaxPacketSize == 0) > > + goto found_ep; > > Why you do special handling for this? You still could give the smallest > maxpacket_limit EP for it, right? Of course it's technically possible. However in case "wMaxPacketSize == 0" gadget driver wants to get maximum possible wMaxPacketSize from endpoint configuration and I was thinking about avoiding regressions if we always provide smaller endpoints. As I can see, providing smallest endpoint that matches requested wMaxPacketSize is OK, but if gadget driver just wants autoconf core to use it with maximum possible value, I'm thinking now if we can even change this part and if wMaxPacketSize is zero, find endpoint with maximum possible wMaxPacketSize Does it make sense? Thanks Ruslan > > Peter > > > + else if (!ep_min) > > + ep_min = ep; > > + else if (ep->maxpacket_limit < > > ep_min->maxpacket_limit) > > + ep_min = ep; > > + } > > } > > > > /* Fail */ > > - return NULL; > > + if (!ep_min) > > + return NULL; > > + > > + ep = ep_min; > > found_ep: > > > > /* > > -- > > 2.17.1 > > > > -- > > Thanks, > Peter Chen
[PATCH v2] usb: gadget: epautoconf: claim smallest endpoints first
UDC hardware may have endpoints with different maxpacket size. Current endpoint matching code takes first matching endpoint from the list. It's always possible that gadget allocates endpoints for small transfers (maxpacket size) first, then larger ones. That works fine if all matching UDC endpoints have same maxpacket size or are big enough to serve that allocation. However, some UDCs have first endpoints in the list with bigger maxpacket size, whereas last endpoints are much smaller. In this case endpoint allocation will fail for the gadget (which allocates smaller endpoints first) on final endpoint allocations. To make endpoint allocation fair, pick up smallest matching endpoints first, leaving bigger ones for heavier applications. Signed-off-by: Ruslan Bilovol --- v2: rebased onto latest balbi/next branch drivers/usb/gadget/epautoconf.c | 23 ++- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 1eb4fa2e623f..6c453b5d87bb 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -66,7 +66,7 @@ struct usb_ep *usb_ep_autoconfig_ss( struct usb_ss_ep_comp_descriptor *ep_comp ) { - struct usb_ep *ep; + struct usb_ep *ep, *ep_min = NULL; if (gadget->ops->match_ep) { ep = gadget->ops->match_ep(gadget, desc, ep_comp); @@ -74,14 +74,27 @@ struct usb_ep *usb_ep_autoconfig_ss( goto found_ep; } - /* Second, look at endpoints until an unclaimed one looks usable */ + /* +* Second, look at endpoints until an unclaimed one looks usable. +* Try to find one with smallest maxpacket limit, leaving larger +* endpoints for heavier applications +*/ list_for_each_entry (ep, >ep_list, ep_list) { - if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp)) - goto found_ep; + if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp)) { + if (desc->wMaxPacketSize == 0) + goto found_ep; + else if (!ep_min) + ep_min = ep; + else if (ep->maxpacket_limit < ep_min->maxpacket_limit) + ep_min = ep; + } } /* Fail */ - return NULL; + if (!ep_min) + return NULL; + + ep = ep_min; found_ep: /* -- 2.17.1
[PATCH] usb: gadget: epautoconf: claim smallest endpoints first
UDC hardware may have endpoints with different maxpacket size. Current endpoint matching code takes first matching endpoint from the list. It's always possible that gadget allocates endpoints for small transfers (maxpacket size) first, then larger ones. That works fine if all matching UDC endpoints have same maxpacket size or are big enough to serve that allocation. However, some UDCs have first endpoints in the list with bigger maxpacket size, whereas last endpoints are much smaller. In this case endpoint allocation will fail for the gadget (which allocates smaller endpoints first) on final endpoint allocations. To make endpoint allocation fair, pick up smallest matching endpoints first, leaving bigger ones for heavier applications. Signed-off-by: Ruslan Bilovol --- drivers/usb/gadget/epautoconf.c | 23 ++- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 71b15c65b90f..1814a760ce48 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -66,7 +66,7 @@ struct usb_ep *usb_ep_autoconfig_ss( struct usb_ss_ep_comp_descriptor *ep_comp ) { - struct usb_ep *ep; + struct usb_ep *ep, *ep_min = NULL; u8 type; type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; @@ -77,14 +77,27 @@ struct usb_ep *usb_ep_autoconfig_ss( goto found_ep; } - /* Second, look at endpoints until an unclaimed one looks usable */ + /* +* Second, look at endpoints until an unclaimed one looks usable. +* Try to find one with smallest maxpacket limit, leaving larger +* endpoints for heavier applications +*/ list_for_each_entry (ep, >ep_list, ep_list) { - if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp)) - goto found_ep; + if (usb_gadget_ep_match_desc(gadget, ep, desc, ep_comp)) { + if (desc->wMaxPacketSize == 0) + goto found_ep; + else if (!ep_min) + ep_min = ep; + else if (ep->maxpacket_limit < ep_min->maxpacket_limit) + ep_min = ep; + } } /* Fail */ - return NULL; + if (!ep_min) + return NULL; + + ep = ep_min; found_ep: /* -- 2.17.1
Re: [PATCH v8 0/5] arm64: ftrace with regs
On Tue, Apr 9, 2019 at 8:52 PM Will Deacon wrote: > > On Mon, Apr 08, 2019 at 04:36:28PM +0100, Mark Rutland wrote: > > On Mon, Mar 11, 2019 at 12:49:46PM +0100, Torsten Duwe wrote: > > > On Wed, Feb 13, 2019 at 11:11:04AM +, Julien Thierry wrote: > > > > Hi Torsten, > > > > > > > > On 08/02/2019 15:08, Torsten Duwe wrote: > > > > > Patch series v8, as discussed. > > > > > The whole series applies cleanly on 5.0-rc5 > > > > > > So what's the status now? Besides debatable minor style > > > issues there were no more objections to v8. Would this > > > go through the ARM repo or via the ftrace repo? > > > > Sorry agains for the delay on this. I'm now back in the office and in > > front of a computer daily, so I can spend a bit more time on this. > > > > Regardless of anything else, I think that we should queue the first > > three patches now. I've poked the relevant maintainers for their acks so > > that those can be taken via the arm64 tree. > > > > I'm happy to do the trivial cleanups on the last couple of patches (e.g. > > s/lr/x30), and I'm actively looking at the API rework I requested. > > Ok, I've picked up patches 1-3 and I'll wait for you to spin updates to the > last two. Ok, I see that patches 1-3 are picked up and are already present in recent kernels. Is there any progress on remaining two patches? Any help required? Thanks, Ruslan
[PATCH] usb: host: xhci-hub: fix extra endianness conversion
Don't do extra cpu_to_le32 conversion for put_unaligned_le32 because it is already implemented in this function. Fixes sparse error: xhci-hub.c:1152:44: warning: incorrect type in argument 1 (different base types) xhci-hub.c:1152:44:expected unsigned int [usertype] val xhci-hub.c:1152:44:got restricted __le32 [usertype] Fixes: 395f540 "xhci: support new USB 3.1 hub request to get extended port status" Cc: Mathias Nyman Signed-off-by: Ruslan Bilovol --- drivers/usb/host/xhci-hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 3abe70f..b7d23c4 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1149,7 +1149,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } port_li = readl(ports[wIndex]->addr + PORTLI); status = xhci_get_ext_port_status(temp, port_li); - put_unaligned_le32(cpu_to_le32(status), [4]); + put_unaligned_le32(status, [4]); } break; case SetPortFeature: -- 1.9.1
Re: [PATCH v2 0/4] ALSA: usb-audio: Insertion Control for BADD Adaptors.
On Thu, Jun 14, 2018 at 7:07 PM, Takashi Iwai wrote: > On Thu, 14 Jun 2018 16:05:54 +0200, > Jorge Sanjuan wrote: >> >> Thanks for review! >> >> Here is what's new in this V2: >> - Split the patchset into a more sensible series. >> >> This patchset adds support for the Insertion Control for BADD devices >> (subset of UAC3). >> >> This control is only pressent in the HEADSET ADAPTER BADD profile. The USB >> interrupt pipe shall be present for this profile so the status pipe >> creation should also happen for BADD devices. >> >> Also, the bi-directional type codes have been defined so they can be used >> as inferred values for the Input and Output terminals for this adapter >> profile. >> >> Based on tag: next-20180612 >> >> Jorge Sanjuan (4): >> ALSA: usb-audio: Add bi-directional terminal types. >> ALSA: usb-audio: Change in connectors control creation interface >> ALSA: usb-audio: UAC3. Add insertion control for BADD. >> ALSA: usb-audio: Always create the interrupt pipe for the mixer > > Thanks, this looks nicer, each of changes is smaller and clearer. > Although it's a bit too late for the merge window, this is a change > that is fairly safe for the existing UAC1/UAC2, I merged it now. > It's too late probably, but I just finished reviewing these patches and don't have any objections. Thanks, Ruslan
Re: [PATCH v2 0/4] ALSA: usb-audio: Insertion Control for BADD Adaptors.
On Thu, Jun 14, 2018 at 7:07 PM, Takashi Iwai wrote: > On Thu, 14 Jun 2018 16:05:54 +0200, > Jorge Sanjuan wrote: >> >> Thanks for review! >> >> Here is what's new in this V2: >> - Split the patchset into a more sensible series. >> >> This patchset adds support for the Insertion Control for BADD devices >> (subset of UAC3). >> >> This control is only pressent in the HEADSET ADAPTER BADD profile. The USB >> interrupt pipe shall be present for this profile so the status pipe >> creation should also happen for BADD devices. >> >> Also, the bi-directional type codes have been defined so they can be used >> as inferred values for the Input and Output terminals for this adapter >> profile. >> >> Based on tag: next-20180612 >> >> Jorge Sanjuan (4): >> ALSA: usb-audio: Add bi-directional terminal types. >> ALSA: usb-audio: Change in connectors control creation interface >> ALSA: usb-audio: UAC3. Add insertion control for BADD. >> ALSA: usb-audio: Always create the interrupt pipe for the mixer > > Thanks, this looks nicer, each of changes is smaller and clearer. > Although it's a bit too late for the merge window, this is a change > that is fairly safe for the existing UAC1/UAC2, I merged it now. > It's too late probably, but I just finished reviewing these patches and don't have any objections. Thanks, Ruslan
[PATCH] ALSA: usb: stream: fix potential memory leak during uac3 interface parsing
UAC3 channel map is created during interface parsing, and in some cases was not freed in failure paths. Reported-by: Dan Carpenter <dan.carpen...@oracle.com> Signed-off-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> --- sound/usb/stream.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index bce3152..d16e1c2 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -982,13 +982,16 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, dev_err(>dev, "%u:%d : bogus bTerminalLink %d\n", iface_no, altno, as->bTerminalLink); + kfree(chmap); return NULL; found_clock: fp = audio_format_alloc_init(chip, alts, UAC_VERSION_3, iface_no, altset_idx, altno, num_channels, clock); - if (!fp) + if (!fp) { + kfree(chmap); return ERR_PTR(-ENOMEM); + } fp->chmap = chmap; @@ -1009,6 +1012,7 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, iface_no); /* ok, let's parse further... */ if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { + kfree(fp->chmap); kfree(fp->rate_table); kfree(fp); return NULL; -- 1.9.1
[PATCH] ALSA: usb: stream: fix potential memory leak during uac3 interface parsing
UAC3 channel map is created during interface parsing, and in some cases was not freed in failure paths. Reported-by: Dan Carpenter Signed-off-by: Ruslan Bilovol --- sound/usb/stream.c | 6 +- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index bce3152..d16e1c2 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -982,13 +982,16 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, dev_err(>dev, "%u:%d : bogus bTerminalLink %d\n", iface_no, altno, as->bTerminalLink); + kfree(chmap); return NULL; found_clock: fp = audio_format_alloc_init(chip, alts, UAC_VERSION_3, iface_no, altset_idx, altno, num_channels, clock); - if (!fp) + if (!fp) { + kfree(chmap); return ERR_PTR(-ENOMEM); + } fp->chmap = chmap; @@ -1009,6 +1012,7 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, iface_no); /* ok, let's parse further... */ if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) { + kfree(fp->chmap); kfree(fp->rate_table); kfree(fp); return NULL; -- 1.9.1
Re: [RESEND PATCH v4 4/4] ALSA: usb-audio: UAC3: Parse Input Terminal number of channels.
On Mon, May 14, 2018 at 2:03 PM, Jorge Sanjuan <jorge.sanj...@codethink.co.uk> wrote: > Obtain the number of channels for the Input Terminal from the > Logical Cluster Descriptor. This achieves a useful minimal parsing > of this unit so it can be used in other units in the topology. Usually 'patch resend' means resend without any changes, and if there are updates in the patch - it's a new version. By the way, as I already said in comments to patch 1/4 [1], I verified this patch successfully. Reviewed-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> Tested-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> [1] http://mailman.alsa-project.org/pipermail/alsa-devel/2018-May/136044.html > > Signed-off-by: Jorge Sanjuan <jorge.sanj...@codethink.co.uk> > --- > sound/usb/mixer.c | 8 ++-- > 1 file changed, 6 insertions(+), 2 deletions(-) > > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index 431f3c319839..99804cd4aed6 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -904,8 +904,12 @@ static int check_input_term(struct mixer_build *state, > int id, > term->id = id; > term->type = le16_to_cpu(d->wTerminalType); > > - /* REVISIT: UAC3 IT doesn't have channels/cfg > */ > - term->channels = 0; > + err = get_cluster_channels_v3(state, > le16_to_cpu(d->wClusterDescrID)); > + if (err < 0) > + return err; > + term->channels = err; > + > + /* REVISIT: UAC3 IT doesn't have channels cfg > */ > term->chconfig = 0; > > term->name = > le16_to_cpu(d->wTerminalDescrStr); > -- > 2.11.0 >
Re: [RESEND PATCH v4 4/4] ALSA: usb-audio: UAC3: Parse Input Terminal number of channels.
On Mon, May 14, 2018 at 2:03 PM, Jorge Sanjuan wrote: > Obtain the number of channels for the Input Terminal from the > Logical Cluster Descriptor. This achieves a useful minimal parsing > of this unit so it can be used in other units in the topology. Usually 'patch resend' means resend without any changes, and if there are updates in the patch - it's a new version. By the way, as I already said in comments to patch 1/4 [1], I verified this patch successfully. Reviewed-by: Ruslan Bilovol Tested-by: Ruslan Bilovol [1] http://mailman.alsa-project.org/pipermail/alsa-devel/2018-May/136044.html > > Signed-off-by: Jorge Sanjuan > --- > sound/usb/mixer.c | 8 ++-- > 1 file changed, 6 insertions(+), 2 deletions(-) > > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index 431f3c319839..99804cd4aed6 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -904,8 +904,12 @@ static int check_input_term(struct mixer_build *state, > int id, > term->id = id; > term->type = le16_to_cpu(d->wTerminalType); > > - /* REVISIT: UAC3 IT doesn't have channels/cfg > */ > - term->channels = 0; > + err = get_cluster_channels_v3(state, > le16_to_cpu(d->wClusterDescrID)); > + if (err < 0) > + return err; > + term->channels = err; > + > + /* REVISIT: UAC3 IT doesn't have channels cfg > */ > term->chconfig = 0; > > term->name = > le16_to_cpu(d->wTerminalDescrStr); > -- > 2.11.0 >
Re: [PATCH v4 2/4] ALSA: usb-audio: Use Class Specific EP for UAC3 devices.
On Fri, May 11, 2018 at 6:25 PM, Jorge Sanjuan <jorge.sanj...@codethink.co.uk> wrote: > bmAtributes offset doesn't exist in the UAC3 CS_EP descriptor. > Hence, checking for pitch control as if it was UAC2 doesn't make > any sense. Use the defined UAC3 offsets instead. This one I already reviewed in v2 and there is no changes in v4, so still: Reviewed-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> By the way, this patch is an independent change and can go into v4.17-rcXX, if it's not too late for it. Thanks, Ruslan > > Signed-off-by: Jorge Sanjuan <jorge.sanj...@codethink.co.uk> > --- > sound/usb/stream.c | 9 - > 1 file changed, 8 insertions(+), 1 deletion(-) > > diff --git a/sound/usb/stream.c b/sound/usb/stream.c > index 764be07474a8..6b2924533d8d 100644 > --- a/sound/usb/stream.c > +++ b/sound/usb/stream.c > @@ -576,7 +576,7 @@ static int parse_uac_endpoint_attributes(struct > snd_usb_audio *chip, > > if (protocol == UAC_VERSION_1) { > attributes = csep->bmAttributes; > - } else { > + } else if (protocol == UAC_VERSION_2) { > struct uac2_iso_endpoint_descriptor *csep2 = > (struct uac2_iso_endpoint_descriptor *) csep; > > @@ -585,6 +585,13 @@ static int parse_uac_endpoint_attributes(struct > snd_usb_audio *chip, > /* emulate the endpoint attributes of a v1 device */ > if (csep2->bmControls & UAC2_CONTROL_PITCH) > attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; > + } else { /* UAC_VERSION_3 */ > + struct uac3_iso_endpoint_descriptor *csep3 = > + (struct uac3_iso_endpoint_descriptor *) csep; > + > + /* emulate the endpoint attributes of a v1 device */ > + if (le32_to_cpu(csep3->bmControls) & UAC2_CONTROL_PITCH) > + attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; > } > > return attributes; > -- > 2.11.0 >
Re: [PATCH v4 2/4] ALSA: usb-audio: Use Class Specific EP for UAC3 devices.
On Fri, May 11, 2018 at 6:25 PM, Jorge Sanjuan wrote: > bmAtributes offset doesn't exist in the UAC3 CS_EP descriptor. > Hence, checking for pitch control as if it was UAC2 doesn't make > any sense. Use the defined UAC3 offsets instead. This one I already reviewed in v2 and there is no changes in v4, so still: Reviewed-by: Ruslan Bilovol By the way, this patch is an independent change and can go into v4.17-rcXX, if it's not too late for it. Thanks, Ruslan > > Signed-off-by: Jorge Sanjuan > --- > sound/usb/stream.c | 9 - > 1 file changed, 8 insertions(+), 1 deletion(-) > > diff --git a/sound/usb/stream.c b/sound/usb/stream.c > index 764be07474a8..6b2924533d8d 100644 > --- a/sound/usb/stream.c > +++ b/sound/usb/stream.c > @@ -576,7 +576,7 @@ static int parse_uac_endpoint_attributes(struct > snd_usb_audio *chip, > > if (protocol == UAC_VERSION_1) { > attributes = csep->bmAttributes; > - } else { > + } else if (protocol == UAC_VERSION_2) { > struct uac2_iso_endpoint_descriptor *csep2 = > (struct uac2_iso_endpoint_descriptor *) csep; > > @@ -585,6 +585,13 @@ static int parse_uac_endpoint_attributes(struct > snd_usb_audio *chip, > /* emulate the endpoint attributes of a v1 device */ > if (csep2->bmControls & UAC2_CONTROL_PITCH) > attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; > + } else { /* UAC_VERSION_3 */ > + struct uac3_iso_endpoint_descriptor *csep3 = > + (struct uac3_iso_endpoint_descriptor *) csep; > + > + /* emulate the endpoint attributes of a v1 device */ > + if (le32_to_cpu(csep3->bmControls) & UAC2_CONTROL_PITCH) > + attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; > } > > return attributes; > -- > 2.11.0 >
Re: [PATCH v4 1/4] ALSA: usb-audio: UAC3. Add support for mixer unit.
On Fri, May 11, 2018 at 6:25 PM, Jorge Sanjuan <jorge.sanj...@codethink.co.uk> wrote: > This adds support for the MIXER UNIT in UAC3. All the information > is obtained from the (HIGH CAPABILITY) Cluster's header. We don't > read the rest of the logical cluster to obtain the channel config > as that wont make any difference in the current mixer behaviour. > > The name of the mixer unit is not yet requested as there is not > support for the UAC3 Class Specific String requests. > > Tested in an UAC3 device working as a HEADSET with a basic mixer > unit (same as the one in the BADD spec) with no controls. I tested this patch in a similar use-case (with a simple mixer unit), but _with_ controls and along with patch [1] from this series, which added parsing input terminal's channels. So everything works fine, I see all needed requests handling and mixer unit creation on ALSA side, which I can use now. So, as a bottom line: Reviewed-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> Tested-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> [1] http://mailman.alsa-project.org/pipermail/alsa-devel/2018-May/136030.html > > Signed-off-by: Jorge Sanjuan <jorge.sanj...@codethink.co.uk> > --- > include/uapi/linux/usb/audio.h | 19 +++-- > sound/usb/mixer.c | 88 > ++ > 2 files changed, 97 insertions(+), 10 deletions(-) > > diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h > index 3a78e7145689..13d98e6e0db1 100644 > --- a/include/uapi/linux/usb/audio.h > +++ b/include/uapi/linux/usb/audio.h > @@ -285,9 +285,22 @@ static inline __u8 uac_mixer_unit_iChannelNames(struct > uac_mixer_unit_descriptor > static inline __u8 *uac_mixer_unit_bmControls(struct > uac_mixer_unit_descriptor *desc, > int protocol) > { > - return (protocol == UAC_VERSION_1) ? > - >baSourceID[desc->bNrInPins + 4] : > - >baSourceID[desc->bNrInPins + 6]; > + switch (protocol) { > + case UAC_VERSION_1: > + return >baSourceID[desc->bNrInPins + 4]; > + case UAC_VERSION_2: > + return >baSourceID[desc->bNrInPins + 6]; > + case UAC_VERSION_3: > + return >baSourceID[desc->bNrInPins + 2]; > + default: > + return NULL; > + } > +} > + > +static inline __u16 uac3_mixer_unit_wClusterDescrID(struct > uac_mixer_unit_descriptor *desc) > +{ > + return (desc->baSourceID[desc->bNrInPins + 1] << 8) | > + desc->baSourceID[desc->bNrInPins]; > } > > static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor > *desc) > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index 76417943ff85..129c1397f0cb 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -719,6 +719,66 @@ static int get_term_name(struct snd_usb_audio *chip, > struct usb_audio_term *iter > } > > /* > + * Get logical cluster information for UAC3 devices. > + */ > +static int get_cluster_channels_v3(struct mixer_build *state, unsigned int > cluster_id) > +{ > + struct uac3_cluster_header_descriptor c_header; > + int err; > + > + err = snd_usb_ctl_msg(state->chip->dev, > + usb_rcvctrlpipe(state->chip->dev, 0), > + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, > + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, > + cluster_id, > + snd_usb_ctrl_intf(state->chip), > + _header, sizeof(c_header)); > + if (err < 0) > + goto error; > + if (err != sizeof(c_header)) { > + err = -EIO; > + goto error; > + } > + > + return c_header.bNrChannels; > + > +error: > + usb_audio_err(state->chip, "cannot request logical cluster ID: %d > (err: %d)\n", cluster_id, err); > + return err; > +} > + > +/* > + * Get number of channels for a Mixer Unit. > + */ > +static int uac_mixer_unit_get_channels(struct mixer_build *state, > + struct uac_mixer_unit_descriptor *desc) > +{ > + int mu_channels; > + > + if (desc->bLength < 11) > + return -EINVAL; > + if (!desc->bNrInPins) > + return -EINVAL; > + > + switch (state->mixer->protocol) { > + case UAC_VERSION_1: > + case UAC_VERSION_2: > + default: > + mu_channels = uac_mixer_unit_bNrChannels(desc); > +
Re: [PATCH v4 1/4] ALSA: usb-audio: UAC3. Add support for mixer unit.
On Fri, May 11, 2018 at 6:25 PM, Jorge Sanjuan wrote: > This adds support for the MIXER UNIT in UAC3. All the information > is obtained from the (HIGH CAPABILITY) Cluster's header. We don't > read the rest of the logical cluster to obtain the channel config > as that wont make any difference in the current mixer behaviour. > > The name of the mixer unit is not yet requested as there is not > support for the UAC3 Class Specific String requests. > > Tested in an UAC3 device working as a HEADSET with a basic mixer > unit (same as the one in the BADD spec) with no controls. I tested this patch in a similar use-case (with a simple mixer unit), but _with_ controls and along with patch [1] from this series, which added parsing input terminal's channels. So everything works fine, I see all needed requests handling and mixer unit creation on ALSA side, which I can use now. So, as a bottom line: Reviewed-by: Ruslan Bilovol Tested-by: Ruslan Bilovol [1] http://mailman.alsa-project.org/pipermail/alsa-devel/2018-May/136030.html > > Signed-off-by: Jorge Sanjuan > --- > include/uapi/linux/usb/audio.h | 19 +++-- > sound/usb/mixer.c | 88 > ++ > 2 files changed, 97 insertions(+), 10 deletions(-) > > diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h > index 3a78e7145689..13d98e6e0db1 100644 > --- a/include/uapi/linux/usb/audio.h > +++ b/include/uapi/linux/usb/audio.h > @@ -285,9 +285,22 @@ static inline __u8 uac_mixer_unit_iChannelNames(struct > uac_mixer_unit_descriptor > static inline __u8 *uac_mixer_unit_bmControls(struct > uac_mixer_unit_descriptor *desc, > int protocol) > { > - return (protocol == UAC_VERSION_1) ? > - >baSourceID[desc->bNrInPins + 4] : > - >baSourceID[desc->bNrInPins + 6]; > + switch (protocol) { > + case UAC_VERSION_1: > + return >baSourceID[desc->bNrInPins + 4]; > + case UAC_VERSION_2: > + return >baSourceID[desc->bNrInPins + 6]; > + case UAC_VERSION_3: > + return >baSourceID[desc->bNrInPins + 2]; > + default: > + return NULL; > + } > +} > + > +static inline __u16 uac3_mixer_unit_wClusterDescrID(struct > uac_mixer_unit_descriptor *desc) > +{ > + return (desc->baSourceID[desc->bNrInPins + 1] << 8) | > + desc->baSourceID[desc->bNrInPins]; > } > > static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor > *desc) > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index 76417943ff85..129c1397f0cb 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -719,6 +719,66 @@ static int get_term_name(struct snd_usb_audio *chip, > struct usb_audio_term *iter > } > > /* > + * Get logical cluster information for UAC3 devices. > + */ > +static int get_cluster_channels_v3(struct mixer_build *state, unsigned int > cluster_id) > +{ > + struct uac3_cluster_header_descriptor c_header; > + int err; > + > + err = snd_usb_ctl_msg(state->chip->dev, > + usb_rcvctrlpipe(state->chip->dev, 0), > + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, > + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, > + cluster_id, > + snd_usb_ctrl_intf(state->chip), > + _header, sizeof(c_header)); > + if (err < 0) > + goto error; > + if (err != sizeof(c_header)) { > + err = -EIO; > + goto error; > + } > + > + return c_header.bNrChannels; > + > +error: > + usb_audio_err(state->chip, "cannot request logical cluster ID: %d > (err: %d)\n", cluster_id, err); > + return err; > +} > + > +/* > + * Get number of channels for a Mixer Unit. > + */ > +static int uac_mixer_unit_get_channels(struct mixer_build *state, > + struct uac_mixer_unit_descriptor *desc) > +{ > + int mu_channels; > + > + if (desc->bLength < 11) > + return -EINVAL; > + if (!desc->bNrInPins) > + return -EINVAL; > + > + switch (state->mixer->protocol) { > + case UAC_VERSION_1: > + case UAC_VERSION_2: > + default: > + mu_channels = uac_mixer_unit_bNrChannels(desc); > + break; > + case UAC_VERSION_3: > + mu_channels = get_cluster_channels_v3(state, > +
Re: [PATCH v4 4/4] ALSA: usb-audio: UAC3: Parse Input Terminal number of channels.
On Mon, May 14, 2018 at 11:54 AM, Jorgewrote: > > > On 11/05/18 16:25, Jorge Sanjuan wrote: >> >> Obtain the number of channels for the Input Terminal from the >> Logical Cluster Descriptor. This achieves a useful minimal parsing >> of this unit so it can be used in other units in the topology. >> >> Signed-off-by: Jorge Sanjuan >> --- >> sound/usb/mixer.c | 4 ++-- >> 1 file changed, 2 insertions(+), 2 deletions(-) >> >> diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c >> index 431f3c319839..19b25fbc7437 100644 >> --- a/sound/usb/mixer.c >> +++ b/sound/usb/mixer.c >> @@ -903,9 +903,9 @@ static int check_input_term(struct mixer_build *state, >> int id, >> * recursion calls */ >> term->id = id; >> term->type = >> le16_to_cpu(d->wTerminalType); >> + term->channels = >> get_cluster_channels_v3(state, d->wClusterDescrID); > > > > Sorry about this. I just spotted that I should have used the helper function > I added to access d->wClusterDescrID `uac3_mixer_unit_wClusterDescrID`. > > I got the sparse warning for the endianess and realized that. I'll resend > this one patch. While here, please add checking output of get_cluster_channels_v3() as it can return negative errno. BTW, I've just tested your Mixer patches and this is the only comment I have so far. Thanks, Ruslan > >> - /* REVISIT: UAC3 IT doesn't have >> channels/cfg */ >> - term->channels = 0; >> + /* REVISIT: UAC3 IT doesn't have channels >> cfg */ >> term->chconfig = 0; >> term->name = >> le16_to_cpu(d->wTerminalDescrStr); >> >
Re: [PATCH v4 4/4] ALSA: usb-audio: UAC3: Parse Input Terminal number of channels.
On Mon, May 14, 2018 at 11:54 AM, Jorge wrote: > > > On 11/05/18 16:25, Jorge Sanjuan wrote: >> >> Obtain the number of channels for the Input Terminal from the >> Logical Cluster Descriptor. This achieves a useful minimal parsing >> of this unit so it can be used in other units in the topology. >> >> Signed-off-by: Jorge Sanjuan >> --- >> sound/usb/mixer.c | 4 ++-- >> 1 file changed, 2 insertions(+), 2 deletions(-) >> >> diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c >> index 431f3c319839..19b25fbc7437 100644 >> --- a/sound/usb/mixer.c >> +++ b/sound/usb/mixer.c >> @@ -903,9 +903,9 @@ static int check_input_term(struct mixer_build *state, >> int id, >> * recursion calls */ >> term->id = id; >> term->type = >> le16_to_cpu(d->wTerminalType); >> + term->channels = >> get_cluster_channels_v3(state, d->wClusterDescrID); > > > > Sorry about this. I just spotted that I should have used the helper function > I added to access d->wClusterDescrID `uac3_mixer_unit_wClusterDescrID`. > > I got the sparse warning for the endianess and realized that. I'll resend > this one patch. While here, please add checking output of get_cluster_channels_v3() as it can return negative errno. BTW, I've just tested your Mixer patches and this is the only comment I have so far. Thanks, Ruslan > >> - /* REVISIT: UAC3 IT doesn't have >> channels/cfg */ >> - term->channels = 0; >> + /* REVISIT: UAC3 IT doesn't have channels >> cfg */ >> term->chconfig = 0; >> term->name = >> le16_to_cpu(d->wTerminalDescrStr); >> >
Re: [PATCH v3 1/4] ALSA: usb-audio: UAC3. Add support for mixer unit.
On Tue, May 8, 2018 at 12:43 PM, Jorge <jorge.sanj...@codethink.co.uk> wrote: > > > On 04/05/18 01:57, Ruslan Bilovol wrote: >> >> On Fri, Apr 27, 2018 at 8:06 PM, Jorge Sanjuan >> <jorge.sanj...@codethink.co.uk> wrote: >>> >>> This adds support for the MIXER UNIT in UAC3. All the information >>> is obtained from the (HIGH CAPABILITY) Cluster's header. We don't >>> read the rest of the logical cluster to obtain the channel config >>> as that wont make any difference in the current mixer behaviour. >>> >>> The name of the mixer unit is not yet requested as there is not >>> support for the UAC3 Class Specific String requests. >>> >>> Tested in an UAC3 device working as a HEADSET with a basic mixer >>> unit (same as the one in the BADD spec) with no controls. >> >> >> So, after deeper looking into the code and after testing this patch, >> in your usecase (mixer with no controls) you'll never execute >> build_mixer_unit_ctl(), correct? So did you try to just fix issues with >> incorrect parsing of mixer unit descriptor? >> >>> >>> Signed-off-by: Jorge Sanjuan <jorge.sanj...@codethink.co.uk> >>> --- >>> include/uapi/linux/usb/audio.h | 19 +++-- >>> sound/usb/mixer.c | 88 >>> ++ >>> 2 files changed, 97 insertions(+), 10 deletions(-) >>> >>> diff --git a/include/uapi/linux/usb/audio.h >>> b/include/uapi/linux/usb/audio.h >>> index 3a78e7145689..13d98e6e0db1 100644 >>> --- a/include/uapi/linux/usb/audio.h >>> +++ b/include/uapi/linux/usb/audio.h >>> @@ -285,9 +285,22 @@ static inline __u8 >>> uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor >>> static inline __u8 *uac_mixer_unit_bmControls(struct >>> uac_mixer_unit_descriptor *desc, >>>int protocol) >>> { >>> - return (protocol == UAC_VERSION_1) ? >>> - >baSourceID[desc->bNrInPins + 4] : >>> - >baSourceID[desc->bNrInPins + 6]; >>> + switch (protocol) { >>> + case UAC_VERSION_1: >>> + return >baSourceID[desc->bNrInPins + 4]; >>> + case UAC_VERSION_2: >>> + return >baSourceID[desc->bNrInPins + 6]; >>> + case UAC_VERSION_3: >>> + return >baSourceID[desc->bNrInPins + 2]; >>> + default: >>> + return NULL; >>> + } >>> +} >>> + >>> +static inline __u16 uac3_mixer_unit_wClusterDescrID(struct >>> uac_mixer_unit_descriptor *desc) >>> +{ >>> + return (desc->baSourceID[desc->bNrInPins + 1] << 8) | >>> + desc->baSourceID[desc->bNrInPins]; >>> } >>> >>> static inline __u8 uac_mixer_unit_iMixer(struct >>> uac_mixer_unit_descriptor *desc) >>> diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c >>> index 301ad61ed426..3503f4840ec3 100644 >>> --- a/sound/usb/mixer.c >>> +++ b/sound/usb/mixer.c >>> @@ -719,6 +719,66 @@ static int get_term_name(struct mixer_build *state, >>> struct usb_audio_term *iterm >>> } >>> >>> /* >>> + * Get logical cluster information for UAC3 devices. >>> + */ >>> +static int get_cluster_channels_v3(struct mixer_build *state, unsigned >>> int cluster_id) >>> +{ >>> + struct uac3_cluster_header_descriptor c_header; >>> + int err; >>> + >>> + err = snd_usb_ctl_msg(state->chip->dev, >>> + usb_rcvctrlpipe(state->chip->dev, 0), >>> + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, >>> + USB_RECIP_INTERFACE | USB_TYPE_CLASS | >>> USB_DIR_IN, >>> + cluster_id, >>> + snd_usb_ctrl_intf(state->chip), >>> + _header, sizeof(c_header)); >>> + if (err < 0) >>> + goto error; >>> + if (err != sizeof(c_header)) { >>> + err = -EIO; >>> + goto error; >>> + } >>> + >>> + return c_header.bNrChannels; >>> + >>> +error: >>> + usb_audio_err(state->chip, "cannot request logical cluster ID: %d >>> (err: %d)\n", cluster_id, err); &g
Re: [PATCH v3 1/4] ALSA: usb-audio: UAC3. Add support for mixer unit.
On Tue, May 8, 2018 at 12:43 PM, Jorge wrote: > > > On 04/05/18 01:57, Ruslan Bilovol wrote: >> >> On Fri, Apr 27, 2018 at 8:06 PM, Jorge Sanjuan >> wrote: >>> >>> This adds support for the MIXER UNIT in UAC3. All the information >>> is obtained from the (HIGH CAPABILITY) Cluster's header. We don't >>> read the rest of the logical cluster to obtain the channel config >>> as that wont make any difference in the current mixer behaviour. >>> >>> The name of the mixer unit is not yet requested as there is not >>> support for the UAC3 Class Specific String requests. >>> >>> Tested in an UAC3 device working as a HEADSET with a basic mixer >>> unit (same as the one in the BADD spec) with no controls. >> >> >> So, after deeper looking into the code and after testing this patch, >> in your usecase (mixer with no controls) you'll never execute >> build_mixer_unit_ctl(), correct? So did you try to just fix issues with >> incorrect parsing of mixer unit descriptor? >> >>> >>> Signed-off-by: Jorge Sanjuan >>> --- >>> include/uapi/linux/usb/audio.h | 19 +++-- >>> sound/usb/mixer.c | 88 >>> ++ >>> 2 files changed, 97 insertions(+), 10 deletions(-) >>> >>> diff --git a/include/uapi/linux/usb/audio.h >>> b/include/uapi/linux/usb/audio.h >>> index 3a78e7145689..13d98e6e0db1 100644 >>> --- a/include/uapi/linux/usb/audio.h >>> +++ b/include/uapi/linux/usb/audio.h >>> @@ -285,9 +285,22 @@ static inline __u8 >>> uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor >>> static inline __u8 *uac_mixer_unit_bmControls(struct >>> uac_mixer_unit_descriptor *desc, >>>int protocol) >>> { >>> - return (protocol == UAC_VERSION_1) ? >>> - >baSourceID[desc->bNrInPins + 4] : >>> - >baSourceID[desc->bNrInPins + 6]; >>> + switch (protocol) { >>> + case UAC_VERSION_1: >>> + return >baSourceID[desc->bNrInPins + 4]; >>> + case UAC_VERSION_2: >>> + return >baSourceID[desc->bNrInPins + 6]; >>> + case UAC_VERSION_3: >>> + return >baSourceID[desc->bNrInPins + 2]; >>> + default: >>> + return NULL; >>> + } >>> +} >>> + >>> +static inline __u16 uac3_mixer_unit_wClusterDescrID(struct >>> uac_mixer_unit_descriptor *desc) >>> +{ >>> + return (desc->baSourceID[desc->bNrInPins + 1] << 8) | >>> + desc->baSourceID[desc->bNrInPins]; >>> } >>> >>> static inline __u8 uac_mixer_unit_iMixer(struct >>> uac_mixer_unit_descriptor *desc) >>> diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c >>> index 301ad61ed426..3503f4840ec3 100644 >>> --- a/sound/usb/mixer.c >>> +++ b/sound/usb/mixer.c >>> @@ -719,6 +719,66 @@ static int get_term_name(struct mixer_build *state, >>> struct usb_audio_term *iterm >>> } >>> >>> /* >>> + * Get logical cluster information for UAC3 devices. >>> + */ >>> +static int get_cluster_channels_v3(struct mixer_build *state, unsigned >>> int cluster_id) >>> +{ >>> + struct uac3_cluster_header_descriptor c_header; >>> + int err; >>> + >>> + err = snd_usb_ctl_msg(state->chip->dev, >>> + usb_rcvctrlpipe(state->chip->dev, 0), >>> + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, >>> + USB_RECIP_INTERFACE | USB_TYPE_CLASS | >>> USB_DIR_IN, >>> + cluster_id, >>> + snd_usb_ctrl_intf(state->chip), >>> + _header, sizeof(c_header)); >>> + if (err < 0) >>> + goto error; >>> + if (err != sizeof(c_header)) { >>> + err = -EIO; >>> + goto error; >>> + } >>> + >>> + return c_header.bNrChannels; >>> + >>> +error: >>> + usb_audio_err(state->chip, "cannot request logical cluster ID: %d >>> (err: %d)\n", cluster_id, err); >>> + return err; >>> +} >>> + >>> +/* >>> + * Get n
[PATCH v2 2/7] ALSA: usb: stream: refactor uac1/2 audio interface parsing
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support. Move class-specific parts of uac1/2 parsing to separate function 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.bilo...@gmail.com> --- sound/usb/stream.c | 333 + 1 file changed, 185 insertions(+), 148 deletions(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 8ec0a52..3369226 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -656,6 +656,156 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return fp; } +static struct audioformat * +snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int protocol, int iface_no, int altset_idx, + 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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%u:%d : bogus bT
[PATCH v2 3/7] ALSA: usb: stream: refactor uac3 audio interface parsing
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support. Move class-specific parts of uac3 parsing to separate function which now produce audioformat structure that is ready to be fed to snd_usb_add_audio_stream(). Signed-off-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> --- sound/usb/stream.c | 289 +++-- 1 file changed, 146 insertions(+), 143 deletions(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 3369226..764be07 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -807,19 +807,154 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return fp; } +static struct audioformat * +snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, +struct usb_host_interface *alts, +int iface_no, int altset_idx, +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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%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), + _header, sizeof(hc_header)); + if (err < 0) + return ERR_PTR(err); + else if (err != sizeof(hc_header)) { + dev_err(>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, + "%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)
[PATCH v2 2/7] ALSA: usb: stream: refactor uac1/2 audio interface parsing
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support. Move class-specific parts of uac1/2 parsing to separate function 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 --- sound/usb/stream.c | 333 + 1 file changed, 185 insertions(+), 148 deletions(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 8ec0a52..3369226 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -656,6 +656,156 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return fp; } +static struct audioformat * +snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int protocol, int iface_no, int altset_idx, + 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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%u:%d : bogus bTerminalLink %d\n", +
[PATCH v2 3/7] ALSA: usb: stream: refactor uac3 audio interface parsing
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support. Move class-specific parts of uac3 parsing to separate function which now produce audioformat structure that is ready to be fed to snd_usb_add_audio_stream(). Signed-off-by: Ruslan Bilovol --- sound/usb/stream.c | 289 +++-- 1 file changed, 146 insertions(+), 143 deletions(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 3369226..764be07 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -807,19 +807,154 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return fp; } +static struct audioformat * +snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, +struct usb_host_interface *alts, +int iface_no, int altset_idx, +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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%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), + _header, sizeof(hc_header)); + if (err < 0) + return ERR_PTR(err); + else if (err != sizeof(hc_header)) { + dev_err(>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, + "%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_
[PATCH v2 1/7] ALSA: usb: stream: move audioformat alloc/init into separate function
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support. Move audioformat allocation and initialization into separate function, this will make easier future refactoring. Attributes left in the original func because it'll be used for UAC3 BADD profiles suport in the future There is no functional change. Signed-off-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> --- sound/usb/stream.c | 52 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 956be9f..8ec0a52 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -626,6 +626,37 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return NULL; } +static struct audioformat * +audio_format_alloc_init(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int protocol, int iface_no, int altset_idx, + int altno, int num_channels, int clock) +{ + struct audioformat *fp; + + fp = kzalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) + return NULL; + + fp->iface = iface_no; + fp->altsetting = altno; + fp->altset_idx = altset_idx; + 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(chip->dev) == USB_SPEED_HIGH) + fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) + * (fp->maxpacksize & 0x7ff); + fp->clock = clock; + INIT_LIST_HEAD(>list); + + return fp; +} + + int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) { struct usb_device *dev; @@ -928,25 +959,14 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) continue; } - fp = kzalloc(sizeof(*fp), GFP_KERNEL); + fp = audio_format_alloc_init(chip, alts, protocol, iface_no, i, +altno, num_channels, clock); if (!fp) return -ENOMEM; - 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(>list); + fp->attributes = parse_uac_endpoint_attributes(chip, alts, + protocol, + iface_no); /* some quirks for attributes here */ snd_usb_audioformat_attributes_quirk(chip, fp, stream); -- 1.9.1
[PATCH v2 1/7] ALSA: usb: stream: move audioformat alloc/init into separate function
Offload snd_usb_parse_audio_interface() function which became quite long after adding UAC3 spec support. Move audioformat allocation and initialization into separate function, this will make easier future refactoring. Attributes left in the original func because it'll be used for UAC3 BADD profiles suport in the future There is no functional change. Signed-off-by: Ruslan Bilovol --- sound/usb/stream.c | 52 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 956be9f..8ec0a52 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -626,6 +626,37 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return NULL; } +static struct audioformat * +audio_format_alloc_init(struct snd_usb_audio *chip, + struct usb_host_interface *alts, + int protocol, int iface_no, int altset_idx, + int altno, int num_channels, int clock) +{ + struct audioformat *fp; + + fp = kzalloc(sizeof(*fp), GFP_KERNEL); + if (!fp) + return NULL; + + fp->iface = iface_no; + fp->altsetting = altno; + fp->altset_idx = altset_idx; + 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(chip->dev) == USB_SPEED_HIGH) + fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) + * (fp->maxpacksize & 0x7ff); + fp->clock = clock; + INIT_LIST_HEAD(>list); + + return fp; +} + + int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) { struct usb_device *dev; @@ -928,25 +959,14 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) continue; } - fp = kzalloc(sizeof(*fp), GFP_KERNEL); + fp = audio_format_alloc_init(chip, alts, protocol, iface_no, i, +altno, num_channels, clock); if (!fp) return -ENOMEM; - 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(>list); + fp->attributes = parse_uac_endpoint_attributes(chip, alts, + protocol, + iface_no); /* some quirks for attributes here */ snd_usb_audioformat_attributes_quirk(chip, fp, stream); -- 1.9.1
[PATCH v2 5/7] ALSA: usb: mixer: make string parsing independent of mixer_build state
Functions like snd_usb_copy_string_desc() or get_term_name() don't actually need mixer_build state but can use snd_usb_audio structure instead to get usb device. This patch has no functional change but prepares to future UAC3 BADD profiles support which don't have class-specific descriptors so won't have mixer_build state. Signed-off-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> --- sound/usb/mixer.c | 30 -- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index bb203b3..e280354 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -201,10 +201,10 @@ static void *find_audio_control_unit(struct mixer_build *state, /* * copy a string with the given id */ -static int snd_usb_copy_string_desc(struct mixer_build *state, +static int snd_usb_copy_string_desc(struct snd_usb_audio *chip, int index, char *buf, int maxlen) { - int len = usb_string(state->chip->dev, index, buf, maxlen - 1); + int len = usb_string(chip->dev, index, buf, maxlen - 1); if (len < 0) return 0; @@ -658,14 +658,14 @@ int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, { 0 }, }; -static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm, +static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iterm, unsigned char *name, int maxlen, int term_only) { struct iterm_name_combo *names; int len; if (iterm->name) { - len = snd_usb_copy_string_desc(state, iterm->name, + len = snd_usb_copy_string_desc(chip, iterm->name, name, maxlen); if (len) return len; @@ -1407,7 +1407,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; if (!len && nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(state->chip, nameid, kctl->id.name, sizeof(kctl->id.name)); switch (control) { @@ -1422,10 +1422,10 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, * - otherwise, anonymous name. */ if (!len) { - len = get_term_name(state, iterm, kctl->id.name, + len = get_term_name(state->chip, iterm, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) - len = get_term_name(state, >oterm, + len = get_term_name(state->chip, >oterm, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) @@ -1498,7 +1498,7 @@ static void get_connector_control_name(struct mixer_build *state, struct usb_audio_term *term, bool is_input, char *name, int name_size) { - int name_len = get_term_name(state, term, name, name_size, 0); + int name_len = get_term_name(state->chip, term, name, name_size, 0); if (name_len == 0) strlcpy(name, "Unknown", name_size); @@ -1597,7 +1597,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, } kctl->private_free = snd_usb_mixer_elem_free; - ret = snd_usb_copy_string_desc(state, hdr->iClockSource, + ret = snd_usb_copy_string_desc(state->chip, hdr->iClockSource, name, sizeof(name)); if (ret > 0) snprintf(kctl->id.name, sizeof(kctl->id.name), @@ -1840,7 +1840,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (!len) - len = get_term_name(state, iterm, kctl->id.name, + len = get_term_name(state->chip, iterm, kctl->id.name, sizeof(kctl->id.name), 0); if (!len) len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1); @@ -2154,7 +2154,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); len = 0; if (nameid) - len = snd_usb_copy_string_desc(state, namei
[PATCH v2 5/7] ALSA: usb: mixer: make string parsing independent of mixer_build state
Functions like snd_usb_copy_string_desc() or get_term_name() don't actually need mixer_build state but can use snd_usb_audio structure instead to get usb device. This patch has no functional change but prepares to future UAC3 BADD profiles support which don't have class-specific descriptors so won't have mixer_build state. Signed-off-by: Ruslan Bilovol --- sound/usb/mixer.c | 30 -- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index bb203b3..e280354 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -201,10 +201,10 @@ static void *find_audio_control_unit(struct mixer_build *state, /* * copy a string with the given id */ -static int snd_usb_copy_string_desc(struct mixer_build *state, +static int snd_usb_copy_string_desc(struct snd_usb_audio *chip, int index, char *buf, int maxlen) { - int len = usb_string(state->chip->dev, index, buf, maxlen - 1); + int len = usb_string(chip->dev, index, buf, maxlen - 1); if (len < 0) return 0; @@ -658,14 +658,14 @@ int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, { 0 }, }; -static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm, +static int get_term_name(struct snd_usb_audio *chip, struct usb_audio_term *iterm, unsigned char *name, int maxlen, int term_only) { struct iterm_name_combo *names; int len; if (iterm->name) { - len = snd_usb_copy_string_desc(state, iterm->name, + len = snd_usb_copy_string_desc(chip, iterm->name, name, maxlen); if (len) return len; @@ -1407,7 +1407,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; if (!len && nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(state->chip, nameid, kctl->id.name, sizeof(kctl->id.name)); switch (control) { @@ -1422,10 +1422,10 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, * - otherwise, anonymous name. */ if (!len) { - len = get_term_name(state, iterm, kctl->id.name, + len = get_term_name(state->chip, iterm, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) - len = get_term_name(state, >oterm, + len = get_term_name(state->chip, >oterm, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) @@ -1498,7 +1498,7 @@ static void get_connector_control_name(struct mixer_build *state, struct usb_audio_term *term, bool is_input, char *name, int name_size) { - int name_len = get_term_name(state, term, name, name_size, 0); + int name_len = get_term_name(state->chip, term, name, name_size, 0); if (name_len == 0) strlcpy(name, "Unknown", name_size); @@ -1597,7 +1597,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, } kctl->private_free = snd_usb_mixer_elem_free; - ret = snd_usb_copy_string_desc(state, hdr->iClockSource, + ret = snd_usb_copy_string_desc(state->chip, hdr->iClockSource, name, sizeof(name)); if (ret > 0) snprintf(kctl->id.name, sizeof(kctl->id.name), @@ -1840,7 +1840,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (!len) - len = get_term_name(state, iterm, kctl->id.name, + len = get_term_name(state->chip, iterm, kctl->id.name, sizeof(kctl->id.name), 0); if (!len) len = sprintf(kctl->id.name, "Mixer Source %d", in_ch + 1); @@ -2154,7 +2154,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); len = 0; if (nameid) - len = snd_usb_copy_string_desc(state, nameid, + len = snd_usb_copy_string_desc(state->chip, +
[PATCH v2 7/7] ALSA: usb: add UAC3 BADD profiles support
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.bilo...@gmail.com> --- sound/usb/card.c | 14 +++ sound/usb/clock.c | 9 +- sound/usb/mixer.c | 327 - sound/usb/mixer_maps.c | 65 ++ sound/usb/stream.c | 83 +++-- sound/usb/usbaudio.h | 2 + 6 files changed, 459 insertions(+), 41 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 0d7a5d7..f6c3c1c 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -307,6 +307,20 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) return -EINVAL; } + if (protocol == UAC_VERSION_3) { + int badd = assoc->bFunctionSubClass; + + if (badd != UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 && + (badd < UAC3_FUNCTION_SUBCLASS_GENERIC_IO || +badd > UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE)) { + dev_err(>dev, + "Unsupported UAC3 BADD profile\n"); + return -EINVAL; + } + + chip->badd_profile = badd; + } + for (i = 0; i < assoc->bInterfaceCount; i++) { int intf = assoc->bFirstInterface + i; diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 0b030d8..17673f3 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -587,8 +587,15 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, default: return set_sample_rate_v1(chip, iface, alts, fmt, rate); - case UAC_VERSION_2: case UAC_VERSION_3: + if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { + if (rate != UAC3_BADD_SAMPLING_RATE) + return -ENXIO; + else + return 0; + } + /* fall through */ + case UAC_VERSION_2: return set_sample_rate_v2v3(chip, iface, alts, fmt, rate); } } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e280354..d98bc3f 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -112,14 +112,12 @@ enum { #include "mixer_maps.c" static const struct usbmix_name_map * -find_map(struct mixer_build *state, int unitid, int control) +find_map(const struct usbmix_name_map *p, int unitid, int control) { - const struct usbmix_name_map *p = state->map; - if (!p) return NULL; - for (p = state->map; p->id; p++) { + for (; p->id; p++) { if (p->id == unitid && (!control || !p->control || control == p->control)) return p; @@ -1333,16 +1331,16 @@ static struct usb_feature_control_info *get_feature_control_info(int control) return NULL; } -static void build_feature_ctl(struct mixer_build *state, void *raw_desc, - unsigned int ctl_mask, int control, - struct usb_audio_term *iterm, int unitid, - int readonly_mask) +static void __build_feature_ctl(struct usb_mixer_interface *mixer, + const struct usbmix_name_map *imap, + unsigned int ctl_mask, int control, + struct usb_audio_term *iterm, + struct usb_audio_term *oterm, + int unitid, int nameid, int readonly_mask) { - struct uac_feature_unit_descriptor *desc = raw_desc; struct usb_feature_control_info *ctl_info; unsigned int len = 0; int mapped_name = 0; - int nameid = uac_feature_unit_iFeature(desc); struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; @@ -1353,14 +1351,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, return; } - map = find_map(state, unitid, control); + map = find_map(imap, unitid, control); if (check_ignored_ctl(map)) return; cval = kzalloc(sizeof(*cval), GFP_KERNEL);
[PATCH v2 7/7] ALSA: usb: add UAC3 BADD profiles support
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 --- sound/usb/card.c | 14 +++ sound/usb/clock.c | 9 +- sound/usb/mixer.c | 327 - sound/usb/mixer_maps.c | 65 ++ sound/usb/stream.c | 83 +++-- sound/usb/usbaudio.h | 2 + 6 files changed, 459 insertions(+), 41 deletions(-) diff --git a/sound/usb/card.c b/sound/usb/card.c index 0d7a5d7..f6c3c1c 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -307,6 +307,20 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) return -EINVAL; } + if (protocol == UAC_VERSION_3) { + int badd = assoc->bFunctionSubClass; + + if (badd != UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0 && + (badd < UAC3_FUNCTION_SUBCLASS_GENERIC_IO || +badd > UAC3_FUNCTION_SUBCLASS_SPEAKERPHONE)) { + dev_err(>dev, + "Unsupported UAC3 BADD profile\n"); + return -EINVAL; + } + + chip->badd_profile = badd; + } + for (i = 0; i < assoc->bInterfaceCount; i++) { int intf = assoc->bFirstInterface + i; diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 0b030d8..17673f3 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -587,8 +587,15 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, default: return set_sample_rate_v1(chip, iface, alts, fmt, rate); - case UAC_VERSION_2: case UAC_VERSION_3: + if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { + if (rate != UAC3_BADD_SAMPLING_RATE) + return -ENXIO; + else + return 0; + } + /* fall through */ + case UAC_VERSION_2: return set_sample_rate_v2v3(chip, iface, alts, fmt, rate); } } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e280354..d98bc3f 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -112,14 +112,12 @@ enum { #include "mixer_maps.c" static const struct usbmix_name_map * -find_map(struct mixer_build *state, int unitid, int control) +find_map(const struct usbmix_name_map *p, int unitid, int control) { - const struct usbmix_name_map *p = state->map; - if (!p) return NULL; - for (p = state->map; p->id; p++) { + for (; p->id; p++) { if (p->id == unitid && (!control || !p->control || control == p->control)) return p; @@ -1333,16 +1331,16 @@ static struct usb_feature_control_info *get_feature_control_info(int control) return NULL; } -static void build_feature_ctl(struct mixer_build *state, void *raw_desc, - unsigned int ctl_mask, int control, - struct usb_audio_term *iterm, int unitid, - int readonly_mask) +static void __build_feature_ctl(struct usb_mixer_interface *mixer, + const struct usbmix_name_map *imap, + unsigned int ctl_mask, int control, + struct usb_audio_term *iterm, + struct usb_audio_term *oterm, + int unitid, int nameid, int readonly_mask) { - struct uac_feature_unit_descriptor *desc = raw_desc; struct usb_feature_control_info *ctl_info; unsigned int len = 0; int mapped_name = 0; - int nameid = uac_feature_unit_iFeature(desc); struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; @@ -1353,14 +1351,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, return; } - map = find_map(state, unitid, control); + map = find_map(imap, unitid, control); if (check_ignored_ctl(map)) return; cval = kzalloc(sizeof(*cval), GFP_KERNEL); i
[PATCH v2 4/7] ALSA: usb: Only get AudioControl header for UAC1 class.
From: Jorge Sanjuan <jorge.sanj...@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.sanj...@codethink.co.uk> [Ruslan: updated with recently added sanity checks] Signed-off-by: Ruslan Bilovol <ruslan.bilo...@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 36c289b..0d7a5d7 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 = _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, "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, "invalid control header\n"); - return -EINVAL; - } - switch (protocol) { default: dev_warn(>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, "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, "invalid control header\n"); + return -EINVAL; + } if (rest_bytes < sizeof(*h1)) { dev_err(>dev, "too short v1 buffer descriptor\n"); -- 1.9.1
[PATCH v2 6/7] include: usb: audio-v3: add BADD-specific values
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.bilo...@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_160x0062 +#define UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24 0x0090 +#define UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_240x0093 +#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_RATE48000 + #endif /* __LINUX_USB_AUDIO_V3_H */ -- 1.9.1
[PATCH v2 4/7] ALSA: usb: Only get AudioControl header for UAC1 class.
From: Jorge Sanjuan 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 [Ruslan: updated with recently added sanity checks] Signed-off-by: Ruslan Bilovol --- 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 36c289b..0d7a5d7 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 = _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, "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, "invalid control header\n"); - return -EINVAL; - } - switch (protocol) { default: dev_warn(>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, "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, "invalid control header\n"); + return -EINVAL; + } if (rest_bytes < sizeof(*h1)) { dev_err(>dev, "too short v1 buffer descriptor\n"); -- 1.9.1
[PATCH v2 6/7] include: usb: audio-v3: add BADD-specific values
Add BADD-specific predefined values to audio-v3 so usb-audio in ALSA and UAC3 gadget can use them Signed-off-by: Ruslan Bilovol --- 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_160x0062 +#define UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24 0x0090 +#define UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_240x0093 +#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_RATE48000 + #endif /* __LINUX_USB_AUDIO_V3_H */ -- 1.9.1
[PATCH v2 0/7] 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. First 5 patches are refactoring and improvements, and last 2 patches actually implement UAC3 BADD profiles support. 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. v2: - split refactoring patch to more atomic and bisectable changes as suggested by Takashi - renamed "Side Tone"->"Sidetone" as suggested by Andrew - revorked main BADD patch to have table lookup for checking BADD channels validity and to reduce identation as suggested by Takashi - also removed code duplication in BADD patch by reusing common part of build_feature_ctl() func v1: http://mailman.alsa-project.org/pipermail/alsa-devel/2018-April/134412.html [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 (6): ALSA: usb: stream: move audioformat alloc/init into separate function ALSA: usb: stream: refactor uac1/2 audio interface parsing ALSA: usb: stream: refactor uac3 audio interface parsing ALSA: usb: mixer: make string parsing independent of mixer_build state 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| 351 +++--- sound/usb/mixer_maps.c | 65 sound/usb/stream.c | 687 +-- sound/usb/usbaudio.h | 2 + 7 files changed, 849 insertions(+), 344 deletions(-) -- 1.9.1
[PATCH v2 0/7] 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. First 5 patches are refactoring and improvements, and last 2 patches actually implement UAC3 BADD profiles support. 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. v2: - split refactoring patch to more atomic and bisectable changes as suggested by Takashi - renamed "Side Tone"->"Sidetone" as suggested by Andrew - revorked main BADD patch to have table lookup for checking BADD channels validity and to reduce identation as suggested by Takashi - also removed code duplication in BADD patch by reusing common part of build_feature_ctl() func v1: http://mailman.alsa-project.org/pipermail/alsa-devel/2018-April/134412.html [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 (6): ALSA: usb: stream: move audioformat alloc/init into separate function ALSA: usb: stream: refactor uac1/2 audio interface parsing ALSA: usb: stream: refactor uac3 audio interface parsing ALSA: usb: mixer: make string parsing independent of mixer_build state 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| 351 +++--- sound/usb/mixer_maps.c | 65 sound/usb/stream.c | 687 +-- sound/usb/usbaudio.h | 2 + 7 files changed, 849 insertions(+), 344 deletions(-) -- 1.9.1
Re: [PATCH v3 1/4] ALSA: usb-audio: UAC3. Add support for mixer unit.
On Fri, Apr 27, 2018 at 8:06 PM, Jorge Sanjuanwrote: > This adds support for the MIXER UNIT in UAC3. All the information > is obtained from the (HIGH CAPABILITY) Cluster's header. We don't > read the rest of the logical cluster to obtain the channel config > as that wont make any difference in the current mixer behaviour. > > The name of the mixer unit is not yet requested as there is not > support for the UAC3 Class Specific String requests. > > Tested in an UAC3 device working as a HEADSET with a basic mixer > unit (same as the one in the BADD spec) with no controls. So, after deeper looking into the code and after testing this patch, in your usecase (mixer with no controls) you'll never execute build_mixer_unit_ctl(), correct? So did you try to just fix issues with incorrect parsing of mixer unit descriptor? > > Signed-off-by: Jorge Sanjuan > --- > include/uapi/linux/usb/audio.h | 19 +++-- > sound/usb/mixer.c | 88 > ++ > 2 files changed, 97 insertions(+), 10 deletions(-) > > diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h > index 3a78e7145689..13d98e6e0db1 100644 > --- a/include/uapi/linux/usb/audio.h > +++ b/include/uapi/linux/usb/audio.h > @@ -285,9 +285,22 @@ static inline __u8 uac_mixer_unit_iChannelNames(struct > uac_mixer_unit_descriptor > static inline __u8 *uac_mixer_unit_bmControls(struct > uac_mixer_unit_descriptor *desc, > int protocol) > { > - return (protocol == UAC_VERSION_1) ? > - >baSourceID[desc->bNrInPins + 4] : > - >baSourceID[desc->bNrInPins + 6]; > + switch (protocol) { > + case UAC_VERSION_1: > + return >baSourceID[desc->bNrInPins + 4]; > + case UAC_VERSION_2: > + return >baSourceID[desc->bNrInPins + 6]; > + case UAC_VERSION_3: > + return >baSourceID[desc->bNrInPins + 2]; > + default: > + return NULL; > + } > +} > + > +static inline __u16 uac3_mixer_unit_wClusterDescrID(struct > uac_mixer_unit_descriptor *desc) > +{ > + return (desc->baSourceID[desc->bNrInPins + 1] << 8) | > + desc->baSourceID[desc->bNrInPins]; > } > > static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor > *desc) > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index 301ad61ed426..3503f4840ec3 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -719,6 +719,66 @@ static int get_term_name(struct mixer_build *state, > struct usb_audio_term *iterm > } > > /* > + * Get logical cluster information for UAC3 devices. > + */ > +static int get_cluster_channels_v3(struct mixer_build *state, unsigned int > cluster_id) > +{ > + struct uac3_cluster_header_descriptor c_header; > + int err; > + > + err = snd_usb_ctl_msg(state->chip->dev, > + usb_rcvctrlpipe(state->chip->dev, 0), > + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, > + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, > + cluster_id, > + snd_usb_ctrl_intf(state->chip), > + _header, sizeof(c_header)); > + if (err < 0) > + goto error; > + if (err != sizeof(c_header)) { > + err = -EIO; > + goto error; > + } > + > + return c_header.bNrChannels; > + > +error: > + usb_audio_err(state->chip, "cannot request logical cluster ID: %d > (err: %d)\n", cluster_id, err); > + return err; > +} > + > +/* > + * Get number of channels for a Mixer Unit. > + */ > +static int uac_mixer_unit_get_channels(struct mixer_build *state, > + struct uac_mixer_unit_descriptor *desc) > +{ > + int mu_channels; > + > + if (desc->bLength < 11) > + return -EINVAL; > + if (!desc->bNrInPins) > + return -EINVAL; > + > + switch (state->mixer->protocol) { > + case UAC_VERSION_1: > + case UAC_VERSION_2: > + default: > + mu_channels = uac_mixer_unit_bNrChannels(desc); > + break; > + case UAC_VERSION_3: > + mu_channels = get_cluster_channels_v3(state, > + uac3_mixer_unit_wClusterDescrID(desc)); > + break; > + } > + > + if (!mu_channels) > + return -EINVAL; > + > + return mu_channels; > +} > + > +/* > * parse the source unit recursively until it reaches to a terminal > * or a branched unit. > */ > @@ -865,6 +925,18 @@ static int check_input_term(struct mixer_build *state, > int id, > term->name = le16_to_cpu(d->wClockSourceStr); > return 0; > } > + case
Re: [PATCH v3 1/4] ALSA: usb-audio: UAC3. Add support for mixer unit.
On Fri, Apr 27, 2018 at 8:06 PM, Jorge Sanjuan wrote: > This adds support for the MIXER UNIT in UAC3. All the information > is obtained from the (HIGH CAPABILITY) Cluster's header. We don't > read the rest of the logical cluster to obtain the channel config > as that wont make any difference in the current mixer behaviour. > > The name of the mixer unit is not yet requested as there is not > support for the UAC3 Class Specific String requests. > > Tested in an UAC3 device working as a HEADSET with a basic mixer > unit (same as the one in the BADD spec) with no controls. So, after deeper looking into the code and after testing this patch, in your usecase (mixer with no controls) you'll never execute build_mixer_unit_ctl(), correct? So did you try to just fix issues with incorrect parsing of mixer unit descriptor? > > Signed-off-by: Jorge Sanjuan > --- > include/uapi/linux/usb/audio.h | 19 +++-- > sound/usb/mixer.c | 88 > ++ > 2 files changed, 97 insertions(+), 10 deletions(-) > > diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h > index 3a78e7145689..13d98e6e0db1 100644 > --- a/include/uapi/linux/usb/audio.h > +++ b/include/uapi/linux/usb/audio.h > @@ -285,9 +285,22 @@ static inline __u8 uac_mixer_unit_iChannelNames(struct > uac_mixer_unit_descriptor > static inline __u8 *uac_mixer_unit_bmControls(struct > uac_mixer_unit_descriptor *desc, > int protocol) > { > - return (protocol == UAC_VERSION_1) ? > - >baSourceID[desc->bNrInPins + 4] : > - >baSourceID[desc->bNrInPins + 6]; > + switch (protocol) { > + case UAC_VERSION_1: > + return >baSourceID[desc->bNrInPins + 4]; > + case UAC_VERSION_2: > + return >baSourceID[desc->bNrInPins + 6]; > + case UAC_VERSION_3: > + return >baSourceID[desc->bNrInPins + 2]; > + default: > + return NULL; > + } > +} > + > +static inline __u16 uac3_mixer_unit_wClusterDescrID(struct > uac_mixer_unit_descriptor *desc) > +{ > + return (desc->baSourceID[desc->bNrInPins + 1] << 8) | > + desc->baSourceID[desc->bNrInPins]; > } > > static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor > *desc) > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index 301ad61ed426..3503f4840ec3 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -719,6 +719,66 @@ static int get_term_name(struct mixer_build *state, > struct usb_audio_term *iterm > } > > /* > + * Get logical cluster information for UAC3 devices. > + */ > +static int get_cluster_channels_v3(struct mixer_build *state, unsigned int > cluster_id) > +{ > + struct uac3_cluster_header_descriptor c_header; > + int err; > + > + err = snd_usb_ctl_msg(state->chip->dev, > + usb_rcvctrlpipe(state->chip->dev, 0), > + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, > + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, > + cluster_id, > + snd_usb_ctrl_intf(state->chip), > + _header, sizeof(c_header)); > + if (err < 0) > + goto error; > + if (err != sizeof(c_header)) { > + err = -EIO; > + goto error; > + } > + > + return c_header.bNrChannels; > + > +error: > + usb_audio_err(state->chip, "cannot request logical cluster ID: %d > (err: %d)\n", cluster_id, err); > + return err; > +} > + > +/* > + * Get number of channels for a Mixer Unit. > + */ > +static int uac_mixer_unit_get_channels(struct mixer_build *state, > + struct uac_mixer_unit_descriptor *desc) > +{ > + int mu_channels; > + > + if (desc->bLength < 11) > + return -EINVAL; > + if (!desc->bNrInPins) > + return -EINVAL; > + > + switch (state->mixer->protocol) { > + case UAC_VERSION_1: > + case UAC_VERSION_2: > + default: > + mu_channels = uac_mixer_unit_bNrChannels(desc); > + break; > + case UAC_VERSION_3: > + mu_channels = get_cluster_channels_v3(state, > + uac3_mixer_unit_wClusterDescrID(desc)); > + break; > + } > + > + if (!mu_channels) > + return -EINVAL; > + > + return mu_channels; > +} > + > +/* > * parse the source unit recursively until it reaches to a terminal > * or a branched unit. > */ > @@ -865,6 +925,18 @@ static int check_input_term(struct mixer_build *state, > int id, > term->name = le16_to_cpu(d->wClockSourceStr); > return 0; > } > + case UAC3_MIXER_UNIT: { > + struct
Re: [alsa-devel] [PATCH v2 0/4] ALSA: usb: UAC3 new features.
On Tue, Apr 24, 2018 at 9:02 PM, Takashi Iwai <ti...@suse.de> wrote: > On Tue, 24 Apr 2018 19:24:41 +0200, > Jorge Sanjuan wrote: >> >> v2 fixes: >> - If/else statements braces style fixes. >> - Add wrapping function to mixer unit code. >> - Make connectors control kctl struct const. >> - Little endian to cpu conversion in several places. >> - Sing off and add Fixes tag to fixup commit. >> - Remove flex-array for a struct that is used statically. >> >> Now that the UAC3 patch [1] has made it to linux-next I have some extra >> features to make a UAC3 device fully work in Linux. Including Jack >> insertion control that I have put on top of this other patch [2] for >> UAC2. Also adding support for the UAC3 Mixer Unit which is most likely >> to appear in most headset type devices. Thanks for adding these improvements! > > These patches look reasonable, I'm OK to merge. But I'll wait for > Ruslan's comments (or at best with test results). I reviewed first 3 patches and will review jack detection patch later, and I'm going to test this patchset in a next few days. > >> UAC3 devices also require to have a Basic Audio Device (BADD) in a separate >> config for which both Ruslan Bilovol and myself have submited different >> approaches[3][4] but I don't know what the final merge will be. Once there >> is official support for BADD, we'll need to test it with an actual UAC3 >> device to confirm it all wokrs. > > Could you guys try to get agreement which approach should we take? > > I have no big preference. Currently Ruslan's patch series look > easier, just because its addition is a bit smaller, though. The BADD devices are quite simple, so direct initialization internal ALSA structures looks easy and straightforward, comparing to generation of missing descriptors. I'm currently improving the patch series so it will look even more smaller and easier, let's see how it goes Thanks, Ruslan > > > Thanks! > > Takashi > >> All this features are tested with an actual UAC3 device that is still in >> development. For this patch series, only the legacy config (#1. UAC1/UAC2) >> and the UAC3 config have been tested. The BADD config is only tested using >> and updated verison of [4]. >> >> [1]: https://patchwork.kernel.org/patch/10298179/ >> [2]: https://patchwork.kernel.org/patch/10305847/ >> [3]: https://patchwork.kernel.org/patch/10340851/ >> [4]: https://www.spinics.net/lists/alsa-devel/msg71617.html >> >> Based on linux-next tag: next-20180420 >> >> Jorge Sanjuan (3): >> ALSA: usb-audio: UAC3. Add support for mixer unit. >> ALSA: usb-audio: Use Class Specific EP for UAC3 devices. >> ALSA: usb-audio: UAC3 Add support for connector insertion. >> >> Michael Drake (1): >> ALSA: usb-audio: ADC3: Fix channel mapping conversion for ADC3. >> >> include/linux/usb/audio-v2.h | 7 ++ >> include/linux/usb/audio-v3.h | 14 +++ >> include/uapi/linux/usb/audio.h | 13 ++- >> sound/usb/mixer.c | 195 >> + >> sound/usb/stream.c | 11 ++- >> 5 files changed, 217 insertions(+), 23 deletions(-) >> >> -- >> 2.11.0 >> >> > ___ > Alsa-devel mailing list > alsa-de...@alsa-project.org > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Re: [alsa-devel] [PATCH v2 0/4] ALSA: usb: UAC3 new features.
On Tue, Apr 24, 2018 at 9:02 PM, Takashi Iwai wrote: > On Tue, 24 Apr 2018 19:24:41 +0200, > Jorge Sanjuan wrote: >> >> v2 fixes: >> - If/else statements braces style fixes. >> - Add wrapping function to mixer unit code. >> - Make connectors control kctl struct const. >> - Little endian to cpu conversion in several places. >> - Sing off and add Fixes tag to fixup commit. >> - Remove flex-array for a struct that is used statically. >> >> Now that the UAC3 patch [1] has made it to linux-next I have some extra >> features to make a UAC3 device fully work in Linux. Including Jack >> insertion control that I have put on top of this other patch [2] for >> UAC2. Also adding support for the UAC3 Mixer Unit which is most likely >> to appear in most headset type devices. Thanks for adding these improvements! > > These patches look reasonable, I'm OK to merge. But I'll wait for > Ruslan's comments (or at best with test results). I reviewed first 3 patches and will review jack detection patch later, and I'm going to test this patchset in a next few days. > >> UAC3 devices also require to have a Basic Audio Device (BADD) in a separate >> config for which both Ruslan Bilovol and myself have submited different >> approaches[3][4] but I don't know what the final merge will be. Once there >> is official support for BADD, we'll need to test it with an actual UAC3 >> device to confirm it all wokrs. > > Could you guys try to get agreement which approach should we take? > > I have no big preference. Currently Ruslan's patch series look > easier, just because its addition is a bit smaller, though. The BADD devices are quite simple, so direct initialization internal ALSA structures looks easy and straightforward, comparing to generation of missing descriptors. I'm currently improving the patch series so it will look even more smaller and easier, let's see how it goes Thanks, Ruslan > > > Thanks! > > Takashi > >> All this features are tested with an actual UAC3 device that is still in >> development. For this patch series, only the legacy config (#1. UAC1/UAC2) >> and the UAC3 config have been tested. The BADD config is only tested using >> and updated verison of [4]. >> >> [1]: https://patchwork.kernel.org/patch/10298179/ >> [2]: https://patchwork.kernel.org/patch/10305847/ >> [3]: https://patchwork.kernel.org/patch/10340851/ >> [4]: https://www.spinics.net/lists/alsa-devel/msg71617.html >> >> Based on linux-next tag: next-20180420 >> >> Jorge Sanjuan (3): >> ALSA: usb-audio: UAC3. Add support for mixer unit. >> ALSA: usb-audio: Use Class Specific EP for UAC3 devices. >> ALSA: usb-audio: UAC3 Add support for connector insertion. >> >> Michael Drake (1): >> ALSA: usb-audio: ADC3: Fix channel mapping conversion for ADC3. >> >> include/linux/usb/audio-v2.h | 7 ++ >> include/linux/usb/audio-v3.h | 14 +++ >> include/uapi/linux/usb/audio.h | 13 ++- >> sound/usb/mixer.c | 195 >> + >> sound/usb/stream.c | 11 ++- >> 5 files changed, 217 insertions(+), 23 deletions(-) >> >> -- >> 2.11.0 >> >> > ___ > Alsa-devel mailing list > alsa-de...@alsa-project.org > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Re: [alsa-devel] [PATCH v2 3/4] ALSA: usb-audio: Use Class Specific EP for UAC3 devices.
On Tue, Apr 24, 2018 at 8:24 PM, Jorge Sanjuan <jorge.sanj...@codethink.co.uk> wrote: > bmAtributes offset doesn't exist in the UAC3 CS_EP descriptor. > Hence, checking for pitch control as if it was UAC2 doesn't make > any sense. Use the defined UAC3 offsets instead. > > Signed-off-by: Jorge Sanjuan <jorge.sanj...@codethink.co.uk> Reviewed-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> > --- > sound/usb/stream.c | 9 - > 1 file changed, 8 insertions(+), 1 deletion(-) > > diff --git a/sound/usb/stream.c b/sound/usb/stream.c > index 956be9f7c72a..5ed334575fc7 100644 > --- a/sound/usb/stream.c > +++ b/sound/usb/stream.c > @@ -576,7 +576,7 @@ static int parse_uac_endpoint_attributes(struct > snd_usb_audio *chip, > > if (protocol == UAC_VERSION_1) { > attributes = csep->bmAttributes; > - } else { > + } else if (protocol == UAC_VERSION_2) { > struct uac2_iso_endpoint_descriptor *csep2 = > (struct uac2_iso_endpoint_descriptor *) csep; > > @@ -585,6 +585,13 @@ static int parse_uac_endpoint_attributes(struct > snd_usb_audio *chip, > /* emulate the endpoint attributes of a v1 device */ > if (csep2->bmControls & UAC2_CONTROL_PITCH) > attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; > + } else { /* UAC_VERSION_3 */ > + struct uac3_iso_endpoint_descriptor *csep3 = > + (struct uac3_iso_endpoint_descriptor *) csep; > + > + /* emulate the endpoint attributes of a v1 device */ > + if (le32_to_cpu(csep3->bmControls) & UAC2_CONTROL_PITCH) > + attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; > } > > return attributes; > -- > 2.11.0 > > ___ > Alsa-devel mailing list > alsa-de...@alsa-project.org > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Re: [alsa-devel] [PATCH v2 3/4] ALSA: usb-audio: Use Class Specific EP for UAC3 devices.
On Tue, Apr 24, 2018 at 8:24 PM, Jorge Sanjuan wrote: > bmAtributes offset doesn't exist in the UAC3 CS_EP descriptor. > Hence, checking for pitch control as if it was UAC2 doesn't make > any sense. Use the defined UAC3 offsets instead. > > Signed-off-by: Jorge Sanjuan Reviewed-by: Ruslan Bilovol > --- > sound/usb/stream.c | 9 - > 1 file changed, 8 insertions(+), 1 deletion(-) > > diff --git a/sound/usb/stream.c b/sound/usb/stream.c > index 956be9f7c72a..5ed334575fc7 100644 > --- a/sound/usb/stream.c > +++ b/sound/usb/stream.c > @@ -576,7 +576,7 @@ static int parse_uac_endpoint_attributes(struct > snd_usb_audio *chip, > > if (protocol == UAC_VERSION_1) { > attributes = csep->bmAttributes; > - } else { > + } else if (protocol == UAC_VERSION_2) { > struct uac2_iso_endpoint_descriptor *csep2 = > (struct uac2_iso_endpoint_descriptor *) csep; > > @@ -585,6 +585,13 @@ static int parse_uac_endpoint_attributes(struct > snd_usb_audio *chip, > /* emulate the endpoint attributes of a v1 device */ > if (csep2->bmControls & UAC2_CONTROL_PITCH) > attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; > + } else { /* UAC_VERSION_3 */ > + struct uac3_iso_endpoint_descriptor *csep3 = > + (struct uac3_iso_endpoint_descriptor *) csep; > + > + /* emulate the endpoint attributes of a v1 device */ > + if (le32_to_cpu(csep3->bmControls) & UAC2_CONTROL_PITCH) > + attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL; > } > > return attributes; > -- > 2.11.0 > > ___ > Alsa-devel mailing list > alsa-de...@alsa-project.org > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Re: [alsa-devel] [PATCH v2 1/4] ALSA: usb-audio: UAC3. Add support for mixer unit.
On Tue, Apr 24, 2018 at 8:24 PM, Jorge Sanjuanwrote: > This adds support for the MIXER UNIT in UAC3. All the information > is obtained from the (HIGH CAPABILITY) Cluster's header. We don't > read the rest of the logical cluster to obtain the channel config > as that wont make any difference in the current mixer behaviour. > > The name of the mixer unit is not yet requested as there is not > support for the UAC3 Class Specific String requests. > > Tested in an UAC3 device working as a HEADSET with a basic mixer > unit (same as the one in the BADD spec) with no controls. The patch looks OK in general, but I have few comments > > Signed-off-by: Jorge Sanjuan > --- > include/uapi/linux/usb/audio.h | 13 +-- > sound/usb/mixer.c | 87 > -- > 2 files changed, 93 insertions(+), 7 deletions(-) > > diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h > index 3a78e7145689..f9be472cd025 100644 > --- a/include/uapi/linux/usb/audio.h > +++ b/include/uapi/linux/usb/audio.h > @@ -285,9 +285,16 @@ static inline __u8 uac_mixer_unit_iChannelNames(struct > uac_mixer_unit_descriptor > static inline __u8 *uac_mixer_unit_bmControls(struct > uac_mixer_unit_descriptor *desc, > int protocol) Name of this function is ambiguous, that's because UAC1 mixer unit has only bmControls bitmap, whereas UAC2/3 mixer unit has two bitmaps (bmMixerControls and bmControls), in latter case this func returns pointer to bmMixerControls. Maybe in the future we will need to rename it, but at least having comment which clarifies that in case of UAC2/3 this function actually returns pointer to bmMixerControls here will be helpful for code readers. > { > - return (protocol == UAC_VERSION_1) ? > - >baSourceID[desc->bNrInPins + 4] : > - >baSourceID[desc->bNrInPins + 6]; > + switch (protocol) { > + case UAC_VERSION_1: > + return >baSourceID[desc->bNrInPins + 4]; > + case UAC_VERSION_2: > + return >baSourceID[desc->bNrInPins + 6]; > + case UAC_VERSION_3: > + return >baSourceID[desc->bNrInPins + 2]; > + default: > + return NULL; > + } > } > > static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor > *desc) > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index 301ad61ed426..bf701b466a4e 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -719,6 +719,66 @@ static int get_term_name(struct mixer_build *state, > struct usb_audio_term *iterm > } > > /* > + * Get logical cluster information for UAC3 devices. > + */ > +static int get_cluster_channels_v3(struct mixer_build *state, unsigned int > cluster_id) > +{ > + struct uac3_cluster_header_descriptor c_header; > + int err; > + > + err = snd_usb_ctl_msg(state->chip->dev, > + usb_rcvctrlpipe(state->chip->dev, 0), > + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, > + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, > + cluster_id, > + snd_usb_ctrl_intf(state->chip), > + _header, sizeof(c_header)); > + if (err < 0) > + goto error; > + if (err != sizeof(c_header)) { > + err = -EIO; > + goto error; > + } > + > + return c_header.bNrChannels; > + > +error: > + usb_audio_err(state->chip, "cannot request logical cluster ID: %d > (err: %d)\n", cluster_id, err); > + return err; > +} > + > +/* > + * Get number of channels for a Mixer Unit. > + */ > +static int uac_mixer_unit_get_channels(struct mixer_build *state, > + struct uac_mixer_unit_descriptor *desc) > +{ > + int mu_channels; > + > + if (desc->bLength < 11) > + return -EINVAL; > + if (!desc->bNrInPins) > + return -EINVAL; > + > + switch (state->mixer->protocol) { > + case UAC_VERSION_1: > + case UAC_VERSION_2: > + default: > + mu_channels = uac_mixer_unit_bNrChannels(desc); > + break; > + case UAC_VERSION_3: > + mu_channels = get_cluster_channels_v3(state, > + > le16_to_cpu(desc->baSourceID[desc->bNrInPins])); It would be good to create and use some helper here, for example implement uac3_mixer_unit_wClusterDescrID() similar to uac_mixer_unit_bNrChannels(). It will put all this conversation to a single place and will improve code readability. > + break; > + } > + > + if (!mu_channels) > + return -EINVAL; > + > + return mu_channels; > +} > + > +/* > * parse the source unit recursively until it reaches to a terminal > * or a branched unit. > */ > @@
Re: [alsa-devel] [PATCH v2 1/4] ALSA: usb-audio: UAC3. Add support for mixer unit.
On Tue, Apr 24, 2018 at 8:24 PM, Jorge Sanjuan wrote: > This adds support for the MIXER UNIT in UAC3. All the information > is obtained from the (HIGH CAPABILITY) Cluster's header. We don't > read the rest of the logical cluster to obtain the channel config > as that wont make any difference in the current mixer behaviour. > > The name of the mixer unit is not yet requested as there is not > support for the UAC3 Class Specific String requests. > > Tested in an UAC3 device working as a HEADSET with a basic mixer > unit (same as the one in the BADD spec) with no controls. The patch looks OK in general, but I have few comments > > Signed-off-by: Jorge Sanjuan > --- > include/uapi/linux/usb/audio.h | 13 +-- > sound/usb/mixer.c | 87 > -- > 2 files changed, 93 insertions(+), 7 deletions(-) > > diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h > index 3a78e7145689..f9be472cd025 100644 > --- a/include/uapi/linux/usb/audio.h > +++ b/include/uapi/linux/usb/audio.h > @@ -285,9 +285,16 @@ static inline __u8 uac_mixer_unit_iChannelNames(struct > uac_mixer_unit_descriptor > static inline __u8 *uac_mixer_unit_bmControls(struct > uac_mixer_unit_descriptor *desc, > int protocol) Name of this function is ambiguous, that's because UAC1 mixer unit has only bmControls bitmap, whereas UAC2/3 mixer unit has two bitmaps (bmMixerControls and bmControls), in latter case this func returns pointer to bmMixerControls. Maybe in the future we will need to rename it, but at least having comment which clarifies that in case of UAC2/3 this function actually returns pointer to bmMixerControls here will be helpful for code readers. > { > - return (protocol == UAC_VERSION_1) ? > - >baSourceID[desc->bNrInPins + 4] : > - >baSourceID[desc->bNrInPins + 6]; > + switch (protocol) { > + case UAC_VERSION_1: > + return >baSourceID[desc->bNrInPins + 4]; > + case UAC_VERSION_2: > + return >baSourceID[desc->bNrInPins + 6]; > + case UAC_VERSION_3: > + return >baSourceID[desc->bNrInPins + 2]; > + default: > + return NULL; > + } > } > > static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor > *desc) > diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c > index 301ad61ed426..bf701b466a4e 100644 > --- a/sound/usb/mixer.c > +++ b/sound/usb/mixer.c > @@ -719,6 +719,66 @@ static int get_term_name(struct mixer_build *state, > struct usb_audio_term *iterm > } > > /* > + * Get logical cluster information for UAC3 devices. > + */ > +static int get_cluster_channels_v3(struct mixer_build *state, unsigned int > cluster_id) > +{ > + struct uac3_cluster_header_descriptor c_header; > + int err; > + > + err = snd_usb_ctl_msg(state->chip->dev, > + usb_rcvctrlpipe(state->chip->dev, 0), > + UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, > + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, > + cluster_id, > + snd_usb_ctrl_intf(state->chip), > + _header, sizeof(c_header)); > + if (err < 0) > + goto error; > + if (err != sizeof(c_header)) { > + err = -EIO; > + goto error; > + } > + > + return c_header.bNrChannels; > + > +error: > + usb_audio_err(state->chip, "cannot request logical cluster ID: %d > (err: %d)\n", cluster_id, err); > + return err; > +} > + > +/* > + * Get number of channels for a Mixer Unit. > + */ > +static int uac_mixer_unit_get_channels(struct mixer_build *state, > + struct uac_mixer_unit_descriptor *desc) > +{ > + int mu_channels; > + > + if (desc->bLength < 11) > + return -EINVAL; > + if (!desc->bNrInPins) > + return -EINVAL; > + > + switch (state->mixer->protocol) { > + case UAC_VERSION_1: > + case UAC_VERSION_2: > + default: > + mu_channels = uac_mixer_unit_bNrChannels(desc); > + break; > + case UAC_VERSION_3: > + mu_channels = get_cluster_channels_v3(state, > + > le16_to_cpu(desc->baSourceID[desc->bNrInPins])); It would be good to create and use some helper here, for example implement uac3_mixer_unit_wClusterDescrID() similar to uac_mixer_unit_bNrChannels(). It will put all this conversation to a single place and will improve code readability. > + break; > + } > + > + if (!mu_channels) > + return -EINVAL; > + > + return mu_channels; > +} > + > +/* > * parse the source unit recursively until it reaches to a terminal > * or a branched unit. > */ > @@ -865,6 +925,18 @@ static int check_input_term(struct
Re: [alsa-devel] [PATCH 2/4] ALSA: usb-audio: ADC3: Fix channel mapping conversion for ADC3.
On Fri, Apr 20, 2018 at 8:03 PM, Jorge Sanjuan <jorge.sanj...@codethink.co.uk> wrote: > From: Michael Drake <michael.dr...@codethink.co.uk> > > The channel mapping is defined by bChRelationship, not bChPurpose. > > Signed-off-by: Michael Drake <michael.dr...@codethink.co.uk> > --- > sound/usb/stream.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/sound/usb/stream.c b/sound/usb/stream.c > index 6a8f5843334e..956be9f7c72a 100644 > --- a/sound/usb/stream.c > +++ b/sound/usb/stream.c > @@ -349,7 +349,7 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct > uac3_cluster_header_descriptor > * TODO: this conversion is not complete, update it > * after adding UAC3 values to asound.h > */ > - switch (is->bChPurpose) { > + switch (is->bChRelationship) { Good catch! Somehow I overlooked this, so in my case of Generic Audio it is always mono. Reviewed-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> > case UAC3_CH_MONO: > map = SNDRV_CHMAP_MONO; > break; > -- > 2.11.0 > > ___ > Alsa-devel mailing list > alsa-de...@alsa-project.org > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Re: [alsa-devel] [PATCH 2/4] ALSA: usb-audio: ADC3: Fix channel mapping conversion for ADC3.
On Fri, Apr 20, 2018 at 8:03 PM, Jorge Sanjuan wrote: > From: Michael Drake > > The channel mapping is defined by bChRelationship, not bChPurpose. > > Signed-off-by: Michael Drake > --- > sound/usb/stream.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/sound/usb/stream.c b/sound/usb/stream.c > index 6a8f5843334e..956be9f7c72a 100644 > --- a/sound/usb/stream.c > +++ b/sound/usb/stream.c > @@ -349,7 +349,7 @@ snd_pcm_chmap_elem *convert_chmap_v3(struct > uac3_cluster_header_descriptor > * TODO: this conversion is not complete, update it > * after adding UAC3 values to asound.h > */ > - switch (is->bChPurpose) { > + switch (is->bChRelationship) { Good catch! Somehow I overlooked this, so in my case of Generic Audio it is always mono. Reviewed-by: Ruslan Bilovol > case UAC3_CH_MONO: > map = SNDRV_CHMAP_MONO; > break; > -- > 2.11.0 > > ___ > Alsa-devel mailing list > alsa-de...@alsa-project.org > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Re: [alsa-devel] [PATCH 4/4] ALSA: usb: add UAC3 BADD profiles support
On Thu, Apr 19, 2018 at 1:19 PM, Takashi Iwai <ti...@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(_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
Re: [alsa-devel] [PATCH 4/4] ALSA: usb: add UAC3 BADD profiles support
On Thu, Apr 19, 2018 at 1:19 PM, Takashi Iwai 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(_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
Re: [alsa-devel] [PATCH 1/4] ALSA: usb: stream: refactor audio interface parsing
On Thu, Apr 19, 2018 at 12:55 PM, Takashi Iwai <ti...@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.bilo...@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
Re: [alsa-devel] [PATCH 1/4] ALSA: usb: stream: refactor audio interface parsing
On Thu, Apr 19, 2018 at 12:55 PM, Takashi Iwai 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 > > 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
Re: [alsa-devel] [PATCH 4/4] ALSA: usb: add UAC3 BADD profiles support
On Thu, Apr 19, 2018 at 12:42 PM, Andrew Chant <ach...@google.com> wrote: > On Sat, Apr 14, 2018 at 6:24 AM, Ruslan Bilovol > <ruslan.bilo...@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.bilo...@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
Re: [alsa-devel] [PATCH 4/4] ALSA: usb: add UAC3 BADD profiles support
On Thu, Apr 19, 2018 at 12:42 PM, Andrew Chant wrote: > On Sat, Apr 14, 2018 at 6:24 AM, Ruslan Bilovol > wrote: >> Recently released USB Audio Class 3.0 specification >> contains BADD (Basic Audio Device Definition) document >> which describes pre-defined UAC3 configurations. >> >> BADD support is mandatory for UAC3 devices, it should be >> implemented as a separate USB device configuration. >> As per BADD document, class-specific descriptors >> shall not be included in the Device’s Configuration >> descriptor ("inferred"), but host can guess them >> from BADD profile number, number of endpoints and >> their max packed sizes. >> >> This patch adds support of all BADD profiles from the spec >> >> Signed-off-by: Ruslan Bilovol >> --- >> 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
Re: [PATCH 4/4] ALSA: usb: add UAC3 BADD profiles support
On Sat, Apr 14, 2018 at 8:55 PM, Jorge Sanjuan <jorge.sanj...@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.bilo...@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, >> + "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,
Re: [PATCH 4/4] ALSA: usb: add UAC3 BADD profiles support
On Sat, Apr 14, 2018 at 8:55 PM, Jorge Sanjuan 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 >> --- >> 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, >> + "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 se
[PATCH 2/4] include: usb: audio-v3: add BADD-specific values
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.bilo...@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_160x0062 +#define UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24 0x0090 +#define UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_240x0093 +#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_RATE48000 + #endif /* __LINUX_USB_AUDIO_V3_H */ -- 1.9.1
[PATCH 4/4] ALSA: usb: add UAC3 BADD profiles support
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.bilo...@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, + "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(>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(_feature_unit_ctl, cval); + + if (!kctl) { +
[PATCH 2/4] include: usb: audio-v3: add BADD-specific values
Add BADD-specific predefined values to audio-v3 so usb-audio in ALSA and UAC3 gadget can use them Signed-off-by: Ruslan Bilovol --- 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_160x0062 +#define UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_24 0x0090 +#define UAC3_BADD_EP_MAXPSIZE_ASYNC_MONO_240x0093 +#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_RATE48000 + #endif /* __LINUX_USB_AUDIO_V3_H */ -- 1.9.1
[PATCH 4/4] ALSA: usb: add UAC3 BADD profiles support
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 --- 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, + "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(>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(_feature_unit_ctl, cval); + + if (!kctl) { + usb_audio_err(
[PATCH 3/4] ALSA: usb: Only get AudioControl header for UAC1 class.
From: Jorge Sanjuan <jorge.sanj...@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.sanj...@codethink.co.uk> [Ruslan: updated with recently added sanity checks] Signed-off-by: Ruslan Bilovol <ruslan.bilo...@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 = _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, "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, "invalid control header\n"); - return -EINVAL; - } - switch (protocol) { default: dev_warn(>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, "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, "invalid control header\n"); + return -EINVAL; + } if (rest_bytes < sizeof(*h1)) { dev_err(>dev, "too short v1 buffer descriptor\n"); -- 1.9.1
[PATCH 3/4] ALSA: usb: Only get AudioControl header for UAC1 class.
From: Jorge Sanjuan 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 [Ruslan: updated with recently added sanity checks] Signed-off-by: Ruslan Bilovol --- 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 = _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, "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, "invalid control header\n"); - return -EINVAL; - } - switch (protocol) { default: dev_warn(>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, "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, "invalid control header\n"); + return -EINVAL; + } if (rest_bytes < sizeof(*h1)) { dev_err(>dev, "too short v1 buffer descriptor\n"); -- 1.9.1
[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(-) -- 1.9.1
[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(-) -- 1.9.1
[PATCH 1/4] ALSA: usb: stream: refactor audio interface parsing
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.bilo...@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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%u:%d : bogus bTerminalLink %d\n", +
[PATCH 1/4] ALSA: usb: stream: refactor audio interface parsing
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 --- 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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%u:%d : UAC_AS_GENERAL descriptor not found\n", + iface_no, altno); + return NULL; + } + + if (as->bLength < sizeof(*as)) { + dev_err(>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, + "%u:%d : bogus bTerminalLink %d\n", + iface_no, altno,
[PATCH v3] USB Audio Device Class 3.0 support
Hi Takashi, This patch adds initial USB Audio Device Class 3.0 [1] support to the ALSA that we discussed at ELCE. The patch was tested with UAC3 gadget [2] that I posted to USB mailing list before. It is good for working with BADD device which implements such topologies like BAIF (Basic Audio Input Functions), BAOF (Basic Audio Output Functions) or compbination of both. UAC3 spec has changed descriptors laout and many other codes comparing to UAC2 spec, thus making reuse of existing sources impossible or quite complex. There are still many areas of improvement, as this patch doesn't have UAC3 Mixer Unit support nor some new features like Power Management I tested this with BeagleBone Black as UAC3 gadget device. [1] http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip [2] http://www.spinics.net/lists/linux-usb/msg162482.html v3 changes: - dropped audioformat quirks patch (already accepted) - addressed coding style requested changes v2 changes: - moved audioformat quirks to quirks.c - addressed many comments from Pierre-Louis Bossart - reorganized audio-v3.h - added more UAC3 channel relationship definitions Ruslan Bilovol (1): ALSA: usb: initial USB Audio Device Class 3.0 support include/linux/usb/audio-v2.h | 4 +- include/linux/usb/audio-v3.h | 395 + include/uapi/linux/usb/audio.h | 1 + sound/usb/card.c | 7 +- sound/usb/card.h | 2 +- sound/usb/clock.c | 228 +--- sound/usb/clock.h | 4 +- sound/usb/format.c | 91 -- sound/usb/format.h | 6 +- sound/usb/mixer.c | 337 +++ sound/usb/stream.c | 365 + 11 files changed, 1246 insertions(+), 194 deletions(-) create mode 100644 include/linux/usb/audio-v3.h -- 1.9.1
[PATCH v3] ALSA: usb: initial USB Audio Device Class 3.0 support
Recently released USB Audio Class 3.0 specification introduces many significant changes comparing to previous versions, like - new Power Domains, support for LPM/L1 - new Cluster descriptor - changed layout of all class-specific descriptors - new High Capability descriptors - New class-specific String descriptors - new and removed units - additional sources for interrupts - removed Type II Audio Data Formats - ... and many other things (check spec) It also provides backward compatibility through multiple configurations, as well as requires mandatory support for BADD (Basic Audio Device Definition) on each ADC3.0 compliant device This patch adds initial support of UAC3 specification that is enough for Generic I/O Profile (BAOF, BAIF) device support from BADD document. Signed-off-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> --- include/linux/usb/audio-v2.h | 4 +- include/linux/usb/audio-v3.h | 395 + include/uapi/linux/usb/audio.h | 1 + sound/usb/card.c | 7 +- sound/usb/card.h | 2 +- sound/usb/clock.c | 228 +--- sound/usb/clock.h | 4 +- sound/usb/format.c | 91 -- sound/usb/format.h | 6 +- sound/usb/mixer.c | 337 +++ sound/usb/stream.c | 365 + 11 files changed, 1246 insertions(+), 194 deletions(-) create mode 100644 include/linux/usb/audio-v3.h diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index 3119d0a..2db83a1 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -34,12 +34,12 @@ * */ -static inline bool uac2_control_is_readable(u32 bmControls, u8 control) +static inline bool uac_v2v3_control_is_readable(u32 bmControls, u8 control) { return (bmControls >> (control * 2)) & 0x1; } -static inline bool uac2_control_is_writeable(u32 bmControls, u8 control) +static inline bool uac_v2v3_control_is_writeable(u32 bmControls, u8 control) { return (bmControls >> (control * 2)) & 0x2; } diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h new file mode 100644 index 000..a8959aa --- /dev/null +++ b/include/linux/usb/audio-v3.h @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Ruslan Bilovol <ruslan.bilo...@gmail.com> + * + * This file holds USB constants and structures defined + * by the USB DEVICE CLASS DEFINITION FOR AUDIO DEVICES Release 3.0. + */ + +#ifndef __LINUX_USB_AUDIO_V3_H +#define __LINUX_USB_AUDIO_V3_H + +#include + +/* + * v1.0, v2.0 and v3.0 of this standard have many things in common. For the rest + * of the definitions, please refer to audio.h and audio-v2.h + */ + +/* All High Capability descriptors have these 2 fields at the beginning */ +struct uac3_hc_descriptor_header { + __le16 wLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __le16 wDescriptorID; +} __attribute__ ((packed)); + +/* 4.3.1 CLUSTER DESCRIPTOR HEADER */ +struct uac3_cluster_header_descriptor { + __le16 wLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __le16 wDescriptorID; + __u8 bNrChannels; +} __attribute__ ((packed)); + +/* 4.3.2.1 SEGMENTS */ +struct uac3_cluster_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; + /* __u8[0]; segment-specific data */ +} __attribute__ ((packed)); + +/* 4.3.2.1.1 END SEGMENT */ +struct uac3_cluster_end_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; /* Constant END_SEGMENT */ +} __attribute__ ((packed)); + +/* 4.3.2.1.3.1 INFORMATION SEGMENT */ +struct uac3_cluster_information_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; + __u8 bChPurpose; + __u8 bChRelationship; + __u8 bChGroupID; +} __attribute__ ((packed)); + +/* 4.5.2 CLASS-SPECIFIC AC INTERFACE DESCRIPTOR */ +struct uac3_ac_header_descriptor { + __u8 bLength; /* 10 */ + __u8 bDescriptorType; /* CS_INTERFACE descriptor type */ + __u8 bDescriptorSubtype;/* HEADER descriptor subtype */ + __u8 bCategory; + + /* includes Clock Source, Unit, Terminal, and Power Domain desc. */ + __le16 wTotalLength; + + __le32 bmControls; +} __attribute__ ((packed)); + +/* 4.5.2.1 INPUT TERMINAL DESCRIPTOR */ +struct uac3_input_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bTerminalID; + __le16 wTerminalType; + __u8 bAssocTerminal; + __u8 bCSourceID; + __le32 bmControls; + __le16 wClusterDescrID; + __le16 wExTerminalDescrID; + __le16 wConnectorsDescrID; + __le16 wTerminalDescrStr; +} __attribute__((packed)); + +/* 4.5.2.2 OUTPU
[PATCH v3] USB Audio Device Class 3.0 support
Hi Takashi, This patch adds initial USB Audio Device Class 3.0 [1] support to the ALSA that we discussed at ELCE. The patch was tested with UAC3 gadget [2] that I posted to USB mailing list before. It is good for working with BADD device which implements such topologies like BAIF (Basic Audio Input Functions), BAOF (Basic Audio Output Functions) or compbination of both. UAC3 spec has changed descriptors laout and many other codes comparing to UAC2 spec, thus making reuse of existing sources impossible or quite complex. There are still many areas of improvement, as this patch doesn't have UAC3 Mixer Unit support nor some new features like Power Management I tested this with BeagleBone Black as UAC3 gadget device. [1] http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip [2] http://www.spinics.net/lists/linux-usb/msg162482.html v3 changes: - dropped audioformat quirks patch (already accepted) - addressed coding style requested changes v2 changes: - moved audioformat quirks to quirks.c - addressed many comments from Pierre-Louis Bossart - reorganized audio-v3.h - added more UAC3 channel relationship definitions Ruslan Bilovol (1): ALSA: usb: initial USB Audio Device Class 3.0 support include/linux/usb/audio-v2.h | 4 +- include/linux/usb/audio-v3.h | 395 + include/uapi/linux/usb/audio.h | 1 + sound/usb/card.c | 7 +- sound/usb/card.h | 2 +- sound/usb/clock.c | 228 +--- sound/usb/clock.h | 4 +- sound/usb/format.c | 91 -- sound/usb/format.h | 6 +- sound/usb/mixer.c | 337 +++ sound/usb/stream.c | 365 + 11 files changed, 1246 insertions(+), 194 deletions(-) create mode 100644 include/linux/usb/audio-v3.h -- 1.9.1
[PATCH v3] ALSA: usb: initial USB Audio Device Class 3.0 support
Recently released USB Audio Class 3.0 specification introduces many significant changes comparing to previous versions, like - new Power Domains, support for LPM/L1 - new Cluster descriptor - changed layout of all class-specific descriptors - new High Capability descriptors - New class-specific String descriptors - new and removed units - additional sources for interrupts - removed Type II Audio Data Formats - ... and many other things (check spec) It also provides backward compatibility through multiple configurations, as well as requires mandatory support for BADD (Basic Audio Device Definition) on each ADC3.0 compliant device This patch adds initial support of UAC3 specification that is enough for Generic I/O Profile (BAOF, BAIF) device support from BADD document. Signed-off-by: Ruslan Bilovol --- include/linux/usb/audio-v2.h | 4 +- include/linux/usb/audio-v3.h | 395 + include/uapi/linux/usb/audio.h | 1 + sound/usb/card.c | 7 +- sound/usb/card.h | 2 +- sound/usb/clock.c | 228 +--- sound/usb/clock.h | 4 +- sound/usb/format.c | 91 -- sound/usb/format.h | 6 +- sound/usb/mixer.c | 337 +++ sound/usb/stream.c | 365 + 11 files changed, 1246 insertions(+), 194 deletions(-) create mode 100644 include/linux/usb/audio-v3.h diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index 3119d0a..2db83a1 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -34,12 +34,12 @@ * */ -static inline bool uac2_control_is_readable(u32 bmControls, u8 control) +static inline bool uac_v2v3_control_is_readable(u32 bmControls, u8 control) { return (bmControls >> (control * 2)) & 0x1; } -static inline bool uac2_control_is_writeable(u32 bmControls, u8 control) +static inline bool uac_v2v3_control_is_writeable(u32 bmControls, u8 control) { return (bmControls >> (control * 2)) & 0x2; } diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h new file mode 100644 index 000..a8959aa --- /dev/null +++ b/include/linux/usb/audio-v3.h @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Ruslan Bilovol + * + * This file holds USB constants and structures defined + * by the USB DEVICE CLASS DEFINITION FOR AUDIO DEVICES Release 3.0. + */ + +#ifndef __LINUX_USB_AUDIO_V3_H +#define __LINUX_USB_AUDIO_V3_H + +#include + +/* + * v1.0, v2.0 and v3.0 of this standard have many things in common. For the rest + * of the definitions, please refer to audio.h and audio-v2.h + */ + +/* All High Capability descriptors have these 2 fields at the beginning */ +struct uac3_hc_descriptor_header { + __le16 wLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __le16 wDescriptorID; +} __attribute__ ((packed)); + +/* 4.3.1 CLUSTER DESCRIPTOR HEADER */ +struct uac3_cluster_header_descriptor { + __le16 wLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __le16 wDescriptorID; + __u8 bNrChannels; +} __attribute__ ((packed)); + +/* 4.3.2.1 SEGMENTS */ +struct uac3_cluster_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; + /* __u8[0]; segment-specific data */ +} __attribute__ ((packed)); + +/* 4.3.2.1.1 END SEGMENT */ +struct uac3_cluster_end_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; /* Constant END_SEGMENT */ +} __attribute__ ((packed)); + +/* 4.3.2.1.3.1 INFORMATION SEGMENT */ +struct uac3_cluster_information_segment_descriptor { + __le16 wLength; + __u8 bSegmentType; + __u8 bChPurpose; + __u8 bChRelationship; + __u8 bChGroupID; +} __attribute__ ((packed)); + +/* 4.5.2 CLASS-SPECIFIC AC INTERFACE DESCRIPTOR */ +struct uac3_ac_header_descriptor { + __u8 bLength; /* 10 */ + __u8 bDescriptorType; /* CS_INTERFACE descriptor type */ + __u8 bDescriptorSubtype;/* HEADER descriptor subtype */ + __u8 bCategory; + + /* includes Clock Source, Unit, Terminal, and Power Domain desc. */ + __le16 wTotalLength; + + __le32 bmControls; +} __attribute__ ((packed)); + +/* 4.5.2.1 INPUT TERMINAL DESCRIPTOR */ +struct uac3_input_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bTerminalID; + __le16 wTerminalType; + __u8 bAssocTerminal; + __u8 bCSourceID; + __le32 bmControls; + __le16 wClusterDescrID; + __le16 wExTerminalDescrID; + __le16 wConnectorsDescrID; + __le16 wTerminalDescrStr; +} __attribute__((packed)); + +/* 4.5.2.2 OUTPUT TERMINAL DESCRIPTOR */ +struct uac3_output_terminal_descriptor {
Re: [PATCH v2 2/2] ALSA: usb: initial USB Audio Device Class 3.0 support
On Mon, Mar 19, 2018 at 6:28 PM, Takashi Iwai <ti...@suse.de> wrote: > On Mon, 19 Mar 2018 02:46:03 +0100, > Ruslan Bilovol wrote: >> >> Recently released USB Audio Class 3.0 specification >> introduces many significant changes comparing to >> previous versions, like >> - new Power Domains, support for LPM/L1 >> - new Cluster descriptor >> - changed layout of all class-specific descriptors >> - new High Capability descriptors >> - New class-specific String descriptors >> - new and removed units >> - additional sources for interrupts >> - removed Type II Audio Data Formats >> - ... and many other things (check spec) >> >> It also provides backward compatibility through >> multiple configurations, as well as requires >> mandatory support for BADD (Basic Audio Device >> Definition) on each ADC3.0 compliant device >> >> This patch adds initial support of UAC3 specification >> that is enough for Generic I/O Profile (BAOF, BAIF) >> device support from BADD document. >> >> Signed-off-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> > > Most code changes look fairly straightforward, and not breaking the > UAC1/UAC2 stuff. So the stuff is good enough through a quick glance. > > I suppose it's better to merge via sound git tree. > Greg, could you check include/linux/usb/* (and uapi) stuff and give an > ack if it's OK? > > In anyway, I'll check the specs and compare with the definitions in > this patch. Please note that I took "A.12 CHANNEL RELATIONSHIP DEFINITIONS" values from previous Pierre's email (they were in a draft UAC3 spec but disappeared in the final version). I've sent an email with my quiestions to audio-ch...@usb.org but didn't get any reply from them yet. > > > And, just minor nitpicking regarding the code change: > > it's better to drop the parentheses in a line like below > >> + if ((protocol == UAC_VERSION_1) || (protocol == >> UAC_VERSION_2)) { > > that is, > if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { > > The indentation became so deep in some functions, and it's better to > refactor, but this can be done later once after all implementations > are finished. Sure, I fixed these and also kbuild test robot's complaint; if there will no any more comments, I'm going to send v3 tomorrow. Thanks, Ruslan
Re: [PATCH v2 2/2] ALSA: usb: initial USB Audio Device Class 3.0 support
On Mon, Mar 19, 2018 at 6:28 PM, Takashi Iwai wrote: > On Mon, 19 Mar 2018 02:46:03 +0100, > Ruslan Bilovol wrote: >> >> Recently released USB Audio Class 3.0 specification >> introduces many significant changes comparing to >> previous versions, like >> - new Power Domains, support for LPM/L1 >> - new Cluster descriptor >> - changed layout of all class-specific descriptors >> - new High Capability descriptors >> - New class-specific String descriptors >> - new and removed units >> - additional sources for interrupts >> - removed Type II Audio Data Formats >> - ... and many other things (check spec) >> >> It also provides backward compatibility through >> multiple configurations, as well as requires >> mandatory support for BADD (Basic Audio Device >> Definition) on each ADC3.0 compliant device >> >> This patch adds initial support of UAC3 specification >> that is enough for Generic I/O Profile (BAOF, BAIF) >> device support from BADD document. >> >> Signed-off-by: Ruslan Bilovol > > Most code changes look fairly straightforward, and not breaking the > UAC1/UAC2 stuff. So the stuff is good enough through a quick glance. > > I suppose it's better to merge via sound git tree. > Greg, could you check include/linux/usb/* (and uapi) stuff and give an > ack if it's OK? > > In anyway, I'll check the specs and compare with the definitions in > this patch. Please note that I took "A.12 CHANNEL RELATIONSHIP DEFINITIONS" values from previous Pierre's email (they were in a draft UAC3 spec but disappeared in the final version). I've sent an email with my quiestions to audio-ch...@usb.org but didn't get any reply from them yet. > > > And, just minor nitpicking regarding the code change: > > it's better to drop the parentheses in a line like below > >> + if ((protocol == UAC_VERSION_1) || (protocol == >> UAC_VERSION_2)) { > > that is, > if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { > > The indentation became so deep in some functions, and it's better to > refactor, but this can be done later once after all implementations > are finished. Sure, I fixed these and also kbuild test robot's complaint; if there will no any more comments, I'm going to send v3 tomorrow. Thanks, Ruslan
Re: [PATCH v2 1/2] ALSA: usb-audio: move audioformat quirks to quirks.c
On Mon, Mar 19, 2018 at 6:00 PM, Takashi Iwai <ti...@suse.de> wrote: > On Mon, 19 Mar 2018 02:46:02 +0100, > Ruslan Bilovol wrote: >> >> Offload USB audio interface parsing function by >> moving quirks to a specially designed location (quirks.c) >> >> Signed-off-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> > > This one is applied now to for-next branch. > Good, I'll drop this one from my series (although don't see it in the for-next branch yet) Thanks, Ruslan
Re: [PATCH v2 1/2] ALSA: usb-audio: move audioformat quirks to quirks.c
On Mon, Mar 19, 2018 at 6:00 PM, Takashi Iwai wrote: > On Mon, 19 Mar 2018 02:46:02 +0100, > Ruslan Bilovol wrote: >> >> Offload USB audio interface parsing function by >> moving quirks to a specially designed location (quirks.c) >> >> Signed-off-by: Ruslan Bilovol > > This one is applied now to for-next branch. > Good, I'll drop this one from my series (although don't see it in the for-next branch yet) Thanks, Ruslan
Re: [PATCH] ALSA: usb-audio: Fix parsing descriptor of UAC2 processing unit
On Mon, Mar 19, 2018 at 5:45 PM, Takashi Iwai <ti...@suse.de> wrote: > On Mon, 19 Mar 2018 08:47:49 +0100, > Takashi Iwai wrote: >> >> On Mon, 19 Mar 2018 08:40:19 +0100, >> Greg Kroah-Hartman wrote: >> > >> > On Mon, Mar 19, 2018 at 07:11:08AM +0100, Kirill Marinushkin wrote: >> > > Currently, the offsets in the UAC2 processing unit descriptor are >> > > calculated incorrectly. It causes an issue when connecting the device >> > > which >> > > provides such a feature: >> > > >> > > >> > > [84126.724420] usb 1-1.3.1: invalid Processing Unit descriptor (id 18) >> > > >> > > >> > > After this patch is applied, the UAC2 processing unit inits w/o this >> > > error. >> > > >> > > Fixes: 5e1ddb481776 ("UAPI: (Scripted) Disintegrate include/linux/usb") >> > > Signed-off-by: Kirill Marinushkin <k.marinush...@gmail.com> >> > > Cc: Mark Brown <broo...@kernel.org> >> > > Cc: Jaroslav Kysela <pe...@perex.cz> >> > > Cc: Takashi Iwai <ti...@suse.com> >> > > Cc: Thomas Gleixner <t...@linutronix.de> >> > > Cc: Felipe Balbi <felipe.ba...@linux.intel.com> >> > > Cc: Philippe Ombredanne <pombreda...@nexb.com> >> > > Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org> >> > > Cc: Ruslan Bilovol <ruslan.bilo...@gmail.com> >> > > Cc: linux-kernel@vger.kernel.org >> > > Cc: alsa-de...@alsa-project.org >> > > --- >> > > include/uapi/linux/usb/audio.h | 4 ++-- >> > > 1 file changed, 2 insertions(+), 2 deletions(-) >> > >> > Cc: stable <sta...@vger.kernel.org> >> > perhaps? >> >> Yes, it deserves. But I'd need to double-check the UAC2 >> specification. It's interesting that such a basic thing hasn't hit >> until now. Maybe just because it's a fairly minor feature. > > ... and the patch looks correct, after confirming the UAC2 spec. > Now I applied it, with a minor correction of Fixes tag, which actually > was introduced by 23caaf19b11e ("ALSA: usb-mixer: Add support for > Audio Class v2.0"), and adding Cc to stable. > I also checked UAC1/UAC2 spec and the patch looks good. What's interesting it seems nobody faced this issue (after searching ower Internet can't find any mention) which was present from the beginning of UAC2 driver implementation. Thanks, Ruslan
Re: [PATCH] ALSA: usb-audio: Fix parsing descriptor of UAC2 processing unit
On Mon, Mar 19, 2018 at 5:45 PM, Takashi Iwai wrote: > On Mon, 19 Mar 2018 08:47:49 +0100, > Takashi Iwai wrote: >> >> On Mon, 19 Mar 2018 08:40:19 +0100, >> Greg Kroah-Hartman wrote: >> > >> > On Mon, Mar 19, 2018 at 07:11:08AM +0100, Kirill Marinushkin wrote: >> > > Currently, the offsets in the UAC2 processing unit descriptor are >> > > calculated incorrectly. It causes an issue when connecting the device >> > > which >> > > provides such a feature: >> > > >> > > >> > > [84126.724420] usb 1-1.3.1: invalid Processing Unit descriptor (id 18) >> > > >> > > >> > > After this patch is applied, the UAC2 processing unit inits w/o this >> > > error. >> > > >> > > Fixes: 5e1ddb481776 ("UAPI: (Scripted) Disintegrate include/linux/usb") >> > > Signed-off-by: Kirill Marinushkin >> > > Cc: Mark Brown >> > > Cc: Jaroslav Kysela >> > > Cc: Takashi Iwai >> > > Cc: Thomas Gleixner >> > > Cc: Felipe Balbi >> > > Cc: Philippe Ombredanne >> > > Cc: Greg Kroah-Hartman >> > > Cc: Ruslan Bilovol >> > > Cc: linux-kernel@vger.kernel.org >> > > Cc: alsa-de...@alsa-project.org >> > > --- >> > > include/uapi/linux/usb/audio.h | 4 ++-- >> > > 1 file changed, 2 insertions(+), 2 deletions(-) >> > >> > Cc: stable >> > perhaps? >> >> Yes, it deserves. But I'd need to double-check the UAC2 >> specification. It's interesting that such a basic thing hasn't hit >> until now. Maybe just because it's a fairly minor feature. > > ... and the patch looks correct, after confirming the UAC2 spec. > Now I applied it, with a minor correction of Fixes tag, which actually > was introduced by 23caaf19b11e ("ALSA: usb-mixer: Add support for > Audio Class v2.0"), and adding Cc to stable. > I also checked UAC1/UAC2 spec and the patch looks good. What's interesting it seems nobody faced this issue (after searching ower Internet can't find any mention) which was present from the beginning of UAC2 driver implementation. Thanks, Ruslan
[PATCH v2 0/2] USB Audio Device Class 3.0 support
Hi Takashi, This patch adds initial USB Audio Device Class 3.0 [1] support to the ALSA that we discussed at ELCE. The patch was tested with UAC3 gadget [2] that I posted to USB mailing list before. It is good for working with BADD device which implements such topologies like BAIF (Basic Audio Input Functions), BAOF (Basic Audio Output Functions) or compbination of both. UAC3 spec has changed descriptors laout and many other codes comparing to UAC2 spec, thus making reuse of existing sources impossible or quite complex. There are still many areas of improvement, as this patch doesn't have UAC3 Mixer Unit support nor some new features like Power Management I tested this with BeagleBone Black as UAC3 gadget device. [1] http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip [2] http://www.spinics.net/lists/linux-usb/msg162482.html v2 changes: - moved audioformat quirks to quirks.c - addressed many comments from Pierre-Louis Bossart - reorganized audio-v3.h - added more UAC3 channel relationship definitions Ruslan Bilovol (2): ALSA: usb-audio: move audioformat quirks to quirks.c ALSA: usb: initial USB Audio Device Class 3.0 support include/linux/usb/audio-v2.h | 4 +- include/linux/usb/audio-v3.h | 395 + include/uapi/linux/usb/audio.h | 1 + sound/usb/card.c | 7 +- sound/usb/card.h | 2 +- sound/usb/clock.c | 228 +--- sound/usb/clock.h | 4 +- sound/usb/format.c | 91 -- sound/usb/format.h | 6 +- sound/usb/mixer.c | 337 +++ sound/usb/quirks.c | 34 sound/usb/quirks.h | 4 + sound/usb/stream.c | 393 +--- 13 files changed, 1284 insertions(+), 222 deletions(-) create mode 100644 include/linux/usb/audio-v3.h -- 1.9.1
[PATCH v2 0/2] USB Audio Device Class 3.0 support
Hi Takashi, This patch adds initial USB Audio Device Class 3.0 [1] support to the ALSA that we discussed at ELCE. The patch was tested with UAC3 gadget [2] that I posted to USB mailing list before. It is good for working with BADD device which implements such topologies like BAIF (Basic Audio Input Functions), BAOF (Basic Audio Output Functions) or compbination of both. UAC3 spec has changed descriptors laout and many other codes comparing to UAC2 spec, thus making reuse of existing sources impossible or quite complex. There are still many areas of improvement, as this patch doesn't have UAC3 Mixer Unit support nor some new features like Power Management I tested this with BeagleBone Black as UAC3 gadget device. [1] http://www.usb.org/developers/docs/devclass_docs/USB_Audio_v3.0.zip [2] http://www.spinics.net/lists/linux-usb/msg162482.html v2 changes: - moved audioformat quirks to quirks.c - addressed many comments from Pierre-Louis Bossart - reorganized audio-v3.h - added more UAC3 channel relationship definitions Ruslan Bilovol (2): ALSA: usb-audio: move audioformat quirks to quirks.c ALSA: usb: initial USB Audio Device Class 3.0 support include/linux/usb/audio-v2.h | 4 +- include/linux/usb/audio-v3.h | 395 + include/uapi/linux/usb/audio.h | 1 + sound/usb/card.c | 7 +- sound/usb/card.h | 2 +- sound/usb/clock.c | 228 +--- sound/usb/clock.h | 4 +- sound/usb/format.c | 91 -- sound/usb/format.h | 6 +- sound/usb/mixer.c | 337 +++ sound/usb/quirks.c | 34 sound/usb/quirks.h | 4 + sound/usb/stream.c | 393 +--- 13 files changed, 1284 insertions(+), 222 deletions(-) create mode 100644 include/linux/usb/audio-v3.h -- 1.9.1
[PATCH v2 1/2] ALSA: usb-audio: move audioformat quirks to quirks.c
Offload USB audio interface parsing function by moving quirks to a specially designed location (quirks.c) Signed-off-by: Ruslan Bilovol <ruslan.bilo...@gmail.com> --- sound/usb/quirks.c | 34 ++ sound/usb/quirks.h | 4 sound/usb/stream.c | 30 +- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index ea8f3de..eeea8e1 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1403,3 +1403,37 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, return 0; } + +void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, + struct audioformat *fp, + int stream) +{ + switch (chip->usb_id) { + case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */ + /* Optoplay sets the sample rate attribute although +* it seems not supporting it in fact. +*/ + fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE; + break; + case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ + case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ + /* doesn't set the sample rate attribute, but supports it */ + fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; + break; + case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */ + case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */ + case USB_ID(0x047f, 0x0ca1): /* plantronics headset */ + case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is + an older model 77d:223) */ + /* +* plantronics headset and Griffin iMic have set adaptive-in +* although it's really not... +*/ + fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE; + else + fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC; + break; + } +} diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index b90c8b7..a80e0dd 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -42,4 +42,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int sample_bytes); +void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, + struct audioformat *fp, + int stream); + #endif /* __USBAUDIO_QUIRKS_H */ diff --git a/sound/usb/stream.c b/sound/usb/stream.c index d1776e5..dbbe854 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -678,35 +678,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) INIT_LIST_HEAD(>list); /* some quirks for attributes here */ - - switch (chip->usb_id) { - case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */ - /* Optoplay sets the sample rate attribute although -* it seems not supporting it in fact. -*/ - fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE; - break; - case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ - case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ - /* doesn't set the sample rate attribute, but supports it */ - fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; - break; - case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */ - case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */ - case USB_ID(0x047f, 0x0ca1): /* plantronics headset */ - case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is - an older model 77d:223) */ - /* -* plantronics headset and Griffin iMic have set adaptive-in -* although it's really not... -*/ - fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE; - else - fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC; - break; - } + 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) { -- 1.9.1
[PATCH v2 1/2] ALSA: usb-audio: move audioformat quirks to quirks.c
Offload USB audio interface parsing function by moving quirks to a specially designed location (quirks.c) Signed-off-by: Ruslan Bilovol --- sound/usb/quirks.c | 34 ++ sound/usb/quirks.h | 4 sound/usb/stream.c | 30 +- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index ea8f3de..eeea8e1 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1403,3 +1403,37 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, return 0; } + +void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, + struct audioformat *fp, + int stream) +{ + switch (chip->usb_id) { + case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */ + /* Optoplay sets the sample rate attribute although +* it seems not supporting it in fact. +*/ + fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE; + break; + case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ + case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ + /* doesn't set the sample rate attribute, but supports it */ + fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; + break; + case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */ + case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */ + case USB_ID(0x047f, 0x0ca1): /* plantronics headset */ + case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is + an older model 77d:223) */ + /* +* plantronics headset and Griffin iMic have set adaptive-in +* although it's really not... +*/ + fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE; + else + fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC; + break; + } +} diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index b90c8b7..a80e0dd 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -42,4 +42,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int sample_bytes); +void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, + struct audioformat *fp, + int stream); + #endif /* __USBAUDIO_QUIRKS_H */ diff --git a/sound/usb/stream.c b/sound/usb/stream.c index d1776e5..dbbe854 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -678,35 +678,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) INIT_LIST_HEAD(>list); /* some quirks for attributes here */ - - switch (chip->usb_id) { - case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */ - /* Optoplay sets the sample rate attribute although -* it seems not supporting it in fact. -*/ - fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE; - break; - case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */ - case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ - /* doesn't set the sample rate attribute, but supports it */ - fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE; - break; - case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */ - case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */ - case USB_ID(0x047f, 0x0ca1): /* plantronics headset */ - case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is - an older model 77d:223) */ - /* -* plantronics headset and Griffin iMic have set adaptive-in -* although it's really not... -*/ - fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE; - else - fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC; - break; - } + 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) { -- 1.9.1