Commit 744f51e8 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/usb-validation' into for-next

Pull USB validation patches.  It's based on the latest 5.3 development
branch, so we shall catch up the whole things.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parents 051c78af b8e4f1fd
......@@ -37,7 +37,7 @@ int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
packets_per_page = PAGE_SIZE / packet_size;
if (WARN_ON(!packets_per_page)) {
err = -EINVAL;
goto error;
goto err_packets;
}
pages = DIV_ROUND_UP(count, packets_per_page);
......
......@@ -598,11 +598,9 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
}
runtime->private_data = azx_dev;
if (chip->gts_present)
azx_pcm_hw.info = azx_pcm_hw.info |
SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
runtime->hw = azx_pcm_hw;
if (chip->gts_present)
runtime->hw.info |= SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
runtime->hw.channels_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max;
runtime->hw.formats = hinfo->formats;
......@@ -615,6 +613,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
20,
178000000);
/* by some reason, the playback stream stalls on PulseAudio with
* tsched=1 when a capture stream triggers. Until we figure out the
* real cause, disable tsched mode by telling the PCM info flag.
*/
if (chip->driver_caps & AZX_DCAPS_AMD_WORKAROUND)
runtime->hw.info |= SNDRV_PCM_INFO_BATCH;
if (chip->align_buffer_size)
/* constrain buffer sizes to be multiple of 128
bytes. This is more efficient in terms of memory
......
......@@ -31,7 +31,7 @@
/* 14 unused */
#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
/* 17 unused */
#define AZX_DCAPS_AMD_WORKAROUND (1 << 17) /* AMD-specific workaround */
#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */
#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */
......
......@@ -6051,6 +6051,24 @@ void snd_hda_gen_free(struct hda_codec *codec)
}
EXPORT_SYMBOL_GPL(snd_hda_gen_free);
/**
* snd_hda_gen_reboot_notify - Make codec enter D3 before rebooting
* @codec: the HDA codec
*
* This can be put as patch_ops reboot_notify function.
*/
void snd_hda_gen_reboot_notify(struct hda_codec *codec)
{
/* Make the codec enter D3 to avoid spurious noises from the internal
* speaker during (and after) reboot
*/
snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3);
snd_hda_codec_write(codec, codec->core.afg, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
msleep(10);
}
EXPORT_SYMBOL_GPL(snd_hda_gen_reboot_notify);
#ifdef CONFIG_PM
/**
* snd_hda_gen_check_power_status - check the loopback power save state
......@@ -6078,6 +6096,7 @@ static const struct hda_codec_ops generic_patch_ops = {
.init = snd_hda_gen_init,
.free = snd_hda_gen_free,
.unsol_event = snd_hda_jack_unsol_event,
.reboot_notify = snd_hda_gen_reboot_notify,
#ifdef CONFIG_PM
.check_power_status = snd_hda_gen_check_power_status,
#endif
......@@ -6100,7 +6119,7 @@ static int snd_hda_parse_generic_codec(struct hda_codec *codec)
err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
if (err < 0)
return err;
goto error;
err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
if (err < 0)
......
......@@ -332,6 +332,7 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg);
int snd_hda_gen_build_controls(struct hda_codec *codec);
int snd_hda_gen_build_pcms(struct hda_codec *codec);
void snd_hda_gen_reboot_notify(struct hda_codec *codec);
/* standard jack event callbacks */
void snd_hda_gen_hp_automute(struct hda_codec *codec,
......
......@@ -65,6 +65,7 @@ enum {
POS_FIX_VIACOMBO,
POS_FIX_COMBO,
POS_FIX_SKL,
POS_FIX_FIFO,
};
/* Defines for ATI HD Audio support in SB450 south bridge */
......@@ -135,7 +136,7 @@ module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "DMA pointer read method."
"(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+).");
"(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+, 6 = FIFO).");
module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
......@@ -335,6 +336,11 @@ enum {
#define AZX_DCAPS_PRESET_ATI_HDMI_NS \
(AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF)
/* quirks for AMD SB */
#define AZX_DCAPS_PRESET_AMD_SB \
(AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_AMD_WORKAROUND |\
AZX_DCAPS_SNOOP_TYPE(ATI) | AZX_DCAPS_PM_RUNTIME)
/* quirks for Nvidia */
#define AZX_DCAPS_PRESET_NVIDIA \
(AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\
......@@ -841,6 +847,49 @@ static unsigned int azx_via_get_position(struct azx *chip,
return bound_pos + mod_dma_pos;
}
#define AMD_FIFO_SIZE 32
/* get the current DMA position with FIFO size correction */
static unsigned int azx_get_pos_fifo(struct azx *chip, struct azx_dev *azx_dev)
{
struct snd_pcm_substream *substream = azx_dev->core.substream;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int pos, delay;
pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
if (!runtime)
return pos;
runtime->delay = AMD_FIFO_SIZE;
delay = frames_to_bytes(runtime, AMD_FIFO_SIZE);
if (azx_dev->insufficient) {
if (pos < delay) {
delay = pos;
runtime->delay = bytes_to_frames(runtime, pos);
} else {
azx_dev->insufficient = 0;
}
}
/* correct the DMA position for capture stream */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
if (pos < delay)
pos += azx_dev->core.bufsize;
pos -= delay;
}
return pos;
}
static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev,
unsigned int pos)
{
struct snd_pcm_substream *substream = azx_dev->core.substream;
/* just read back the calculated value in the above */
return substream->runtime->delay;
}
static unsigned int azx_skl_get_dpib_pos(struct azx *chip,
struct azx_dev *azx_dev)
{
......@@ -1417,6 +1466,7 @@ static int check_position_fix(struct azx *chip, int fix)
case POS_FIX_VIACOMBO:
case POS_FIX_COMBO:
case POS_FIX_SKL:
case POS_FIX_FIFO:
return fix;
}
......@@ -1433,6 +1483,10 @@ static int check_position_fix(struct azx *chip, int fix)
dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n");
return POS_FIX_VIACOMBO;
}
if (chip->driver_caps & AZX_DCAPS_AMD_WORKAROUND) {
dev_dbg(chip->card->dev, "Using FIFO position fix\n");
return POS_FIX_FIFO;
}
if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) {
dev_dbg(chip->card->dev, "Using LPIB position fix\n");
return POS_FIX_LPIB;
......@@ -1453,6 +1507,7 @@ static void assign_position_fix(struct azx *chip, int fix)
[POS_FIX_VIACOMBO] = azx_via_get_position,
[POS_FIX_COMBO] = azx_get_pos_lpib,
[POS_FIX_SKL] = azx_get_pos_skl,
[POS_FIX_FIFO] = azx_get_pos_fifo,
};
chip->get_position[0] = chip->get_position[1] = callbacks[fix];
......@@ -1467,6 +1522,9 @@ static void assign_position_fix(struct azx *chip, int fix)
azx_get_delay_from_lpib;
}
if (fix == POS_FIX_FIFO)
chip->get_delay[0] = chip->get_delay[1] =
azx_get_delay_from_fifo;
}
/*
......@@ -2416,6 +2474,12 @@ static const struct pci_device_id azx_ids[] = {
/* AMD Hudson */
{ PCI_DEVICE(0x1022, 0x780d),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
/* AMD, X370 & co */
{ PCI_DEVICE(0x1022, 0x1457),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
/* AMD, X570 & co */
{ PCI_DEVICE(0x1022, 0x1487),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
/* AMD Stoney */
{ PCI_DEVICE(0x1022, 0x157a),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB |
......
......@@ -1175,6 +1175,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = {
SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ),
SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ),
SND_PCI_QUIRK(0x1102, 0x0027, "Sound Blaster Z", QUIRK_SBZ),
SND_PCI_QUIRK(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ),
SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI),
......
......@@ -163,23 +163,10 @@ static void cx_auto_reboot_notify(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
switch (codec->core.vendor_id) {
case 0x14f12008: /* CX8200 */
case 0x14f150f2: /* CX20722 */
case 0x14f150f4: /* CX20724 */
break;
default:
return;
}
/* Turn the problematic codec into D3 to avoid spurious noises
from the internal speaker during (and after) reboot */
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3);
snd_hda_codec_write(codec, codec->core.afg, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
msleep(10);
snd_hda_gen_reboot_notify(codec);
}
static void cx_auto_free(struct hda_codec *codec)
......@@ -624,18 +611,20 @@ static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
/* update LED status via GPIO */
static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask,
bool enabled)
bool led_on)
{
struct conexant_spec *spec = codec->spec;
unsigned int oldval = spec->gpio_led;
if (spec->mute_led_polarity)
enabled = !enabled;
led_on = !led_on;
if (enabled)
spec->gpio_led &= ~mask;
else
if (led_on)
spec->gpio_led |= mask;
else
spec->gpio_led &= ~mask;
codec_dbg(codec, "mask:%d enabled:%d gpio_led:%d\n",
mask, led_on, spec->gpio_led);
if (spec->gpio_led != oldval)
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
spec->gpio_led);
......@@ -646,8 +635,8 @@ static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled)
{
struct hda_codec *codec = private_data;
struct conexant_spec *spec = codec->spec;
cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
/* muted -> LED on */
cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, !enabled);
}
/* turn on/off mic-mute LED via GPIO per capture hook */
......@@ -669,7 +658,6 @@ static void cxt_fixup_mute_led_gpio(struct hda_codec *codec,
{ 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03 },
{}
};
codec_info(codec, "action: %d gpio_led: %d\n", action, spec->gpio_led);
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook;
......
......@@ -869,15 +869,6 @@ static void alc_reboot_notify(struct hda_codec *codec)
alc_shutup(codec);
}
/* power down codec to D3 at reboot/shutdown; set as reboot_notify ops */
static void alc_d3_at_reboot(struct hda_codec *codec)
{
snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3);
snd_hda_codec_write(codec, codec->core.afg, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
msleep(10);
}
#define alc_free snd_hda_gen_free
#ifdef CONFIG_PM
......@@ -5222,7 +5213,7 @@ static void alc_fixup_tpt440_dock(struct hda_codec *codec,
struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->reboot_notify = alc_d3_at_reboot; /* reduce noise */
spec->reboot_notify = snd_hda_gen_reboot_notify; /* reduce noise */
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
codec->power_save_node = 0; /* avoid click noises */
snd_hda_apply_pincfgs(codec, pincfgs);
......@@ -7064,6 +7055,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
......
......@@ -275,7 +275,8 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
goto retry;
}
spin_unlock(&sound_loader_lock);
return -EBUSY;
r = -EBUSY;
goto fail;
}
}
......
......@@ -17,7 +17,8 @@ snd-usb-audio-objs := card.o \
power.o \
proc.o \
quirks.o \
stream.o
stream.o \
validate.o
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
......
......@@ -38,39 +38,37 @@ static void *find_uac_clock_desc(struct usb_host_interface *iface, int id,
static bool validate_clock_source_v2(void *p, int id)
{
struct uac_clock_source_descriptor *cs = p;
return cs->bLength == sizeof(*cs) && cs->bClockID == id;
return cs->bClockID == id;
}
static bool validate_clock_source_v3(void *p, int id)
{
struct uac3_clock_source_descriptor *cs = p;
return cs->bLength == sizeof(*cs) && cs->bClockID == id;
return cs->bClockID == id;
}
static bool validate_clock_selector_v2(void *p, int id)
{
struct uac_clock_selector_descriptor *cs = p;
return cs->bLength >= sizeof(*cs) && cs->bClockID == id &&
cs->bLength == 7 + cs->bNrInPins;
return cs->bClockID == id;
}
static bool validate_clock_selector_v3(void *p, int id)
{
struct uac3_clock_selector_descriptor *cs = p;
return cs->bLength >= sizeof(*cs) && cs->bClockID == id &&
cs->bLength == 11 + cs->bNrInPins;
return cs->bClockID == id;
}
static bool validate_clock_multiplier_v2(void *p, int id)
{
struct uac_clock_multiplier_descriptor *cs = p;
return cs->bLength == sizeof(*cs) && cs->bClockID == id;
return cs->bClockID == id;
}
static bool validate_clock_multiplier_v3(void *p, int id)
{
struct uac3_clock_multiplier_descriptor *cs = p;
return cs->bLength == sizeof(*cs) && cs->bClockID == id;
return cs->bClockID == id;
}
#define DEFINE_FIND_HELPER(name, obj, validator, type) \
......
......@@ -31,4 +31,8 @@ static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber;
}
/* in validate.c */
bool snd_usb_validate_audio_desc(void *p, int protocol);
bool snd_usb_validate_midi_desc(void *p);
#endif /* __USBAUDIO_HELPER_H */
......@@ -600,14 +600,13 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
ret = hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP,
hiface_pcm_out_urb_handler);
if (ret < 0)
return ret;
goto error;
}
ret = snd_pcm_new(chip->card, "USB-SPDIF Audio", 0, 1, 0, &pcm);
if (ret < 0) {
kfree(rt);
dev_err(&chip->dev->dev, "Cannot create pcm instance\n");
return ret;
goto error;
}
pcm->private_data = rt;
......@@ -620,4 +619,10 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
chip->pcm = rt;
return 0;
error:
for (i = 0; i < PCM_N_URBS; i++)
kfree(rt->out_urbs[i].buffer);
kfree(rt);
return ret;
}
......@@ -550,6 +550,15 @@ int line6_init_pcm(struct usb_line6 *line6,
line6pcm->volume_monitor = 255;
line6pcm->line6 = line6;
spin_lock_init(&line6pcm->out.lock);
spin_lock_init(&line6pcm->in.lock);
line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
line6->line6pcm = line6pcm;
pcm->private_data = line6pcm;
pcm->private_free = line6_cleanup_pcm;
line6pcm->max_packet_size_in =
usb_maxpacket(line6->usbdev,
usb_rcvisocpipe(line6->usbdev, ep_read), 0);
......@@ -562,15 +571,6 @@ int line6_init_pcm(struct usb_line6 *line6,
return -EINVAL;
}
spin_lock_init(&line6pcm->out.lock);
spin_lock_init(&line6pcm->in.lock);
line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
line6->line6pcm = line6pcm;
pcm->private_data = line6pcm;
pcm->private_free = line6_cleanup_pcm;
err = line6_create_audio_out_urbs(line6pcm);
if (err < 0)
return err;
......
This diff is collapsed.
......@@ -1156,17 +1156,17 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
{
struct usb_mixer_interface *mixer;
struct usb_mixer_elem_info *cval;
int unitid = 12; /* SamleRate ExtensionUnit ID */
int unitid = 12; /* SampleRate ExtensionUnit ID */
list_for_each_entry(mixer, &chip->mixer_list, list) {
cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
if (cval) {
if (mixer->id_elems[unitid]) {
cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
cval->control << 8,
samplerate_id);
snd_usb_mixer_notify_id(mixer, unitid);
break;
}
break;
}
}
......
......@@ -339,6 +339,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
ep = 0x81;
ifnum = 2;
goto add_sync_ep_from_ifnum;
case USB_ID(0x1397, 0x0001): /* Behringer UFX1604 */
case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */
ep = 0x81;
ifnum = 1;
......
......@@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
struct uac3_power_domain_descriptor *pd_desc = p;
int i;
if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3))
continue;
for (i = 0; i < pd_desc->bNrEntities; i++) {
if (pd_desc->baEntityID[i] == id) {
pd->pd_id = pd_desc->bPowerDomainID;
......
......@@ -248,6 +248,9 @@ static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
NULL, USB_MS_MIDI_OUT_JACK);
if (!injd && !outjd)
return -ENODEV;
if (!snd_usb_validate_midi_desc(injd) ||
!snd_usb_validate_midi_desc(outjd))
return -ENODEV;
if (injd && (injd->bLength < 5 ||
(injd->bJackType != USB_MS_EMBEDDED &&
injd->bJackType != USB_MS_EXTERNAL)))
......
......@@ -632,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;
......@@ -652,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;
......@@ -660,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;
}
......@@ -736,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);
......@@ -772,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))
......@@ -781,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;
......@@ -1006,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;
......
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Validation of USB-audio class descriptors
//
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/usb/audio-v3.h>
#include <linux/usb/midi.h>
#include "usbaudio.h"
#include "helper.h"
struct usb_desc_validator {
unsigned char protocol;
unsigned char type;
bool (*func)(const void *p, const struct usb_desc_validator *v);
size_t size;
};
#define UAC_VERSION_ALL (unsigned char)(-1)
/* UAC1 only */
static bool validate_uac1_header(const void *p,
const struct usb_desc_validator *v)
{
const struct uac1_ac_header_descriptor *d = p;
return d->bLength >= sizeof(*d) &&
d->bLength >= sizeof(*d) + d->bInCollection;
}
/* for mixer unit; covering all UACs */
static bool validate_mixer_unit(const void *p,
const struct usb_desc_validator *v)
{
const struct uac_mixer_unit_descriptor *d = p;
size_t len;
if (d->bLength < sizeof(*d) || !d->bNrInPins)
return false;
len = sizeof(*d) + d->bNrInPins;
/* We can't determine the bitmap size only from this unit descriptor,
* so just check with the remaining length.
* The actual bitmap is checked at mixer unit parser.
*/
switch (v->protocol) {
case UAC_VERSION_1:
default:
len += 2 + 1; /* wChannelConfig, iChannelNames */
/* bmControls[n*m] */
len += 1; /* iMixer */
break;
case UAC_VERSION_2:
len += 4 + 1; /* bmChannelConfig, iChannelNames */
/* bmMixerControls[n*m] */
len += 1 + 1; /* bmControls, iMixer */
break;
case UAC_VERSION_3:
len += 2; /* wClusterDescrID */
/* bmMixerControls[n*m] */
break;
}
return d->bLength >= len;
}
/* both for processing and extension units; covering all UACs */
static bool validate_processing_unit(const void *p,
const struct usb_desc_validator *v)
{
const struct uac_processing_unit_descriptor *d = p;
const unsigned char *hdr = p;
size_t len, m;
if (d->bLength < sizeof(*d))
return false;
len = d->bLength < sizeof(*d) + d->bNrInPins;
if (d->bLength < len)
return false;
switch (v->protocol) {
case UAC_VERSION_1:
default:
/* bNrChannels, wChannelConfig, iChannelNames, bControlSize */
len += 1 + 2 + 1 + 1;
if (d->bLength < len) /* bControlSize */
return false;
m = hdr[len];
len += 1 + m + 1; /* bControlSize, bmControls, iProcessing */
break;
case UAC_VERSION_2:
/* bNrChannels, bmChannelConfig, iChannelNames */
len += 1 + 4 + 1;
if (v->type == UAC2_PROCESSING_UNIT_V2)
len += 2; /* bmControls -- 2 bytes for PU */
else
len += 1; /* bmControls -- 1 byte for EU */
len += 1; /* iProcessing */
break;
case UAC_VERSION_3:
/* wProcessingDescrStr, bmControls */
len += 2 + 4;
break;
}
if (d->bLength < len)
return false;
switch (v->protocol) {
case UAC_VERSION_1:
default:
if (v->type == UAC1_EXTENSION_UNIT)
return true; /* OK */
switch (d->wProcessType) {
case UAC_PROCESS_UP_DOWNMIX:
case UAC_PROCESS_DOLBY_PROLOGIC:
if (d->bLength < len + 1) /* bNrModes */
return false;
m = hdr[len];
len += 1 + m * 2; /* bNrModes, waModes(n) */
break;
default:
break;
}
break;
case UAC_VERSION_2:
if (v->type == UAC2_EXTENSION_UNIT_V2)
return true; /* OK */
switch (d->wProcessType) {
case UAC2_PROCESS_UP_DOWNMIX:
case UAC2_PROCESS_DOLBY_PROLOCIC: /* SiC! */
if (d->bLength < len + 1) /* bNrModes */
return false;
m = hdr[len];
len += 1 + m * 4; /* bNrModes, daModes(n) */
break;
default:
break;
}
break;
case UAC_VERSION_3:
if (v->type == UAC3_EXTENSION_UNIT) {
len += 2; /* wClusterDescrID */
break;
}
switch (d->wProcessType) {
case UAC3_PROCESS_UP_DOWNMIX:
if (d->bLength < len + 1) /* bNrModes */
return false;
m = hdr[len];
len += 1 + m * 2; /* bNrModes, waClusterDescrID(n) */
break;
case UAC3_PROCESS_MULTI_FUNCTION:
len += 2 + 4; /* wClusterDescrID, bmAlgorighms */
break;
default:
break;
}
break;
}
if (d->bLength < len)
return false;
return true;
}
/* both for selector and clock selector units; covering all UACs */
static bool validate_selector_unit(const void *p,
const struct usb_desc_validator *v)
{
const struct uac_selector_unit_descriptor *d = p;
size_t len;
if (d->bLength < sizeof(*d))
return false;
len = sizeof(*d) + d->bNrInPins;
switch (v->protocol) {
case UAC_VERSION_1:
default:
len += 1; /* iSelector */
break;
case UAC_VERSION_2:
len += 1 + 1; /* bmControls, iSelector */
break;
case UAC_VERSION_3:
len += 4 + 2; /* bmControls, wSelectorDescrStr */
break;
}
return d->bLength >= len;
}
static bool validate_uac1_feature_unit(const void *p,
const struct usb_desc_validator *v)
{
const struct uac_feature_unit_descriptor *d = p;
if (d->bLength < sizeof(*d) || !d->bControlSize)
return false;
/* at least bmaControls(0) for master channel + iFeature */
return d->bLength >= sizeof(*d) + d->bControlSize + 1;
}
static bool validate_uac2_feature_unit(const void *p,
const struct usb_desc_validator *v)
{
const struct uac2_feature_unit_descriptor *d = p;
if (d->bLength < sizeof(*d))
return false;
/* at least bmaControls(0) for master channel + iFeature */
return d->bLength >= sizeof(*d) + 4 + 1;
}
static bool validate_uac3_feature_unit(const void *p,
const struct usb_desc_validator *v)
{
const struct uac3_feature_unit_descriptor *d = p;
if (d->bLength < sizeof(*d))
return false;
/* at least bmaControls(0) for master channel + wFeatureDescrStr */
return d->bLength >= sizeof(*d) + 4 + 2;
}
static bool validate_midi_out_jack(const void *p,
const struct usb_desc_validator *v)
{
const struct usb_midi_out_jack_descriptor *d = p;
return d->bLength >= sizeof(*d) &&
d->bLength >= sizeof(*d) + d->bNrInputPins * 2;
}
#define FIXED(p, t, s) { .protocol = (p), .type = (t), .size = sizeof(s) }
#define FUNC(p, t, f) { .protocol = (p), .type = (t), .func = (f) }
static struct usb_desc_validator audio_validators[] = {
/* UAC1 */
FUNC(UAC_VERSION_1, UAC_HEADER, validate_uac1_header),
FIXED(UAC_VERSION_1, UAC_INPUT_TERMINAL,
struct uac_input_terminal_descriptor),
FIXED(UAC_VERSION_1, UAC_OUTPUT_TERMINAL,
struct uac1_output_terminal_descriptor),
FUNC(UAC_VERSION_1, UAC_MIXER_UNIT, validate_mixer_unit),
FUNC(UAC_VERSION_1, UAC_SELECTOR_UNIT, validate_selector_unit),
FUNC(UAC_VERSION_1, UAC_FEATURE_UNIT, validate_uac1_feature_unit),
FUNC(UAC_VERSION_1, UAC1_PROCESSING_UNIT, validate_processing_unit),
FUNC(UAC_VERSION_1, UAC1_EXTENSION_UNIT, validate_processing_unit),
/* UAC2 */
FIXED(UAC_VERSION_2, UAC_HEADER, struct uac2_ac_header_descriptor),
FIXED(UAC_VERSION_2, UAC_INPUT_TERMINAL,
struct uac2_input_terminal_descriptor),
FIXED(UAC_VERSION_2, UAC_OUTPUT_TERMINAL,
struct uac2_output_terminal_descriptor),
FUNC(UAC_VERSION_2, UAC_MIXER_UNIT, validate_mixer_unit),
FUNC(UAC_VERSION_2, UAC_SELECTOR_UNIT, validate_selector_unit),
FUNC(UAC_VERSION_2, UAC_FEATURE_UNIT, validate_uac2_feature_unit),
/* UAC_VERSION_2, UAC2_EFFECT_UNIT: not implemented yet */
FUNC(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2, validate_processing_unit),
FUNC(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2, validate_processing_unit),
FIXED(UAC_VERSION_2, UAC2_CLOCK_SOURCE,
struct uac_clock_source_descriptor),
FUNC(UAC_VERSION_2, UAC2_CLOCK_SELECTOR, validate_selector_unit),
FIXED(UAC_VERSION_2, UAC2_CLOCK_MULTIPLIER,
struct uac_clock_multiplier_descriptor),
/* UAC_VERSION_2, UAC2_SAMPLE_RATE_CONVERTER: not implemented yet */
/* UAC3 */
FIXED(UAC_VERSION_2, UAC_HEADER, struct uac3_ac_header_descriptor),
FIXED(UAC_VERSION_3, UAC_INPUT_TERMINAL,
struct uac3_input_terminal_descriptor),
FIXED(UAC_VERSION_3, UAC_OUTPUT_TERMINAL,
struct uac3_output_terminal_descriptor),
/* UAC_VERSION_3, UAC3_EXTENDED_TERMINAL: not implemented yet */
FUNC(UAC_VERSION_3, UAC3_MIXER_UNIT, validate_mixer_unit),
FUNC(UAC_VERSION_3, UAC3_SELECTOR_UNIT, validate_selector_unit),
FUNC(UAC_VERSION_3, UAC_FEATURE_UNIT, validate_uac3_feature_unit),
/* UAC_VERSION_3, UAC3_EFFECT_UNIT: not implemented yet */
FUNC(UAC_VERSION_3, UAC3_PROCESSING_UNIT, validate_processing_unit),
FUNC(UAC_VERSION_3, UAC3_EXTENSION_UNIT, validate_processing_unit),
FIXED(UAC_VERSION_3, UAC3_CLOCK_SOURCE,
struct uac3_clock_source_descriptor),
FUNC(UAC_VERSION_3, UAC3_CLOCK_SELECTOR, validate_selector_unit),
FIXED(UAC_VERSION_3, UAC3_CLOCK_MULTIPLIER,
struct uac3_clock_multiplier_descriptor),
/* UAC_VERSION_3, UAC3_SAMPLE_RATE_CONVERTER: not implemented yet */
/* UAC_VERSION_3, UAC3_CONNECTORS: not implemented yet */
{ } /* terminator */
};
static struct usb_desc_validator midi_validators[] = {
FIXED(UAC_VERSION_ALL, USB_MS_HEADER,
struct usb_ms_header_descriptor),
FIXED(UAC_VERSION_ALL, USB_MS_MIDI_IN_JACK,
struct usb_midi_in_jack_descriptor),
FUNC(UAC_VERSION_ALL, USB_MS_MIDI_OUT_JACK,
validate_midi_out_jack),
{ } /* terminator */
};
/* Validate the given unit descriptor, return true if it's OK */
static bool validate_desc(unsigned char *hdr, int protocol,
const struct usb_desc_validator *v)
{
if (hdr[1] != USB_DT_CS_INTERFACE)
return true; /* don't care */
for (; v->type; v++) {
if (v->type == hdr[2] &&
(v->protocol == UAC_VERSION_ALL ||
v->protocol == protocol)) {
if (v->func)
return v->func(hdr, v);
/* check for the fixed size */
return hdr[0] >= v->size;
}
}
return true; /* not matching, skip validation */
}
bool snd_usb_validate_audio_desc(void *p, int protocol)
{
return validate_desc(p, protocol, audio_validators);
}
bool snd_usb_validate_midi_desc(void *p)
{
return validate_desc(p, UAC_VERSION_1, midi_validators);
}
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