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.bilovol@gmail.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
68faa86364
commit
eda553f432
1 changed files with 146 additions and 143 deletions
|
@ -807,19 +807,154 @@ snd_usb_get_audioformat_uac12(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->dev,
|
||||
"%u:%d : UAC_AS_GENERAL descriptor not found\n",
|
||||
iface_no, altno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (as->bLength < sizeof(*as)) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : invalid UAC_AS_GENERAL desc\n",
|
||||
iface_no, altno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cluster_id = le16_to_cpu(as->wClusterDescrID);
|
||||
if (!cluster_id) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : no cluster descriptor\n",
|
||||
iface_no, altno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get number of channels and channel map through
|
||||
* High Capability Cluster Descriptor
|
||||
*
|
||||
* First step: get High Capability header and
|
||||
* read size of Cluster Descriptor
|
||||
*/
|
||||
err = snd_usb_ctl_msg(chip->dev,
|
||||
usb_rcvctrlpipe(chip->dev, 0),
|
||||
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
cluster_id,
|
||||
snd_usb_ctrl_intf(chip),
|
||||
&hc_header, sizeof(hc_header));
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
else if (err != sizeof(hc_header)) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : can't get High Capability descriptor\n",
|
||||
iface_no, altno);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Second step: allocate needed amount of memory
|
||||
* and request Cluster Descriptor
|
||||
*/
|
||||
wLength = le16_to_cpu(hc_header.wLength);
|
||||
cluster = kzalloc(wLength, GFP_KERNEL);
|
||||
if (!cluster)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
err = snd_usb_ctl_msg(chip->dev,
|
||||
usb_rcvctrlpipe(chip->dev, 0),
|
||||
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
cluster_id,
|
||||
snd_usb_ctrl_intf(chip),
|
||||
cluster, wLength);
|
||||
if (err < 0) {
|
||||
kfree(cluster);
|
||||
return ERR_PTR(err);
|
||||
} else if (err != wLength) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : can't get Cluster Descriptor\n",
|
||||
iface_no, altno);
|
||||
kfree(cluster);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
num_channels = cluster->bNrChannels;
|
||||
chmap = convert_chmap_v3(cluster);
|
||||
kfree(cluster);
|
||||
|
||||
/*
|
||||
* lookup the terminal associated to this interface
|
||||
* to extract the clock
|
||||
*/
|
||||
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
|
||||
as->bTerminalLink);
|
||||
if (input_term) {
|
||||
clock = input_term->bCSourceID;
|
||||
goto found_clock;
|
||||
}
|
||||
|
||||
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
|
||||
as->bTerminalLink);
|
||||
if (output_term) {
|
||||
clock = output_term->bCSourceID;
|
||||
goto found_clock;
|
||||
}
|
||||
|
||||
dev_err(&dev->dev, "%u:%d : bogus bTerminalLink %d\n",
|
||||
iface_no, altno, as->bTerminalLink);
|
||||
return NULL;
|
||||
|
||||
found_clock:
|
||||
fp = audio_format_alloc_init(chip, alts, UAC_VERSION_3, iface_no,
|
||||
altset_idx, altno, num_channels, clock);
|
||||
if (!fp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fp->attributes = parse_uac_endpoint_attributes(chip, alts,
|
||||
UAC_VERSION_3,
|
||||
iface_no);
|
||||
fp->chmap = chmap;
|
||||
|
||||
/* ok, let's parse further... */
|
||||
if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
|
||||
kfree(fp->rate_table);
|
||||
kfree(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
int num, protocol;
|
||||
|
||||
dev = chip->dev;
|
||||
|
||||
|
@ -900,149 +1035,17 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
|
|||
stream, bm_quirk);
|
||||
break;
|
||||
}
|
||||
case UAC_VERSION_3: {
|
||||
struct uac3_input_terminal_descriptor *input_term;
|
||||
struct uac3_output_terminal_descriptor *output_term;
|
||||
struct uac3_cluster_header_descriptor *cluster;
|
||||
struct uac3_hc_descriptor_header hc_header;
|
||||
u16 cluster_id, wLength;
|
||||
|
||||
as = snd_usb_find_csint_desc(alts->extra,
|
||||
alts->extralen,
|
||||
NULL, UAC_AS_GENERAL);
|
||||
|
||||
if (!as) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : UAC_AS_GENERAL descriptor not found\n",
|
||||
iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (as->bLength < sizeof(*as)) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : invalid UAC_AS_GENERAL desc\n",
|
||||
iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
cluster_id = le16_to_cpu(as->wClusterDescrID);
|
||||
if (!cluster_id) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : no cluster descriptor\n",
|
||||
iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get number of channels and channel map through
|
||||
* High Capability Cluster Descriptor
|
||||
*
|
||||
* First step: get High Capability header and
|
||||
* read size of Cluster Descriptor
|
||||
*/
|
||||
err = snd_usb_ctl_msg(chip->dev,
|
||||
usb_rcvctrlpipe(chip->dev, 0),
|
||||
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
cluster_id,
|
||||
snd_usb_ctrl_intf(chip),
|
||||
&hc_header, sizeof(hc_header));
|
||||
if (err < 0)
|
||||
return err;
|
||||
else if (err != sizeof(hc_header)) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : can't get High Capability descriptor\n",
|
||||
iface_no, altno);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Second step: allocate needed amount of memory
|
||||
* and request Cluster Descriptor
|
||||
*/
|
||||
wLength = le16_to_cpu(hc_header.wLength);
|
||||
cluster = kzalloc(wLength, GFP_KERNEL);
|
||||
if (!cluster)
|
||||
return -ENOMEM;
|
||||
err = snd_usb_ctl_msg(chip->dev,
|
||||
usb_rcvctrlpipe(chip->dev, 0),
|
||||
UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
cluster_id,
|
||||
snd_usb_ctrl_intf(chip),
|
||||
cluster, wLength);
|
||||
if (err < 0) {
|
||||
kfree(cluster);
|
||||
return err;
|
||||
} else if (err != wLength) {
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : can't get Cluster Descriptor\n",
|
||||
iface_no, altno);
|
||||
kfree(cluster);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
num_channels = cluster->bNrChannels;
|
||||
chmap_v3 = convert_chmap_v3(cluster);
|
||||
|
||||
kfree(cluster);
|
||||
|
||||
format = le64_to_cpu(as->bmFormats);
|
||||
|
||||
/* lookup the terminal associated to this interface
|
||||
* to extract the clock */
|
||||
input_term = snd_usb_find_input_terminal_descriptor(
|
||||
chip->ctrl_intf,
|
||||
as->bTerminalLink);
|
||||
|
||||
if (input_term) {
|
||||
clock = input_term->bCSourceID;
|
||||
break;
|
||||
}
|
||||
|
||||
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
|
||||
as->bTerminalLink);
|
||||
if (output_term) {
|
||||
clock = output_term->bCSourceID;
|
||||
break;
|
||||
}
|
||||
|
||||
dev_err(&dev->dev,
|
||||
"%u:%d : bogus bTerminalLink %d\n",
|
||||
iface_no, altno, as->bTerminalLink);
|
||||
continue;
|
||||
}
|
||||
case UAC_VERSION_3:
|
||||
fp = snd_usb_get_audioformat_uac3(chip, alts,
|
||||
iface_no, i, altno, stream);
|
||||
break;
|
||||
}
|
||||
|
||||
if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
|
||||
if (!fp)
|
||||
continue;
|
||||
else if (IS_ERR(fp))
|
||||
return PTR_ERR(fp);
|
||||
|
||||
goto skip_uac3;
|
||||
}
|
||||
|
||||
fp = audio_format_alloc_init(chip, alts, protocol, iface_no, i,
|
||||
altno, num_channels, clock);
|
||||
if (!fp)
|
||||
return -ENOMEM;
|
||||
|
||||
fp->attributes = parse_uac_endpoint_attributes(chip, alts,
|
||||
protocol,
|
||||
iface_no);
|
||||
fp->chmap = chmap_v3;
|
||||
|
||||
/* ok, let's parse further... */
|
||||
if (snd_usb_parse_audio_format_v3(chip, fp, as,
|
||||
stream) < 0) {
|
||||
kfree(fp->rate_table);
|
||||
kfree(fp);
|
||||
fp = NULL;
|
||||
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) {
|
||||
|
|
Loading…
Reference in a new issue