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: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
744f51e863
@ -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 @@ retry:
|
||||
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;
|
||||
|
@ -68,6 +68,7 @@ struct mixer_build {
|
||||
unsigned char *buffer;
|
||||
unsigned int buflen;
|
||||
DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS);
|
||||
DECLARE_BITMAP(termbitmap, MAX_ID_ELEMS);
|
||||
struct usb_audio_term oterm;
|
||||
const struct usbmix_name_map *map;
|
||||
const struct usbmix_selector_map *selector_map;
|
||||
@ -738,12 +739,6 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
|
||||
struct uac_mixer_unit_descriptor *desc)
|
||||
{
|
||||
int mu_channels;
|
||||
void *c;
|
||||
|
||||
if (desc->bLength < sizeof(*desc))
|
||||
return -EINVAL;
|
||||
if (!desc->bNrInPins)
|
||||
return -EINVAL;
|
||||
|
||||
switch (state->mixer->protocol) {
|
||||
case UAC_VERSION_1:
|
||||
@ -759,13 +754,6 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mu_channels)
|
||||
return 0;
|
||||
|
||||
c = uac_mixer_unit_bmControls(desc, state->mixer->protocol);
|
||||
if (c - (void *)desc + (mu_channels - 1) / 8 >= desc->bLength)
|
||||
return 0; /* no bmControls -> skip */
|
||||
|
||||
return mu_channels;
|
||||
}
|
||||
|
||||
@ -773,16 +761,27 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state,
|
||||
* parse the source unit recursively until it reaches to a terminal
|
||||
* or a branched unit.
|
||||
*/
|
||||
static int check_input_term(struct mixer_build *state, int id,
|
||||
static int __check_input_term(struct mixer_build *state, int id,
|
||||
struct usb_audio_term *term)
|
||||
{
|
||||
int protocol = state->mixer->protocol;
|
||||
int err;
|
||||
void *p1;
|
||||
unsigned char *hdr;
|
||||
|
||||
memset(term, 0, sizeof(*term));
|
||||
while ((p1 = find_audio_control_unit(state, id)) != NULL) {
|
||||
unsigned char *hdr = p1;
|
||||
for (;;) {
|
||||
/* a loop in the terminal chain? */
|
||||
if (test_and_set_bit(id, state->termbitmap))
|
||||
return -EINVAL;
|
||||
|
||||
p1 = find_audio_control_unit(state, id);
|
||||
if (!p1)
|
||||
break;
|
||||
if (!snd_usb_validate_audio_desc(p1, protocol))
|
||||
break; /* bad descriptor */
|
||||
|
||||
hdr = p1;
|
||||
term->id = id;
|
||||
|
||||
if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
|
||||
@ -800,7 +799,7 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||
|
||||
/* call recursively to verify that the
|
||||
* referenced clock entity is valid */
|
||||
err = check_input_term(state, d->bCSourceID, term);
|
||||
err = __check_input_term(state, d->bCSourceID, term);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -834,7 +833,7 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||
case UAC2_CLOCK_SELECTOR: {
|
||||
struct uac_selector_unit_descriptor *d = p1;
|
||||
/* call recursively to retrieve the channel info */
|
||||
err = check_input_term(state, d->baSourceID[0], term);
|
||||
err = __check_input_term(state, d->baSourceID[0], term);
|
||||
if (err < 0)
|
||||
return err;
|
||||
term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
|
||||
@ -897,7 +896,7 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||
|
||||
/* call recursively to verify that the
|
||||
* referenced clock entity is valid */
|
||||
err = check_input_term(state, d->bCSourceID, term);
|
||||
err = __check_input_term(state, d->bCSourceID, term);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -948,7 +947,7 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||
case UAC3_CLOCK_SELECTOR: {
|
||||
struct uac_selector_unit_descriptor *d = p1;
|
||||
/* call recursively to retrieve the channel info */
|
||||
err = check_input_term(state, d->baSourceID[0], term);
|
||||
err = __check_input_term(state, d->baSourceID[0], term);
|
||||
if (err < 0)
|
||||
return err;
|
||||
term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */
|
||||
@ -964,7 +963,7 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||
return -EINVAL;
|
||||
|
||||
/* call recursively to retrieve the channel info */
|
||||
err = check_input_term(state, d->baSourceID[0], term);
|
||||
err = __check_input_term(state, d->baSourceID[0], term);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -982,6 +981,15 @@ static int check_input_term(struct mixer_build *state, int id,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static int check_input_term(struct mixer_build *state, int id,
|
||||
struct usb_audio_term *term)
|
||||
{
|
||||
memset(term, 0, sizeof(*term));
|
||||
memset(state->termbitmap, 0, sizeof(state->termbitmap));
|
||||
return __check_input_term(state, id, term);
|
||||
}
|
||||
|
||||
/*
|
||||
* Feature Unit
|
||||
*/
|
||||
@ -1011,10 +1019,15 @@ static struct usb_feature_control_info audio_feature_info[] = {
|
||||
{ UAC2_FU_PHASE_INVERTER, "Phase Inverter Control", USB_MIXER_BOOLEAN, -1 },
|
||||
};
|
||||
|
||||
static void usb_mixer_elem_info_free(struct usb_mixer_elem_info *cval)
|
||||
{
|
||||
kfree(cval);
|
||||
}
|
||||
|
||||
/* private_free callback */
|
||||
void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl)
|
||||
{
|
||||
kfree(kctl->private_data);
|
||||
usb_mixer_elem_info_free(kctl->private_data);
|
||||
kctl->private_data = NULL;
|
||||
}
|
||||
|
||||
@ -1537,7 +1550,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
||||
|
||||
ctl_info = get_feature_control_info(control);
|
||||
if (!ctl_info) {
|
||||
kfree(cval);
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return;
|
||||
}
|
||||
if (mixer->protocol == UAC_VERSION_1)
|
||||
@ -1570,7 +1583,7 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
|
||||
|
||||
if (!kctl) {
|
||||
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
|
||||
kfree(cval);
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return;
|
||||
}
|
||||
kctl->private_free = snd_usb_mixer_elem_free;
|
||||
@ -1740,7 +1753,7 @@ static void build_connector_control(struct usb_mixer_interface *mixer,
|
||||
kctl = snd_ctl_new1(&usb_connector_ctl_ro, cval);
|
||||
if (!kctl) {
|
||||
usb_audio_err(mixer->chip, "cannot malloc kcontrol\n");
|
||||
kfree(cval);
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return;
|
||||
}
|
||||
get_connector_control_name(mixer, term, is_input, kctl->id.name,
|
||||
@ -1761,13 +1774,6 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
|
||||
if (state->mixer->protocol != UAC_VERSION_2)
|
||||
return -EINVAL;
|
||||
|
||||
if (hdr->bLength != sizeof(*hdr)) {
|
||||
usb_audio_dbg(state->chip,
|
||||
"Bogus clock source descriptor length of %d, ignoring.\n",
|
||||
hdr->bLength);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The only property of this unit we are interested in is the
|
||||
* clock source validity. If that isn't readable, just bail out.
|
||||
@ -1793,7 +1799,7 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid,
|
||||
kctl = snd_ctl_new1(&usb_bool_master_control_ctl_ro, cval);
|
||||
|
||||
if (!kctl) {
|
||||
kfree(cval);
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1826,62 +1832,20 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||
__u8 *bmaControls;
|
||||
|
||||
if (state->mixer->protocol == UAC_VERSION_1) {
|
||||
if (hdr->bLength < 7) {
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
|
||||
unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
csize = hdr->bControlSize;
|
||||
if (!csize) {
|
||||
usb_audio_dbg(state->chip,
|
||||
"unit %u: invalid bControlSize == 0\n",
|
||||
unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
channels = (hdr->bLength - 7) / csize - 1;
|
||||
bmaControls = hdr->bmaControls;
|
||||
if (hdr->bLength < 7 + csize) {
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
|
||||
unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (state->mixer->protocol == UAC_VERSION_2) {
|
||||
struct uac2_feature_unit_descriptor *ftr = _ftr;
|
||||
if (hdr->bLength < 6) {
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
|
||||
unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
csize = 4;
|
||||
channels = (hdr->bLength - 6) / 4 - 1;
|
||||
bmaControls = ftr->bmaControls;
|
||||
if (hdr->bLength < 6 + csize) {
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: invalid UAC_FEATURE_UNIT descriptor\n",
|
||||
unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else { /* UAC_VERSION_3 */
|
||||
struct uac3_feature_unit_descriptor *ftr = _ftr;
|
||||
|
||||
if (hdr->bLength < 7) {
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
|
||||
unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
csize = 4;
|
||||
channels = (ftr->bLength - 7) / 4 - 1;
|
||||
bmaControls = ftr->bmaControls;
|
||||
if (hdr->bLength < 7 + csize) {
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: invalid UAC3_FEATURE_UNIT descriptor\n",
|
||||
unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse the source unit */
|
||||
@ -1988,6 +1952,31 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid,
|
||||
* Mixer Unit
|
||||
*/
|
||||
|
||||
/* check whether the given in/out overflows bmMixerControls matrix */
|
||||
static bool mixer_bitmap_overflow(struct uac_mixer_unit_descriptor *desc,
|
||||
int protocol, int num_ins, int num_outs)
|
||||
{
|
||||
u8 *hdr = (u8 *)desc;
|
||||
u8 *c = uac_mixer_unit_bmControls(desc, protocol);
|
||||
size_t rest; /* remaining bytes after bmMixerControls */
|
||||
|
||||
switch (protocol) {
|
||||
case UAC_VERSION_1:
|
||||
default:
|
||||
rest = 1; /* iMixer */
|
||||
break;
|
||||
case UAC_VERSION_2:
|
||||
rest = 2; /* bmControls + iMixer */
|
||||
break;
|
||||
case UAC_VERSION_3:
|
||||
rest = 6; /* bmControls + wMixerDescrStr */
|
||||
break;
|
||||
}
|
||||
|
||||
/* overflow? */
|
||||
return c + (num_ins * num_outs + 7) / 8 + rest > hdr + hdr[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* build a mixer unit control
|
||||
*
|
||||
@ -2030,7 +2019,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state,
|
||||
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
|
||||
if (!kctl) {
|
||||
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
|
||||
kfree(cval);
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return;
|
||||
}
|
||||
kctl->private_free = snd_usb_mixer_elem_free;
|
||||
@ -2056,15 +2045,11 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid,
|
||||
|
||||
if (state->mixer->protocol == UAC_VERSION_2) {
|
||||
struct uac2_input_terminal_descriptor *d_v2 = raw_desc;
|
||||
if (d_v2->bLength < sizeof(*d_v2))
|
||||
return -EINVAL;
|
||||
control = UAC2_TE_CONNECTOR;
|
||||
term_id = d_v2->bTerminalID;
|
||||
bmctls = le16_to_cpu(d_v2->bmControls);
|
||||
} else if (state->mixer->protocol == UAC_VERSION_3) {
|
||||
struct uac3_input_terminal_descriptor *d_v3 = raw_desc;
|
||||
if (d_v3->bLength < sizeof(*d_v3))
|
||||
return -EINVAL;
|
||||
control = UAC3_TE_INSERTION;
|
||||
term_id = d_v3->bTerminalID;
|
||||
bmctls = le32_to_cpu(d_v3->bmControls);
|
||||
@ -2116,6 +2101,9 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
|
||||
if (err < 0)
|
||||
return err;
|
||||
num_ins += iterm.channels;
|
||||
if (mixer_bitmap_overflow(desc, state->mixer->protocol,
|
||||
num_ins, num_outs))
|
||||
break;
|
||||
for (; ich < num_ins; ich++) {
|
||||
int och, ich_has_controls = 0;
|
||||
|
||||
@ -2323,18 +2311,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
|
||||
const char *name = extension_unit ?
|
||||
"Extension Unit" : "Processing Unit";
|
||||
|
||||
if (desc->bLength < 13) {
|
||||
usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
num_ins = desc->bNrInPins;
|
||||
if (desc->bLength < 13 + num_ins ||
|
||||
desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) {
|
||||
usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_ins; i++) {
|
||||
err = parse_audio_unit(state, desc->baSourceID[i]);
|
||||
if (err < 0)
|
||||
@ -2425,7 +2402,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
|
||||
|
||||
kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
|
||||
if (!kctl) {
|
||||
kfree(cval);
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return -ENOMEM;
|
||||
}
|
||||
kctl->private_free = snd_usb_mixer_elem_free;
|
||||
@ -2563,7 +2540,7 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
|
||||
if (kctl->private_data) {
|
||||
struct usb_mixer_elem_info *cval = kctl->private_data;
|
||||
num_ins = cval->max;
|
||||
kfree(cval);
|
||||
usb_mixer_elem_info_free(cval);
|
||||
kctl->private_data = NULL;
|
||||
}
|
||||
if (kctl->private_value) {
|
||||
@ -2589,13 +2566,6 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
||||
const struct usbmix_name_map *map;
|
||||
char **namelist;
|
||||
|
||||
if (desc->bLength < 5 || !desc->bNrInPins ||
|
||||
desc->bLength < 5 + desc->bNrInPins) {
|
||||
usb_audio_err(state->chip,
|
||||
"invalid SELECTOR UNIT descriptor %d\n", unitid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < desc->bNrInPins; i++) {
|
||||
err = parse_audio_unit(state, desc->baSourceID[i]);
|
||||
if (err < 0)
|
||||
@ -2635,10 +2605,10 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
||||
break;
|
||||
}
|
||||
|
||||
namelist = kmalloc_array(desc->bNrInPins, sizeof(char *), GFP_KERNEL);
|
||||
namelist = kcalloc(desc->bNrInPins, sizeof(char *), GFP_KERNEL);
|
||||
if (!namelist) {
|
||||
kfree(cval);
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto error_cval;
|
||||
}
|
||||
#define MAX_ITEM_NAME_LEN 64
|
||||
for (i = 0; i < desc->bNrInPins; i++) {
|
||||
@ -2646,11 +2616,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
||||
len = 0;
|
||||
namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
|
||||
if (!namelist[i]) {
|
||||
while (i--)
|
||||
kfree(namelist[i]);
|
||||
kfree(namelist);
|
||||
kfree(cval);
|
||||
return -ENOMEM;
|
||||
err = -ENOMEM;
|
||||
goto error_name;
|
||||
}
|
||||
len = check_mapped_selector_name(state, unitid, i, namelist[i],
|
||||
MAX_ITEM_NAME_LEN);
|
||||
@ -2664,10 +2631,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
||||
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
|
||||
if (! kctl) {
|
||||
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
|
||||
for (i = 0; i < desc->bNrInPins; i++)
|
||||
kfree(namelist[i]);
|
||||
kfree(namelist);
|
||||
kfree(cval);
|
||||
err = -ENOMEM;
|
||||
goto error_name;
|
||||
return -ENOMEM;
|
||||
}
|
||||
kctl->private_value = (unsigned long)namelist;
|
||||
@ -2714,6 +2679,14 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
|
||||
usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n",
|
||||
cval->head.id, kctl->id.name, desc->bNrInPins);
|
||||
return snd_usb_mixer_add_control(&cval->head, kctl);
|
||||
|
||||
error_name:
|
||||
for (i = 0; i < desc->bNrInPins; i++)
|
||||
kfree(namelist[i]);
|
||||
kfree(namelist);
|
||||
error_cval:
|
||||
usb_mixer_elem_info_free(cval);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2734,62 +2707,50 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) {
|
||||
switch (p1[2]) {
|
||||
case UAC_INPUT_TERMINAL:
|
||||
return parse_audio_input_terminal(state, unitid, p1);
|
||||
case UAC_MIXER_UNIT:
|
||||
return parse_audio_mixer_unit(state, unitid, p1);
|
||||
case UAC2_CLOCK_SOURCE:
|
||||
return parse_clock_source_unit(state, unitid, p1);
|
||||
case UAC_SELECTOR_UNIT:
|
||||
case UAC2_CLOCK_SELECTOR:
|
||||
return parse_audio_selector_unit(state, unitid, p1);
|
||||
case UAC_FEATURE_UNIT:
|
||||
return parse_audio_feature_unit(state, unitid, p1);
|
||||
case UAC1_PROCESSING_UNIT:
|
||||
/* UAC2_EFFECT_UNIT has the same value */
|
||||
if (protocol == UAC_VERSION_1)
|
||||
return parse_audio_processing_unit(state, unitid, p1);
|
||||
else
|
||||
return 0; /* FIXME - effect units not implemented yet */
|
||||
case UAC1_EXTENSION_UNIT:
|
||||
/* UAC2_PROCESSING_UNIT_V2 has the same value */
|
||||
if (protocol == UAC_VERSION_1)
|
||||
return parse_audio_extension_unit(state, unitid, p1);
|
||||
else /* UAC_VERSION_2 */
|
||||
return parse_audio_processing_unit(state, unitid, p1);
|
||||
case UAC2_EXTENSION_UNIT_V2:
|
||||
return parse_audio_extension_unit(state, unitid, p1);
|
||||
default:
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else { /* UAC_VERSION_3 */
|
||||
switch (p1[2]) {
|
||||
case UAC_INPUT_TERMINAL:
|
||||
return parse_audio_input_terminal(state, unitid, p1);
|
||||
case UAC3_MIXER_UNIT:
|
||||
return parse_audio_mixer_unit(state, unitid, p1);
|
||||
case UAC3_CLOCK_SOURCE:
|
||||
return parse_clock_source_unit(state, unitid, p1);
|
||||
case UAC3_SELECTOR_UNIT:
|
||||
case UAC3_CLOCK_SELECTOR:
|
||||
return parse_audio_selector_unit(state, unitid, p1);
|
||||
case UAC3_FEATURE_UNIT:
|
||||
return parse_audio_feature_unit(state, unitid, p1);
|
||||
case UAC3_EFFECT_UNIT:
|
||||
return 0; /* FIXME - effect units not implemented yet */
|
||||
case UAC3_PROCESSING_UNIT:
|
||||
return parse_audio_processing_unit(state, unitid, p1);
|
||||
case UAC3_EXTENSION_UNIT:
|
||||
return parse_audio_extension_unit(state, unitid, p1);
|
||||
default:
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!snd_usb_validate_audio_desc(p1, protocol)) {
|
||||
usb_audio_dbg(state->chip, "invalid unit %d\n", unitid);
|
||||
return 0; /* skip invalid unit */
|
||||
}
|
||||
|
||||
#define PTYPE(a, b) ((a) << 8 | (b))
|
||||
switch (PTYPE(protocol, p1[2])) {
|
||||
case PTYPE(UAC_VERSION_1, UAC_INPUT_TERMINAL):
|
||||
case PTYPE(UAC_VERSION_2, UAC_INPUT_TERMINAL):
|
||||
case PTYPE(UAC_VERSION_3, UAC_INPUT_TERMINAL):
|
||||
return parse_audio_input_terminal(state, unitid, p1);
|
||||
case PTYPE(UAC_VERSION_1, UAC_MIXER_UNIT):
|
||||
case PTYPE(UAC_VERSION_2, UAC_MIXER_UNIT):
|
||||
case PTYPE(UAC_VERSION_3, UAC3_MIXER_UNIT):
|
||||
return parse_audio_mixer_unit(state, unitid, p1);
|
||||
case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SOURCE):
|
||||
case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SOURCE):
|
||||
return parse_clock_source_unit(state, unitid, p1);
|
||||
case PTYPE(UAC_VERSION_1, UAC_SELECTOR_UNIT):
|
||||
case PTYPE(UAC_VERSION_2, UAC_SELECTOR_UNIT):
|
||||
case PTYPE(UAC_VERSION_3, UAC3_SELECTOR_UNIT):
|
||||
case PTYPE(UAC_VERSION_2, UAC2_CLOCK_SELECTOR):
|
||||
case PTYPE(UAC_VERSION_3, UAC3_CLOCK_SELECTOR):
|
||||
return parse_audio_selector_unit(state, unitid, p1);
|
||||
case PTYPE(UAC_VERSION_1, UAC_FEATURE_UNIT):
|
||||
case PTYPE(UAC_VERSION_2, UAC_FEATURE_UNIT):
|
||||
case PTYPE(UAC_VERSION_3, UAC3_FEATURE_UNIT):
|
||||
return parse_audio_feature_unit(state, unitid, p1);
|
||||
case PTYPE(UAC_VERSION_1, UAC1_PROCESSING_UNIT):
|
||||
case PTYPE(UAC_VERSION_2, UAC2_PROCESSING_UNIT_V2):
|
||||
case PTYPE(UAC_VERSION_3, UAC3_PROCESSING_UNIT):
|
||||
return parse_audio_processing_unit(state, unitid, p1);
|
||||
case PTYPE(UAC_VERSION_1, UAC1_EXTENSION_UNIT):
|
||||
case PTYPE(UAC_VERSION_2, UAC2_EXTENSION_UNIT_V2):
|
||||
case PTYPE(UAC_VERSION_3, UAC3_EXTENSION_UNIT):
|
||||
return parse_audio_extension_unit(state, unitid, p1);
|
||||
case PTYPE(UAC_VERSION_2, UAC2_EFFECT_UNIT):
|
||||
case PTYPE(UAC_VERSION_3, UAC3_EFFECT_UNIT):
|
||||
return 0; /* FIXME - effect units not implemented yet */
|
||||
default:
|
||||
usb_audio_err(state->chip,
|
||||
"unit %u: unexpected type 0x%02x\n",
|
||||
unitid, p1[2]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3104,11 +3065,12 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
||||
while ((p = snd_usb_find_csint_desc(mixer->hostif->extra,
|
||||
mixer->hostif->extralen,
|
||||
p, UAC_OUTPUT_TERMINAL)) != NULL) {
|
||||
if (!snd_usb_validate_audio_desc(p, mixer->protocol))
|
||||
continue; /* skip invalid descriptor */
|
||||
|
||||
if (mixer->protocol == UAC_VERSION_1) {
|
||||
struct uac1_output_terminal_descriptor *desc = p;
|
||||
|
||||
if (desc->bLength < sizeof(*desc))
|
||||
continue; /* invalid descriptor? */
|
||||
/* mark terminal ID as visited */
|
||||
set_bit(desc->bTerminalID, state.unitbitmap);
|
||||
state.oterm.id = desc->bTerminalID;
|
||||
@ -3120,8 +3082,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
||||
} else if (mixer->protocol == UAC_VERSION_2) {
|
||||
struct uac2_output_terminal_descriptor *desc = p;
|
||||
|
||||
if (desc->bLength < sizeof(*desc))
|
||||
continue; /* invalid descriptor? */
|
||||
/* mark terminal ID as visited */
|
||||
set_bit(desc->bTerminalID, state.unitbitmap);
|
||||
state.oterm.id = desc->bTerminalID;
|
||||
@ -3147,8 +3107,6 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
|
||||
} else { /* UAC_VERSION_3 */
|
||||
struct uac3_output_terminal_descriptor *desc = p;
|
||||
|
||||
if (desc->bLength < sizeof(*desc))
|
||||
continue; /* invalid descriptor? */
|
||||
/* mark terminal ID as visited */
|
||||
set_bit(desc->bTerminalID, state.unitbitmap);
|
||||
state.oterm.id = desc->bTerminalID;
|
||||
|
@ -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;
|
||||
|
332
sound/usb/validate.c
Normal file
332
sound/usb/validate.c
Normal file
@ -0,0 +1,332 @@
|
||||
// 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user