Commit 15c5ab60 authored by Daniel Mack's avatar Daniel Mack Committed by Takashi Iwai

ALSA: snd-usb-caiaq: Add support for Traktor Kontrol S4

This patch adds support for the new Traktor Kontrol S4 by Native
Instruments. It features a new audio data streaming model, MIDI
in and out ports, a huge number of 174 dimmable LEDs, 96 buttons
and 46 absolute encoder axis, including some rotary encoders.

All features are supported by the driver now.

Did some code refactoring along the way.
Signed-off-by: default avatarDaniel Mack <daniel@caiaq.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 6008fd5a
......@@ -65,6 +65,7 @@ config SND_USB_CAIAQ
* Native Instruments Guitar Rig Session I/O
* Native Instruments Guitar Rig mobile
* Native Instruments Traktor Kontrol X1
* Native Instruments Traktor Kontrol S4
To compile this driver as a module, choose M here: the module
will be called snd-usb-caiaq.
......@@ -82,6 +83,7 @@ config SND_USB_CAIAQ_INPUT
* Native Instruments Kore Controller
* Native Instruments Kore Controller 2
* Native Instruments Audio Kontrol 1
* Native Instruments Traktor Kontrol S4
config SND_USB_US122L
tristate "Tascam US-122L USB driver"
......
......@@ -111,7 +111,7 @@ static int stream_start(struct snd_usb_caiaqdev *dev)
memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
dev->input_panic = 0;
dev->output_panic = 0;
dev->first_packet = 1;
dev->first_packet = 4;
dev->streaming = 1;
dev->warned = 0;
......@@ -169,7 +169,7 @@ 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)
struct snd_pcm_hw_params *hw_params)
{
debug("%s(%p)\n", __func__, sub);
return snd_pcm_lib_malloc_pages(sub, params_buffer_bytes(hw_params));
......@@ -189,7 +189,7 @@ static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
#endif
static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
48000, 64000, 88200, 96000, 176400, 192000 };
48000, 64000, 88200, 96000, 176400, 192000 };
static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
{
......@@ -201,12 +201,39 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
debug("%s(%p)\n", __func__, substream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
dev->period_out_count[index] = BYTES_PER_SAMPLE + 1;
dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1;
int out_pos;
switch (dev->spec.data_alignment) {
case 0:
case 2:
out_pos = BYTES_PER_SAMPLE + 1;
break;
case 3:
default:
out_pos = 0;
break;
}
dev->period_out_count[index] = out_pos;
dev->audio_out_buf_pos[index] = out_pos;
} else {
int in_pos = (dev->spec.data_alignment == 2) ? 0 : 2;
dev->period_in_count[index] = BYTES_PER_SAMPLE + in_pos;
dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE + in_pos;
int in_pos;
switch (dev->spec.data_alignment) {
case 0:
in_pos = BYTES_PER_SAMPLE + 2;
break;
case 2:
in_pos = BYTES_PER_SAMPLE;
break;
case 3:
default:
in_pos = 0;
break;
}
dev->period_in_count[index] = in_pos;
dev->audio_in_buf_pos[index] = in_pos;
}
if (dev->streaming)
......@@ -221,7 +248,7 @@ static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream)
snd_pcm_limit_hw_rates(runtime);
bytes_per_sample = BYTES_PER_SAMPLE;
if (dev->spec.data_alignment == 2)
if (dev->spec.data_alignment >= 2)
bytes_per_sample++;
bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)
......@@ -253,6 +280,8 @@ static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd)
{
struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub);
debug("%s(%p) cmd %d\n", __func__, sub, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
......@@ -402,6 +431,61 @@ static void read_in_urb_mode2(struct snd_usb_caiaqdev *dev,
}
}
static void read_in_urb_mode3(struct snd_usb_caiaqdev *dev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
int stream, i;
/* paranoia check */
if (iso->actual_length % (BYTES_PER_SAMPLE_USB * CHANNELS_PER_STREAM))
return;
for (i = 0; i < iso->actual_length;) {
for (stream = 0; stream < dev->n_streams; stream++) {
struct snd_pcm_substream *sub = dev->sub_capture[stream];
char *audio_buf = NULL;
int c, n, sz = 0;
if (sub && !dev->input_panic) {
struct snd_pcm_runtime *rt = sub->runtime;
audio_buf = rt->dma_area;
sz = frames_to_bytes(rt, rt->buffer_size);
}
for (c = 0; c < CHANNELS_PER_STREAM; c++) {
/* 3 audio data bytes, followed by 1 check byte */
if (audio_buf) {
for (n = 0; n < BYTES_PER_SAMPLE; n++) {
audio_buf[dev->audio_in_buf_pos[stream]++] = usb_buf[i+n];
if (dev->audio_in_buf_pos[stream] == sz)
dev->audio_in_buf_pos[stream] = 0;
}
dev->period_in_count[stream] += BYTES_PER_SAMPLE;
}
i += BYTES_PER_SAMPLE;
if (usb_buf[i] != ((stream << 1) | c) &&
!dev->first_packet) {
if (!dev->input_panic)
printk(" EXPECTED: %02x got %02x, c %d, stream %d, i %d\n",
((stream << 1) | c), usb_buf[i], c, stream, i);
dev->input_panic = 1;
}
i++;
}
}
}
if (dev->first_packet > 0)
dev->first_packet--;
}
static void read_in_urb(struct snd_usb_caiaqdev *dev,
const struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
......@@ -419,6 +503,9 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev,
case 2:
read_in_urb_mode2(dev, urb, iso);
break;
case 3:
read_in_urb_mode3(dev, urb, iso);
break;
}
if ((dev->input_panic || dev->output_panic) && !dev->warned) {
......@@ -429,9 +516,9 @@ static void read_in_urb(struct snd_usb_caiaqdev *dev,
}
}
static void fill_out_urb(struct snd_usb_caiaqdev *dev,
struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
static void fill_out_urb_mode_0(struct snd_usb_caiaqdev *dev,
struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
struct snd_pcm_substream *sub;
......@@ -457,9 +544,67 @@ static void fill_out_urb(struct snd_usb_caiaqdev *dev,
/* fill in the check bytes */
if (dev->spec.data_alignment == 2 &&
i % (dev->n_streams * BYTES_PER_SAMPLE_USB) ==
(dev->n_streams * CHANNELS_PER_STREAM))
for (stream = 0; stream < dev->n_streams; stream++, i++)
usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
(dev->n_streams * CHANNELS_PER_STREAM))
for (stream = 0; stream < dev->n_streams; stream++, i++)
usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
}
}
static void fill_out_urb_mode_3(struct snd_usb_caiaqdev *dev,
struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
int stream, i;
for (i = 0; i < iso->length;) {
for (stream = 0; stream < dev->n_streams; stream++) {
struct snd_pcm_substream *sub = dev->sub_playback[stream];
char *audio_buf = NULL;
int c, n, sz = 0;
if (sub) {
struct snd_pcm_runtime *rt = sub->runtime;
audio_buf = rt->dma_area;
sz = frames_to_bytes(rt, rt->buffer_size);
}
for (c = 0; c < CHANNELS_PER_STREAM; c++) {
for (n = 0; n < BYTES_PER_SAMPLE; n++) {
if (audio_buf) {
usb_buf[i+n] = audio_buf[dev->audio_out_buf_pos[stream]++];
if (dev->audio_out_buf_pos[stream] == sz)
dev->audio_out_buf_pos[stream] = 0;
} else {
usb_buf[i+n] = 0;
}
}
if (audio_buf)
dev->period_out_count[stream] += BYTES_PER_SAMPLE;
i += BYTES_PER_SAMPLE;
/* fill in the check byte pattern */
usb_buf[i++] = (stream << 1) | c;
}
}
}
}
static inline void fill_out_urb(struct snd_usb_caiaqdev *dev,
struct urb *urb,
const struct usb_iso_packet_descriptor *iso)
{
switch (dev->spec.data_alignment) {
case 0:
case 2:
fill_out_urb_mode_0(dev, urb, iso);
break;
case 3:
fill_out_urb_mode_3(dev, urb, iso);
break;
}
}
......
This diff is collapsed.
......@@ -48,7 +48,8 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
"{Native Instruments, Audio 8 DJ},"
"{Native Instruments, Session I/O},"
"{Native Instruments, GuitarRig mobile}"
"{Native Instruments, Traktor Kontrol X1}");
"{Native Instruments, Traktor Kontrol X1}"
"{Native Instruments, Traktor Kontrol S4}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
......@@ -134,6 +135,11 @@ static struct usb_device_id snd_usb_id_table[] = {
.idVendor = USB_VID_NATIVEINSTRUMENTS,
.idProduct = USB_PID_TRAKTORKONTROLX1
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = USB_VID_NATIVEINSTRUMENTS,
.idProduct = USB_PID_TRAKTORKONTROLS4
},
{ /* terminator */ }
};
......
......@@ -16,6 +16,7 @@
#define USB_PID_SESSIONIO 0x1915
#define USB_PID_GUITARRIGMOBILE 0x0d8d
#define USB_PID_TRAKTORKONTROLX1 0x2305
#define USB_PID_TRAKTORKONTROLS4 0xbaff
#define EP1_BUFSIZE 64
#define EP4_BUFSIZE 512
......@@ -99,13 +100,14 @@ struct snd_usb_caiaqdev {
struct snd_pcm_substream *sub_capture[MAX_STREAMS];
/* Controls */
unsigned char control_state[64];
unsigned char control_state[256];
unsigned char ep8_out_buf[2];
/* Linux input */
#ifdef CONFIG_SND_USB_CAIAQ_INPUT
struct input_dev *input_dev;
char phys[64]; /* physical device path */
unsigned short keycode[64];
unsigned short keycode[128];
struct urb *ep4_in_urb;
unsigned char ep4_in_buf[EP4_BUFSIZE];
#endif
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment