From 79289e24194a9d099bf18f200894832c5760cd83 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jan 2016 11:33:34 +0100 Subject: ALSA: usb-audio: Refer to chip->usb_id for quirks and MIDI creation This is a preliminary patch for the later change to allow a better quirk ID management. In the current USB-audio code, there are a few places looking at usb_device idVendor and idProduct fields directly even though we have already a static member in snd_usb_audio.usb_id. This patch modifies such codes to refer to the latter field. For achieving this, two slightly intensive changes have been done: - The snd_usb_audio object is set/reset via dev_getdrv() for the given USB device; it's needed for minimizing the changes for some existing quirks that take only usb_device object. - __snd_usbmidi_create() is introduced to receive the pre-given usb_id argument. The exported snd_usbmidi_create() is unchanged by calling this new function internally. Signed-off-by: Takashi Iwai --- sound/usb/card.c | 9 ++++++--- sound/usb/midi.c | 15 +++++++++------ sound/usb/midi.h | 14 ++++++++++++-- sound/usb/quirks.c | 36 +++++++++++++++++++++--------------- sound/usb/quirks.h | 3 ++- 5 files changed, 50 insertions(+), 27 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index 1f09d9591276..2c0269014b85 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -171,8 +171,9 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { - int err = snd_usbmidi_create(chip->card, iface, - &chip->midi_list, NULL); + int err = __snd_usbmidi_create(chip->card, iface, + &chip->midi_list, NULL, + chip->usb_id); if (err < 0) { dev_err(&dev->dev, "%u:%d: cannot create sequencer device\n", @@ -311,6 +312,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip) snd_usb_endpoint_free(ep); mutex_destroy(&chip->mutex); + dev_set_drvdata(&chip->dev->dev, NULL); kfree(chip); return 0; } @@ -484,7 +486,7 @@ static int usb_audio_probe(struct usb_interface *intf, if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) return -ENXIO; - err = snd_usb_apply_boot_quirk(dev, intf, quirk); + err = snd_usb_apply_boot_quirk(dev, intf, quirk, id); if (err < 0) return err; @@ -503,6 +505,7 @@ static int usb_audio_probe(struct usb_interface *intf, goto __error; } chip = usb_chip[i]; + dev_set_drvdata(&dev->dev, chip); atomic_inc(&chip->active); /* avoid autopm */ break; } diff --git a/sound/usb/midi.c b/sound/usb/midi.c index cc39f63299ef..b79875ebec1e 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2320,10 +2320,11 @@ EXPORT_SYMBOL(snd_usbmidi_resume); /* * Creates and registers everything needed for a MIDI streaming interface. */ -int snd_usbmidi_create(struct snd_card *card, - struct usb_interface *iface, - struct list_head *midi_list, - const struct snd_usb_audio_quirk *quirk) +int __snd_usbmidi_create(struct snd_card *card, + struct usb_interface *iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id) { struct snd_usb_midi *umidi; struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS]; @@ -2341,8 +2342,10 @@ int snd_usbmidi_create(struct snd_card *card, spin_lock_init(&umidi->disc_lock); init_rwsem(&umidi->disc_rwsem); mutex_init(&umidi->mutex); - umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), + if (!usb_id) + usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), le16_to_cpu(umidi->dev->descriptor.idProduct)); + umidi->usb_id = usb_id; setup_timer(&umidi->error_timer, snd_usbmidi_error_timer, (unsigned long)umidi); @@ -2464,4 +2467,4 @@ int snd_usbmidi_create(struct snd_card *card, list_add_tail(&umidi->list, midi_list); return 0; } -EXPORT_SYMBOL(snd_usbmidi_create); +EXPORT_SYMBOL(__snd_usbmidi_create); diff --git a/sound/usb/midi.h b/sound/usb/midi.h index ad8a3211f8e7..5e25a3fd6c1d 100644 --- a/sound/usb/midi.h +++ b/sound/usb/midi.h @@ -39,10 +39,20 @@ struct snd_usb_midi_endpoint_info { /* for QUIRK_MIDI_AKAI, data is NULL */ -int snd_usbmidi_create(struct snd_card *card, +int __snd_usbmidi_create(struct snd_card *card, + struct usb_interface *iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id); + +static inline int snd_usbmidi_create(struct snd_card *card, struct usb_interface *iface, struct list_head *midi_list, - const struct snd_usb_audio_quirk *quirk); + const struct snd_usb_audio_quirk *quirk) +{ + return __snd_usbmidi_create(card, iface, midi_list, quirk, 0); +} + void snd_usbmidi_input_stop(struct list_head *p); void snd_usbmidi_input_start(struct list_head *p); void snd_usbmidi_disconnect(struct list_head *p); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a75d9ce7d77a..16364d94f17b 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -446,8 +446,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, const struct snd_usb_audio_quirk *quirk = chip->usb_id == USB_ID(0x0582, 0x002b) ? &ua700_quirk : &uaxx_quirk; - return snd_usbmidi_create(chip->card, iface, - &chip->midi_list, quirk); + return __snd_usbmidi_create(chip->card, iface, + &chip->midi_list, quirk, + chip->usb_id); } if (altsd->bNumEndpoints != 1) @@ -974,11 +975,9 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int snd_usb_apply_boot_quirk(struct usb_device *dev, struct usb_interface *intf, - const struct snd_usb_audio_quirk *quirk) + const struct snd_usb_audio_quirk *quirk, + unsigned int id) { - u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - switch (id) { case USB_ID(0x041e, 0x3000): /* SB Extigy needs special boot-up sequence */ @@ -1182,7 +1181,7 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) * "Playback Design" products send bogus feedback data at the start * of the stream. Ignore them. */ - if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) && + if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba && ep->type == SND_USB_ENDPOINT_TYPE_SYNC) ep->skip_packets = 4; @@ -1201,11 +1200,15 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) void snd_usb_set_interface_quirk(struct usb_device *dev) { + struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev); + + if (!chip) + return; /* * "Playback Design" products need a 50ms delay after setting the * USB interface. */ - switch (le16_to_cpu(dev->descriptor.idVendor)) { + switch (USB_ID_VENDOR(chip->usb_id)) { case 0x23ba: /* Playback Design */ case 0x0644: /* TEAC Corp. */ mdelay(50); @@ -1213,15 +1216,20 @@ void snd_usb_set_interface_quirk(struct usb_device *dev) } } +/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size) { + struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev); + + if (!chip) + return; /* * "Playback Design" products need a 20ms delay after each * class compliant request */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) && + if (USB_ID_VENDOR(chip->usb_id) == 0x23ba && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); @@ -1229,23 +1237,21 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, * "TEAC Corp." products need a 20ms delay after each * class compliant request */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) && + if (USB_ID_VENDOR(chip->usb_id) == 0x0644 && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); /* Marantz/Denon devices with USB DAC functionality need a delay * after each class compliant request */ - if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct))) + if (is_marantz_denon_dac(chip->usb_id) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); /* Zoom R16/24 needs a tiny delay here, otherwise requests like * get/set frequency return as failed despite actually succeeding. */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) && - (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) && + if (chip->usb_id == USB_ID(0x1686, 0x00dd) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(1); } @@ -1262,7 +1268,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, unsigned int sample_bytes) { /* Playback Designs */ - if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) { + if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) { switch (fp->altsetting) { case 1: fp->dsd_dop = true; diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 2cd71ed1201f..192ff5ce9452 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -16,7 +16,8 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int snd_usb_apply_boot_quirk(struct usb_device *dev, struct usb_interface *intf, - const struct snd_usb_audio_quirk *quirk); + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id); void snd_usb_set_format_quirk(struct snd_usb_substream *subs, struct audioformat *fmt); -- cgit From e2703363316278cd4a8880671d38f783f7de18ba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 11 Jan 2016 14:39:12 +0100 Subject: ALSA: usb-audio: Add quirk_alias option This patch adds a new option "quirk_alias" to snd-usb-audio driver for allowing user to pass the quirk alias list. A quirk alias consists of a string form like 0123abcd:5678beef, which makes to apply a quirk to a device with USB ID 0123:abcd treated as if it were 5678:beef. This feature is useful to test an existing quirk, typically for a newer model of the same vendor, without patching / rebuilding the kernel driver. The current implementation is fairly simplistic: since there is no API for matching a usb_device_id to the given ID pair, it has an open code to loop over the id table and matches only with vendor:product pair. So far, this is OK, as all existing entries are with vendor:product pairs, indeed. Once when we have another matching entry, however, we'd need to update get_alias_quirk() as well. Note that this option is provided only for testing / development. If you want to have a proper support, contact to upstream for adding the matching quirk in the driver code statically. Signed-off-by: Takashi Iwai --- sound/usb/card.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/card.c b/sound/usb/card.c index 2c0269014b85..3fc63583a537 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -82,6 +82,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; static bool autoclock = true; +static char *quirk_alias[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); @@ -100,6 +101,8 @@ MODULE_PARM_DESC(ignore_ctl_error, "Ignore errors from USB controller for mixer interfaces."); module_param(autoclock, bool, 0444); MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes)."); +module_param_array(quirk_alias, charp, NULL, 0444); +MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); /* * we keep the snd_usb_audio_t instances by ourselves for merging @@ -457,6 +460,48 @@ static int snd_usb_audio_create(struct usb_interface *intf, return 0; } +/* look for a matching quirk alias id */ +static bool get_alias_id(struct usb_device *dev, unsigned int *id) +{ + int i; + unsigned int src, dst; + + for (i = 0; i < ARRAY_SIZE(quirk_alias); i++) { + if (!quirk_alias[i] || + sscanf(quirk_alias[i], "%x:%x", &src, &dst) != 2 || + src != *id) + continue; + dev_info(&dev->dev, + "device (%04x:%04x): applying quirk alias %04x:%04x\n", + USB_ID_VENDOR(*id), USB_ID_PRODUCT(*id), + USB_ID_VENDOR(dst), USB_ID_PRODUCT(dst)); + *id = dst; + return true; + } + + return false; +} + +static struct usb_device_id usb_audio_ids[]; /* defined below */ + +/* look for the corresponding quirk */ +static const struct snd_usb_audio_quirk * +get_alias_quirk(struct usb_device *dev, unsigned int id) +{ + const struct usb_device_id *p; + + for (p = usb_audio_ids; p->match_flags; p++) { + /* FIXME: this checks only vendor:product pair in the list */ + if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) == + USB_DEVICE_ID_MATCH_DEVICE && + p->idVendor == USB_ID_VENDOR(id) && + p->idProduct == USB_ID_PRODUCT(id)) + return (const struct snd_usb_audio_quirk *)p->driver_info; + } + + return NULL; +} + /* * probe the active usb device * @@ -483,6 +528,8 @@ static int usb_audio_probe(struct usb_interface *intf, ifnum = get_iface_desc(alts)->bInterfaceNumber; id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); + if (get_alias_id(dev, &id)) + quirk = get_alias_quirk(dev, id); if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) return -ENXIO; -- cgit From 0f886ca12765d20124bd06291c82951fd49a33be Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 12:09:10 +0100 Subject: ALSA: usb-audio: Fix NULL dereference in create_fixed_stream_quirk() create_fixed_stream_quirk() may cause a NULL-pointer dereference by accessing the non-existing endpoint when a USB device with a malformed USB descriptor is used. This patch avoids it simply by adding a sanity check of bNumEndpoints before the accesses. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=971125 Cc: Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 5b03296a5bd9..529c35cceaa6 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -180,6 +180,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, } alts = &iface->altsetting[fp->altset_idx]; altsd = get_iface_desc(alts); + if (altsd->bNumEndpoints < 1) { + kfree(fp); + kfree(rate_table); + return -EINVAL; + } + fp->protocol = altsd->bInterfaceProtocol; if (fp->datainterval == 0) -- cgit From 902eb7fd1e4af3ac69b9b30f8373f118c92b9729 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 12:14:49 +0100 Subject: ALSA: usb-audio: Minor code cleanup in create_fixed_stream_quirk() Just a minor code cleanup: unify the error paths. Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound/usb') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 529c35cceaa6..a889d433cbdf 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -167,23 +167,18 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, stream = (fp->endpoint & USB_DIR_IN) ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; err = snd_usb_add_audio_stream(chip, stream, fp); - if (err < 0) { - kfree(fp); - kfree(rate_table); - return err; - } + if (err < 0) + goto error; if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber || fp->altset_idx >= iface->num_altsetting) { - kfree(fp); - kfree(rate_table); - return -EINVAL; + err = -EINVAL; + goto error; } alts = &iface->altsetting[fp->altset_idx]; altsd = get_iface_desc(alts); if (altsd->bNumEndpoints < 1) { - kfree(fp); - kfree(rate_table); - return -EINVAL; + err = -EINVAL; + goto error; } fp->protocol = altsd->bInterfaceProtocol; @@ -196,6 +191,11 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, snd_usb_init_pitch(chip, fp->iface, alts, fp); snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max); return 0; + + error: + kfree(fp); + kfree(rate_table); + return err; } static int create_auto_pcm_quirk(struct snd_usb_audio *chip, -- cgit From 447d6275f0c21f6cc97a88b3a0c601436a4cdf2a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Mar 2016 15:20:58 +0100 Subject: ALSA: usb-audio: Add sanity checks for endpoint accesses Add some sanity check codes before actually accessing the endpoint via get_endpoint() in order to avoid the invalid access through a malformed USB descriptor. Mostly just checking bNumEndpoints, but in one place (snd_microii_spdif_default_get()), the validity of iface and altsetting index is checked as well. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=971125 Cc: Signed-off-by: Takashi Iwai --- sound/usb/clock.c | 2 ++ sound/usb/endpoint.c | 3 +++ sound/usb/mixer_quirks.c | 4 ++++ sound/usb/pcm.c | 2 ++ 4 files changed, 11 insertions(+) (limited to 'sound/usb') diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 2ed260b10f6d..7ccbcaf6a147 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -285,6 +285,8 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, unsigned char data[3]; int err, crate; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; /* if endpoint doesn't have sampling rate control, bail out */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 7b1cb365ffab..c07a7eda42a2 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -438,6 +438,9 @@ exit_clear: * * New endpoints will be added to chip->ep_list and must be freed by * calling snd_usb_endpoint_free(). + * + * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that + * bNumEndpoints > 1 beforehand. */ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, struct usb_host_interface *alts, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 279025650568..f6c3bf79af9a 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1519,7 +1519,11 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, /* use known values for that card: interface#1 altsetting#1 */ iface = usb_ifnum_to_if(chip->dev, 1); + if (!iface || iface->num_altsetting < 2) + return -EINVAL; alts = &iface->altsetting[1]; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; err = snd_usb_ctl_msg(chip->dev, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 9245f52d43bd..44d178ee9177 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -159,6 +159,8 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface, unsigned char data[1]; int err; + if (get_iface_desc(alts)->bNumEndpoints < 1) + return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; data[0] = 1; -- cgit