diff options
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/6fire/chip.c | 2 | ||||
-rw-r--r-- | sound/usb/6fire/pcm.c | 12 | ||||
-rw-r--r-- | sound/usb/Kconfig | 31 | ||||
-rw-r--r-- | sound/usb/Makefile | 2 | ||||
-rw-r--r-- | sound/usb/caiaq/audio.c | 14 | ||||
-rw-r--r-- | sound/usb/caiaq/device.c | 31 | ||||
-rw-r--r-- | sound/usb/card.c | 22 | ||||
-rw-r--r-- | sound/usb/card.h | 1 | ||||
-rw-r--r-- | sound/usb/clock.c | 4 | ||||
-rw-r--r-- | sound/usb/format.c | 34 | ||||
-rw-r--r-- | sound/usb/format.h | 2 | ||||
-rw-r--r-- | sound/usb/hiface/Makefile | 2 | ||||
-rw-r--r-- | sound/usb/hiface/chip.c | 297 | ||||
-rw-r--r-- | sound/usb/hiface/chip.h | 30 | ||||
-rw-r--r-- | sound/usb/hiface/pcm.c | 621 | ||||
-rw-r--r-- | sound/usb/hiface/pcm.h | 24 | ||||
-rw-r--r-- | sound/usb/midi.c | 74 | ||||
-rw-r--r-- | sound/usb/misc/ua101.c | 2 | ||||
-rw-r--r-- | sound/usb/mixer.c | 1 | ||||
-rw-r--r-- | sound/usb/mixer_quirks.c | 212 | ||||
-rw-r--r-- | sound/usb/pcm.c | 45 | ||||
-rw-r--r-- | sound/usb/quirks-table.h | 509 | ||||
-rw-r--r-- | sound/usb/quirks.c | 213 | ||||
-rw-r--r-- | sound/usb/stream.c | 18 | ||||
-rw-r--r-- | sound/usb/usbaudio.h | 2 | ||||
-rw-r--r-- | sound/usb/usx2y/usbusx2y.c | 2 | ||||
-rw-r--r-- | sound/usb/usx2y/usbusx2yaudio.c | 3 |
27 files changed, 1729 insertions, 481 deletions
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 4394ae796356..c39c77978468 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -30,7 +30,7 @@ MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>"); MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver"); MODULE_LICENSE("GPL v2"); -MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}"); +MODULE_SUPPORTED_DEVICE("{{TerraTec,DMX 6Fire USB}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */ diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index 40dd50a80f55..c5b9cac37dc4 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -450,13 +450,13 @@ static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub) static int usb6fire_pcm_hw_params(struct snd_pcm_substream *alsa_sub, struct snd_pcm_hw_params *hw_params) { - return snd_pcm_lib_malloc_pages(alsa_sub, - params_buffer_bytes(hw_params)); + return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub, + params_buffer_bytes(hw_params)); } static int usb6fire_pcm_hw_free(struct snd_pcm_substream *alsa_sub) { - return snd_pcm_lib_free_pages(alsa_sub); + return snd_pcm_lib_free_vmalloc_buffer(alsa_sub); } static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) @@ -560,6 +560,8 @@ static struct snd_pcm_ops pcm_ops = { .prepare = usb6fire_pcm_prepare, .trigger = usb6fire_pcm_trigger, .pointer = usb6fire_pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, }; static void usb6fire_pcm_init_urb(struct pcm_urb *urb, @@ -622,10 +624,6 @@ int usb6fire_pcm_init(struct sfire_chip *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); - ret = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - MAX_BUFSIZE, MAX_BUFSIZE); if (ret) { kfree(rt); snd_printk(KERN_ERR PREFIX diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 225dfd737265..de9408b83f75 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -115,5 +115,36 @@ config SND_USB_6FIRE and further help can be found at http://sixfireusb.sourceforge.net +config SND_USB_HIFACE + tristate "M2Tech hiFace USB-SPDIF driver" + select SND_PCM + help + Select this option to include support for M2Tech hiFace USB-SPDIF + interface. + + This driver supports the original M2Tech hiFace and some other + compatible devices. The supported products are: + + * M2Tech Young + * M2Tech hiFace + * M2Tech North Star + * M2Tech W4S Young + * M2Tech Corrson + * M2Tech AUDIA + * M2Tech SL Audio + * M2Tech Empirical + * M2Tech Rockna + * M2Tech Pathos + * M2Tech Metronome + * M2Tech CAD + * M2Tech Audio Esclusive + * M2Tech Rotel + * M2Tech Eeaudio + * The Chord Company CHORD + * AVA Group A/S Vitus + + To compile this driver as a module, choose M here: the module + will be called snd-usb-hiface. + endif # SND_USB diff --git a/sound/usb/Makefile b/sound/usb/Makefile index ac256dc4c6be..abe668f660d1 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -23,4 +23,4 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o -obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ +obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index c1916184e2e1..7103b0908d13 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -183,14 +183,15 @@ static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub, struct snd_pcm_hw_params *hw_params) { - return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params)); + return snd_pcm_lib_alloc_vmalloc_buffer(sub, + params_buffer_bytes(hw_params)); } static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) { struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub); deactivate_substream(cdev, sub); - return snd_pcm_lib_free_pages(sub); + return snd_pcm_lib_free_vmalloc_buffer(sub); } /* this should probably go upstream */ @@ -345,7 +346,9 @@ static struct snd_pcm_ops snd_usb_caiaq_ops = { .hw_free = snd_usb_caiaq_pcm_hw_free, .prepare = snd_usb_caiaq_pcm_prepare, .trigger = snd_usb_caiaq_pcm_trigger, - .pointer = snd_usb_caiaq_pcm_pointer + .pointer = snd_usb_caiaq_pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, }; static void check_for_elapsed_periods(struct snd_usb_caiaqdev *cdev, @@ -852,11 +855,6 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev) snd_pcm_set_ops(cdev->pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_caiaq_ops); - snd_pcm_lib_preallocate_pages_for_all(cdev->pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - MAX_BUFFER_SIZE, MAX_BUFFER_SIZE); - cdev->data_cb_info = kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS, GFP_KERNEL); diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 48b63ccc78c7..1a61dd12fe38 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -39,25 +39,24 @@ MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); MODULE_DESCRIPTION("caiaq USB audio"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," - "{Native Instruments, RigKontrol3}," - "{Native Instruments, Kore Controller}," - "{Native Instruments, Kore Controller 2}," - "{Native Instruments, Audio Kontrol 1}," - "{Native Instruments, Audio 2 DJ}," - "{Native Instruments, Audio 4 DJ}," - "{Native Instruments, Audio 8 DJ}," - "{Native Instruments, Traktor Audio 2}," - "{Native Instruments, Session I/O}," - "{Native Instruments, GuitarRig mobile}," - "{Native Instruments, Traktor Kontrol X1}," - "{Native Instruments, Traktor Kontrol S4}," - "{Native Instruments, Maschine Controller}}"); +MODULE_SUPPORTED_DEVICE("{{Native Instruments,RigKontrol2}," + "{Native Instruments,RigKontrol3}," + "{Native Instruments,Kore Controller}," + "{Native Instruments,Kore Controller 2}," + "{Native Instruments,Audio Kontrol 1}," + "{Native Instruments,Audio 2 DJ}," + "{Native Instruments,Audio 4 DJ}," + "{Native Instruments,Audio 8 DJ}," + "{Native Instruments,Traktor Audio 2}," + "{Native Instruments,Session I/O}," + "{Native Instruments,GuitarRig mobile}," + "{Native Instruments,Traktor Kontrol X1}," + "{Native Instruments,Traktor Kontrol S4}," + "{Native Instruments,Maschine Controller}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int snd_card_used[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the caiaq sound device"); @@ -388,7 +387,7 @@ static int create_card(struct usb_device *usb_dev, struct snd_usb_caiaqdev *cdev; for (devnum = 0; devnum < SNDRV_CARDS; devnum++) - if (enable[devnum] && !snd_card_used[devnum]) + if (enable[devnum]) break; if (devnum >= SNDRV_CARDS) diff --git a/sound/usb/card.c b/sound/usb/card.c index 1a033177b83f..64952e2d3ed1 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -147,14 +147,32 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int return -EINVAL; } + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + + /* + * Android with both accessory and audio interfaces enabled gets the + * interface numbers wrong. + */ + if ((chip->usb_id == USB_ID(0x18d1, 0x2d04) || + chip->usb_id == USB_ID(0x18d1, 0x2d05)) && + interface == 0 && + altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && + altsd->bInterfaceSubClass == USB_SUBCLASS_VENDOR_SPEC) { + interface = 2; + iface = usb_ifnum_to_if(dev, interface); + if (!iface) + return -EINVAL; + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + } + if (usb_interface_claimed(iface)) { snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n", dev->devnum, ctrlif, interface); return -EINVAL; } - alts = &iface->altsetting[0]; - altsd = get_iface_desc(alts); if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { diff --git a/sound/usb/card.h b/sound/usb/card.h index bf2889a2cae5..5ecacaa90b53 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -21,6 +21,7 @@ struct audioformat { unsigned char endpoint; /* endpoint */ unsigned char ep_attr; /* endpoint attributes */ unsigned char datainterval; /* log_2 of data packet interval */ + unsigned char protocol; /* UAC_VERSION_1/2 */ unsigned int maxpacksize; /* max. packet size */ unsigned int rates; /* rate bitmasks */ unsigned int rate_min, rate_max; /* min/max rates */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 3a2ce390e278..86f80c60b21f 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -407,9 +407,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt, int rate) { - struct usb_interface_descriptor *altsd = get_iface_desc(alts); - - switch (altsd->bInterfaceProtocol) { + switch (fmt->protocol) { case UAC_VERSION_1: default: return set_sample_rate_v1(chip, iface, alts, fmt, rate); diff --git a/sound/usb/format.c b/sound/usb/format.c index 99299ffb33ac..3525231c6b97 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -43,13 +43,12 @@ */ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, struct audioformat *fp, - unsigned int format, void *_fmt, - int protocol) + unsigned int format, void *_fmt) { int sample_width, sample_bytes; u64 pcm_formats = 0; - switch (protocol) { + switch (fp->protocol) { case UAC_VERSION_1: default: { struct uac_format_type_i_discrete_descriptor *fmt = _fmt; @@ -360,11 +359,8 @@ err: */ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int format, - struct uac_format_type_i_continuous_descriptor *fmt, - struct usb_host_interface *iface) + struct uac_format_type_i_continuous_descriptor *fmt) { - struct usb_interface_descriptor *altsd = get_iface_desc(iface); - int protocol = altsd->bInterfaceProtocol; snd_pcm_format_t pcm_format; int ret; @@ -387,8 +383,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, } fp->formats = pcm_format_to_bits(pcm_format); } else { - fp->formats = parse_audio_format_i_type(chip, fp, format, - fmt, protocol); + fp->formats = parse_audio_format_i_type(chip, fp, format, fmt); if (!fp->formats) return -EINVAL; } @@ -398,11 +393,8 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, * proprietary class specific descriptor. * audio class v2 uses class specific EP0 range requests for that. */ - switch (protocol) { + switch (fp->protocol) { default: - snd_printdd(KERN_WARNING "%d:%u:%d : invalid protocol version %d, assuming v1\n", - chip->dev->devnum, fp->iface, fp->altsetting, protocol); - /* fall through */ case UAC_VERSION_1: fp->channels = fmt->bNrChannels; ret = parse_audio_format_rates_v1(chip, fp, (unsigned char *) fmt, 7); @@ -427,12 +419,9 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, */ static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat *fp, - int format, void *_fmt, - struct usb_host_interface *iface) + int format, void *_fmt) { int brate, framesize, ret; - struct usb_interface_descriptor *altsd = get_iface_desc(iface); - int protocol = altsd->bInterfaceProtocol; switch (format) { case UAC_FORMAT_TYPE_II_AC3: @@ -452,11 +441,8 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, fp->channels = 1; - switch (protocol) { + switch (fp->protocol) { default: - snd_printdd(KERN_WARNING "%d:%u:%d : invalid protocol version %d, assuming v1\n", - chip->dev->devnum, fp->iface, fp->altsetting, protocol); - /* fall through */ case UAC_VERSION_1: { struct uac_format_type_ii_discrete_descriptor *fmt = _fmt; brate = le16_to_cpu(fmt->wMaxBitRate); @@ -483,17 +469,17 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int format, struct uac_format_type_i_continuous_descriptor *fmt, - int stream, struct usb_host_interface *iface) + int stream) { int err; switch (fmt->bFormatType) { case UAC_FORMAT_TYPE_I: case UAC_FORMAT_TYPE_III: - err = parse_audio_format_i(chip, fp, format, fmt, iface); + err = parse_audio_format_i(chip, fp, format, fmt); break; case UAC_FORMAT_TYPE_II: - err = parse_audio_format_ii(chip, fp, format, fmt, iface); + err = parse_audio_format_ii(chip, fp, format, fmt); break; default: snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n", diff --git a/sound/usb/format.h b/sound/usb/format.h index 6f315226f320..4b8a01129f24 100644 --- a/sound/usb/format.h +++ b/sound/usb/format.h @@ -4,6 +4,6 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int format, struct uac_format_type_i_continuous_descriptor *fmt, - int stream, struct usb_host_interface *iface); + int stream); #endif /* __USBAUDIO_FORMAT_H */ diff --git a/sound/usb/hiface/Makefile b/sound/usb/hiface/Makefile new file mode 100644 index 000000000000..463b136d1d89 --- /dev/null +++ b/sound/usb/hiface/Makefile @@ -0,0 +1,2 @@ +snd-usb-hiface-objs := chip.o pcm.o +obj-$(CONFIG_SND_USB_HIFACE) += snd-usb-hiface.o diff --git a/sound/usb/hiface/chip.c b/sound/usb/hiface/chip.c new file mode 100644 index 000000000000..b0dcb3924ce5 --- /dev/null +++ b/sound/usb/hiface/chip.c @@ -0,0 +1,297 @@ +/* + * Linux driver for M2Tech hiFace compatible devices + * + * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V. + * + * Authors: Michael Trimarchi <michael@amarulasolutions.com> + * Antonio Ospite <ao2@amarulasolutions.com> + * + * The driver is based on the work done in TerraTec DMX 6Fire USB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <sound/initval.h> + +#include "chip.h" +#include "pcm.h" + +MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>"); +MODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>"); +MODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("{{M2Tech,Young}," + "{M2Tech,hiFace}," + "{M2Tech,North Star}," + "{M2Tech,W4S Young}," + "{M2Tech,Corrson}," + "{M2Tech,AUDIA}," + "{M2Tech,SL Audio}," + "{M2Tech,Empirical}," + "{M2Tech,Rockna}," + "{M2Tech,Pathos}," + "{M2Tech,Metronome}," + "{M2Tech,CAD}," + "{M2Tech,Audio Esclusive}," + "{M2Tech,Rotel}," + "{M2Tech,Eeaudio}," + "{The Chord Company,CHORD}," + "{AVA Group A/S,Vitus}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */ +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +#define DRIVER_NAME "snd-usb-hiface" +#define CARD_NAME "hiFace" + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); + +static DEFINE_MUTEX(register_mutex); + +struct hiface_vendor_quirk { + const char *device_name; + u8 extra_freq; +}; + +static int hiface_chip_create(struct usb_device *device, int idx, + const struct hiface_vendor_quirk *quirk, + struct hiface_chip **rchip) +{ + struct snd_card *card = NULL; + struct hiface_chip *chip; + int ret; + int len; + + *rchip = NULL; + + /* if we are here, card can be registered in alsa. */ + ret = snd_card_create(index[idx], id[idx], THIS_MODULE, sizeof(*chip), &card); + if (ret < 0) { + dev_err(&device->dev, "cannot create alsa card.\n"); + return ret; + } + + strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver)); + + if (quirk && quirk->device_name) + strlcpy(card->shortname, quirk->device_name, sizeof(card->shortname)); + else + strlcpy(card->shortname, "M2Tech generic audio", sizeof(card->shortname)); + + strlcat(card->longname, card->shortname, sizeof(card->longname)); + len = strlcat(card->longname, " at ", sizeof(card->longname)); + if (len < sizeof(card->longname)) + usb_make_path(device, card->longname + len, + sizeof(card->longname) - len); + + chip = card->private_data; + chip->dev = device; + chip->card = card; + + *rchip = chip; + return 0; +} + +static int hiface_chip_probe(struct usb_interface *intf, + const struct usb_device_id *usb_id) +{ + const struct hiface_vendor_quirk *quirk = (struct hiface_vendor_quirk *)usb_id->driver_info; + int ret; + int i; + struct hiface_chip *chip; + struct usb_device *device = interface_to_usbdev(intf); + + ret = usb_set_interface(device, 0, 0); + if (ret != 0) { + dev_err(&device->dev, "can't set first interface for " CARD_NAME " device.\n"); + return -EIO; + } + + /* check whether the card is already registered */ + chip = NULL; + mutex_lock(®ister_mutex); + + for (i = 0; i < SNDRV_CARDS; i++) + if (enable[i]) + break; + + if (i >= SNDRV_CARDS) { + dev_err(&device->dev, "no available " CARD_NAME " audio device\n"); + ret = -ENODEV; + goto err; + } + + ret = hiface_chip_create(device, i, quirk, &chip); + if (ret < 0) + goto err; + + snd_card_set_dev(chip->card, &intf->dev); + + ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0); + if (ret < 0) + goto err_chip_destroy; + + ret = snd_card_register(chip->card); + if (ret < 0) { + dev_err(&device->dev, "cannot register " CARD_NAME " card\n"); + goto err_chip_destroy; + } + + mutex_unlock(®ister_mutex); + + usb_set_intfdata(intf, chip); + return 0; + +err_chip_destroy: + snd_card_free(chip->card); +err: + mutex_unlock(®ister_mutex); + return ret; +} + +static void hiface_chip_disconnect(struct usb_interface *intf) +{ + struct hiface_chip *chip; + struct snd_card *card; + + chip = usb_get_intfdata(intf); + if (!chip) + return; + + card = chip->card; + + /* Make sure that the userspace cannot create new request */ + snd_card_disconnect(card); + + hiface_pcm_abort(chip); + snd_card_free_when_closed(card); +} + +static const struct usb_device_id device_table[] = { + { + USB_DEVICE(0x04b4, 0x0384), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Young", + .extra_freq = 1, + } + }, + { + USB_DEVICE(0x04b4, 0x930b), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "hiFace", + } + }, + { + USB_DEVICE(0x04b4, 0x931b), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "North Star", + } + }, + { + USB_DEVICE(0x04b4, 0x931c), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "W4S Young", + } + }, + { + USB_DEVICE(0x04b4, 0x931d), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Corrson", + } + }, + { + USB_DEVICE(0x04b4, 0x931e), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "AUDIA", + } + }, + { + USB_DEVICE(0x04b4, 0x931f), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "SL Audio", + } + }, + { + USB_DEVICE(0x04b4, 0x9320), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Empirical", + } + }, + { + USB_DEVICE(0x04b4, 0x9321), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Rockna", + } + }, + { + USB_DEVICE(0x249c, 0x9001), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Pathos", + } + }, + { + USB_DEVICE(0x249c, 0x9002), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Metronome", + } + }, + { + USB_DEVICE(0x249c, 0x9006), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "CAD", + } + }, + { + USB_DEVICE(0x249c, 0x9008), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Audio Esclusive", + } + }, + { + USB_DEVICE(0x249c, 0x931c), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Rotel", + } + }, + { + USB_DEVICE(0x249c, 0x932c), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Eeaudio", + } + }, + { + USB_DEVICE(0x245f, 0x931c), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "CHORD", + } + }, + { + USB_DEVICE(0x25c6, 0x9002), + .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { + .device_name = "Vitus", + } + }, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +static struct usb_driver hiface_usb_driver = { + .name = DRIVER_NAME, + .probe = hiface_chip_probe, + .disconnect = hiface_chip_disconnect, + .id_table = device_table, +}; + +module_usb_driver(hiface_usb_driver); diff --git a/sound/usb/hiface/chip.h b/sound/usb/hiface/chip.h new file mode 100644 index 000000000000..189a1371b7c4 --- /dev/null +++ b/sound/usb/hiface/chip.h @@ -0,0 +1,30 @@ +/* + * Linux driver for M2Tech hiFace compatible devices + * + * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V. + * + * Authors: Michael Trimarchi <michael@amarulasolutions.com> + * Antonio Ospite <ao2@amarulasolutions.com> + * + * The driver is based on the work done in TerraTec DMX 6Fire USB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef HIFACE_CHIP_H +#define HIFACE_CHIP_H + +#include <linux/usb.h> +#include <sound/core.h> + +struct pcm_runtime; + +struct hiface_chip { + struct usb_device *dev; + struct snd_card *card; + struct pcm_runtime *pcm; +}; +#endif /* HIFACE_CHIP_H */ diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c new file mode 100644 index 000000000000..6430ed2a9f65 --- /dev/null +++ b/sound/usb/hiface/pcm.c @@ -0,0 +1,621 @@ +/* + * Linux driver for M2Tech hiFace compatible devices + * + * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V. + * + * Authors: Michael Trimarchi <michael@amarulasolutions.com> + * Antonio Ospite <ao2@amarulasolutions.com> + * + * The driver is based on the work done in TerraTec DMX 6Fire USB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/slab.h> +#include <sound/pcm.h> + +#include "pcm.h" +#include "chip.h" + +#define OUT_EP 0x2 +#define PCM_N_URBS 8 +#define PCM_PACKET_SIZE 4096 +#define PCM_BUFFER_SIZE (2 * PCM_N_URBS * PCM_PACKET_SIZE) + +struct pcm_urb { + struct hiface_chip *chip; + + struct urb instance; + struct usb_anchor submitted; + u8 *buffer; +}; + +struct pcm_substream { + spinlock_t lock; + struct snd_pcm_substream *instance; + + bool active; + snd_pcm_uframes_t dma_off; /* current position in alsa dma_area */ + snd_pcm_uframes_t period_off; /* current position in current period */ +}; + +enum { /* pcm streaming states */ + STREAM_DISABLED, /* no pcm streaming */ + STREAM_STARTING, /* pcm streaming requested, waiting to become ready */ + STREAM_RUNNING, /* pcm streaming running */ + STREAM_STOPPING +}; + +struct pcm_runtime { + struct hiface_chip *chip; + struct snd_pcm *instance; + + struct pcm_substream playback; + bool panic; /* if set driver won't do anymore pcm on device */ + + struct pcm_urb out_urbs[PCM_N_URBS]; + + struct mutex stream_mutex; + u8 stream_state; /* one of STREAM_XXX */ + u8 extra_freq; + wait_queue_head_t stream_wait_queue; + bool stream_wait_cond; +}; + +static const unsigned int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000, + 352800, 384000 }; +static const struct snd_pcm_hw_constraint_list constraints_extra_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const struct snd_pcm_hardware pcm_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH, + + .formats = SNDRV_PCM_FMTBIT_S32_LE, + + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + + .rate_min = 44100, + .rate_max = 192000, /* changes in hiface_pcm_open to support extra rates */ + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = PCM_BUFFER_SIZE, + .period_bytes_min = PCM_PACKET_SIZE, + .period_bytes_max = PCM_BUFFER_SIZE, + .periods_min = 2, + .periods_max = 1024 +}; + +/* message values used to change the sample rate */ +#define HIFACE_SET_RATE_REQUEST 0xb0 + +#define HIFACE_RATE_44100 0x43 +#define HIFACE_RATE_48000 0x4b +#define HIFACE_RATE_88200 0x42 +#define HIFACE_RATE_96000 0x4a +#define HIFACE_RATE_176400 0x40 +#define HIFACE_RATE_192000 0x48 +#define HIFACE_RATE_352000 0x58 +#define HIFACE_RATE_384000 0x68 + +static int hiface_pcm_set_rate(struct pcm_runtime *rt, unsigned int rate) +{ + struct usb_device *device = rt->chip->dev; + u16 rate_value; + int ret; + + /* We are already sure that the rate is supported here thanks to + * ALSA constraints + */ + switch (rate) { + case 44100: + rate_value = HIFACE_RATE_44100; + break; + case 48000: + rate_value = HIFACE_RATE_48000; + break; + case 88200: + rate_value = HIFACE_RATE_88200; + break; + case 96000: + rate_value = HIFACE_RATE_96000; + break; + case 176400: + rate_value = HIFACE_RATE_176400; + break; + case 192000: + rate_value = HIFACE_RATE_192000; + break; + case 352000: + rate_value = HIFACE_RATE_352000; + break; + case 384000: + rate_value = HIFACE_RATE_384000; + break; + default: + dev_err(&device->dev, "Unsupported rate %d\n", rate); + return -EINVAL; + } + + /* + * USBIO: Vendor 0xb0(wValue=0x0043, wIndex=0x0000) + * 43 b0 43 00 00 00 00 00 + * USBIO: Vendor 0xb0(wValue=0x004b, wIndex=0x0000) + * 43 b0 4b 00 00 00 00 00 + * This control message doesn't have any ack from the + * other side + */ + ret = usb_control_msg(device, usb_sndctrlpipe(device, 0), + HIFACE_SET_RATE_REQUEST, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + rate_value, 0, NULL, 0, 100); + if (ret < 0) { + dev_err(&device->dev, "Error setting samplerate %d.\n", rate); + return ret; + } + + return 0; +} + +static struct pcm_substream *hiface_pcm_get_substream(struct snd_pcm_substream + *alsa_sub) +{ + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + struct device *device = &rt->chip->dev->dev; + + if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) + return &rt->playback; + + dev_err(device, "Error getting pcm substream slot.\n"); + return NULL; +} + +/* call with stream_mutex locked */ +static void hiface_pcm_stream_stop(struct pcm_runtime *rt) +{ + int i, time; + + if (rt->stream_state != STREAM_DISABLED) { + rt->stream_state = STREAM_STOPPING; + + for (i = 0; i < PCM_N_URBS; i++) { + time = usb_wait_anchor_empty_timeout( + &rt->out_urbs[i].submitted, 100); + if (!time) + usb_kill_anchored_urbs( + &rt->out_urbs[i].submitted); + usb_kill_urb(&rt->out_urbs[i].instance); + } + + rt->stream_state = STREAM_DISABLED; + } +} + +/* call with stream_mutex locked */ +static int hiface_pcm_stream_start(struct pcm_runtime *rt) +{ + int ret = 0; + int i; + + if (rt->stream_state == STREAM_DISABLED) { + + /* reset panic state when starting a new stream */ + rt->panic = false; + + /* submit our out urbs zero init */ + rt->stream_state = STREAM_STARTING; + for (i = 0; i < PCM_N_URBS; i++) { + memset(rt->out_urbs[i].buffer, 0, PCM_PACKET_SIZE); + usb_anchor_urb(&rt->out_urbs[i].instance, + &rt->out_urbs[i].submitted); + ret = usb_submit_urb(&rt->out_urbs[i].instance, + GFP_ATOMIC); + if (ret) { + hiface_pcm_stream_stop(rt); + return ret; + } + } + + /* wait for first out urb to return (sent in in urb handler) */ + wait_event_timeout(rt->stream_wait_queue, rt->stream_wait_cond, + HZ); + if (rt->stream_wait_cond) { + struct device *device = &rt->chip->dev->dev; + dev_dbg(device, "%s: Stream is running wakeup event\n", + __func__); + rt->stream_state = STREAM_RUNNING; + } else { + hiface_pcm_stream_stop(rt); + return -EIO; + } + } + return ret; +} + +/* The hardware wants word-swapped 32-bit values */ +static void memcpy_swahw32(u8 *dest, u8 *src, unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n / 4; i++) + ((u32 *)dest)[i] = swahw32(((u32 *)src)[i]); +} + +/* call with substream locked */ +/* returns true if a period elapsed */ +static bool hiface_pcm_playback(struct pcm_substream *sub, struct pcm_urb *urb) +{ + struct snd_pcm_runtime *alsa_rt = sub->instance->runtime; + struct device *device = &urb->chip->dev->dev; + u8 *source; + unsigned int pcm_buffer_size; + + WARN_ON(alsa_rt->format != SNDRV_PCM_FORMAT_S32_LE); + + pcm_buffer_size = snd_pcm_lib_buffer_bytes(sub->instance); + + if (sub->dma_off + PCM_PACKET_SIZE <= pcm_buffer_size) { + dev_dbg(device, "%s: (1) buffer_size %#x dma_offset %#x\n", __func__, + (unsigned int) pcm_buffer_size, + (unsigned int) sub->dma_off); + + source = alsa_rt->dma_area + sub->dma_off; + memcpy_swahw32(urb->buffer, source, PCM_PACKET_SIZE); + } else { + /* wrap around at end of ring buffer */ + unsigned int len; + + dev_dbg(device, "%s: (2) buffer_size %#x dma_offset %#x\n", __func__, + (unsigned int) pcm_buffer_size, + (unsigned int) sub->dma_off); + + len = pcm_buffer_size - sub->dma_off; + + source = alsa_rt->dma_area + sub->dma_off; + memcpy_swahw32(urb->buffer, source, len); + + source = alsa_rt->dma_area; + memcpy_swahw32(urb->buffer + len, source, + PCM_PACKET_SIZE - len); + } + sub->dma_off += PCM_PACKET_SIZE; + if (sub->dma_off >= pcm_buffer_size) + sub->dma_off -= pcm_buffer_size; + + sub->period_off += PCM_PACKET_SIZE; + if (sub->period_off >= alsa_rt->period_size) { + sub->period_off %= alsa_rt->period_size; + return true; + } + return false; +} + +static void hiface_pcm_out_urb_handler(struct urb *usb_urb) +{ + struct pcm_urb *out_urb = usb_urb->context; + struct pcm_runtime *rt = out_urb->chip->pcm; + struct pcm_substream *sub; + bool do_period_elapsed = false; + unsigned long flags; + int ret; + + if (rt->panic || rt->stream_state == STREAM_STOPPING) + return; + + if (unlikely(usb_urb->status == -ENOENT || /* unlinked */ + usb_urb->status == -ENODEV || /* device removed */ + usb_urb->status == -ECONNRESET || /* unlinked */ + usb_urb->status == -ESHUTDOWN)) { /* device disabled */ + goto out_fail; + } + + if (rt->stream_state == STREAM_STARTING) { + rt->stream_wait_cond = true; + wake_up(&rt->stream_wait_queue); + } + + /* now send our playback data (if a free out urb was found) */ + sub = &rt->playback; + spin_lock_irqsave(&sub->lock, flags); + if (sub->active) + do_period_elapsed = hiface_pcm_playback(sub, out_urb); + else + memset(out_urb->buffer, 0, PCM_PACKET_SIZE); + + spin_unlock_irqrestore(&sub->lock, flags); + + if (do_period_elapsed) + snd_pcm_period_elapsed(sub->instance); + + ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC); + if (ret < 0) + goto out_fail; + + return; + +out_fail: + rt->panic = true; +} + +static int hiface_pcm_open(struct snd_pcm_substream *alsa_sub) +{ + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + struct pcm_substream *sub = NULL; + struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime; + int ret; + + if (rt->panic) + return -EPIPE; + + mutex_lock(&rt->stream_mutex); + alsa_rt->hw = pcm_hw; + + if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) + sub = &rt->playback; + + if (!sub) { + struct device *device = &rt->chip->dev->dev; + mutex_unlock(&rt->stream_mutex); + dev_err(device, "Invalid stream type\n"); + return -EINVAL; + } + + if (rt->extra_freq) { + alsa_rt->hw.rates |= SNDRV_PCM_RATE_KNOT; + alsa_rt->hw.rate_max = 384000; + + /* explicit constraints needed as we added SNDRV_PCM_RATE_KNOT */ + ret = snd_pcm_hw_constraint_list(alsa_sub->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_extra_rates); + if (ret < 0) { + mutex_unlock(&rt->stream_mutex); + return ret; + } + } + + sub->instance = alsa_sub; + sub->active = false; + mutex_unlock(&rt->stream_mutex); + return 0; +} + +static int hiface_pcm_close(struct snd_pcm_substream *alsa_sub) +{ + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub); + unsigned long flags; + + if (rt->panic) + return 0; + + mutex_lock(&rt->stream_mutex); + if (sub) { + hiface_pcm_stream_stop(rt); + + /* deactivate substream */ + spin_lock_irqsave(&sub->lock, flags); + sub->instance = NULL; + sub->active = false; + spin_unlock_irqrestore(&sub->lock, flags); + + } + mutex_unlock(&rt->stream_mutex); + return 0; +} + +static int hiface_pcm_hw_params(struct snd_pcm_substream *alsa_sub, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_alloc_vmalloc_buffer(alsa_sub, + params_buffer_bytes(hw_params)); +} + +static int hiface_pcm_hw_free(struct snd_pcm_substream *alsa_sub) +{ + return snd_pcm_lib_free_vmalloc_buffer(alsa_sub); +} + +static int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub) +{ + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub); + struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime; + int ret; + + if (rt->panic) + return -EPIPE; + if (!sub) + return -ENODEV; + + mutex_lock(&rt->stream_mutex); + + sub->dma_off = 0; + sub->period_off = 0; + + if (rt->stream_state == STREAM_DISABLED) { + + ret = hiface_pcm_set_rate(rt, alsa_rt->rate); + if (ret) { + mutex_unlock(&rt->stream_mutex); + return ret; + } + ret = hiface_pcm_stream_start(rt); + if (ret) { + mutex_unlock(&rt->stream_mutex); + return ret; + } + } + mutex_unlock(&rt->stream_mutex); + return 0; +} + +static int hiface_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd) +{ + struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub); + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + + if (rt->panic) + return -EPIPE; + if (!sub) + return -ENODEV; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irq(&sub->lock); + sub->active = true; + spin_unlock_irq(&sub->lock); + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irq(&sub->lock); + sub->active = false; + spin_unlock_irq(&sub->lock); + return 0; + + default: + return -EINVAL; + } +} + +static snd_pcm_uframes_t hiface_pcm_pointer(struct snd_pcm_substream *alsa_sub) +{ + struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub); + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + unsigned long flags; + snd_pcm_uframes_t dma_offset; + + if (rt->panic || !sub) + return SNDRV_PCM_STATE_XRUN; + + spin_lock_irqsave(&sub->lock, flags); + dma_offset = sub->dma_off; + spin_unlock_irqrestore(&sub->lock, flags); + return bytes_to_frames(alsa_sub->runtime, dma_offset); +} + +static struct snd_pcm_ops pcm_ops = { + .open = hiface_pcm_open, + .close = hiface_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = hiface_pcm_hw_params, + .hw_free = hiface_pcm_hw_free, + .prepare = hiface_pcm_prepare, + .trigger = hiface_pcm_trigger, + .pointer = hiface_pcm_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, +}; + +static int hiface_pcm_init_urb(struct pcm_urb *urb, + struct hiface_chip *chip, + unsigned int ep, + void (*handler)(struct urb *)) +{ + urb->chip = chip; + usb_init_urb(&urb->instance); + + urb->buffer = kzalloc(PCM_PACKET_SIZE, GFP_KERNEL); + if (!urb->buffer) + return -ENOMEM; + + usb_fill_bulk_urb(&urb->instance, chip->dev, + usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer, + PCM_PACKET_SIZE, handler, urb); + init_usb_anchor(&urb->submitted); + + return 0; +} + +void hiface_pcm_abort(struct hiface_chip *chip) +{ + struct pcm_runtime *rt = chip->pcm; + + if (rt) { + rt->panic = true; + + mutex_lock(&rt->stream_mutex); + hiface_pcm_stream_stop(rt); + mutex_unlock(&rt->stream_mutex); + } +} + +static void hiface_pcm_destroy(struct hiface_chip *chip) +{ + struct pcm_runtime *rt = chip->pcm; + int i; + + for (i = 0; i < PCM_N_URBS; i++) + kfree(rt->out_urbs[i].buffer); + + kfree(chip->pcm); + chip->pcm = NULL; +} + +static void hiface_pcm_free(struct snd_pcm *pcm) +{ + struct pcm_runtime *rt = pcm->private_data; + + if (rt) + hiface_pcm_destroy(rt->chip); +} + +int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) +{ + int i; + int ret; + struct snd_pcm *pcm; + struct pcm_runtime *rt; + + rt = kzalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) + return -ENOMEM; + + rt->chip = chip; + rt->stream_state = STREAM_DISABLED; + if (extra_freq) + rt->extra_freq = 1; + + init_waitqueue_head(&rt->stream_wait_queue); + mutex_init(&rt->stream_mutex); + spin_lock_init(&rt->playback.lock); + + for (i = 0; i < PCM_N_URBS; i++) + hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP, + hiface_pcm_out_urb_handler); + + 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; + } + + pcm->private_data = rt; + pcm->private_free = hiface_pcm_free; + + strlcpy(pcm->name, "USB-SPDIF Audio", sizeof(pcm->name)); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); + + rt->instance = pcm; + + chip->pcm = rt; + return 0; +} diff --git a/sound/usb/hiface/pcm.h b/sound/usb/hiface/pcm.h new file mode 100644 index 000000000000..77edd7c12e19 --- /dev/null +++ b/sound/usb/hiface/pcm.h @@ -0,0 +1,24 @@ +/* + * Linux driver for M2Tech hiFace compatible devices + * + * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V. + * + * Authors: Michael Trimarchi <michael@amarulasolutions.com> + * Antonio Ospite <ao2@amarulasolutions.com> + * + * The driver is based on the work done in TerraTec DMX 6Fire USB + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef HIFACE_PCM_H +#define HIFACE_PCM_H + +struct hiface_chip; + +int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq); +void hiface_pcm_abort(struct hiface_chip *chip); +#endif /* HIFACE_PCM_H */ diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 8e01fa4991c5..b901f468b67a 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1575,8 +1575,41 @@ static struct port_info { EXTERNAL_PORT(0x0582, 0x004d, 0, "%s MIDI"), EXTERNAL_PORT(0x0582, 0x004d, 1, "%s 1"), EXTERNAL_PORT(0x0582, 0x004d, 2, "%s 2"), + /* BOSS GT-PRO */ + CONTROL_PORT(0x0582, 0x0089, 0, "%s Control"), /* Edirol UM-3EX */ CONTROL_PORT(0x0582, 0x009a, 3, "%s Control"), + /* Roland VG-99 */ + CONTROL_PORT(0x0582, 0x00b2, 0, "%s Control"), + EXTERNAL_PORT(0x0582, 0x00b2, 1, "%s MIDI"), + /* Cakewalk Sonar V-Studio 100 */ + EXTERNAL_PORT(0x0582, 0x00eb, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x00eb, 1, "%s Control"), + /* Roland VB-99 */ + CONTROL_PORT(0x0582, 0x0102, 0, "%s Control"), + EXTERNAL_PORT(0x0582, 0x0102, 1, "%s MIDI"), + /* Roland A-PRO */ + EXTERNAL_PORT(0x0582, 0x010f, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x010f, 1, "%s 1"), + CONTROL_PORT(0x0582, 0x010f, 2, "%s 2"), + /* Roland SD-50 */ + ROLAND_SYNTH_PORT(0x0582, 0x0114, 0, "%s Synth", 128), + EXTERNAL_PORT(0x0582, 0x0114, 1, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0114, 2, "%s Control"), + /* Roland OCTA-CAPTURE */ + EXTERNAL_PORT(0x0582, 0x0120, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0120, 1, "%s Control"), + EXTERNAL_PORT(0x0582, 0x0121, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0121, 1, "%s Control"), + /* Roland SPD-SX */ + CONTROL_PORT(0x0582, 0x0145, 0, "%s Control"), + EXTERNAL_PORT(0x0582, 0x0145, 1, "%s MIDI"), + /* Roland A-Series */ + CONTROL_PORT(0x0582, 0x0156, 0, "%s Keyboard"), + EXTERNAL_PORT(0x0582, 0x0156, 1, "%s MIDI"), + /* Roland INTEGRA-7 */ + ROLAND_SYNTH_PORT(0x0582, 0x015b, 0, "%s Synth", 128), + CONTROL_PORT(0x0582, 0x015b, 1, "%s Control"), /* M-Audio MidiSport 8x8 */ CONTROL_PORT(0x0763, 0x1031, 8, "%s Control"), CONTROL_PORT(0x0763, 0x1033, 8, "%s Control"), @@ -1948,6 +1981,44 @@ static int snd_usbmidi_detect_yamaha(struct snd_usb_midi* umidi, } /* + * Detects the endpoints and ports of Roland devices. + */ +static int snd_usbmidi_detect_roland(struct snd_usb_midi* umidi, + struct snd_usb_midi_endpoint_info* endpoint) +{ + struct usb_interface* intf; + struct usb_host_interface *hostif; + u8* cs_desc; + + intf = umidi->iface; + if (!intf) + return -ENOENT; + hostif = intf->altsetting; + /* + * Some devices have a descriptor <06 24 F1 02 <inputs> <outputs>>, + * some have standard class descriptors, or both kinds, or neither. + */ + for (cs_desc = hostif->extra; + cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2; + cs_desc += cs_desc[0]) { + if (cs_desc[0] >= 6 && + cs_desc[1] == USB_DT_CS_INTERFACE && + cs_desc[2] == 0xf1 && + cs_desc[3] == 0x02) { + endpoint->in_cables = (1 << cs_desc[4]) - 1; + endpoint->out_cables = (1 << cs_desc[5]) - 1; + return snd_usbmidi_detect_endpoints(umidi, endpoint, 1); + } else if (cs_desc[0] >= 7 && + cs_desc[1] == USB_DT_CS_INTERFACE && + cs_desc[2] == UAC_HEADER) { + return snd_usbmidi_get_ms_info(umidi, endpoint); + } + } + + return -ENODEV; +} + +/* * Creates the endpoints and their ports for Midiman devices. */ static int snd_usbmidi_create_endpoints_midiman(struct snd_usb_midi* umidi, @@ -2162,6 +2233,9 @@ int snd_usbmidi_create(struct snd_card *card, case QUIRK_MIDI_YAMAHA: err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]); break; + case QUIRK_MIDI_ROLAND: + err = snd_usbmidi_detect_roland(umidi, &endpoints[0]); + break; case QUIRK_MIDI_MIDIMAN: umidi->usb_protocol_ops = &snd_usbmidi_midiman_ops; memcpy(&endpoints[0], quirk->data, diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 6ad617b94732..8b5d2c564e04 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -1349,7 +1349,7 @@ static void ua101_disconnect(struct usb_interface *interface) snd_card_disconnect(ua->card); /* make sure that there are no pending USB requests */ - __list_for_each(midi, &ua->midi_list) + list_for_each(midi, &ua->midi_list) snd_usbmidi_disconnect(midi); abort_alsa_playback(ua); abort_alsa_capture(ua); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index e5c7f9f20fdd..d5438083fd6a 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -885,6 +885,7 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, case USB_ID(0x046d, 0x0808): case USB_ID(0x046d, 0x0809): + case USB_ID(0x046d, 0x081b): /* HD Webcam c310 */ case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */ case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */ case USB_ID(0x046d, 0x0991): diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index ebe91440a068..d42a584cf829 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -9,6 +9,8 @@ * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * + * Audio Advantage Micro II support added by: + * Przemek Rudy (prudy1@o2.pl) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,6 +32,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> +#include <sound/asoundef.h> #include <sound/core.h> #include <sound/control.h> #include <sound/hwdep.h> @@ -1315,6 +1318,211 @@ static struct std_mono_table ebox44_table[] = { {} }; +/* Audio Advantage Micro II findings: + * + * Mapping spdif AES bits to vendor register.bit: + * AES0: [0 0 0 0 2.3 2.2 2.1 2.0] - default 0x00 + * AES1: [3.3 3.2.3.1.3.0 2.7 2.6 2.5 2.4] - default: 0x01 + * AES2: [0 0 0 0 0 0 0 0] + * AES3: [0 0 0 0 0 0 x 0] - 'x' bit is set basing on standard usb request + * (UAC_EP_CS_ATTR_SAMPLE_RATE) for Audio Devices + * + * power on values: + * r2: 0x10 + * r3: 0x20 (b7 is zeroed just before playback (except IEC61937) and set + * just after it to 0xa0, presumably it disables/mutes some analog + * parts when there is no audio.) + * r9: 0x28 + * + * Optical transmitter on/off: + * vendor register.bit: 9.1 + * 0 - on (0x28 register value) + * 1 - off (0x2a register value) + * + */ +static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + int err; + struct usb_interface *iface; + struct usb_host_interface *alts; + unsigned int ep; + unsigned char data[3]; + int rate; + + ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff; + ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff; + ucontrol->value.iec958.status[2] = 0x00; + + /* use known values for that card: interface#1 altsetting#1 */ + iface = usb_ifnum_to_if(mixer->chip->dev, 1); + alts = &iface->altsetting[1]; + ep = get_endpoint(alts, 0)->bEndpointAddress; + + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_rcvctrlpipe(mixer->chip->dev, 0), + UAC_GET_CUR, + USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, + ep, + data, + sizeof(data)); + if (err < 0) + goto end; + + rate = data[0] | (data[1] << 8) | (data[2] << 16); + ucontrol->value.iec958.status[3] = (rate == 48000) ? + IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100; + + err = 0; +end: + return err; +} + +static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + int err; + u8 reg; + unsigned long priv_backup = kcontrol->private_value; + + reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) | + (ucontrol->value.iec958.status[0] & 0x0f); + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 2, + NULL, + 0); + if (err < 0) + goto end; + + kcontrol->private_value &= 0xfffff0f0; + kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; + kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f); + + reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ? + 0xa0 : 0x20; + reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f; + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 3, + NULL, + 0); + if (err < 0) + goto end; + + kcontrol->private_value &= 0xffff0fff; + kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8; + + /* The frequency bits in AES3 cannot be set via register access. */ + + /* Silently ignore any bits from the request that cannot be set. */ + + err = (priv_backup != kcontrol->private_value); +end: + return err; +} + +static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0x0f; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0x00; + ucontrol->value.iec958.status[3] = 0x00; + + return 0; +} + +static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02); + + return 0; +} + +static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + int err; + u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a; + + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 9, + NULL, + 0); + + if (!err) { + err = (reg != (kcontrol->private_value & 0x0ff)); + if (err) + kcontrol->private_value = reg; + } + + return err; +} + +static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = snd_microii_spdif_info, + .get = snd_microii_spdif_default_get, + .put = snd_microii_spdif_default_put, + .private_value = 0x00000100UL,/* reset value */ + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = snd_microii_spdif_info, + .get = snd_microii_spdif_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), + .info = snd_ctl_boolean_mono_info, + .get = snd_microii_spdif_switch_get, + .put = snd_microii_spdif_switch_put, + .private_value = 0x00000028UL,/* reset value */ + } +}; + +static int snd_microii_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) { + err = snd_ctl_add(mixer->chip->card, + snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer)); + if (err < 0) + return err; + } + + return err; +} + int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) { int err = 0; @@ -1353,6 +1561,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_xonar_u1_controls_create(mixer); break; + case USB_ID(0x0d8c, 0x0103): /* Audio Advantage Micro II */ + err = snd_microii_controls_create(mixer); + break; + case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ err = snd_nativeinstruments_create_mixer(mixer, snd_nativeinstruments_ta6_mixers, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 93b6e32cfead..15b151ed4899 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -202,13 +202,11 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt) { - struct usb_interface_descriptor *altsd = get_iface_desc(alts); - /* if endpoint doesn't have pitch control, bail out */ if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL)) return 0; - switch (altsd->bInterfaceProtocol) { + switch (fmt->protocol) { case UAC_VERSION_1: default: return init_pitch_v1(chip, iface, alts, fmt); @@ -300,6 +298,35 @@ static int deactivate_endpoints(struct snd_usb_substream *subs) return 0; } +static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, + unsigned int altsetting, + struct usb_host_interface **alts, + unsigned int *ep) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *altsd; + struct usb_endpoint_descriptor *epd; + + iface = usb_ifnum_to_if(dev, ifnum); + if (!iface || iface->num_altsetting < altsetting + 1) + return -ENOENT; + *alts = &iface->altsetting[altsetting]; + altsd = get_iface_desc(*alts); + if (altsd->bAlternateSetting != altsetting || + altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC || + (altsd->bInterfaceSubClass != 2 && + altsd->bInterfaceProtocol != 2 ) || + altsd->bNumEndpoints < 1) + return -ENOENT; + epd = get_endpoint(*alts, 0); + if (!usb_endpoint_is_isoc_in(epd) || + (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != + USB_ENDPOINT_USAGE_IMPLICIT_FB) + return -ENOENT; + *ep = epd->bEndpointAddress; + return 0; +} + /* * find a matching format and set up the interface */ @@ -395,6 +422,18 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) goto add_sync_ep; } } + if (is_playback && + attr == USB_ENDPOINT_SYNC_ASYNC && + altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && + altsd->bInterfaceProtocol == 2 && + altsd->bNumEndpoints == 1 && + USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ && + search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1, + altsd->bAlternateSetting, + &alts, &ep) >= 0) { + implicit_fb = 1; + goto add_sync_ep; + } if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 8b75bcf136f6..f5f0595ef9c7 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -461,6 +461,17 @@ YAMAHA_DEVICE(0x7000, "DTX"), YAMAHA_DEVICE(0x7010, "UB99"), #undef YAMAHA_DEVICE #undef YAMAHA_INTERFACE +/* this catches most recent vendor-specific Yamaha devices */ +{ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | + USB_DEVICE_ID_MATCH_INT_CLASS, + .idVendor = 0x0499, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_AUTODETECT + } +}, /* * Roland/RolandED/Edirol/BOSS devices @@ -1136,7 +1147,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, - /* TODO: add Roland M-1000 support */ { /* * Has ID 0x0038 when not in "Advanced Driver" mode; @@ -1251,7 +1261,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, - /* TODO: add Edirol M-100FX support */ { /* has ID 0x004e when not in "Advanced Driver" mode */ USB_DEVICE(0x0582, 0x004c), @@ -1371,20 +1380,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - /* has ID 0x006b when not in "Advanced Driver" mode */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x006a), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SP-606", - .ifnum = 3, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ /* has ID 0x006e when not in "Advanced Driver" mode */ USB_DEVICE(0x0582, 0x006d), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { @@ -1471,8 +1466,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, - /* TODO: add Roland V-SYNTH XT support */ - /* TODO: add BOSS GT-PRO support */ { /* has ID 0x008c when not in "Advanced Driver" mode */ USB_DEVICE(0x0582, 0x008b), @@ -1487,42 +1480,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, - /* TODO: add Edirol PC-80 support */ -{ - USB_DEVICE(0x0582, 0x0096), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UA-1EX", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x009a), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UM-3EX", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x000f, - .in_cables = 0x000f - } - } -}, { /* * This quirk is for the "Advanced Driver" mode. If off, the UA-4FX @@ -1553,124 +1510,8 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, - /* TODO: add Edirol MD-P1 support */ -{ - USB_DEVICE(0x582, 0x00a6), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "Juno-G", - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ - /* Roland SH-201 */ - USB_DEVICE(0x0582, 0x00ad), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SH-201", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* Advanced mode of the Roland VG-99, with MIDI and 24-bit PCM at 44.1 - * kHz. In standard mode, the device has ID 0582:00b3, and offers - * 16-bit PCM at 44.1 kHz with no MIDI. - */ - USB_DEVICE(0x0582, 0x00b2), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "VG-99", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0003 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* Roland SonicCell */ - USB_DEVICE(0x0582, 0x00c2), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "SonicCell", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, { /* Edirol M-16DX */ - /* FIXME: This quirk gives a good-working capture stream but the - * playback seems problematic because of lacking of sync - * with capture stream. It needs to sync with the capture - * clock. As now, you'll get frequent sound distortions - * via the playback. - */ USB_DEVICE(0x0582, 0x00c4), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { .ifnum = QUIRK_ANY_INTERFACE, @@ -1699,35 +1540,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - /* BOSS GT-10 */ - USB_DEVICE(0x0582, 0x00da), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } - } -}, -{ /* Advanced modes of the Edirol UA-25EX. * For the standard mode, UA-25EX has ID 0582:00e7, which * offers only 16-bit PCM at 44.1 kHz and no MIDI. @@ -1758,42 +1570,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - /* has ID 0x00ea when not in Advanced Driver mode */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x00e9), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "Roland", */ - /* .product_name = "UA-1G", */ - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x0104), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "Roland", */ - /* .product_name = "UM-1G", */ - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - } -}, -{ /* Edirol UM-3G */ USB_DEVICE_VENDOR_SPEC(0x0582, 0x0108), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { @@ -1806,92 +1582,49 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - /* Boss JS-8 Jam Station */ - USB_DEVICE(0x0582, 0x0109), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "BOSS", */ - /* .product_name = "JS-8", */ - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_STANDARD_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, -{ - /* has ID 0x0110 when not in Advanced Driver mode */ - USB_DEVICE_VENDOR_SPEC(0x0582, 0x010f), + /* only 44.1 kHz works at the moment */ + USB_DEVICE(0x0582, 0x0120), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "Roland", */ - /* .product_name = "A-PRO", */ - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0007 - } - } -}, -{ - /* Roland GAIA SH-01 */ - USB_DEVICE(0x0582, 0x0111), - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .vendor_name = "Roland", - .product_name = "GAIA", + /* .product_name = "OCTO-CAPTURE", */ .ifnum = QUIRK_ANY_INTERFACE, .type = QUIRK_COMPOSITE, .data = (const struct snd_usb_audio_quirk[]) { { .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = &(const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0003, - .in_cables = 0x0003 + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels = 10, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x05, + .ep_attr = 0x05, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } } }, { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0113), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "BOSS", */ - /* .product_name = "ME-25", */ - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels = 12, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x85, + .ep_attr = 0x25, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } }, { .ifnum = 2, @@ -1902,30 +1635,12 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0127), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "Roland", */ - /* .product_name = "GR-55", */ - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE + .ifnum = 3, + .type = QUIRK_IGNORE_INTERFACE }, { - .ifnum = 2, - .type = QUIRK_MIDI_STANDARD_INTERFACE + .ifnum = 4, + .type = QUIRK_IGNORE_INTERFACE }, { .ifnum = -1 @@ -1934,34 +1649,49 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - /* Added support for Roland UM-ONE which differs from UM-1 */ - USB_DEVICE(0x0582, 0x012a), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "ROLAND", */ - /* .product_name = "UM-ONE", */ - .ifnum = 0, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0003 - } - } -}, -{ - USB_DEVICE(0x0582, 0x011e), + /* only 44.1 kHz works at the moment */ + USB_DEVICE(0x0582, 0x012f), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "BOSS", */ - /* .product_name = "BR-800", */ + /* .vendor_name = "Roland", */ + /* .product_name = "QUAD-CAPTURE", */ .ifnum = QUIRK_ANY_INTERFACE, .type = QUIRK_COMPOSITE, .data = (const struct snd_usb_audio_quirk[]) { { .ifnum = 0, - .type = QUIRK_AUDIO_STANDARD_INTERFACE + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels = 4, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x05, + .ep_attr = 0x05, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } }, { .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = & (const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .channels = 6, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x85, + .ep_attr = 0x25, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } }, { .ifnum = 2, @@ -1972,38 +1702,12 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { - .ifnum = -1 - } - } - } -}, -{ - USB_DEVICE(0x0582, 0x0130), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "BOSS", */ - /* .product_name = "MICRO BR-80", */ - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 0, + .ifnum = 3, .type = QUIRK_IGNORE_INTERFACE }, { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 3, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } + .ifnum = 4, + .type = QUIRK_IGNORE_INTERFACE }, { .ifnum = -1 @@ -2011,34 +1715,15 @@ YAMAHA_DEVICE(0x7010, "UB99"), } } }, +/* this catches most recent vendor-specific Roland devices */ { - USB_DEVICE(0x0582, 0x014d), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - /* .vendor_name = "BOSS", */ - /* .product_name = "GT-100", */ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | + USB_DEVICE_ID_MATCH_INT_CLASS, + .idVendor = 0x0582, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 3, - .type = QUIRK_MIDI_FIXED_ENDPOINT, - .data = & (const struct snd_usb_midi_endpoint_info) { - .out_cables = 0x0001, - .in_cables = 0x0001 - } - }, - { - .ifnum = -1 - } - } + .type = QUIRK_AUTODETECT } }, @@ -3434,4 +3119,16 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +{ + /* + * The original product_name is "USB Sound Device", however this name + * is also used by the CM106 based cards, so make it unique. + */ + USB_DEVICE(0x0d8c, 0x0103), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .product_name = "Audio Advantage MicroII", + .ifnum = QUIRK_NO_INTERFACE + } +}, + #undef USB_DEVICE_VENDOR_SPEC diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 3879eae7e874..1bc45e71f1fe 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/usb.h> #include <linux/usb/audio.h> +#include <linux/usb/midi.h> #include <sound/control.h> #include <sound/core.h> @@ -128,6 +129,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, { struct audioformat *fp; struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; int stream, err; unsigned *rate_table = NULL; @@ -165,6 +167,9 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, return -EINVAL; } alts = &iface->altsetting[fp->altset_idx]; + altsd = get_iface_desc(alts); + fp->protocol = altsd->bInterfaceProtocol; + if (fp->datainterval == 0) fp->datainterval = snd_usb_parse_datainterval(chip, alts); if (fp->maxpacksize == 0) @@ -175,6 +180,212 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, return 0; } +static int create_auto_pcm_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver) +{ + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_endpoint_descriptor *epd; + struct uac1_as_header_descriptor *ashd; + struct uac_format_type_i_discrete_descriptor *fmtd; + + /* + * Most Roland/Yamaha audio streaming interfaces have more or less + * standard descriptors, but older devices might lack descriptors, and + * future ones might change, so ensure that we fail silently if the + * interface doesn't look exactly right. + */ + + /* must have a non-zero altsetting for streaming */ + if (iface->num_altsetting < 2) + return -ENODEV; + alts = &iface->altsetting[1]; + altsd = get_iface_desc(alts); + + /* must have an isochronous endpoint for streaming */ + if (altsd->bNumEndpoints < 1) + return -ENODEV; + epd = get_endpoint(alts, 0); + if (!usb_endpoint_xfer_isoc(epd)) + return -ENODEV; + + /* must have format descriptors */ + ashd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, + UAC_AS_GENERAL); + fmtd = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, + UAC_FORMAT_TYPE); + if (!ashd || ashd->bLength < 7 || + !fmtd || fmtd->bLength < 8) + return -ENODEV; + + return create_standard_audio_quirk(chip, iface, driver, NULL); +} + +static int create_yamaha_midi_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + struct usb_host_interface *alts) +{ + static const struct snd_usb_audio_quirk yamaha_midi_quirk = { + .type = QUIRK_MIDI_YAMAHA + }; + struct usb_midi_in_jack_descriptor *injd; + struct usb_midi_out_jack_descriptor *outjd; + + /* must have some valid jack descriptors */ + injd = snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, USB_MS_MIDI_IN_JACK); + outjd = snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, USB_MS_MIDI_OUT_JACK); + if (!injd && !outjd) + return -ENODEV; + if (injd && (injd->bLength < 5 || + (injd->bJackType != USB_MS_EMBEDDED && + injd->bJackType != USB_MS_EXTERNAL))) + return -ENODEV; + if (outjd && (outjd->bLength < 6 || + (outjd->bJackType != USB_MS_EMBEDDED && + outjd->bJackType != USB_MS_EXTERNAL))) + return -ENODEV; + return create_any_midi_quirk(chip, iface, driver, &yamaha_midi_quirk); +} + +static int create_roland_midi_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + struct usb_host_interface *alts) +{ + static const struct snd_usb_audio_quirk roland_midi_quirk = { + .type = QUIRK_MIDI_ROLAND + }; + u8 *roland_desc = NULL; + + /* might have a vendor-specific descriptor <06 24 F1 02 ...> */ + for (;;) { + roland_desc = snd_usb_find_csint_desc(alts->extra, + alts->extralen, + roland_desc, 0xf1); + if (!roland_desc) + return -ENODEV; + if (roland_desc[0] < 6 || roland_desc[3] != 2) + continue; + return create_any_midi_quirk(chip, iface, driver, + &roland_midi_quirk); + } +} + +static int create_std_midi_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + struct usb_host_interface *alts) +{ + struct usb_ms_header_descriptor *mshd; + struct usb_ms_endpoint_descriptor *msepd; + + /* must have the MIDIStreaming interface header descriptor*/ + mshd = (struct usb_ms_header_descriptor *)alts->extra; + if (alts->extralen < 7 || + mshd->bLength < 7 || + mshd->bDescriptorType != USB_DT_CS_INTERFACE || + mshd->bDescriptorSubtype != USB_MS_HEADER) + return -ENODEV; + /* must have the MIDIStreaming endpoint descriptor*/ + msepd = (struct usb_ms_endpoint_descriptor *)alts->endpoint[0].extra; + if (alts->endpoint[0].extralen < 4 || + msepd->bLength < 4 || + msepd->bDescriptorType != USB_DT_CS_ENDPOINT || + msepd->bDescriptorSubtype != UAC_MS_GENERAL || + msepd->bNumEmbMIDIJack < 1 || + msepd->bNumEmbMIDIJack > 16) + return -ENODEV; + + return create_any_midi_quirk(chip, iface, driver, NULL); +} + +static int create_auto_midi_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver) +{ + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_endpoint_descriptor *epd; + int err; + + alts = &iface->altsetting[0]; + altsd = get_iface_desc(alts); + + /* must have at least one bulk/interrupt endpoint for streaming */ + if (altsd->bNumEndpoints < 1) + return -ENODEV; + epd = get_endpoint(alts, 0); + if (!usb_endpoint_xfer_bulk(epd) || + !usb_endpoint_xfer_int(epd)) + return -ENODEV; + + switch (USB_ID_VENDOR(chip->usb_id)) { + case 0x0499: /* Yamaha */ + err = create_yamaha_midi_quirk(chip, iface, driver, alts); + if (err < 0 && err != -ENODEV) + return err; + break; + case 0x0582: /* Roland */ + err = create_roland_midi_quirk(chip, iface, driver, alts); + if (err < 0 && err != -ENODEV) + return err; + break; + } + + return create_std_midi_quirk(chip, iface, driver, alts); +} + +static int create_autodetect_quirk(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver) +{ + int err; + + err = create_auto_pcm_quirk(chip, iface, driver); + if (err == -ENODEV) + err = create_auto_midi_quirk(chip, iface, driver); + return err; +} + +static int create_autodetect_quirks(struct snd_usb_audio *chip, + struct usb_interface *iface, + struct usb_driver *driver, + const struct snd_usb_audio_quirk *quirk) +{ + int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; + int ifcount, ifnum, err; + + err = create_autodetect_quirk(chip, iface, driver); + if (err < 0) + return err; + + /* + * ALSA PCM playback/capture devices cannot be registered in two steps, + * so we have to claim the other corresponding interface here. + */ + ifcount = chip->dev->actconfig->desc.bNumInterfaces; + for (ifnum = 0; ifnum < ifcount; ifnum++) { + if (ifnum == probed_ifnum || quirk->ifnum >= 0) + continue; + iface = usb_ifnum_to_if(chip->dev, ifnum); + if (!iface || + usb_interface_claimed(iface) || + get_iface_desc(iface->altsetting)->bInterfaceClass != + USB_CLASS_VENDOR_SPEC) + continue; + + err = create_autodetect_quirk(chip, iface, driver); + if (err >= 0) + usb_driver_claim_interface(driver, iface, (void *)-1L); + } + + return 0; +} + /* * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface. * The only way to detect the sample rate is by looking at wMaxPacketSize. @@ -303,9 +514,11 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, static const quirk_func_t quirk_funcs[] = { [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk, [QUIRK_COMPOSITE] = create_composite_quirk, + [QUIRK_AUTODETECT] = create_autodetect_quirks, [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk, [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk, [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk, + [QUIRK_MIDI_ROLAND] = create_any_midi_quirk, [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk, [QUIRK_MIDI_NOVATION] = create_any_midi_quirk, [QUIRK_MIDI_RAW_BYTES] = create_any_midi_quirk, diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 7db2f8958e79..c4339f97226b 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -493,10 +493,10 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) altsd = get_iface_desc(alts); protocol = altsd->bInterfaceProtocol; /* skip invalid one */ - if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && + if (((altsd->bInterfaceClass != USB_CLASS_AUDIO || + (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING && + altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC)) && altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || - (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING && - altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) || altsd->bNumEndpoints < 1 || le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0) continue; @@ -512,6 +512,15 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) if (snd_usb_apply_interface_quirk(chip, iface_no, altno)) continue; + /* + * Roland audio streaming interfaces are marked with protocols + * 0/1/2, but are UAC 1 compatible. + */ + if (USB_ID_VENDOR(chip->usb_id) == 0x0582 && + altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && + protocol <= 2) + protocol = UAC_VERSION_1; + chconfig = 0; /* get audio formats */ switch (protocol) { @@ -635,6 +644,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; fp->datainterval = snd_usb_parse_datainterval(chip, alts); + fp->protocol = protocol; fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); fp->channels = num_channels; if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) @@ -676,7 +686,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) } /* ok, let's parse further... */ - if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) { + if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream) < 0) { kfree(fp->rate_table); kfree(fp->chmap); kfree(fp); diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index bc43bcaddf4d..caabe9b3af49 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -72,9 +72,11 @@ struct snd_usb_audio { enum quirk_type { QUIRK_IGNORE_INTERFACE, QUIRK_COMPOSITE, + QUIRK_AUTODETECT, QUIRK_MIDI_STANDARD_INTERFACE, QUIRK_MIDI_FIXED_ENDPOINT, QUIRK_MIDI_YAMAHA, + QUIRK_MIDI_ROLAND, QUIRK_MIDI_MIDIMAN, QUIRK_MIDI_NOVATION, QUIRK_MIDI_RAW_BYTES, diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 9af7c1f17413..1f9bbd55553f 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -150,7 +150,7 @@ MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>"); MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604), "NAME_ALLCAPS"(0x8001)(0x8005)(0x8007) }}"); +MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604),"NAME_ALLCAPS"(0x8001)(0x8005)(0x8007)}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index b37653247ef4..4967fe9c938d 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -695,9 +695,6 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate) ((char*)(usbdata + i))[1] = ra[i].c2; usb_fill_bulk_urb(us->urb[i], usX2Y->dev, usb_sndbulkpipe(usX2Y->dev, 4), usbdata + i, 2, i_usX2Y_04Int, usX2Y); -#ifdef OLD_USB - us->urb[i]->transfer_flags = USB_QUEUE_BULK; -#endif } us->submitted = 0; us->len = NOOF_SETRATE_URBS; |