Commit eda553f4 authored by Ruslan Bilovol's avatar Ruslan Bilovol Committed by Takashi Iwai

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: default avatarRuslan Bilovol <ruslan.bilovol@gmail.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 68faa863
......@@ -807,122 +807,39 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
return fp;
}
int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
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;
struct usb_interface *iface;
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
struct uac3_as_header_descriptor *as = NULL;
int i, altno, err, stream;
u64 format = 0;
unsigned int num_channels = 0;
struct audioformat *fp = NULL;
int num, protocol, clock = 0;
struct snd_pcm_chmap_elem *chmap_v3 = NULL;
dev = chip->dev;
/* parse the interface's altsettings */
iface = usb_ifnum_to_if(dev, iface_no);
num = iface->num_altsetting;
/*
* Dallas DS4201 workaround: It presents 5 altsettings, but the last
* one misses syncpipe, and does not produce any sound.
*/
if (chip->usb_id == USB_ID(0x04fa, 0x4201))
num = 4;
for (i = 0; i < num; i++) {
alts = &iface->altsetting[i];
altsd = get_iface_desc(alts);
protocol = altsd->bInterfaceProtocol;
/* skip invalid one */
if (((altsd->bInterfaceClass != USB_CLASS_AUDIO ||
(altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC)) &&
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
altsd->bNumEndpoints < 1 ||
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
continue;
/* must be isochronous */
if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
USB_ENDPOINT_XFER_ISOC)
continue;
/* check direction */
stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
altno = altsd->bAlternateSetting;
if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
continue;
/*
* Roland audio streaming interfaces are marked with protocols
* 0/1/2, but are UAC 1 compatible.
*/
if (USB_ID_VENDOR(chip->usb_id) == 0x0582 &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
protocol <= 2)
protocol = UAC_VERSION_1;
switch (protocol) {
default:
dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n",
iface_no, altno, protocol);
protocol = UAC_VERSION_1;
/* fall through */
case UAC_VERSION_1:
/* fall through */
case UAC_VERSION_2: {
int bm_quirk = 0;
/*
* Blue Microphones workaround: The last altsetting is
* identical with the previous one, except for a larger
* packet size, but is actually a mislabeled two-channel
* setting; ignore it.
*
* Part 1: prepare quirk flag
*/
if (altno == 2 && num == 3 &&
fp && fp->altsetting == 1 && fp->channels == 1 &&
fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
protocol == UAC_VERSION_1 &&
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
fp->maxpacksize * 2)
bm_quirk = 1;
fp = snd_usb_get_audioformat_uac12(chip, alts, protocol,
iface_no, i, altno,
stream, bm_quirk);
break;
}
case UAC_VERSION_3: {
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,
as = snd_usb_find_csint_desc(alts->extra, alts->extralen,
NULL, UAC_AS_GENERAL);
if (!as) {
dev_err(&dev->dev,
"%u:%d : UAC_AS_GENERAL descriptor not found\n",
iface_no, altno);
continue;
return NULL;
}
if (as->bLength < sizeof(*as)) {
dev_err(&dev->dev,
"%u:%d : invalid UAC_AS_GENERAL desc\n",
iface_no, altno);
continue;
return NULL;
}
cluster_id = le16_to_cpu(as->wClusterDescrID);
......@@ -930,7 +847,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
dev_err(&dev->dev,
"%u:%d : no cluster descriptor\n",
iface_no, altno);
continue;
return NULL;
}
/*
......@@ -948,12 +865,12 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
snd_usb_ctrl_intf(chip),
&hc_header, sizeof(hc_header));
if (err < 0)
return err;
return ERR_PTR(err);
else if (err != sizeof(hc_header)) {
dev_err(&dev->dev,
"%u:%d : can't get High Capability descriptor\n",
iface_no, altno);
return -EIO;
return ERR_PTR(-EIO);
}
/*
......@@ -963,7 +880,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
wLength = le16_to_cpu(hc_header.wLength);
cluster = kzalloc(wLength, GFP_KERNEL);
if (!cluster)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
err = snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(chip->dev, 0),
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
......@@ -973,76 +890,162 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
cluster, wLength);
if (err < 0) {
kfree(cluster);
return err;
return ERR_PTR(err);
} else if (err != wLength) {
dev_err(&dev->dev,
"%u:%d : can't get Cluster Descriptor\n",
iface_no, altno);
kfree(cluster);
return -EIO;
return ERR_PTR(-EIO);
}
num_channels = cluster->bNrChannels;
chmap_v3 = convert_chmap_v3(cluster);
chmap = convert_chmap_v3(cluster);
kfree(cluster);
format = le64_to_cpu(as->bmFormats);
/* lookup the terminal associated to this interface
* to extract the clock */
input_term = snd_usb_find_input_terminal_descriptor(
chip->ctrl_intf,
/*
* lookup the terminal associated to this interface
* to extract the clock
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink);
if (input_term) {
clock = input_term->bCSourceID;
break;
goto found_clock;
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink);
if (output_term) {
clock = output_term->bCSourceID;
break;
goto found_clock;
}
dev_err(&dev->dev,
"%u:%d : bogus bTerminalLink %d\n",
dev_err(&dev->dev, "%u:%d : bogus bTerminalLink %d\n",
iface_no, altno, as->bTerminalLink);
continue;
}
}
if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
if (!fp)
continue;
else if (IS_ERR(fp))
return PTR_ERR(fp);
goto skip_uac3;
}
return NULL;
fp = audio_format_alloc_init(chip, alts, protocol, iface_no, i,
altno, num_channels, clock);
found_clock:
fp = audio_format_alloc_init(chip, alts, UAC_VERSION_3, iface_no,
altset_idx, altno, num_channels, clock);
if (!fp)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
fp->attributes = parse_uac_endpoint_attributes(chip, alts,
protocol,
UAC_VERSION_3,
iface_no);
fp->chmap = chmap_v3;
fp->chmap = chmap;
/* ok, let's parse further... */
if (snd_usb_parse_audio_format_v3(chip, fp, as,
stream) < 0) {
if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
kfree(fp->rate_table);
kfree(fp);
fp = NULL;
return NULL;
}
return fp;
}
int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
{
struct usb_device *dev;
struct usb_interface *iface;
struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd;
int i, altno, err, stream;
struct audioformat *fp = NULL;
int num, protocol;
dev = chip->dev;
/* parse the interface's altsettings */
iface = usb_ifnum_to_if(dev, iface_no);
num = iface->num_altsetting;
/*
* Dallas DS4201 workaround: It presents 5 altsettings, but the last
* one misses syncpipe, and does not produce any sound.
*/
if (chip->usb_id == USB_ID(0x04fa, 0x4201))
num = 4;
for (i = 0; i < num; i++) {
alts = &iface->altsetting[i];
altsd = get_iface_desc(alts);
protocol = altsd->bInterfaceProtocol;
/* skip invalid one */
if (((altsd->bInterfaceClass != USB_CLASS_AUDIO ||
(altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC)) &&
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
altsd->bNumEndpoints < 1 ||
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
continue;
/* must be isochronous */
if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
USB_ENDPOINT_XFER_ISOC)
continue;
/* check direction */
stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
altno = altsd->bAlternateSetting;
if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
continue;
/*
* Roland audio streaming interfaces are marked with protocols
* 0/1/2, but are UAC 1 compatible.
*/
if (USB_ID_VENDOR(chip->usb_id) == 0x0582 &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
protocol <= 2)
protocol = UAC_VERSION_1;
switch (protocol) {
default:
dev_dbg(&dev->dev, "%u:%d: unknown interface protocol %#02x, assuming v1\n",
iface_no, altno, protocol);
protocol = UAC_VERSION_1;
/* fall through */
case UAC_VERSION_1:
/* fall through */
case UAC_VERSION_2: {
int bm_quirk = 0;
/*
* Blue Microphones workaround: The last altsetting is
* identical with the previous one, except for a larger
* packet size, but is actually a mislabeled two-channel
* setting; ignore it.
*
* Part 1: prepare quirk flag
*/
if (altno == 2 && num == 3 &&
fp && fp->altsetting == 1 && fp->channels == 1 &&
fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
protocol == UAC_VERSION_1 &&
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
fp->maxpacksize * 2)
bm_quirk = 1;
fp = snd_usb_get_audioformat_uac12(chip, alts, protocol,
iface_no, i, altno,
stream, bm_quirk);
break;
}
case UAC_VERSION_3:
fp = snd_usb_get_audioformat_uac3(chip, alts,
iface_no, i, altno, stream);
break;
}
if (!fp)
continue;
else if (IS_ERR(fp))
return PTR_ERR(fp);
skip_uac3:
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment