aboutsummaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-10-28 14:25:01 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-10-28 14:25:01 -0700
commit68d99b2c8efcb6ed3807a55569300c53b5f88be5 (patch)
treef189c8f2132d3668a2f0e503f5c3f8695b26a1c8 /sound/usb
parent0e59e7e7feb5a12938fbf9135147eeda3238c6c4 (diff)
parent8128c9f21509f9a8b6da94ac432d845dda458406 (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.c25
-rw-r--r--sound/usb/Kconfig2
-rw-r--r--sound/usb/Makefile12
-rw-r--r--sound/usb/caiaq/device.c8
-rw-r--r--sound/usb/caiaq/device.h1
-rw-r--r--sound/usb/caiaq/input.c155
-rw-r--r--sound/usb/card.c4
-rw-r--r--sound/usb/card.h2
-rw-r--r--sound/usb/clock.c12
-rw-r--r--sound/usb/endpoint.c1199
-rw-r--r--sound/usb/endpoint.h20
-rw-r--r--sound/usb/format.c4
-rw-r--r--sound/usb/helper.c4
-rw-r--r--sound/usb/helper.h2
-rw-r--r--sound/usb/midi.c27
-rw-r--r--sound/usb/mixer.c21
-rw-r--r--sound/usb/mixer_quirks.c10
-rw-r--r--sound/usb/pcm.c34
-rw-r--r--sound/usb/pcm.h3
-rw-r--r--sound/usb/quirks-table.h25
-rw-r--r--sound/usb/quirks.c16
-rw-r--r--sound/usb/stream.c452
-rw-r--r--sound/usb/stream.h12
-rw-r--r--sound/usb/urb.c941
-rw-r--r--sound/usb/urb.h21
-rw-r--r--sound/usb/usbaudio.h1
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-&