diff options
Diffstat (limited to 'sound/usb')
37 files changed, 5755 insertions, 4773 deletions
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index c570ae3e6d5..44d6d2ec964 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -22,8 +22,7 @@ config SND_USB_AUDIO will be called snd-usb-audio. config SND_USB_UA101 - tristate "Edirol UA-101/UA-1000 driver (EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "Edirol UA-101/UA-1000 driver" select SND_PCM select SND_RAWMIDI help @@ -65,6 +64,7 @@ config SND_USB_CAIAQ * Native Instruments Audio 8 DJ * Native Instruments Guitar Rig Session I/O * Native Instruments Guitar Rig mobile + * Native Instruments Traktor Kontrol X1 To compile this driver as a module, choose M here: the module will be called snd-usb-caiaq. diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 5bf64aef955..e7ac7f493a8 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -2,14 +2,24 @@ # Makefile for ALSA # -snd-usb-audio-objs := usbaudio.o usbmixer.o -snd-usb-lib-objs := usbmidi.o -snd-ua101-objs := ua101.o +snd-usb-audio-objs := card.o \ + mixer.o \ + mixer_quirks.o \ + proc.o \ + quirks.o \ + format.o \ + endpoint.o \ + urb.o \ + pcm.o \ + helper.o + +snd-usbmidi-lib-objs := midi.o # Toplevel Module Dependency -obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o -obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o snd-usb-lib.o -obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o -obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o +obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o + +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) += usx2y/ caiaq/ +obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c index 537102ba6b9..36ed703a741 100644 --- a/sound/usb/caiaq/control.c +++ b/sound/usb/caiaq/control.c @@ -35,33 +35,41 @@ static int control_info(struct snd_kcontrol *kcontrol, struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); int pos = kcontrol->private_value; int is_intval = pos & CNT_INTVAL; - unsigned int id = dev->chip.usb_id; + int maxval = 63; uinfo->count = 1; pos &= ~CNT_INTVAL; - if (id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ) - && (pos == 0)) { - /* current input mode of A8DJ */ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 2; - return 0; - } + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): + if (pos == 0) { + /* current input mode of A8DJ */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 2; + return 0; + } + break; - if (id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ) - && (pos == 0)) { - /* current input mode of A4DJ */ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): + if (pos == 0) { + /* current input mode of A4DJ */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; + } + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + maxval = 127; + break; } if (is_intval) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 64; + uinfo->value.integer.max = maxval; } else { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->value.integer.min = 0; @@ -102,9 +110,10 @@ static int control_put(struct snd_kcontrol *kcontrol, struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); int pos = kcontrol->private_value; + unsigned char cmd = EP1_CMD_WRITE_IO; - if (dev->chip.usb_id == - USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)) { + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): { /* A4DJ has only one control */ /* do not expose hardware input mode 0 */ dev->control_state[0] = ucontrol->value.integer.value[0] + 1; @@ -113,10 +122,15 @@ static int control_put(struct snd_kcontrol *kcontrol, return 1; } + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + cmd = EP1_CMD_DIMM_LEDS; + break; + } + if (pos & CNT_INTVAL) { dev->control_state[pos & ~CNT_INTVAL] = ucontrol->value.integer.value[0]; - snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, + snd_usb_caiaq_send_command(dev, cmd, dev->control_state, sizeof(dev->control_state)); } else { if (ucontrol->value.integer.value[0]) @@ -124,7 +138,7 @@ static int control_put(struct snd_kcontrol *kcontrol, else dev->control_state[pos / 8] &= ~(1 << (pos % 8)); - snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, + snd_usb_caiaq_send_command(dev, cmd, dev->control_state, sizeof(dev->control_state)); } @@ -273,6 +287,43 @@ static struct caiaq_controller a4dj_controller[] = { { "Current input mode", 0 | CNT_INTVAL } }; +static struct caiaq_controller kontrolx1_controller[] = { + { "LED FX A: ON", 7 | CNT_INTVAL }, + { "LED FX A: 1", 6 | CNT_INTVAL }, + { "LED FX A: 2", 5 | CNT_INTVAL }, + { "LED FX A: 3", 4 | CNT_INTVAL }, + { "LED FX B: ON", 3 | CNT_INTVAL }, + { "LED FX B: 1", 2 | CNT_INTVAL }, + { "LED FX B: 2", 1 | CNT_INTVAL }, + { "LED FX B: 3", 0 | CNT_INTVAL }, + + { "LED Hotcue", 28 | CNT_INTVAL }, + { "LED Shift (white)", 29 | CNT_INTVAL }, + { "LED Shift (green)", 30 | CNT_INTVAL }, + + { "LED Deck A: FX1", 24 | CNT_INTVAL }, + { "LED Deck A: FX2", 25 | CNT_INTVAL }, + { "LED Deck A: IN", 17 | CNT_INTVAL }, + { "LED Deck A: OUT", 16 | CNT_INTVAL }, + { "LED Deck A: < BEAT", 19 | CNT_INTVAL }, + { "LED Deck A: BEAT >", 18 | CNT_INTVAL }, + { "LED Deck A: CUE/ABS", 21 | CNT_INTVAL }, + { "LED Deck A: CUP/REL", 20 | CNT_INTVAL }, + { "LED Deck A: PLAY", 23 | CNT_INTVAL }, + { "LED Deck A: SYNC", 22 | CNT_INTVAL }, + + { "LED Deck B: FX1", 26 | CNT_INTVAL }, + { "LED Deck B: FX2", 27 | CNT_INTVAL }, + { "LED Deck B: IN", 15 | CNT_INTVAL }, + { "LED Deck B: OUT", 14 | CNT_INTVAL }, + { "LED Deck B: < BEAT", 13 | CNT_INTVAL }, + { "LED Deck B: BEAT >", 12 | CNT_INTVAL }, + { "LED Deck B: CUE/ABS", 11 | CNT_INTVAL }, + { "LED Deck B: CUP/REL", 10 | CNT_INTVAL }, + { "LED Deck B: PLAY", 9 | CNT_INTVAL }, + { "LED Deck B: SYNC", 8 | CNT_INTVAL }, +}; + static int __devinit add_controls(struct caiaq_controller *c, int num, struct snd_usb_caiaqdev *dev) { @@ -321,10 +372,16 @@ int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev) ret = add_controls(a8dj_controller, ARRAY_SIZE(a8dj_controller), dev); break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): ret = add_controls(a4dj_controller, ARRAY_SIZE(a4dj_controller), dev); break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + ret = add_controls(kontrolx1_controller, + ARRAY_SIZE(kontrolx1_controller), dev); + break; } return ret; diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index afc5aeb6800..80527182767 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -47,7 +47,8 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, Audio 4 DJ}," "{Native Instruments, Audio 8 DJ}," "{Native Instruments, Session I/O}," - "{Native Instruments, GuitarRig mobile}"); + "{Native Instruments, GuitarRig mobile}" + "{Native Instruments, Traktor Kontrol X1}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ @@ -128,6 +129,11 @@ static struct usb_device_id snd_usb_id_table[] = { .idVendor = USB_VID_NATIVEINSTRUMENTS, .idProduct = USB_PID_AUDIO2DJ }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_TRAKTORKONTROLX1 + }, { /* terminator */ } }; diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index 44e3edf88be..f1117ecc84f 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -5,18 +5,20 @@ #define USB_VID_NATIVEINSTRUMENTS 0x17cc -#define USB_PID_RIGKONTROL2 0x1969 -#define USB_PID_RIGKONTROL3 0x1940 -#define USB_PID_KORECONTROLLER 0x4711 -#define USB_PID_KORECONTROLLER2 0x4712 -#define USB_PID_AK1 0x0815 -#define USB_PID_AUDIO2DJ 0x041c -#define USB_PID_AUDIO4DJ 0x0839 -#define USB_PID_AUDIO8DJ 0x1978 -#define USB_PID_SESSIONIO 0x1915 -#define USB_PID_GUITARRIGMOBILE 0x0d8d +#define USB_PID_RIGKONTROL2 0x1969 +#define USB_PID_RIGKONTROL3 0x1940 +#define USB_PID_KORECONTROLLER 0x4711 +#define USB_PID_KORECONTROLLER2 0x4712 +#define USB_PID_AK1 0x0815 +#define USB_PID_AUDIO2DJ 0x041c +#define USB_PID_AUDIO4DJ 0x0839 +#define USB_PID_AUDIO8DJ 0x1978 +#define USB_PID_SESSIONIO 0x1915 +#define USB_PID_GUITARRIGMOBILE 0x0d8d +#define USB_PID_TRAKTORKONTROLX1 0x2305 #define EP1_BUFSIZE 64 +#define EP4_BUFSIZE 512 #define CAIAQ_USB_STR_LEN 0xff #define MAX_STREAMS 32 @@ -104,6 +106,8 @@ struct snd_usb_caiaqdev { struct input_dev *input_dev; char phys[64]; /* physical device path */ unsigned short keycode[64]; + struct urb *ep4_in_urb; + unsigned char ep4_in_buf[EP4_BUFSIZE]; #endif /* ALSA */ diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index a48d309bd94..8bbfbfd4c65 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -16,9 +16,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/gfp.h> #include <linux/init.h> #include <linux/usb.h> #include <linux/usb/input.h> +#include <sound/core.h> #include <sound/pcm.h> #include "device.h" @@ -65,6 +67,8 @@ static unsigned short keycode_kore[] = { KEY_BRL_DOT5 }; +#define KONTROLX1_INPUTS 40 + #define DEG90 (range / 2) #define DEG180 (range) #define DEG270 (DEG90 + DEG180) @@ -162,6 +166,17 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]); input_sync(input_dev); break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + input_report_abs(input_dev, ABS_HAT0X, (buf[8] << 8) | buf[9]); + input_report_abs(input_dev, ABS_HAT0Y, (buf[4] << 8) | buf[5]); + input_report_abs(input_dev, ABS_HAT1X, (buf[12] << 8) | buf[13]); + input_report_abs(input_dev, ABS_HAT1Y, (buf[2] << 8) | buf[3]); + input_report_abs(input_dev, ABS_HAT2X, (buf[15] << 8) | buf[15]); + input_report_abs(input_dev, ABS_HAT2Y, (buf[0] << 8) | buf[1]); + input_report_abs(input_dev, ABS_HAT3X, (buf[10] << 8) | buf[11]); + input_report_abs(input_dev, ABS_HAT3Y, (buf[6] << 8) | buf[7]); + input_sync(input_dev); + break; } } @@ -201,7 +216,7 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, } static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, - char *buf, unsigned int len) + unsigned char *buf, unsigned int len) { struct input_dev *input_dev = dev->input_dev; unsigned short *keycode = input_dev->keycode; @@ -218,15 +233,84 @@ static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, input_report_key(input_dev, keycode[i], buf[i / 8] & (1 << (i % 8))); - if (dev->chip.usb_id == - USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER) || - dev->chip.usb_id == - USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2)) + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + /* rotary encoders */ + input_report_abs(dev->input_dev, ABS_X, buf[5] & 0xf); + input_report_abs(dev->input_dev, ABS_Y, buf[5] >> 4); + input_report_abs(dev->input_dev, ABS_Z, buf[6] & 0xf); + input_report_abs(dev->input_dev, ABS_MISC, buf[6] >> 4); + break; + } input_sync(input_dev); } +static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb) +{ + struct snd_usb_caiaqdev *dev = urb->context; + unsigned char *buf = urb->transfer_buffer; + int ret; + + if (urb->status || !dev || urb != dev->ep4_in_urb) + return; + + if (urb->actual_length < 24) + goto requeue; + + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + if (buf[0] & 0x3) + snd_caiaq_input_read_io(dev, buf + 1, 7); + + if (buf[0] & 0x4) + snd_caiaq_input_read_analog(dev, buf + 8, 16); + + break; + } + +requeue: + dev->ep4_in_urb->actual_length = 0; + ret = usb_submit_urb(dev->ep4_in_urb, GFP_ATOMIC); + if (ret < 0) + log("unable to submit urb. OOM!?\n"); +} + +static int snd_usb_caiaq_input_open(struct input_dev *idev) +{ + struct snd_usb_caiaqdev *dev = input_get_drvdata(idev); + + if (!dev) + return -EINVAL; + + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0) + return -EIO; + break; + } + + return 0; +} + +static void snd_usb_caiaq_input_close(struct input_dev *idev) +{ + struct snd_usb_caiaqdev *dev = input_get_drvdata(idev); + + if (!dev) + return; + + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + usb_kill_urb(dev->ep4_in_urb); + break; + } +} + void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, char *buf, unsigned int len) @@ -251,7 +335,7 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev) { struct usb_device *usb_dev = dev->chip.dev; struct input_dev *input; - int i, ret; + int i, ret = 0; input = input_allocate_device(); if (!input) @@ -265,7 +349,9 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev) usb_to_input_id(usb_dev, &input->id); input->dev.parent = &usb_dev->dev; - switch (dev->chip.usb_id) { + input_set_drvdata(input, dev); + + switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | @@ -326,25 +412,72 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev) input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1); snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5); break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->absbit[0] = BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) | + BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y) | + BIT_MASK(ABS_HAT2X) | BIT_MASK(ABS_HAT2Y) | + BIT_MASK(ABS_HAT3X) | BIT_MASK(ABS_HAT3Y) | + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | + BIT_MASK(ABS_Z); + input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); + BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLX1_INPUTS); + for (i = 0; i < KONTROLX1_INPUTS; i++) + dev->keycode[i] = BTN_MISC + i; + input->keycodemax = KONTROLX1_INPUTS; + + /* analog potentiometers */ + input_set_abs_params(input, ABS_HAT0X, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_HAT0Y, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_HAT1X, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_HAT1Y, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_HAT2X, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_HAT2Y, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_HAT3X, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_HAT3Y, 0, 4096, 0, 10); + + /* rotary encoders */ + input_set_abs_params(input, ABS_X, 0, 0xf, 0, 1); + input_set_abs_params(input, ABS_Y, 0, 0xf, 0, 1); + input_set_abs_params(input, ABS_Z, 0, 0xf, 0, 1); + input_set_abs_params(input, ABS_MISC, 0, 0xf, 0, 1); + + dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->ep4_in_urb) { + ret = -ENOMEM; + goto exit_free_idev; + } + + usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev, + usb_rcvbulkpipe(usb_dev, 0x4), + dev->ep4_in_buf, EP4_BUFSIZE, + snd_usb_caiaq_ep4_reply_dispatch, dev); + + snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5); + + break; default: /* no input methods supported on this device */ - input_free_device(input); - return 0; + goto exit_free_idev; } + input->open = snd_usb_caiaq_input_open; + input->close = snd_usb_caiaq_input_close; input->keycode = dev->keycode; input->keycodesize = sizeof(unsigned short); for (i = 0; i < input->keycodemax; i++) __set_bit(dev->keycode[i], input->keybit); ret = input_register_device(input); - if (ret < 0) { - input_free_device(input); - return ret; - } + if (ret < 0) + goto exit_free_idev; dev->input_dev = input; return 0; + +exit_free_idev: + input_free_device(input); + return ret; } void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev) @@ -352,6 +485,10 @@ void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev) if (!dev || !dev->input_dev) return; + usb_kill_urb(dev->ep4_in_urb); + usb_free_urb(dev->ep4_in_urb); + dev->ep4_in_urb = NULL; + input_unregister_device(dev->input_dev); dev->input_dev = NULL; } diff --git a/sound/usb/card.c b/sound/usb/card.c new file mode 100644 index 00000000000..da1346bd485 --- /dev/null +++ b/sound/usb/card.c @@ -0,0 +1,652 @@ +/* + * (Tentative) USB Audio Driver for ALSA + * + * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> + * + * Many codes borrowed from audio.c by + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * NOTES: + * + * - async unlink should be used for avoiding the sleep inside lock. + * 2.4.22 usb-uhci seems buggy for async unlinking and results in + * oops. in such a cse, pass async_unlink=0 option. + * - the linked URBs would be preferred but not used so far because of + * the instability of unlinking. + * - type II is not supported properly. there is no device which supports + * this type *correctly*. SB extigy looks as if it supports, but it's + * indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream). + */ + + +#include <linux/bitops.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/usb.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/usb/audio.h> +#include <linux/usb/audio-v2.h> + +#include <sound/core.h> +#include <sound/info.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> + +#include "usbaudio.h" +#include "card.h" +#include "midi.h" +#include "mixer.h" +#include "proc.h" +#include "quirks.h" +#include "endpoint.h" +#include "helper.h" +#include "debug.h" +#include "pcm.h" +#include "urb.h" +#include "format.h" + +MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); +MODULE_DESCRIPTION("USB Audio"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}"); + + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ +/* Vendor/product IDs for this card */ +static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; +static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; +static int nrpacks = 8; /* max. number of packets per urb */ +static int async_unlink = 1; +static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ +static int ignore_ctl_error; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for the USB audio adapter."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable USB audio adapter."); +module_param_array(vid, int, NULL, 0444); +MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device."); +module_param_array(pid, int, NULL, 0444); +MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); +module_param(nrpacks, int, 0644); +MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); +module_param(async_unlink, bool, 0444); +MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); +module_param_array(device_setup, int, NULL, 0444); +MODULE_PARM_DESC(device_setup, "Specific device setup (if needed)."); +module_param(ignore_ctl_error, bool, 0444); +MODULE_PARM_DESC(ignore_ctl_error, + "Ignore errors from USB controller for mixer interfaces."); + +/* + * we keep the snd_usb_audio_t instances by ourselves for merging + * the all interfaces on the same card as one sound device. + */ + +static DEFINE_MUTEX(register_mutex); +static struct snd_usb_audio *usb_chip[SNDRV_CARDS]; +static struct usb_driver usb_audio_driver; + +/* + * disconnect streams + * called from snd_usb_audio_disconnect() + */ +static void snd_usb_stream_disconnect(struct list_head *head) +{ + int idx; + struct snd_usb_stream *as; + struct snd_usb_substream *subs; + + as = list_entry(head, struct snd_usb_stream, list); + for (idx = 0; idx < 2; idx++) { + subs = &as->substream[idx]; + if (!subs->num_formats) + return; + snd_usb_release_substream_urbs(subs, 1); + subs->interface = -1; + } +} + +static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface) +{ + struct usb_device *dev = chip->dev; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_interface *iface = usb_ifnum_to_if(dev, interface); + + if (!iface) { + snd_printk(KERN_ERR "%d:%u:%d : does not exist\n", + dev->devnum, ctrlif, interface); + return -EINVAL; + } + + 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) { + int err = snd_usbmidi_create(chip->card, iface, + &chip->midi_list, NULL); + if (err < 0) { + snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", + dev->devnum, ctrlif, interface); + return -EINVAL; + } + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); + + return 0; + } + + if ((altsd->bInterfaceClass != USB_CLASS_AUDIO && + altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) || + altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) { + snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", + dev->devnum, ctrlif, interface, altsd->bInterfaceClass); + /* skip non-supported classes */ + return -EINVAL; + } + + if (snd_usb_get_speed(dev) == USB_SPEED_LOW) { + snd_printk(KERN_ERR "low speed audio streaming not supported\n"); + return -EINVAL; + } + + if (! snd_usb_parse_audio_endpoints(chip, interface)) { + usb_set_interface(dev, interface, 0); /* reset the current interface */ + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); + return -EINVAL; + } + + return 0; +} + +/* + * parse audio control descriptor and create pcm/midi streams + */ +static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) +{ + struct usb_device *dev = chip->dev; + struct usb_host_interface *host_iface; + struct usb_interface_descriptor *altsd; + void *control_header; + int i, protocol; + + /* find audiocontrol interface */ + host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0]; + control_header = snd_usb_find_csint_desc(host_iface->extra, + host_iface->extralen, + NULL, UAC_HEADER); + altsd = get_iface_desc(host_iface); + protocol = altsd->bInterfaceProtocol; + + if (!control_header) { + snd_printk(KERN_ERR "cannot find UAC_HEADER\n"); + return -EINVAL; + } + + switch (protocol) { + case UAC_VERSION_1: { + struct uac_ac_header_descriptor_v1 *h1 = control_header; + + if (!h1->bInCollection) { + snd_printk(KERN_INFO "skipping empty audio interface (v1)\n"); + return -EINVAL; + } + + if (h1->bLength < sizeof(*h1) + h1->bInCollection) { + snd_printk(KERN_ERR "invalid UAC_HEADER (v1)\n"); + return -EINVAL; + } + + for (i = 0; i < h1->bInCollection; i++) + snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]); + + break; + } + + case UAC_VERSION_2: { + struct uac_clock_source_descriptor *cs; + struct usb_interface_assoc_descriptor *assoc = + usb_ifnum_to_if(dev, ctrlif)->intf_assoc; + + if (!assoc) { + snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n"); + return -EINVAL; + } + + /* FIXME: for now, we expect there is at least one clock source + * descriptor and we always take the first one. + * We should properly support devices with multiple clock sources, + * clock selectors and sample rate conversion units. */ + + cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, + NULL, UAC2_CLOCK_SOURCE); + + if (!cs) { + snd_printk(KERN_ERR "CLOCK_SOURCE |