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, ...@@ -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; packets_per_page = PAGE_SIZE / packet_size;
if (WARN_ON(!packets_per_page)) { if (WARN_ON(!packets_per_page)) {
err = -EINVAL; err = -EINVAL;
goto error; goto err_packets;
} }
pages = DIV_ROUND_UP(count, packets_per_page); pages = DIV_ROUND_UP(count, packets_per_page);
......
...@@ -598,11 +598,9 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) ...@@ -598,11 +598,9 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
} }
runtime->private_data = azx_dev; 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; 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_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max; runtime->hw.channels_max = hinfo->channels_max;
runtime->hw.formats = hinfo->formats; runtime->hw.formats = hinfo->formats;
...@@ -615,6 +613,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) ...@@ -615,6 +613,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
20, 20,
178000000); 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) if (chip->align_buffer_size)
/* constrain buffer sizes to be multiple of 128 /* constrain buffer sizes to be multiple of 128
bytes. This is more efficient in terms of memory bytes. This is more efficient in terms of memory
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
/* 14 unused */ /* 14 unused */
#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ #define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ #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_NO_64BIT (1 << 18) /* No 64bit address */
#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ #define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */
#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ #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) ...@@ -6051,6 +6051,24 @@ void snd_hda_gen_free(struct hda_codec *codec)
} }
EXPORT_SYMBOL_GPL(snd_hda_gen_free); 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 #ifdef CONFIG_PM
/** /**
* snd_hda_gen_check_power_status - check the loopback power save state * 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 = { ...@@ -6078,6 +6096,7 @@ static const struct hda_codec_ops generic_patch_ops = {
.init = snd_hda_gen_init, .init = snd_hda_gen_init,
.free = snd_hda_gen_free, .free = snd_hda_gen_free,
.unsol_event = snd_hda_jack_unsol_event, .unsol_event = snd_hda_jack_unsol_event,
.reboot_notify = snd_hda_gen_reboot_notify,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.check_power_status = snd_hda_gen_check_power_status, .check_power_status = snd_hda_gen_check_power_status,
#endif #endif
...@@ -6100,7 +6119,7 @@ static int snd_hda_parse_generic_codec(struct hda_codec *codec) ...@@ -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); err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
if (err < 0) if (err < 0)
return err; goto error;
err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
if (err < 0) if (err < 0)
......
...@@ -332,6 +332,7 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, ...@@ -332,6 +332,7 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg); struct auto_pin_cfg *cfg);
int snd_hda_gen_build_controls(struct hda_codec *codec); int snd_hda_gen_build_controls(struct hda_codec *codec);
int snd_hda_gen_build_pcms(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 */ /* standard jack event callbacks */
void snd_hda_gen_hp_automute(struct hda_codec *codec, void snd_hda_gen_hp_automute(struct hda_codec *codec,
......
...@@ -65,6 +65,7 @@ enum { ...@@ -65,6 +65,7 @@ enum {
POS_FIX_VIACOMBO, POS_FIX_VIACOMBO,
POS_FIX_COMBO, POS_FIX_COMBO,
POS_FIX_SKL, POS_FIX_SKL,
POS_FIX_FIFO,
}; };
/* Defines for ATI HD Audio support in SB450 south bridge */ /* Defines for ATI HD Audio support in SB450 south bridge */
...@@ -135,7 +136,7 @@ module_param_array(model, charp, NULL, 0444); ...@@ -135,7 +136,7 @@ module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model."); MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444); module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "DMA pointer read method." 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_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444); module_param_array(probe_mask, int, NULL, 0444);
...@@ -335,6 +336,11 @@ enum { ...@@ -335,6 +336,11 @@ enum {
#define AZX_DCAPS_PRESET_ATI_HDMI_NS \ #define AZX_DCAPS_PRESET_ATI_HDMI_NS \
(AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF) (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 */ /* quirks for Nvidia */
#define AZX_DCAPS_PRESET_NVIDIA \ #define AZX_DCAPS_PRESET_NVIDIA \
(AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\ (AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\
...@@ -841,6 +847,49 @@ static unsigned int azx_via_get_position(struct azx *chip, ...@@ -841,6 +847,49 @@ static unsigned int azx_via_get_position(struct azx *chip,
return bound_pos + mod_dma_pos; 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, static unsigned int azx_skl_get_dpib_pos(struct azx *chip,
struct azx_dev *azx_dev) struct azx_dev *azx_dev)
{ {
...@@ -1417,6 +1466,7 @@ static int check_position_fix(struct azx *chip, int fix) ...@@ -1417,6 +1466,7 @@ static int check_position_fix(struct azx *chip, int fix)
case POS_FIX_VIACOMBO: case POS_FIX_VIACOMBO:
case POS_FIX_COMBO: case POS_FIX_COMBO:
case POS_FIX_SKL: case POS_FIX_SKL:
case POS_FIX_FIFO:
return fix; return fix;
} }
...@@ -1433,6 +1483,10 @@ static int check_position_fix(struct azx *chip, int 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"); dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n");
return POS_FIX_VIACOMBO; 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) { if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) {
dev_dbg(chip->card->dev, "Using LPIB position fix\n"); dev_dbg(chip->card->dev, "Using LPIB position fix\n");
return POS_FIX_LPIB; return POS_FIX_LPIB;
...@@ -1453,6 +1507,7 @@ static void assign_position_fix(struct azx *chip, int fix) ...@@ -1453,6 +1507,7 @@ static void assign_position_fix(struct azx *chip, int fix)
[POS_FIX_VIACOMBO] = azx_via_get_position, [POS_FIX_VIACOMBO] = azx_via_get_position,
[POS_FIX_COMBO] = azx_get_pos_lpib, [POS_FIX_COMBO] = azx_get_pos_lpib,
[POS_FIX_SKL] = azx_get_pos_skl, [POS_FIX_SKL] = azx_get_pos_skl,
[POS_FIX_FIFO] = azx_get_pos_fifo,
}; };
chip->get_position[0] = chip->get_position[1] = callbacks[fix]; chip->get_position[0] = chip->get_position[1] = callbacks[fix];
...@@ -1467,6 +1522,9 @@ static void assign_position_fix(struct azx *chip, int fix) ...@@ -1467,6 +1522,9 @@ static void assign_position_fix(struct azx *chip, int fix)
azx_get_delay_from_lpib; 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[] = { ...@@ -2416,6 +2474,12 @@ static const struct pci_device_id azx_ids[] = {
/* AMD Hudson */ /* AMD Hudson */
{ PCI_DEVICE(0x1022, 0x780d), { PCI_DEVICE(0x1022, 0x780d),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, .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 */ /* AMD Stoney */
{ PCI_DEVICE(0x1022, 0x157a), { PCI_DEVICE(0x1022, 0x157a),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB | .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB |
......
...@@ -1175,6 +1175,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = { ...@@ -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(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ), SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ),
SND_PCI_QUIRK(0x1102, 0x0023, "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(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ),
SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", 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) ...@@ -163,23 +163,10 @@ static void cx_auto_reboot_notify(struct hda_codec *codec)
{ {
struct conexant_spec *spec = codec->spec; 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 /* Turn the problematic codec into D3 to avoid spurious noises
from the internal speaker during (and after) reboot */ from the internal speaker during (and after) reboot */
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
snd_hda_gen_reboot_notify(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);
} }
static void cx_auto_free(struct hda_codec *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, ...@@ -624,18 +611,20 @@ static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
/* update LED status via GPIO */ /* update LED status via GPIO */
static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask, static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask,
bool enabled) bool led_on)
{ {
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
unsigned int oldval = spec->gpio_led; unsigned int oldval = spec->gpio_led;
if (spec->mute_led_polarity) if (spec->mute_led_polarity)
enabled = !enabled; led_on = !led_on;
if (enabled) if (led_on)
spec->gpio_led &= ~mask;
else
spec->gpio_led |= mask; 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) if (spec->gpio_led != oldval)
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
spec->gpio_led); spec->gpio_led);
...@@ -646,8 +635,8 @@ static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled) ...@@ -646,8 +635,8 @@ static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled)
{ {
struct hda_codec *codec = private_data; struct hda_codec *codec = private_data;
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
/* muted -> LED on */
cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled); cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, !enabled);
} }
/* turn on/off mic-mute LED via GPIO per capture hook */ /* 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, ...@@ -669,7 +658,6 @@ static void cxt_fixup_mute_led_gpio(struct hda_codec *codec,
{ 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03 }, { 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) { if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook; spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook;
......
...@@ -869,15 +869,6 @@ static void alc_reboot_notify(struct hda_codec *codec) ...@@ -869,15 +869,6 @@ static void alc_reboot_notify(struct hda_codec *codec)
alc_shutup(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 #define alc_free snd_hda_gen_free
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -5222,7 +5213,7 @@ static void alc_fixup_tpt440_dock(struct hda_codec *codec, ...@@ -5222,7 +5213,7 @@ static void alc_fixup_tpt440_dock(struct hda_codec *codec,
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) { 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; spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
codec->power_save_node = 0; /* avoid click noises */ codec->power_save_node = 0; /* avoid click noises */
snd_hda_apply_pincfgs(codec, pincfgs); snd_hda_apply_pincfgs(codec, pincfgs);
...@@ -7064,6 +7055,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { ...@@ -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, 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, 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, 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, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), 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 ...@@ -275,7 +275,8 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
goto retry; goto retry;
} }
spin_unlock(&sound_loader_lock); spin_unlock(&sound_loader_lock);
return -EBUSY; r = -EBUSY;
goto fail;
} }
} }
......
...@@ -17,7 +17,8 @@ snd-usb-audio-objs := card.o \ ...@@ -17,7 +17,8 @@ snd-usb-audio-objs := card.o \
power.o \ power.o \
proc.o \ proc.o \
quirks.o \ quirks.o \
stream.o stream.o \
validate.o
snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.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, ...@@ -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) static bool validate_clock_source_v2(void *p, int id)
{ {
struct uac_clock_source_descriptor *cs = p; 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) static bool validate_clock_source_v3(void *p, int id)
{ {
struct uac3_clock_source_descriptor *cs = p; 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) static bool validate_clock_selector_v2(void *p, int id)
{ {
struct uac_clock_selector_descriptor *cs = p; struct uac_clock_selector_descriptor *cs = p;
return cs->bLength >= sizeof(*cs) && cs->bClockID == id && return cs->bClockID == id;
cs->bLength == 7 + cs->bNrInPins;
} }
static bool validate_clock_selector_v3(void *p, int id) static bool validate_clock_selector_v3(void *p, int id)
{ {
struct uac3_clock_selector_descriptor *cs = p; struct uac3_clock_selector_descriptor *cs = p;
return cs->bLength >= sizeof(*cs) && cs->bClockID == id && return cs->bClockID == id;
cs->bLength == 11 + cs->bNrInPins;
} }
static bool validate_clock_multiplier_v2(void *p, int id) static bool validate_clock_multiplier_v2(void *p, int id)
{ {
struct uac_clock_multiplier_descriptor *cs = p; 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) static bool validate_clock_multiplier_v3(void *p, int id)
{ {
struct uac3_clock_multiplier_descriptor *cs = p; 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) \ #define DEFINE_FIND_HELPER(name, obj, validator, type) \
......
...@@ -31,4 +31,8 @@ static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip) ...@@ -31,4 +31,8 @@ static inline int snd_usb_ctrl_intf(struct snd_usb_audio *chip)
return get_iface_desc(chip->ctrl_intf)->bInterfaceNumber; 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 */ #endif /* __USBAUDIO_HELPER_H */
...@@ -600,14 +600,13 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) ...@@ -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, ret = hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP,
hiface_pcm_out_urb_handler); hiface_pcm_out_urb_handler);
if (ret < 0) if (ret < 0)
return ret; goto error;
} }
ret = snd_pcm_new(chip->card, "USB-SPDIF Audio", 0, 1, 0, &pcm); ret = snd_pcm_new(chip->card, "USB-SPDIF Audio", 0, 1, 0, &pcm);
if (ret < 0) { if (ret < 0) {
kfree(rt);
dev_err(&chip->dev->dev, "Cannot create pcm instance\n"); dev_err(&chip->dev->dev, "Cannot create pcm instance\n");
return ret; goto error;
} }
pcm->private_data = rt; pcm->private_data = rt;
...@@ -620,4 +619,10 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) ...@@ -620,4 +619,10 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
chip->pcm = rt; chip->pcm = rt;
return 0; 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, ...@@ -550,6 +550,15 @@ int line6_init_pcm(struct usb_line6 *line6,
line6pcm->volume_monitor = 255; line6pcm->volume_monitor = 255;
line6pcm->line6 = line6; 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 = line6pcm->max_packet_size_in =
usb_maxpacket(line6->usbdev, usb_maxpacket(line6->usbdev,
usb_rcvisocpipe(line6->usbdev, ep_read), 0); usb_rcvisocpipe(line6->usbdev, ep_read), 0);
...@@ -562,15 +571,6 @@ int line6_init_pcm(struct usb_line6 *line6, ...@@ -562,15 +571,6 @@ int line6_init_pcm(struct usb_line6 *line6,
return -EINVAL; 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); err = line6_create_audio_out_urbs(line6pcm);
if (err < 0) if (err < 0)
return err; return err;
......
This diff is collapsed.
...@@ -1156,17 +1156,17 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, ...@@ -1156,17 +1156,17 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
{ {
struct usb_mixer_interface *mixer; struct usb_mixer_interface *mixer;
struct usb_mixer_elem_info *cval; 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) { list_for_each_entry(mixer, &chip->mixer_list, list) {
cval = mixer_elem_list_to_info(mixer->id_elems[unitid]); if (mixer->id_elems[unitid]) {
if (cval) { cval = mixer_elem_list_to_info(mixer->id_elems[unitid]);
snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
cval->control << 8, cval->control << 8,
samplerate_id); samplerate_id);
snd_usb_mixer_notify_id(mixer, unitid); 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, ...@@ -339,6 +339,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
ep = 0x81; ep = 0x81;
ifnum = 2; ifnum = 2;
goto add_sync_ep_from_ifnum; goto add_sync_ep_from_ifnum;
case USB_ID(0x1397, 0x0001): /* Behringer UFX1604 */
case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */
ep = 0x81; ep = 0x81;
ifnum = 1; ifnum = 1;
......
...@@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface, ...@@ -31,6 +31,8 @@ snd_usb_find_power_domain(struct usb_host_interface *ctrl_iface,
struct uac3_power_domain_descriptor *pd_desc = p; struct uac3_power_domain_descriptor *pd_desc = p;
int i; int i;
if (!snd_usb_validate_audio_desc(p, UAC_VERSION_3))
continue;
for (i = 0; i < pd_desc->bNrEntities; i++) { for (i = 0; i < pd_desc->bNrEntities; i++) {
if (pd_desc->baEntityID[i] == id) { if (pd_desc->baEntityID[i] == id) {
pd->pd_id = pd_desc->bPowerDomainID; pd->pd_id = pd_desc->bPowerDomainID;
......
...@@ -248,6 +248,9 @@ static int create_yamaha_midi_quirk(struct snd_usb_audio *chip, ...@@ -248,6 +248,9 @@ static int create_yamaha_midi_quirk(struct snd_usb_audio *chip,
NULL, USB_MS_MIDI_OUT_JACK); NULL, USB_MS_MIDI_OUT_JACK);
if (!injd && !outjd) if (!injd && !outjd)
return -ENODEV; return -ENODEV;
if (!snd_usb_validate_midi_desc(injd) ||
!snd_usb_validate_midi_desc(outjd))
return -ENODEV;
if (injd && (injd->bLength < 5 || if (injd && (injd->bLength < 5 ||
(injd->bJackType != USB_MS_EMBEDDED && (injd->bJackType != USB_MS_EMBEDDED &&
injd->bJackType != USB_MS_EXTERNAL))) injd->bJackType != USB_MS_EXTERNAL)))
......
...@@ -632,16 +632,14 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, ...@@ -632,16 +632,14 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
*/ */
static void * static void *
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, 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; 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, while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen, ctrl_iface->extralen,
term, UAC_INPUT_TERMINAL))) { term, UAC_INPUT_TERMINAL))) {
if (term->bLength < minlen) if (!snd_usb_validate_audio_desc(term, protocol))
continue; continue;
if (term->bTerminalID == terminal_id) if (term->bTerminalID == terminal_id)
return term; return term;
...@@ -652,7 +650,7 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, ...@@ -652,7 +650,7 @@ snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
static void * static void *
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, 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 */ /* OK to use with both UAC2 and UAC3 */
struct uac2_output_terminal_descriptor *term = NULL; struct uac2_output_terminal_descriptor *term = NULL;
...@@ -660,8 +658,9 @@ snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface, ...@@ -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, while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
ctrl_iface->extralen, ctrl_iface->extralen,
term, UAC_OUTPUT_TERMINAL))) { term, UAC_OUTPUT_TERMINAL))) {
if (term->bLength >= sizeof(*term) && if (!snd_usb_validate_audio_desc(term, protocol))
term->bTerminalID == terminal_id) continue;
if (term->bTerminalID == terminal_id)
return term; return term;
} }
...@@ -736,7 +735,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, ...@@ -736,7 +735,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip,
iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink, as->bTerminalLink,
false); protocol);
if (iterm) { if (iterm) {
num_channels = iterm->bNrChannels; num_channels = iterm->bNrChannels;
chconfig = le16_to_cpu(iterm->wChannelConfig); chconfig = le16_to_cpu(iterm->wChannelConfig);
...@@ -772,7 +771,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, ...@@ -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, input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink, as->bTerminalLink,
true); protocol);
if (input_term) { if (input_term) {
clock = input_term->bCSourceID; clock = input_term->bCSourceID;
if (!chconfig && (num_channels == input_term->bNrChannels)) if (!chconfig && (num_channels == input_term->bNrChannels))
...@@ -781,7 +780,8 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, ...@@ -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, output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink); as->bTerminalLink,
protocol);
if (output_term) { if (output_term) {
clock = output_term->bCSourceID; clock = output_term->bCSourceID;
goto found_clock; goto found_clock;
...@@ -1006,14 +1006,15 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, ...@@ -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, input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink, as->bTerminalLink,
true); UAC_VERSION_3);
if (input_term) { if (input_term) {
clock = input_term->bCSourceID; clock = input_term->bCSourceID;
goto found_clock; goto found_clock;
} }
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
as->bTerminalLink); as->bTerminalLink,
UAC_VERSION_3);
if (output_term) { if (output_term) {
clock = output_term->bCSourceID; clock = output_term->bCSourceID;
goto found_clock; 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