aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/stream.c')
-rw-r--r--sound/usb/stream.c90
1 files changed, 60 insertions, 30 deletions
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index e852c7fd6109..11785f9652ad 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -28,6 +28,14 @@
#include "power.h"
#include "media.h"
+static void audioformat_free(struct audioformat *fp)
+{
+ list_del(&fp->list); /* unlink for avoiding double-free */
+ kfree(fp->rate_table);
+ kfree(fp->chmap);
+ kfree(fp);
+}
+
/*
* free a substream
*/
@@ -37,11 +45,8 @@ static void free_substream(struct snd_usb_substream *subs)
if (!subs->num_formats)
return; /* not initialized */
- list_for_each_entry_safe(fp, n, &subs->fmt_list, list) {
- kfree(fp->rate_table);
- kfree(fp->chmap);
- kfree(fp);
- }
+ list_for_each_entry_safe(fp, n, &subs->fmt_list, list)
+ audioformat_free(fp);
kfree(subs->rate_list.list);
kfree(subs->str_pd);
snd_media_stream_delete(subs);
@@ -627,16 +632,14 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
*/
static void *
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id, bool uac23)
+ int terminal_id, int protocol)
{
struct uac2_input_terminal_descriptor *term = NULL;
- size_t minlen = uac23 ? sizeof(struct uac2_input_terminal_descriptor) :
- sizeof(struct uac_input_terminal_descriptor);
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_INPUT_TERMINAL))) {
- if (term->bLength < minlen)
+ if (!snd_usb_validate_audio_desc(term, protocol))
continue;
if (term->bTerminalID == terminal_id)
return term;
@@ -647,7 +650,7 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
static void *
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
- int terminal_id)
+ int terminal_id, int protocol)
{
/* OK to use with both UAC2 and UAC3 */
struct uac2_output_terminal_descriptor *term = NULL;
@@ -655,8 +658,9 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen,
term, UAC_OUTPUT_TERMINAL))) {
- if (term->bLength >= sizeof(*term) &&
- term->bTerminalID == terminal_id)
+ if (!snd_usb_validate_audio_desc(term, protocol))
+ continue;
+ if (term->bTerminalID == terminal_id)
return term;
}
@@ -731,7 +735,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- false);
+ protocol);
if (iterm) {
num_channels = iterm->bNrChannels;
chconfig = le16_to_cpu(iterm->wChannelConfig);
@@ -767,7 +771,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- true);
+ protocol);
if (input_term) {
clock = input_term->bCSourceID;
if (!chconfig && (num_channels == input_term->bNrChannels))
@@ -776,7 +780,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
+ as->bTerminalLink,
+ protocol);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
@@ -832,8 +837,7 @@ found_clock:
/* ok, let's parse further... */
if (snd_usb_parse_audio_format(chip, fp, format,
fmt, stream) < 0) {
- kfree(fp->rate_table);
- kfree(fp);
+ audioformat_free(fp);
return NULL;
}
@@ -1002,14 +1006,15 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip,
*/
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink,
- true);
+ UAC_VERSION_3);
if (input_term) {
clock = input_term->bCSourceID;
goto found_clock;
}
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
- as->bTerminalLink);
+ as->bTerminalLink,
+ UAC_VERSION_3);
if (output_term) {
clock = output_term->bCSourceID;
goto found_clock;
@@ -1043,9 +1048,7 @@ found_clock:
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd) {
- kfree(fp->chmap);
- kfree(fp->rate_table);
- kfree(fp);
+ audioformat_free(fp);
return NULL;
}
pd->pd_id = (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
@@ -1064,9 +1067,7 @@ found_clock:
/* ok, let's parse further... */
if (snd_usb_parse_audio_format_v3(chip, fp, as, stream) < 0) {
kfree(pd);
- kfree(fp->chmap);
- kfree(fp->rate_table);
- kfree(fp);
+ audioformat_free(fp);
return NULL;
}
}
@@ -1077,7 +1078,9 @@ found_clock:
return fp;
}
-int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
+static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
+ int iface_no,
+ bool *has_non_pcm, bool non_pcm)
{
struct usb_device *dev;
struct usb_interface *iface;
@@ -1178,6 +1181,16 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
else if (IS_ERR(fp))
return PTR_ERR(fp);
+ if (fp->fmt_type != UAC_FORMAT_TYPE_I)
+ *has_non_pcm = true;
+ if ((fp->fmt_type == UAC_FORMAT_TYPE_I) == non_pcm) {
+ audioformat_free(fp);
+ kfree(pd);
+ fp = NULL;
+ pd = NULL;
+ continue;
+ }
+
dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint);
if (protocol == UAC_VERSION_3)
err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd);
@@ -1185,11 +1198,8 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
err = snd_usb_add_audio_stream(chip, stream, fp);
if (err < 0) {
- list_del(&fp->list); /* unlink for avoiding double-free */
+ audioformat_free(fp);
kfree(pd);
- kfree(fp->rate_table);
- kfree(fp->chmap);
- kfree(fp);
return err;
}
/* try to set the interface... */
@@ -1200,3 +1210,23 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
return 0;
}
+int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
+{
+ int err;
+ bool has_non_pcm = false;
+
+ /* parse PCM formats */
+ err = __snd_usb_parse_audio_interface(chip, iface_no, &has_non_pcm, false);
+ if (err < 0)
+ return err;
+
+ if (has_non_pcm) {
+ /* parse non-PCM formats */
+ err = __snd_usb_parse_audio_interface(chip, iface_no, &has_non_pcm, true);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+