diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-28 14:25:01 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-10-28 14:25:01 -0700 |
commit | 68d99b2c8efcb6ed3807a55569300c53b5f88be5 (patch) | |
tree | f189c8f2132d3668a2f0e503f5c3f8695b26a1c8 /sound/usb | |
parent | 0e59e7e7feb5a12938fbf9135147eeda3238c6c4 (diff) | |
parent | 8128c9f21509f9a8b6da94ac432d845dda458406 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (549 commits)
ALSA: hda - Fix ADC input-amp handling for Cx20549 codec
ALSA: hda - Keep EAPD turned on for old Conexant chips
ALSA: hda/realtek - Fix missing volume controls with ALC260
ASoC: wm8940: Properly set codec->dapm.bias_level
ALSA: hda - Fix pin-config for ASUS W90V
ALSA: hda - Fix surround/CLFE headphone and speaker pins order
ALSA: hda - Fix typo
ALSA: Update the sound git tree URL
ALSA: HDA: Add new revision for ALC662
ASoC: max98095: Convert codec->hw_write to snd_soc_write
ASoC: keep pointer to resource so it can be freed
ASoC: sgtl5000: Fix wrong mask in some snd_soc_update_bits calls
ASoC: wm8996: Fix wrong mask for setting WM8996_AIF_CLOCKING_2
ASoC: da7210: Add support for line out and DAC
ASoC: da7210: Add support for DAPM
ALSA: hda/realtek - Fix DAC assignments of multiple speakers
ASoC: Use SGTL5000_LINREG_VDDD_MASK instead of hardcoded mask value
ASoC: Set sgtl5000->ldo in ldo_regulator_register
ASoC: wm8996: Use SND_SOC_DAPM_AIF_OUT for AIF2 Capture
ASoC: wm8994: Use SND_SOC_DAPM_AIF_OUT for AIF3 Capture
...
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/6fire/firmware.c | 25 | ||||
-rw-r--r-- | sound/usb/Kconfig | 2 | ||||
-rw-r--r-- | sound/usb/Makefile | 12 | ||||
-rw-r--r-- | sound/usb/caiaq/device.c | 8 | ||||
-rw-r--r-- | sound/usb/caiaq/device.h | 1 | ||||
-rw-r--r-- | sound/usb/caiaq/input.c | 155 | ||||
-rw-r--r-- | sound/usb/card.c | 4 | ||||
-rw-r--r-- | sound/usb/card.h | 2 | ||||
-rw-r--r-- | sound/usb/clock.c | 12 | ||||
-rw-r--r-- | sound/usb/endpoint.c | 1199 | ||||
-rw-r--r-- | sound/usb/endpoint.h | 20 | ||||
-rw-r--r-- | sound/usb/format.c | 4 | ||||
-rw-r--r-- | sound/usb/helper.c | 4 | ||||
-rw-r--r-- | sound/usb/helper.h | 2 | ||||
-rw-r--r-- | sound/usb/midi.c | 27 | ||||
-rw-r--r-- | sound/usb/mixer.c | 21 | ||||
-rw-r--r-- | sound/usb/mixer_quirks.c | 10 | ||||
-rw-r--r-- | sound/usb/pcm.c | 34 | ||||
-rw-r--r-- | sound/usb/pcm.h | 3 | ||||
-rw-r--r-- | sound/usb/quirks-table.h | 25 | ||||
-rw-r--r-- | sound/usb/quirks.c | 16 | ||||
-rw-r--r-- | sound/usb/stream.c | 452 | ||||
-rw-r--r-- | sound/usb/stream.h | 12 | ||||
-rw-r--r-- | sound/usb/urb.c | 941 | ||||
-rw-r--r-- | sound/usb/urb.h | 21 | ||||
-rw-r--r-- | sound/usb/usbaudio.h | 1 |
26 files changed, 1648 insertions, 1365 deletions
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 1e3ae3327dd..07bcfe4d18a 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -16,6 +16,7 @@ #include <linux/firmware.h> #include <linux/bitrev.h> +#include <linux/kernel.h> #include "firmware.h" #include "chip.h" @@ -59,21 +60,19 @@ struct ihex_record { unsigned int txt_offset; /* current position in txt_data */ }; -static u8 usb6fire_fw_ihex_nibble(const u8 n) -{ - if (n >= '0' && n <= '9') - return n - '0'; - else if (n >= 'A' && n <= 'F') - return n - ('A' - 10); - else if (n >= 'a' && n <= 'f') - return n - ('a' - 10); - return 0; -} - static u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc) { - u8 val = (usb6fire_fw_ihex_nibble(data[0]) << 4) | - usb6fire_fw_ihex_nibble(data[1]); + u8 val = 0; + int hval; + + hval = hex_to_bin(data[0]); + if (hval >= 0) + val |= (hval << 4); + + hval = hex_to_bin(data[1]); + if (hval >= 0) + val |= hval; + *crc += val; return val; } diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 8beb77563da..3efc21c3d67 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -67,6 +67,7 @@ config SND_USB_CAIAQ * Native Instruments Guitar Rig mobile * Native Instruments Traktor Kontrol X1 * Native Instruments Traktor Kontrol S4 + * Native Instruments Maschine Controller To compile this driver as a module, choose M here: the module will be called snd-usb-caiaq. @@ -85,6 +86,7 @@ config SND_USB_CAIAQ_INPUT * Native Instruments Kore Controller 2 * Native Instruments Audio Kontrol 1 * Native Instruments Traktor Kontrol S4 + * Native Instruments Maschine Controller config SND_USB_US122L tristate "Tascam US-122L USB driver" diff --git a/sound/usb/Makefile b/sound/usb/Makefile index cf9ed66445f..ac256dc4c6b 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -3,16 +3,16 @@ # snd-usb-audio-objs := card.o \ + clock.o \ + endpoint.o \ + format.o \ + helper.o \ mixer.o \ mixer_quirks.o \ + pcm.o \ proc.o \ quirks.o \ - format.o \ - endpoint.o \ - urb.o \ - pcm.o \ - helper.o \ - clock.o + stream.o snd-usbmidi-lib-objs := midi.o diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 45bc4a2dc6f..3eb605bd950 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -50,7 +50,8 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, Session I/O}," "{Native Instruments, GuitarRig mobile}" "{Native Instruments, Traktor Kontrol X1}" - "{Native Instruments, Traktor Kontrol S4}"); + "{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 */ @@ -146,6 +147,11 @@ static struct usb_device_id snd_usb_id_table[] = { .idVendor = USB_VID_NATIVEINSTRUMENTS, .idProduct = USB_PID_TRAKTORAUDIO2 }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_MASCHINECONTROLLER + }, { /* terminator */ } }; diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index 3f9c6339ae9..562b0bff9c4 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -18,6 +18,7 @@ #define USB_PID_TRAKTORKONTROLX1 0x2305 #define USB_PID_TRAKTORKONTROLS4 0xbaff #define USB_PID_TRAKTORAUDIO2 0x041d +#define USB_PID_MASCHINECONTROLLER 0x0808 #define EP1_BUFSIZE 64 #define EP4_BUFSIZE 512 diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index a213813487b..26a121b42c3 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -67,6 +67,61 @@ static unsigned short keycode_kore[] = { KEY_BRL_DOT5 }; +#define MASCHINE_BUTTONS (42) +#define MASCHINE_BUTTON(X) ((X) + BTN_MISC) +#define MASCHINE_PADS (16) +#define MASCHINE_PAD(X) ((X) + ABS_PRESSURE) + +static unsigned short keycode_maschine[] = { + MASCHINE_BUTTON(40), /* mute */ + MASCHINE_BUTTON(39), /* solo */ + MASCHINE_BUTTON(38), /* select */ + MASCHINE_BUTTON(37), /* duplicate */ + MASCHINE_BUTTON(36), /* navigate */ + MASCHINE_BUTTON(35), /* pad mode */ + MASCHINE_BUTTON(34), /* pattern */ + MASCHINE_BUTTON(33), /* scene */ + KEY_RESERVED, /* spacer */ + + MASCHINE_BUTTON(30), /* rec */ + MASCHINE_BUTTON(31), /* erase */ + MASCHINE_BUTTON(32), /* shift */ + MASCHINE_BUTTON(28), /* grid */ + MASCHINE_BUTTON(27), /* > */ + MASCHINE_BUTTON(26), /* < */ + MASCHINE_BUTTON(25), /* restart */ + + MASCHINE_BUTTON(21), /* E */ + MASCHINE_BUTTON(22), /* F */ + MASCHINE_BUTTON(23), /* G */ + MASCHINE_BUTTON(24), /* H */ + MASCHINE_BUTTON(20), /* D */ + MASCHINE_BUTTON(19), /* C */ + MASCHINE_BUTTON(18), /* B */ + MASCHINE_BUTTON(17), /* A */ + + MASCHINE_BUTTON(0), /* control */ + MASCHINE_BUTTON(2), /* browse */ + MASCHINE_BUTTON(4), /* < */ + MASCHINE_BUTTON(6), /* snap */ + MASCHINE_BUTTON(7), /* autowrite */ + MASCHINE_BUTTON(5), /* > */ + MASCHINE_BUTTON(3), /* sampling */ + MASCHINE_BUTTON(1), /* step */ + + MASCHINE_BUTTON(15), /* 8 softkeys */ + MASCHINE_BUTTON(14), + MASCHINE_BUTTON(13), + MASCHINE_BUTTON(12), + MASCHINE_BUTTON(11), + MASCHINE_BUTTON(10), + MASCHINE_BUTTON(9), + MASCHINE_BUTTON(8), + + MASCHINE_BUTTON(16), /* note repeat */ + MASCHINE_BUTTON(29) /* play */ +}; + #define KONTROLX1_INPUTS (40) #define KONTROLS4_BUTTONS (12 * 8) #define KONTROLS4_AXIS (46) @@ -218,6 +273,29 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, input_report_abs(input_dev, ABS_HAT3Y, i); input_sync(input_dev); break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): + /* 4 under the left screen */ + input_report_abs(input_dev, ABS_HAT0X, decode_erp(buf[21], buf[20])); + input_report_abs(input_dev, ABS_HAT0Y, decode_erp(buf[15], buf[14])); + input_report_abs(input_dev, ABS_HAT1X, decode_erp(buf[9], buf[8])); + input_report_abs(input_dev, ABS_HAT1Y, decode_erp(buf[3], buf[2])); + + /* 4 under the right screen */ + input_report_abs(input_dev, ABS_HAT2X, decode_erp(buf[19], buf[18])); + input_report_abs(input_dev, ABS_HAT2Y, decode_erp(buf[13], buf[12])); + input_report_abs(input_dev, ABS_HAT3X, decode_erp(buf[7], buf[6])); + input_report_abs(input_dev, ABS_HAT3Y, decode_erp(buf[1], buf[0])); + + /* volume */ + input_report_abs(input_dev, ABS_RX, decode_erp(buf[17], buf[16])); + /* tempo */ + input_report_abs(input_dev, ABS_RY, decode_erp(buf[11], buf[10])); + /* swing */ + input_report_abs(input_dev, ABS_RZ, decode_erp(buf[5], buf[4])); + + input_sync(input_dev); + break; } } @@ -400,6 +478,25 @@ static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *dev, input_sync(dev->input_dev); } +#define MASCHINE_MSGBLOCK_SIZE 2 + +static void snd_usb_caiaq_maschine_dispatch(struct snd_usb_caiaqdev *dev, + const unsigned char *buf, + unsigned int len) +{ + unsigned int i, pad_id; + uint16_t pressure; + + for (i = 0; i < MASCHINE_PADS; i++) { + pressure = be16_to_cpu(buf[i * 2] << 8 | buf[(i * 2) + 1]); + pad_id = pressure >> 12; + + input_report_abs(dev->input_dev, MASCHINE_PAD(pad_id), pressure & 0xfff); + } + + input_sync(dev->input_dev); +} + static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb) { struct snd_usb_caiaqdev *dev = urb->context; @@ -425,6 +522,13 @@ static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb) case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): snd_usb_caiaq_tks4_dispatch(dev, buf, urb->actual_length); break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): + if (urb->actual_length < (MASCHINE_PADS * MASCHINE_MSGBLOCK_SIZE)) + goto requeue; + + snd_usb_caiaq_maschine_dispatch(dev, buf, urb->actual_length); + break; } requeue: @@ -444,6 +548,7 @@ static int snd_usb_caiaq_input_open(struct input_dev *idev) switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0) return -EIO; break; @@ -462,6 +567,7 @@ static void snd_usb_caiaq_input_close(struct input_dev *idev) switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): usb_kill_urb(dev->ep4_in_urb); break; } @@ -652,6 +758,50 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev) break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): + 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_RX) | BIT_MASK(ABS_RY) | + BIT_MASK(ABS_RZ); + + BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_maschine)); + memcpy(dev->keycode, keycode_maschine, sizeof(keycode_maschine)); + input->keycodemax = ARRAY_SIZE(keycode_maschine); + + for (i = 0; i < MASCHINE_PADS; i++) { + input->absbit[0] |= MASCHINE_PAD(i); + input_set_abs_params(input, MASCHINE_PAD(i), 0, 0xfff, 5, 10); + } + + input_set_abs_params(input, ABS_HAT0X, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT0Y, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT1X, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT1Y, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT2X, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT2Y, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT3X, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT3Y, 0, 999, 0, 10); + input_set_abs_params(input, ABS_RX, 0, 999, 0, 10); + input_set_abs_params(input, ABS_RY, 0, 999, 0, 10); + input_set_abs_params(input, ABS_RZ, 0, 999, 0, 10); + + 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 */ goto exit_free_idev; @@ -664,15 +814,17 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev) for (i = 0; i < input->keycodemax; i++) __set_bit(dev->keycode[i], input->keybit); + dev->input_dev = input; + ret = input_register_device(input); if (ret < 0) goto exit_free_idev; - dev->input_dev = input; return 0; exit_free_idev: input_free_device(input); + dev->input_dev = NULL; return ret; } @@ -688,4 +840,3 @@ void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev) input_unregister_device(dev->input_dev); dev->input_dev = NULL; } - diff --git a/sound/usb/card.c b/sound/usb/card.c index 3068f043099..05c1aae0b01 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -65,9 +65,9 @@ #include "helper.h" #include "debug.h" #include "pcm.h" -#include "urb.h" #include "format.h" #include "power.h" +#include "stream.h" MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); MODULE_DESCRIPTION("USB Audio"); @@ -185,7 +185,7 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int return -EINVAL; } - if (! snd_usb_parse_audio_endpoints(chip, interface)) { + if (! snd_usb_parse_audio_interface(chip, interface)) { usb_set_interface(dev, interface, 0); /* reset the current interface */ usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L); return -EINVAL; diff --git a/sound/usb/card.h b/sound/usb/card.h index ae4251d5abf..a39edcc32a9 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -94,6 +94,8 @@ struct snd_usb_substream { spinlock_t lock; struct snd_urb_ops ops; /* callbacks (must be filled at init) */ + int last_frame_number; /* stored frame number */ + int last_delay; /* stored delay */ }; struct snd_usb_stream { diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 075195e8661..379baad3d5a 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -91,7 +91,7 @@ static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_i USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, UAC2_CX_CLOCK_SELECTOR << 8, snd_usb_ctrl_intf(chip) | (selector_id << 8), - &buf, sizeof(buf), 1000); + &buf, sizeof(buf)); if (ret < 0) return ret; @@ -118,7 +118,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id) USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, UAC2_CS_CONTROL_CLOCK_VALID << 8, snd_usb_ctrl_intf(chip) | (source_id << 8), - &data, sizeof(data), 1000); + &data, sizeof(data)); if (err < 0) { snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n", @@ -222,7 +222,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, - data, sizeof(data), 1000)) < 0) { + data, sizeof(data))) < 0) { snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n", dev->devnum, iface, fmt->altsetting, rate, ep); return err; @@ -231,7 +231,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(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), 1000)) < 0) { + data, sizeof(data))) < 0) { snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n", dev->devnum, iface, fmt->altsetting, ep); return 0; /* some devices don't support reading */ @@ -273,7 +273,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, UAC2_CS_CONTROL_SAM_FREQ << 8, snd_usb_ctrl_intf(chip) | (clock << 8), - data, sizeof(data), 1000)) < 0) { + data, sizeof(data))) < 0) { snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n", dev->devnum, iface, fmt->altsetting, rate); return err; @@ -283,7 +283,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, UAC2_CS_CONTROL_SAM_FREQ << 8, snd_usb_ctrl_intf(chip) | (clock << 8), - data, sizeof(data), 1000)) < 0) { + data, sizeof(data))) < 0) { snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n", dev->devnum, iface, fmt->altsetting); return err; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 7d46e482375..81c6edecd86 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -15,436 +15,951 @@ * */ +#include <linux/gfp.h> #include <linux/init.h> -#include <linux/slab.h> #include <linux/usb.h> #include <linux/usb/audio.h> -#include <linux/usb/audio-v2.h> #include <sound/core.h> #include <sound/pcm.h> #include "usbaudio.h" +#include "helper.h" #include "card.h" -#include "proc.h" -#include "quirks.h" #include "endpoint.h" -#include "urb.h" #include "pcm.h" -#include "helper.h" -#include "format.h" -#include "clock.h" /* - * free a substream + * convert a sampling rate into our full speed format (fs/1000 in Q16.16) + * this will overflow at approx 524 kHz */ -static void free_substream(struct snd_usb_substream *subs) +static inline unsigned get_usb_full_speed_rate(unsigned int rate) { - struct list_head *p, *n; - - if (!subs->num_formats) - return; /* not initialized */ - list_for_each_safe(p, n, &subs->fmt_list) { - struct audioformat *fp = list_entry(p, struct audioformat, list); - kfree(fp->rate_table); - kfree(fp); - } - kfree(subs->rate_list.list); + return ((rate << 13) + 62) / 125; } +/* + * convert a sampling rate into USB high speed format (fs/8000 in Q16.16) + * this will overflow at approx 4 MHz + */ +static inline unsigned get_usb_high_speed_rate(unsigned int rate) +{ + return ((rate << 10) + 62) / 125; +} /* - * free a usb stream instance + * unlink active urbs. */ -static void snd_usb_audio_stream_free(struct snd_usb_stream *stream) +static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep) { - free_substream(&stream->substream[0]); - free_substream(&stream->substream[1]); - list_del(&stream->list); - kfree(stream); + struct snd_usb_audio *chip = subs->stream->chip; + unsigned int i; + int async; + + subs->running = 0; + + if (!force && subs->stream->chip->shutdown) /* to be sure... */ + return -EBADFD; + + async = !can_sleep && chip->async_unlink; + + if (!async && in_interrupt()) + return 0; + + for (i = 0; i < subs->nurbs; i++) { + if (test_bit(i, &subs->active_mask)) { + if (!test_and_set_bit(i, &subs->unlink_mask)) { + struct urb *u = subs->dataurb[i].urb; + if (async) + usb_unlink_urb(u); + else + usb_kill_urb(u); + } + } + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + if (test_bit(i+16, &subs->active_mask)) { + if (!test_and_set_bit(i+16, &subs->unlink_mask)) { + struct urb *u = subs->syncurb[i].urb; + if (async) + usb_unlink_urb(u); + else + usb_kill_urb(u); + } + } + } + } + return 0; } -static void snd_usb_audio_pcm_free(struct snd_pcm *pcm) + +/* + * release a urb data + */ +static void release_urb_ctx(struct snd_urb_ctx *u) { - struct snd_usb_stream *stream = pcm->private_data; - if (stream) { - stream->pcm = NULL; - snd_usb_audio_stream_free(stream); + if (u->urb) { + if (u->buffer_size) + usb_free_coherent(u->subs->dev, u->buffer_size, + u->urb->transfer_buffer, + u->urb->transfer_dma); + usb_free_urb(u->urb); + u->urb = NULL; } } +/* + * wait until all urbs are processed. + */ +static int wait_clear_urbs(struct snd_usb_substream *subs) +{ + unsigned long end_time = jiffies + msecs_to_jiffies(1000); + unsigned int i; + int alive; + + do { + alive = 0; + for (i = 0; i < subs->nurbs; i++) { + if (test_bit(i, &subs->active_mask)) + alive++; + } + if (subs->syncpipe) { + for (i = 0; i < SYNC_URBS; i++) { + if (test_bit(i + 16, &subs->active_mask)) + alive++; + } + } + if (! alive) + break; + schedule_timeout_uninterruptible(1); + } while (time_before(jiffies, end_time)); + if (alive) + snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); + return 0; +} /* - * add this endpoint to the chip instance. - * if a stream with the same endpoint already exists, append to it. - * if not, create a new pcm stream. + * release a substream */ -int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp) +void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force) { - struct list_head *p; - struct snd_usb_stream *as; - struct snd_usb_substream *subs; - struct snd_pcm *pcm; - int err; + int i; + + /* stop urbs (to be sure) */ + deactivate_urbs(subs, force, 1); + wait_clear_urbs(subs); + + for (i = 0; i < MAX_URBS; i++) + release_urb_ctx(&subs->dataurb[i]); + for (i = 0; i < SYNC_URBS; i++) + release_urb_ctx(&subs->syncurb[i]); + usb_free_coherent(subs->dev, SYNC_URBS * 4, + subs->syncbuf, subs->sync_dma); + subs->syncbuf = NULL; + subs->nurbs = 0; +} - list_for_each(p, &chip->pcm_list) { - as = list_entry(p, struct snd_usb_stream, list); - if (as->fmt_type != fp->fmt_type) - continue; - subs = &as->substream[stream]; - if (!subs->endpoint) - continue; - if (subs->endpoint == fp->endpoint) { - list_add_tail(&fp->list, &subs->fmt_list); - subs->num_formats++; - subs->formats |= fp->formats; - return 0; +/* + * complete callback from data urb + */ +static void snd_complete_urb(struct urb *urb) +{ + struct snd_urb_ctx *ctx = urb->context; + struct snd_usb_substream *subs = ctx->subs; + struct snd_pcm_substream *substream = ctx->subs->pcm_substream; + int err = 0; + + if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) || + !subs->running || /* can be stopped during retire callback */ + (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 || + (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + clear_bit(ctx->index, &subs->active_mask); + if (err < 0) { + snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); } } - /* look for an empty stream */ - list_for_each(p, &chip->pcm_list) { - as = list_entry(p, struct snd_usb_stream, list); - if (as->fmt_type != fp->fmt_type) - continue; - subs = &as->substream[stream]; - if (subs->endpoint) - continue; - err = snd_pcm_new_stream(as->pcm, stream, 1); - if (err < 0) - return err; - snd_usb_init_substream(as, stream, fp); - return 0; +} + + +/* + * complete callback from sync urb + */ +static void snd_complete_sync_urb(struct urb *urb) +{ + struct snd_urb_ctx *ctx = urb->context; + struct snd_usb_substream *subs = ctx->subs; + struct snd_pcm_substream *substream = ctx->subs->pcm_substream; + int err = 0; + + if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) || + !subs->running || /* can be stopped during retire callback */ + (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 || + (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + clear_bit(ctx->index + 16, &subs->active_mask); + if (err < 0) { + snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + } } +} + - /* create a new pcm */ - as = kzalloc(sizeof(*as), GFP_KERNEL); - if (!as) - return -ENOMEM; - as->pcm_index = chip->pcm_devs; - as->chip = chip; - as->fmt_type = fp->fmt_type; - err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, - stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, - stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, - &pcm); - if (err < 0) { - kfree(as); - return err; +/* + * initialize a substream for plaback/capture + */ +int snd_usb_init_substream_urbs(struct snd_usb_substream *subs, + unsigned int period_bytes, + unsigned int rate, + unsigned int frame_bits) +{ + unsigned int maxsize, i; + int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int urb_packs, total_packs, packs_per_ms; + struct snd_usb_audio *chip = subs->stream->chip; + + /* calculate the frequency in 16.16 format */ + if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) + subs->freqn = get_usb_full_speed_rate(rate); + else + subs->freqn = get_usb_high_speed_rate(rate); + subs->freqm = subs->freqn; + subs->freqshift = INT_MIN; + /* calculate max. frequency */ + if (subs->maxpacksize) { + /* whatever fits into a max. size packet */ + maxsize = subs->maxpacksize; + subs->freqmax = (maxsize / (frame_bits >> 3)) + << (16 - subs->datainterval); + } else { + /* no max. packet size: just take 25% higher than nominal */ + subs->freqmax = subs->freqn + (subs->freqn >> 2); + maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) + >> (16 - subs->datainterval); } - as->pcm = pcm; - pcm->private_data = as; - pcm->private_free = snd_usb_audio_pcm_free; - pcm->info_flags = 0; - if (chip->pcm_devs > 0) - sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); + subs->phase = 0; + + if (subs->fill_max) + subs->curpacksize = subs->maxpacksize; else - strcpy(pcm->name, "USB Audio"); + subs->curpacksize = maxsize; - snd_usb_init_substream(as, stream, fp); + if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) + packs_per_ms = 8 >> subs->datainterval; + else + packs_per_ms = 1; + + if (is_playback) { + urb_packs = max(chip->nrpacks, 1); + urb_packs = min(urb_packs, (unsigned int)MAX_PACKS); + } else + urb_packs = 1; + urb_packs *= packs_per_ms; + if (subs->syncpipe) + urb_packs = min(urb_packs, 1U << subs->syncinterval); + + /* decide how many packets to be used */ + if (is_playback) { + unsigned int minsize, maxpacks; + /* determine how small a packet can be */ + minsize = (subs->freqn >> (16 - subs->datainterval)) + * (frame_bits >> 3); + /* with sync from device, assume it can be 12% lower */ + if (subs->syncpipe) + minsize -= minsize >> 3; + minsize = max(minsize, 1u); + total_packs = (period_bytes + minsize - 1) / minsize; + /* we need at least two URBs for queueing */ + if (total_packs < 2) { + total_packs = 2; + } else { + /* and we don't want too long a queue either */ + maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); + total_packs = min(total_packs, maxpacks); + } + } else { + while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + urb_packs >>= 1; + total_packs = MAX_URBS * urb_packs; + } + subs->nurbs = (total_packs + urb_packs - 1) / urb_packs; + if (subs->nurbs > MAX_URBS) { + /* too much... */ + subs->nurbs = MAX_URBS; + total_packs = MAX_URBS * urb_packs; + } else if (subs->nurbs < 2) { + /* too little - we need at least two packets + * to ensure contiguous playback/capture + */ + subs->nurbs = 2; + } - list_add(&as->list, &chip->pcm_list); - chip->pcm_devs++; + /* allocate and initialize data urbs */ + for (i = 0; i < subs->nurbs; i++) { + struct snd_urb_ctx *u = &subs->dataurb[i]; + u->index = i; + u->subs = subs; + u->packets = (i + 1) * total_packs / subs->nurbs + - i * total_packs / subs->nurbs; + u->buffer_size = maxsize * u->packets; + if (subs->fmt_type == UAC_FORMAT_TYPE_II) + u->packets++; /* for transfer delimiter */ + u-& |