From: Daniel Mack Date: Wed, 1 Apr 2009 17:05:39 +0000 (+0200) Subject: ALSA: snd-usb-caiaq: rename files to remove redundant information in file pathes X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=936e7d03394bc6238091db10d060326622c87ed7;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git ALSA: snd-usb-caiaq: rename files to remove redundant information in file pathes Cleanup only, no functional change. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai --- diff --git a/sound/usb/caiaq/Makefile b/sound/usb/caiaq/Makefile index 23dadd5a11cd..388999653aaa 100644 --- a/sound/usb/caiaq/Makefile +++ b/sound/usb/caiaq/Makefile @@ -1,4 +1,4 @@ -snd-usb-caiaq-y := caiaq-device.o caiaq-audio.o caiaq-midi.o caiaq-control.o -snd-usb-caiaq-$(CONFIG_SND_USB_CAIAQ_INPUT) += caiaq-input.o +snd-usb-caiaq-y := device.o audio.o midi.o control.o +snd-usb-caiaq-$(CONFIG_SND_USB_CAIAQ_INPUT) += input.o obj-$(CONFIG_SND_USB_CAIAQ) += snd-usb-caiaq.o diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c new file mode 100644 index 000000000000..3f45c0fe61ab --- /dev/null +++ b/sound/usb/caiaq/audio.c @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2006-2008 Daniel Mack, Karsten Wiese + * + * 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 +*/ + +#include +#include +#include +#include +#include + +#include "device.h" +#include "audio.h" + +#define N_URBS 32 +#define CLOCK_DRIFT_TOLERANCE 5 +#define FRAMES_PER_URB 8 +#define BYTES_PER_FRAME 512 +#define CHANNELS_PER_STREAM 2 +#define BYTES_PER_SAMPLE 3 +#define BYTES_PER_SAMPLE_USB 4 +#define MAX_BUFFER_SIZE (128*1024) +#define MAX_ENDPOINT_SIZE 512 + +#define ENDPOINT_CAPTURE 2 +#define ENDPOINT_PLAYBACK 6 + +#define MAKE_CHECKBYTE(dev,stream,i) \ + (stream << 1) | (~(i / (dev->n_streams * BYTES_PER_SAMPLE_USB)) & 1) + +static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER), + .formats = SNDRV_PCM_FMTBIT_S24_3BE, + .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000), + .rate_min = 44100, + .rate_max = 0, /* will overwrite later */ + .channels_min = CHANNELS_PER_STREAM, + .channels_max = CHANNELS_PER_STREAM, + .buffer_bytes_max = MAX_BUFFER_SIZE, + .period_bytes_min = 128, + .period_bytes_max = MAX_BUFFER_SIZE, + .periods_min = 1, + .periods_max = 1024, +}; + +static void +activate_substream(struct snd_usb_caiaqdev *dev, + struct snd_pcm_substream *sub) +{ + if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) + dev->sub_playback[sub->number] = sub; + else + dev->sub_capture[sub->number] = sub; +} + +static void +deactivate_substream(struct snd_usb_caiaqdev *dev, + struct snd_pcm_substream *sub) +{ + unsigned long flags; + spin_lock_irqsave(&dev->spinlock, flags); + + if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) + dev->sub_playback[sub->number] = NULL; + else + dev->sub_capture[sub->number] = NULL; + + spin_unlock_irqrestore(&dev->spinlock, flags); +} + +static int +all_substreams_zero(struct snd_pcm_substream **subs) +{ + int i; + for (i = 0; i < MAX_STREAMS; i++) + if (subs[i] != NULL) + return 0; + return 1; +} + +static int stream_start(struct snd_usb_caiaqdev *dev) +{ + int i, ret; + + debug("%s(%p)\n", __func__, dev); + + if (dev->streaming) + return -EINVAL; + + memset(dev->sub_playback, 0, sizeof(dev->sub_playback)); + memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); + dev->input_panic = 0; + dev->output_panic = 0; + dev->first_packet = 1; + dev->streaming = 1; + dev->warned = 0; + + for (i = 0; i < N_URBS; i++) { + ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC); + if (ret) { + log("unable to trigger read #%d! (ret %d)\n", i, ret); + dev->streaming = 0; + return -EPIPE; + } + } + + return 0; +} + +static void stream_stop(struct snd_usb_caiaqdev *dev) +{ + int i; + + debug("%s(%p)\n", __func__, dev); + if (!dev->streaming) + return; + + dev->streaming = 0; + + for (i = 0; i < N_URBS; i++) { + usb_kill_urb(dev->data_urbs_in[i]); + usb_kill_urb(dev->data_urbs_out[i]); + } +} + +static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream) +{ + struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); + debug("%s(%p)\n", __func__, substream); + substream->runtime->hw = dev->pcm_info; + snd_pcm_limit_hw_rates(substream->runtime); + return 0; +} + +static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) +{ + struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); + + debug("%s(%p)\n", __func__, substream); + if (all_substreams_zero(dev->sub_playback) && + all_substreams_zero(dev->sub_capture)) { + /* when the last client has stopped streaming, + * all sample rates are allowed again */ + stream_stop(dev); + dev->pcm_info.rates = dev->samplerates; + } + + return 0; +} + +static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub, + 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)); +} + +static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) +{ + struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); + debug("%s(%p)\n", __func__, sub); + deactivate_substream(dev, sub); + return snd_pcm_lib_free_pages(sub); +} + +/* this should probably go upstream */ +#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 +#error "Change this table" +#endif + +static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000 }; + +static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) +{ + int bytes_per_sample, bpp, ret, i; + int index = substream->number; + struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + debug("%s(%p)\n", __func__, substream); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1; + else + dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE; + + if (dev->streaming) + return 0; + + /* the first client that opens a stream defines the sample rate + * setting for all subsequent calls, until the last client closed. */ + for (i=0; i < ARRAY_SIZE(rates); i++) + if (runtime->rate == rates[i]) + dev->pcm_info.rates = 1 << i; + + snd_pcm_limit_hw_rates(runtime); + + bytes_per_sample = BYTES_PER_SAMPLE; + if (dev->spec.data_alignment == 2) + bytes_per_sample++; + + bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE) + * bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams; + + if (bpp > MAX_ENDPOINT_SIZE) + bpp = MAX_ENDPOINT_SIZE; + + ret = snd_usb_caiaq_set_audio_params(dev, runtime->rate, + runtime->sample_bits, bpp); + if (ret) + return ret; + + ret = stream_start(dev); + if (ret) + return ret; + + dev->output_running = 0; + wait_event_timeout(dev->prepare_wait_queue, dev->output_running, HZ); + if (!dev->output_running) { + stream_stop(dev); + return -EPIPE; + } + + return 0; +} + +static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd) +{ + struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + activate_substream(dev, sub); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + deactivate_substream(dev, sub); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t +snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub) +{ + int index = sub->number; + struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); + + if (dev->input_panic || dev->output_panic) + return SNDRV_PCM_POS_XRUN; + + if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) + return bytes_to_frames(sub->runtime, + dev->audio_out_buf_pos[index]); + else + return bytes_to_frames(sub->runtime, + dev->audio_in_buf_pos[index]); +} + +/* operators for both playback and capture */ +static struct snd_pcm_ops snd_usb_caiaq_ops = { + .open = snd_usb_caiaq_substream_open, + .close = snd_usb_caiaq_substream_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usb_caiaq_pcm_hw_params, + .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 +}; + +static void check_for_elapsed_periods(struct snd_usb_caiaqdev *dev, + struct snd_pcm_substream **subs) +{ + int stream, pb, *cnt; + struct snd_pcm_substream *sub; + + for (stream = 0; stream < dev->n_streams; stream++) { + sub = subs[stream]; + if (!sub) + continue; + + pb = frames_to_bytes(sub->runtime, + sub->runtime->period_size); + cnt = (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &dev->period_out_count[stream] : + &dev->period_in_count[stream]; + + if (*cnt >= pb) { + snd_pcm_period_elapsed(sub); + *cnt %= pb; + } + } +} + +static void read_in_urb_mode0(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; + struct snd_pcm_substream *sub; + int stream, i; + + if (all_substreams_zero(dev->sub_capture)) + return; + + for (i = 0; i < iso->actual_length;) { + for (stream = 0; stream < dev->n_streams; stream++, i++) { + sub = dev->sub_capture[stream]; + if (sub) { + struct snd_pcm_runtime *rt = sub->runtime; + char *audio_buf = rt->dma_area; + int sz = frames_to_bytes(rt, rt->buffer_size); + audio_buf[dev->audio_in_buf_pos[stream]++] + = usb_buf[i]; + dev->period_in_count[stream]++; + if (dev->audio_in_buf_pos[stream] == sz) + dev->audio_in_buf_pos[stream] = 0; + } + } + } +} + +static void read_in_urb_mode2(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; + unsigned char check_byte; + struct snd_pcm_substream *sub; + int stream, i; + + for (i = 0; i < iso->actual_length;) { + if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) { + for (stream = 0; + stream < dev->n_streams; + stream++, i++) { + if (dev->first_packet) + continue; + + check_byte = MAKE_CHECKBYTE(dev, stream, i); + + if ((usb_buf[i] & 0x3f) != check_byte) + dev->input_panic = 1; + + if (usb_buf[i] & 0x80) + dev->output_panic = 1; + } + } + dev->first_packet = 0; + + for (stream = 0; stream < dev->n_streams; stream++, i++) { + sub = dev->sub_capture[stream]; + if (dev->input_panic) + usb_buf[i] = 0; + + if (sub) { + struct snd_pcm_runtime *rt = sub->runtime; + char *audio_buf = rt->dma_area; + int sz = frames_to_bytes(rt, rt->buffer_size); + audio_buf[dev->audio_in_buf_pos[stream]++] = + usb_buf[i]; + dev->period_in_count[stream]++; + if (dev->audio_in_buf_pos[stream] == sz) + dev->audio_in_buf_pos[stream] = 0; + } + } + } +} + +static void read_in_urb(struct snd_usb_caiaqdev *dev, + const struct urb *urb, + const struct usb_iso_packet_descriptor *iso) +{ + if (!dev->streaming) + return; + + if (iso->actual_length < dev->bpp) + return; + + switch (dev->spec.data_alignment) { + case 0: + read_in_urb_mode0(dev, urb, iso); + break; + case 2: + read_in_urb_mode2(dev, urb, iso); + break; + } + + if ((dev->input_panic || dev->output_panic) && !dev->warned) { + debug("streaming error detected %s %s\n", + dev->input_panic ? "(input)" : "", + dev->output_panic ? "(output)" : ""); + dev->warned = 1; + } +} + +static void fill_out_urb(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; + int stream, i; + + for (i = 0; i < iso->length;) { + for (stream = 0; stream < dev->n_streams; stream++, i++) { + sub = dev->sub_playback[stream]; + if (sub) { + struct snd_pcm_runtime *rt = sub->runtime; + char *audio_buf = rt->dma_area; + int sz = frames_to_bytes(rt, rt->buffer_size); + usb_buf[i] = + audio_buf[dev->audio_out_buf_pos[stream]]; + dev->period_out_count[stream]++; + 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] = 0; + } + + /* 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); + } +} + +static void read_completed(struct urb *urb) +{ + struct snd_usb_caiaq_cb_info *info = urb->context; + struct snd_usb_caiaqdev *dev; + struct urb *out; + int frame, len, send_it = 0, outframe = 0; + + if (urb->status || !info) + return; + + dev = info->dev; + + if (!dev->streaming) + return; + + out = dev->data_urbs_out[info->index]; + + /* read the recently received packet and send back one which has + * the same layout */ + for (frame = 0; frame < FRAMES_PER_URB; frame++) { + if (urb->iso_frame_desc[frame].status) + continue; + + len = urb->iso_frame_desc[outframe].actual_length; + out->iso_frame_desc[outframe].length = len; + out->iso_frame_desc[outframe].actual_length = 0; + out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame; + + if (len > 0) { + spin_lock(&dev->spinlock); + fill_out_urb(dev, out, &out->iso_frame_desc[outframe]); + read_in_urb(dev, urb, &urb->iso_frame_desc[frame]); + spin_unlock(&dev->spinlock); + check_for_elapsed_periods(dev, dev->sub_playback); + check_for_elapsed_periods(dev, dev->sub_capture); + send_it = 1; + } + + outframe++; + } + + if (send_it) { + out->number_of_packets = FRAMES_PER_URB; + out->transfer_flags = URB_ISO_ASAP; + usb_submit_urb(out, GFP_ATOMIC); + } + + /* re-submit inbound urb */ + for (frame = 0; frame < FRAMES_PER_URB; frame++) { + urb->iso_frame_desc[frame].offset = BYTES_PER_FRAME * frame; + urb->iso_frame_desc[frame].length = BYTES_PER_FRAME; + urb->iso_frame_desc[frame].actual_length = 0; + } + + urb->number_of_packets = FRAMES_PER_URB; + urb->transfer_flags = URB_ISO_ASAP; + usb_submit_urb(urb, GFP_ATOMIC); +} + +static void write_completed(struct urb *urb) +{ + struct snd_usb_caiaq_cb_info *info = urb->context; + struct snd_usb_caiaqdev *dev = info->dev; + + if (!dev->output_running) { + dev->output_running = 1; + wake_up(&dev->prepare_wait_queue); + } +} + +static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret) +{ + int i, frame; + struct urb **urbs; + struct usb_device *usb_dev = dev->chip.dev; + unsigned int pipe; + + pipe = (dir == SNDRV_PCM_STREAM_PLAYBACK) ? + usb_sndisocpipe(usb_dev, ENDPOINT_PLAYBACK) : + usb_rcvisocpipe(usb_dev, ENDPOINT_CAPTURE); + + urbs = kmalloc(N_URBS * sizeof(*urbs), GFP_KERNEL); + if (!urbs) { + log("unable to kmalloc() urbs, OOM!?\n"); + *ret = -ENOMEM; + return NULL; + } + + for (i = 0; i < N_URBS; i++) { + urbs[i] = usb_alloc_urb(FRAMES_PER_URB, GFP_KERNEL); + if (!urbs[i]) { + log("unable to usb_alloc_urb(), OOM!?\n"); + *ret = -ENOMEM; + return urbs; + } + + urbs[i]->transfer_buffer = + kmalloc(FRAMES_PER_URB * BYTES_PER_FRAME, GFP_KERNEL); + if (!urbs[i]->transfer_buffer) { + log("unable to kmalloc() transfer buffer, OOM!?\n"); + *ret = -ENOMEM; + return urbs; + } + + for (frame = 0; frame < FRAMES_PER_URB; frame++) { + struct usb_iso_packet_descriptor *iso = + &urbs[i]->iso_frame_desc[frame]; + + iso->offset = BYTES_PER_FRAME * frame; + iso->length = BYTES_PER_FRAME; + } + + urbs[i]->dev = usb_dev; + urbs[i]->pipe = pipe; + urbs[i]->transfer_buffer_length = FRAMES_PER_URB + * BYTES_PER_FRAME; + urbs[i]->context = &dev->data_cb_info[i]; + urbs[i]->interval = 1; + urbs[i]->transfer_flags = URB_ISO_ASAP; + urbs[i]->number_of_packets = FRAMES_PER_URB; + urbs[i]->complete = (dir == SNDRV_PCM_STREAM_CAPTURE) ? + read_completed : write_completed; + } + + *ret = 0; + return urbs; +} + +static void free_urbs(struct urb **urbs) +{ + int i; + + if (!urbs) + return; + + for (i = 0; i < N_URBS; i++) { + if (!urbs[i]) + continue; + + usb_kill_urb(urbs[i]); + kfree(urbs[i]->transfer_buffer); + usb_free_urb(urbs[i]); + } + + kfree(urbs); +} + +int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) +{ + int i, ret; + + dev->n_audio_in = max(dev->spec.num_analog_audio_in, + dev->spec.num_digital_audio_in) / + CHANNELS_PER_STREAM; + dev->n_audio_out = max(dev->spec.num_analog_audio_out, + dev->spec.num_digital_audio_out) / + CHANNELS_PER_STREAM; + dev->n_streams = max(dev->n_audio_in, dev->n_audio_out); + + debug("dev->n_audio_in = %d\n", dev->n_audio_in); + debug("dev->n_audio_out = %d\n", dev->n_audio_out); + debug("dev->n_streams = %d\n", dev->n_streams); + + if (dev->n_streams > MAX_STREAMS) { + log("unable to initialize device, too many streams.\n"); + return -EINVAL; + } + + ret = snd_pcm_new(dev->chip.card, dev->product_name, 0, + dev->n_audio_out, dev->n_audio_in, &dev->pcm); + + if (ret < 0) { + log("snd_pcm_new() returned %d\n", ret); + return ret; + } + + dev->pcm->private_data = dev; + strcpy(dev->pcm->name, dev->product_name); + + memset(dev->sub_playback, 0, sizeof(dev->sub_playback)); + memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); + + memcpy(&dev->pcm_info, &snd_usb_caiaq_pcm_hardware, + sizeof(snd_usb_caiaq_pcm_hardware)); + + /* setup samplerates */ + dev->samplerates = dev->pcm_info.rates; + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_GUITARRIGMOBILE): + dev->samplerates |= SNDRV_PCM_RATE_192000; + /* fall thru */ + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): + dev->samplerates |= SNDRV_PCM_RATE_88200; + break; + } + + snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_usb_caiaq_ops); + snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_usb_caiaq_ops); + + snd_pcm_lib_preallocate_pages_for_all(dev->pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + MAX_BUFFER_SIZE, MAX_BUFFER_SIZE); + + dev->data_cb_info = + kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS, + GFP_KERNEL); + + if (!dev->data_cb_info) + return -ENOMEM; + + for (i = 0; i < N_URBS; i++) { + dev->data_cb_info[i].dev = dev; + dev->data_cb_info[i].index = i; + } + + dev->data_urbs_in = alloc_urbs(dev, SNDRV_PCM_STREAM_CAPTURE, &ret); + if (ret < 0) { + kfree(dev->data_cb_info); + free_urbs(dev->data_urbs_in); + return ret; + } + + dev->data_urbs_out = alloc_urbs(dev, SNDRV_PCM_STREAM_PLAYBACK, &ret); + if (ret < 0) { + kfree(dev->data_cb_info); + free_urbs(dev->data_urbs_in); + free_urbs(dev->data_urbs_out); + return ret; + } + + return 0; +} + +void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev) +{ + debug("%s(%p)\n", __func__, dev); + stream_stop(dev); + free_urbs(dev->data_urbs_in); + free_urbs(dev->data_urbs_out); + kfree(dev->data_cb_info); +} + diff --git a/sound/usb/caiaq/audio.h b/sound/usb/caiaq/audio.h new file mode 100644 index 000000000000..8ab1f8d9529e --- /dev/null +++ b/sound/usb/caiaq/audio.h @@ -0,0 +1,7 @@ +#ifndef CAIAQ_AUDIO_H +#define CAIAQ_AUDIO_H + +int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev); +void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev); + +#endif /* CAIAQ_AUDIO_H */ diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c deleted file mode 100644 index cf3733110862..000000000000 --- a/sound/usb/caiaq/caiaq-audio.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Copyright (c) 2006-2008 Daniel Mack, Karsten Wiese - * - * 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 -*/ - -#include -#include -#include -#include -#include - -#include "caiaq-device.h" -#include "caiaq-audio.h" - -#define N_URBS 32 -#define CLOCK_DRIFT_TOLERANCE 5 -#define FRAMES_PER_URB 8 -#define BYTES_PER_FRAME 512 -#define CHANNELS_PER_STREAM 2 -#define BYTES_PER_SAMPLE 3 -#define BYTES_PER_SAMPLE_USB 4 -#define MAX_BUFFER_SIZE (128*1024) -#define MAX_ENDPOINT_SIZE 512 - -#define ENDPOINT_CAPTURE 2 -#define ENDPOINT_PLAYBACK 6 - -#define MAKE_CHECKBYTE(dev,stream,i) \ - (stream << 1) | (~(i / (dev->n_streams * BYTES_PER_SAMPLE_USB)) & 1) - -static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = { - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER), - .formats = SNDRV_PCM_FMTBIT_S24_3BE, - .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000), - .rate_min = 44100, - .rate_max = 0, /* will overwrite later */ - .channels_min = CHANNELS_PER_STREAM, - .channels_max = CHANNELS_PER_STREAM, - .buffer_bytes_max = MAX_BUFFER_SIZE, - .period_bytes_min = 128, - .period_bytes_max = MAX_BUFFER_SIZE, - .periods_min = 1, - .periods_max = 1024, -}; - -static void -activate_substream(struct snd_usb_caiaqdev *dev, - struct snd_pcm_substream *sub) -{ - if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) - dev->sub_playback[sub->number] = sub; - else - dev->sub_capture[sub->number] = sub; -} - -static void -deactivate_substream(struct snd_usb_caiaqdev *dev, - struct snd_pcm_substream *sub) -{ - unsigned long flags; - spin_lock_irqsave(&dev->spinlock, flags); - - if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) - dev->sub_playback[sub->number] = NULL; - else - dev->sub_capture[sub->number] = NULL; - - spin_unlock_irqrestore(&dev->spinlock, flags); -} - -static int -all_substreams_zero(struct snd_pcm_substream **subs) -{ - int i; - for (i = 0; i < MAX_STREAMS; i++) - if (subs[i] != NULL) - return 0; - return 1; -} - -static int stream_start(struct snd_usb_caiaqdev *dev) -{ - int i, ret; - - debug("%s(%p)\n", __func__, dev); - - if (dev->streaming) - return -EINVAL; - - memset(dev->sub_playback, 0, sizeof(dev->sub_playback)); - memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); - dev->input_panic = 0; - dev->output_panic = 0; - dev->first_packet = 1; - dev->streaming = 1; - dev->warned = 0; - - for (i = 0; i < N_URBS; i++) { - ret = usb_submit_urb(dev->data_urbs_in[i], GFP_ATOMIC); - if (ret) { - log("unable to trigger read #%d! (ret %d)\n", i, ret); - dev->streaming = 0; - return -EPIPE; - } - } - - return 0; -} - -static void stream_stop(struct snd_usb_caiaqdev *dev) -{ - int i; - - debug("%s(%p)\n", __func__, dev); - if (!dev->streaming) - return; - - dev->streaming = 0; - - for (i = 0; i < N_URBS; i++) { - usb_kill_urb(dev->data_urbs_in[i]); - usb_kill_urb(dev->data_urbs_out[i]); - } -} - -static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream) -{ - struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); - debug("%s(%p)\n", __func__, substream); - substream->runtime->hw = dev->pcm_info; - snd_pcm_limit_hw_rates(substream->runtime); - return 0; -} - -static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream) -{ - struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); - - debug("%s(%p)\n", __func__, substream); - if (all_substreams_zero(dev->sub_playback) && - all_substreams_zero(dev->sub_capture)) { - /* when the last client has stopped streaming, - * all sample rates are allowed again */ - stream_stop(dev); - dev->pcm_info.rates = dev->samplerates; - } - - return 0; -} - -static int snd_usb_caiaq_pcm_hw_params(struct snd_pcm_substream *sub, - 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)); -} - -static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub) -{ - struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); - debug("%s(%p)\n", __func__, sub); - deactivate_substream(dev, sub); - return snd_pcm_lib_free_pages(sub); -} - -/* this should probably go upstream */ -#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 -#error "Change this table" -#endif - -static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000 }; - -static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) -{ - int bytes_per_sample, bpp, ret, i; - int index = substream->number; - struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - debug("%s(%p)\n", __func__, substream); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1; - else - dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE; - - if (dev->streaming) - return 0; - - /* the first client that opens a stream defines the sample rate - * setting for all subsequent calls, until the last client closed. */ - for (i=0; i < ARRAY_SIZE(rates); i++) - if (runtime->rate == rates[i]) - dev->pcm_info.rates = 1 << i; - - snd_pcm_limit_hw_rates(runtime); - - bytes_per_sample = BYTES_PER_SAMPLE; - if (dev->spec.data_alignment == 2) - bytes_per_sample++; - - bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE) - * bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams; - - if (bpp > MAX_ENDPOINT_SIZE) - bpp = MAX_ENDPOINT_SIZE; - - ret = snd_usb_caiaq_set_audio_params(dev, runtime->rate, - runtime->sample_bits, bpp); - if (ret) - return ret; - - ret = stream_start(dev); - if (ret) - return ret; - - dev->output_running = 0; - wait_event_timeout(dev->prepare_wait_queue, dev->output_running, HZ); - if (!dev->output_running) { - stream_stop(dev); - return -EPIPE; - } - - return 0; -} - -static int snd_usb_caiaq_pcm_trigger(struct snd_pcm_substream *sub, int cmd) -{ - struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - activate_substream(dev, sub); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - deactivate_substream(dev, sub); - break; - default: - return -EINVAL; - } - - return 0; -} - -static snd_pcm_uframes_t -snd_usb_caiaq_pcm_pointer(struct snd_pcm_substream *sub) -{ - int index = sub->number; - struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(sub); - - if (dev->input_panic || dev->output_panic) - return SNDRV_PCM_POS_XRUN; - - if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) - return bytes_to_frames(sub->runtime, - dev->audio_out_buf_pos[index]); - else - return bytes_to_frames(sub->runtime, - dev->audio_in_buf_pos[index]); -} - -/* operators for both playback and capture */ -static struct snd_pcm_ops snd_usb_caiaq_ops = { - .open = snd_usb_caiaq_substream_open, - .close = snd_usb_caiaq_substream_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_usb_caiaq_pcm_hw_params, - .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 -}; - -static void check_for_elapsed_periods(struct snd_usb_caiaqdev *dev, - struct snd_pcm_substream **subs) -{ - int stream, pb, *cnt; - struct snd_pcm_substream *sub; - - for (stream = 0; stream < dev->n_streams; stream++) { - sub = subs[stream]; - if (!sub) - continue; - - pb = frames_to_bytes(sub->runtime, - sub->runtime->period_size); - cnt = (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - &dev->period_out_count[stream] : - &dev->period_in_count[stream]; - - if (*cnt >= pb) { - snd_pcm_period_elapsed(sub); - *cnt %= pb; - } - } -} - -static void read_in_urb_mode0(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; - struct snd_pcm_substream *sub; - int stream, i; - - if (all_substreams_zero(dev->sub_capture)) - return; - - for (i = 0; i < iso->actual_length;) { - for (stream = 0; stream < dev->n_streams; stream++, i++) { - sub = dev->sub_capture[stream]; - if (sub) { - struct snd_pcm_runtime *rt = sub->runtime; - char *audio_buf = rt->dma_area; - int sz = frames_to_bytes(rt, rt->buffer_size); - audio_buf[dev->audio_in_buf_pos[stream]++] - = usb_buf[i]; - dev->period_in_count[stream]++; - if (dev->audio_in_buf_pos[stream] == sz) - dev->audio_in_buf_pos[stream] = 0; - } - } - } -} - -static void read_in_urb_mode2(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; - unsigned char check_byte; - struct snd_pcm_substream *sub; - int stream, i; - - for (i = 0; i < iso->actual_length;) { - if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) { - for (stream = 0; - stream < dev->n_streams; - stream++, i++) { - if (dev->first_packet) - continue; - - check_byte = MAKE_CHECKBYTE(dev, stream, i); - - if ((usb_buf[i] & 0x3f) != check_byte) - dev->input_panic = 1; - - if (usb_buf[i] & 0x80) - dev->output_panic = 1; - } - } - dev->first_packet = 0; - - for (stream = 0; stream < dev->n_streams; stream++, i++) { - sub = dev->sub_capture[stream]; - if (dev->input_panic) - usb_buf[i] = 0; - - if (sub) { - struct snd_pcm_runtime *rt = sub->runtime; - char *audio_buf = rt->dma_area; - int sz = frames_to_bytes(rt, rt->buffer_size); - audio_buf[dev->audio_in_buf_pos[stream]++] = - usb_buf[i]; - dev->period_in_count[stream]++; - if (dev->audio_in_buf_pos[stream] == sz) - dev->audio_in_buf_pos[stream] = 0; - } - } - } -} - -static void read_in_urb(struct snd_usb_caiaqdev *dev, - const struct urb *urb, - const struct usb_iso_packet_descriptor *iso) -{ - if (!dev->streaming) - return; - - if (iso->actual_length < dev->bpp) - return; - - switch (dev->spec.data_alignment) { - case 0: - read_in_urb_mode0(dev, urb, iso); - break; - case 2: - read_in_urb_mode2(dev, urb, iso); - break; - } - - if ((dev->input_panic || dev->output_panic) && !dev->warned) { - debug("streaming error detected %s %s\n", - dev->input_panic ? "(input)" : "", - dev->output_panic ? "(output)" : ""); - dev->warned = 1; - } -} - -static void fill_out_urb(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; - int stream, i; - - for (i = 0; i < iso->length;) { - for (stream = 0; stream < dev->n_streams; stream++, i++) { - sub = dev->sub_playback[stream]; - if (sub) { - struct snd_pcm_runtime *rt = sub->runtime; - char *audio_buf = rt->dma_area; - int sz = frames_to_bytes(rt, rt->buffer_size); - usb_buf[i] = - audio_buf[dev->audio_out_buf_pos[stream]]; - dev->period_out_count[stream]++; - 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] = 0; - } - - /* 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); - } -} - -static void read_completed(struct urb *urb) -{ - struct snd_usb_caiaq_cb_info *info = urb->context; - struct snd_usb_caiaqdev *dev; - struct urb *out; - int frame, len, send_it = 0, outframe = 0; - - if (urb->status || !info) - return; - - dev = info->dev; - - if (!dev->streaming) - return; - - out = dev->data_urbs_out[info->index]; - - /* read the recently received packet and send back one which has - * the same layout */ - for (frame = 0; frame < FRAMES_PER_URB; frame++) { - if (urb->iso_frame_desc[frame].status) - continue; - - len = urb->iso_frame_desc[outframe].actual_length; - out->iso_frame_desc[outframe].length = len; - out->iso_frame_desc[outframe].actual_length = 0; - out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame; - - if (len > 0) { - spin_lock(&dev->spinlock); - fill_out_urb(dev, out, &out->iso_frame_desc[outframe]); - read_in_urb(dev, urb, &urb->iso_frame_desc[frame]); - spin_unlock(&dev->spinlock); - check_for_elapsed_periods(dev, dev->sub_playback); - check_for_elapsed_periods(dev, dev->sub_capture); - send_it = 1; - } - - outframe++; - } - - if (send_it) { - out->number_of_packets = FRAMES_PER_URB; - out->transfer_flags = URB_ISO_ASAP; - usb_submit_urb(out, GFP_ATOMIC); - } - - /* re-submit inbound urb */ - for (frame = 0; frame < FRAMES_PER_URB; frame++) { - urb->iso_frame_desc[frame].offset = BYTES_PER_FRAME * frame; - urb->iso_frame_desc[frame].length = BYTES_PER_FRAME; - urb->iso_frame_desc[frame].actual_length = 0; - } - - urb->number_of_packets = FRAMES_PER_URB; - urb->transfer_flags = URB_ISO_ASAP; - usb_submit_urb(urb, GFP_ATOMIC); -} - -static void write_completed(struct urb *urb) -{ - struct snd_usb_caiaq_cb_info *info = urb->context; - struct snd_usb_caiaqdev *dev = info->dev; - - if (!dev->output_running) { - dev->output_running = 1; - wake_up(&dev->prepare_wait_queue); - } -} - -static struct urb **alloc_urbs(struct snd_usb_caiaqdev *dev, int dir, int *ret) -{ - int i, frame; - struct urb **urbs; - struct usb_device *usb_dev = dev->chip.dev; - unsigned int pipe; - - pipe = (dir == SNDRV_PCM_STREAM_PLAYBACK) ? - usb_sndisocpipe(usb_dev, ENDPOINT_PLAYBACK) : - usb_rcvisocpipe(usb_dev, ENDPOINT_CAPTURE); - - urbs = kmalloc(N_URBS * sizeof(*urbs), GFP_KERNEL); - if (!urbs) { - log("unable to kmalloc() urbs, OOM!?\n"); - *ret = -ENOMEM; - return NULL; - } - - for (i = 0; i < N_URBS; i++) { - urbs[i] = usb_alloc_urb(FRAMES_PER_URB, GFP_KERNEL); - if (!urbs[i]) { - log("unable to usb_alloc_urb(), OOM!?\n"); - *ret = -ENOMEM; - return urbs; - } - - urbs[i]->transfer_buffer = - kmalloc(FRAMES_PER_URB * BYTES_PER_FRAME, GFP_KERNEL); - if (!urbs[i]->transfer_buffer) { - log("unable to kmalloc() transfer buffer, OOM!?\n"); - *ret = -ENOMEM; - return urbs; - } - - for (frame = 0; frame < FRAMES_PER_URB; frame++) { - struct usb_iso_packet_descriptor *iso = - &urbs[i]->iso_frame_desc[frame]; - - iso->offset = BYTES_PER_FRAME * frame; - iso->length = BYTES_PER_FRAME; - } - - urbs[i]->dev = usb_dev; - urbs[i]->pipe = pipe; - urbs[i]->transfer_buffer_length = FRAMES_PER_URB - * BYTES_PER_FRAME; - urbs[i]->context = &dev->data_cb_info[i]; - urbs[i]->interval = 1; - urbs[i]->transfer_flags = URB_ISO_ASAP; - urbs[i]->number_of_packets = FRAMES_PER_URB; - urbs[i]->complete = (dir == SNDRV_PCM_STREAM_CAPTURE) ? - read_completed : write_completed; - } - - *ret = 0; - return urbs; -} - -static void free_urbs(struct urb **urbs) -{ - int i; - - if (!urbs) - return; - - for (i = 0; i < N_URBS; i++) { - if (!urbs[i]) - continue; - - usb_kill_urb(urbs[i]); - kfree(urbs[i]->transfer_buffer); - usb_free_urb(urbs[i]); - } - - kfree(urbs); -} - -int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) -{ - int i, ret; - - dev->n_audio_in = max(dev->spec.num_analog_audio_in, - dev->spec.num_digital_audio_in) / - CHANNELS_PER_STREAM; - dev->n_audio_out = max(dev->spec.num_analog_audio_out, - dev->spec.num_digital_audio_out) / - CHANNELS_PER_STREAM; - dev->n_streams = max(dev->n_audio_in, dev->n_audio_out); - - debug("dev->n_audio_in = %d\n", dev->n_audio_in); - debug("dev->n_audio_out = %d\n", dev->n_audio_out); - debug("dev->n_streams = %d\n", dev->n_streams); - - if (dev->n_streams > MAX_STREAMS) { - log("unable to initialize device, too many streams.\n"); - return -EINVAL; - } - - ret = snd_pcm_new(dev->chip.card, dev->product_name, 0, - dev->n_audio_out, dev->n_audio_in, &dev->pcm); - - if (ret < 0) { - log("snd_pcm_new() returned %d\n", ret); - return ret; - } - - dev->pcm->private_data = dev; - strcpy(dev->pcm->name, dev->product_name); - - memset(dev->sub_playback, 0, sizeof(dev->sub_playback)); - memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); - - memcpy(&dev->pcm_info, &snd_usb_caiaq_pcm_hardware, - sizeof(snd_usb_caiaq_pcm_hardware)); - - /* setup samplerates */ - dev->samplerates = dev->pcm_info.rates; - switch (dev->chip.usb_id) { - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_SESSIONIO): - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_GUITARRIGMOBILE): - dev->samplerates |= SNDRV_PCM_RATE_192000; - /* fall thru */ - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): - dev->samplerates |= SNDRV_PCM_RATE_88200; - break; - } - - snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_usb_caiaq_ops); - snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_usb_caiaq_ops); - - snd_pcm_lib_preallocate_pages_for_all(dev->pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - MAX_BUFFER_SIZE, MAX_BUFFER_SIZE); - - dev->data_cb_info = - kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS, - GFP_KERNEL); - - if (!dev->data_cb_info) - return -ENOMEM; - - for (i = 0; i < N_URBS; i++) { - dev->data_cb_info[i].dev = dev; - dev->data_cb_info[i].index = i; - } - - dev->data_urbs_in = alloc_urbs(dev, SNDRV_PCM_STREAM_CAPTURE, &ret); - if (ret < 0) { - kfree(dev->data_cb_info); - free_urbs(dev->data_urbs_in); - return ret; - } - - dev->data_urbs_out = alloc_urbs(dev, SNDRV_PCM_STREAM_PLAYBACK, &ret); - if (ret < 0) { - kfree(dev->data_cb_info); - free_urbs(dev->data_urbs_in); - free_urbs(dev->data_urbs_out); - return ret; - } - - return 0; -} - -void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev) -{ - debug("%s(%p)\n", __func__, dev); - stream_stop(dev); - free_urbs(dev->data_urbs_in); - free_urbs(dev->data_urbs_out); - kfree(dev->data_cb_info); -} - diff --git a/sound/usb/caiaq/caiaq-audio.h b/sound/usb/caiaq/caiaq-audio.h deleted file mode 100644 index 8ab1f8d9529e..000000000000 --- a/sound/usb/caiaq/caiaq-audio.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef CAIAQ_AUDIO_H -#define CAIAQ_AUDIO_H - -int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev); -void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *dev); - -#endif /* CAIAQ_AUDIO_H */ diff --git a/sound/usb/caiaq/caiaq-control.c b/sound/usb/caiaq/caiaq-control.c deleted file mode 100644 index bb21fcf0726e..000000000000 --- a/sound/usb/caiaq/caiaq-control.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (c) 2007 Daniel Mack - * friendly supported by NI. - * - * 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 - */ - -#include -#include -#include -#include -#include - -#include "caiaq-device.h" -#include "caiaq-control.h" - -#define CNT_INTVAL 0x10000 - -static int control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_usb_audio *chip = snd_kcontrol_chip(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; - - 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; - } - - 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; - } - - if (is_intval) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 64; - } else { - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - } - - return 0; -} - -static int control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); - struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); - int pos = kcontrol->private_value; - - if (dev->chip.usb_id == - USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)) { - /* A4DJ has only one control */ - /* do not expose hardware input mode 0 */ - ucontrol->value.integer.value[0] = dev->control_state[0] - 1; - return 0; - } - - if (pos & CNT_INTVAL) - ucontrol->value.integer.value[0] - = dev->control_state[pos & ~CNT_INTVAL]; - else - ucontrol->value.integer.value[0] - = !!(dev->control_state[pos / 8] & (1 << pos % 8)); - - return 0; -} - -static int control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); - struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); - int pos = kcontrol->private_value; - - if (dev->chip.usb_id == - 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; - snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, - dev->control_state, sizeof(dev->control_state)); - return 1; - } - - 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, - dev->control_state, sizeof(dev->control_state)); - } else { - if (ucontrol->value.integer.value[0]) - dev->control_state[pos / 8] |= 1 << (pos % 8); - else - dev->control_state[pos / 8] &= ~(1 << (pos % 8)); - - snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, - dev->control_state, sizeof(dev->control_state)); - } - - return 1; -} - -static struct snd_kcontrol_new kcontrol_template __devinitdata = { - .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .index = 0, - .info = control_info, - .get = control_get, - .put = control_put, - /* name and private_value filled later */ -}; - -struct caiaq_controller { - char *name; - int index; -}; - -static struct caiaq_controller ak1_controller[] = { - { "LED left", 2 }, - { "LED middle", 1 }, - { "LED right", 0 }, - { "LED ring", 3 } -}; - -static struct caiaq_controller rk2_controller[] = { - { "LED 1", 5 }, - { "LED 2", 4 }, - { "LED 3", 3 }, - { "LED 4", 2 }, - { "LED 5", 1 }, - { "LED 6", 0 }, - { "LED pedal", 6 }, - { "LED 7seg_1b", 8 }, - { "LED 7seg_1c", 9 }, - { "LED 7seg_2a", 10 }, - { "LED 7seg_2b", 11 }, - { "LED 7seg_2c", 12 }, - { "LED 7seg_2d", 13 }, - { "LED 7seg_2e", 14 }, - { "LED 7seg_2f", 15 }, - { "LED 7seg_2g", 16 }, - { "LED 7seg_3a", 17 }, - { "LED 7seg_3b", 18 }, - { "LED 7seg_3c", 19 }, - { "LED 7seg_3d", 20 }, - { "LED 7seg_3e", 21 }, - { "LED 7seg_3f", 22 }, - { "LED 7seg_3g", 23 } -}; - -static struct caiaq_controller rk3_controller[] = { - { "LED 7seg_1a", 0 + 0 }, - { "LED 7seg_1b", 0 + 1 }, - { "LED 7seg_1c", 0 + 2 }, - { "LED 7seg_1d", 0 + 3 }, - { "LED 7seg_1e", 0 + 4 }, - { "LED 7seg_1f", 0 + 5 }, - { "LED 7seg_1g", 0 + 6 }, - { "LED 7seg_1p", 0 + 7 }, - - { "LED 7seg_2a", 8 + 0 }, - { "LED 7seg_2b", 8 + 1 }, - { "LED 7seg_2c", 8 + 2 }, - { "LED 7seg_2d", 8 + 3 }, - { "LED 7seg_2e", 8 + 4 }, - { "LED 7seg_2f", 8 + 5 }, - { "LED 7seg_2g", 8 + 6 }, - { "LED 7seg_2p", 8 + 7 }, - - { "LED 7seg_3a", 16 + 0 }, - { "LED 7seg_3b", 16 + 1 }, - { "LED 7seg_3c", 16 + 2 }, - { "LED 7seg_3d", 16 + 3 }, - { "LED 7seg_3e", 16 + 4 }, - { "LED 7seg_3f", 16 + 5 }, - { "LED 7seg_3g", 16 + 6 }, - { "LED 7seg_3p", 16 + 7 }, - - { "LED 7seg_4a", 24 + 0 }, - { "LED 7seg_4b", 24 + 1 }, - { "LED 7seg_4c", 24 + 2 }, - { "LED 7seg_4d", 24 + 3 }, - { "LED 7seg_4e", 24 + 4 }, - { "LED 7seg_4f", 24 + 5 }, - { "LED 7seg_4g", 24 + 6 }, - { "LED 7seg_4p", 24 + 7 }, - - { "LED 1", 32 + 0 }, - { "LED 2", 32 + 1 }, - { "LED 3", 32 + 2 }, - { "LED 4", 32 + 3 }, - { "LED 5", 32 + 4 }, - { "LED 6", 32 + 5 }, - { "LED 7", 32 + 6 }, - { "LED 8", 32 + 7 }, - { "LED pedal", 32 + 8 } -}; - -static struct caiaq_controller kore_controller[] = { - { "LED F1", 8 | CNT_INTVAL }, - { "LED F2", 12 | CNT_INTVAL }, - { "LED F3", 0 | CNT_INTVAL }, - { "LED F4", 4 | CNT_INTVAL }, - { "LED F5", 11 | CNT_INTVAL }, - { "LED F6", 15 | CNT_INTVAL }, - { "LED F7", 3 | CNT_INTVAL }, - { "LED F8", 7 | CNT_INTVAL }, - { "LED touch1", 10 | CNT_INTVAL }, - { "LED touch2", 14 | CNT_INTVAL }, - { "LED touch3", 2 | CNT_INTVAL }, - { "LED touch4", 6 | CNT_INTVAL }, - { "LED touch5", 9 | CNT_INTVAL }, - { "LED touch6", 13 | CNT_INTVAL }, - { "LED touch7", 1 | CNT_INTVAL }, - { "LED touch8", 5 | CNT_INTVAL }, - { "LED left", 18 | CNT_INTVAL }, - { "LED right", 22 | CNT_INTVAL }, - { "LED up", 16 | CNT_INTVAL }, - { "LED down", 20 | CNT_INTVAL }, - { "LED stop", 23 | CNT_INTVAL }, - { "LED play", 21 | CNT_INTVAL }, - { "LED record", 19 | CNT_INTVAL }, - { "LED listen", 17 | CNT_INTVAL }, - { "LED lcd", 30 | CNT_INTVAL }, - { "LED menu", 28 | CNT_INTVAL }, - { "LED sound", 31 | CNT_INTVAL }, - { "LED esc", 29 | CNT_INTVAL }, - { "LED view", 27 | CNT_INTVAL }, - { "LED enter", 24 | CNT_INTVAL }, - { "LED control", 26 | CNT_INTVAL } -}; - -static struct caiaq_controller a8dj_controller[] = { - { "Current input mode", 0 | CNT_INTVAL }, - { "GND lift for TC Vinyl mode", 24 + 0 }, - { "GND lift for TC CD/Line mode", 24 + 1 }, - { "GND lift for phono mode", 24 + 2 }, - { "Software lock", 40 } -}; - -static struct caiaq_controller a4dj_controller[] = { - { "Current input mode", 0 | CNT_INTVAL } -}; - -static int __devinit add_controls(struct caiaq_controller *c, int num, - struct snd_usb_caiaqdev *dev) -{ - int i, ret; - struct snd_kcontrol *kc; - - for (i = 0; i < num; i++, c++) { - kcontrol_template.name = c->name; - kcontrol_template.private_value = c->index; - kc = snd_ctl_new1(&kcontrol_template, dev); - ret = snd_ctl_add(dev->chip.card, kc); - if (ret < 0) - return ret; - } - - return 0; -} - -int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev) -{ - int ret = 0; - - switch (dev->chip.usb_id) { - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): - ret = add_controls(ak1_controller, - ARRAY_SIZE(ak1_controller), dev); - break; - - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): - ret = add_controls(rk2_controller, - ARRAY_SIZE(rk2_controller), dev); - break; - - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): - ret = add_controls(rk3_controller, - ARRAY_SIZE(rk3_controller), dev); - break; - - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): - ret = add_controls(kore_controller, - ARRAY_SIZE(kore_controller), dev); - break; - - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): - 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; - } - - return ret; -} - diff --git a/sound/usb/caiaq/caiaq-control.h b/sound/usb/caiaq/caiaq-control.h deleted file mode 100644 index 2e7ab1aa4fb3..000000000000 --- a/sound/usb/caiaq/caiaq-control.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef CAIAQ_CONTROL_H -#define CAIAQ_CONTROL_H - -int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev); - -#endif /* CAIAQ_CONTROL_H */ diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c deleted file mode 100644 index 89f8b68058e1..000000000000 --- a/sound/usb/caiaq/caiaq-device.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * caiaq.c: ALSA driver for caiaq/NativeInstruments devices - * - * Copyright (c) 2007 Daniel Mack - * Karsten Wiese - * - * 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 -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "caiaq-device.h" -#include "caiaq-audio.h" -#include "caiaq-midi.h" -#include "caiaq-control.h" -#include "caiaq-input.h" - -MODULE_AUTHOR("Daniel Mack "); -MODULE_DESCRIPTION("caiaq USB audio, version 1.3.13"); -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 4 DJ}," - "{Native Instruments, Audio 8 DJ}," - "{Native Instruments, Session I/O}," - "{Native Instruments, GuitarRig mobile}"); - -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 */ -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"); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string for the caiaq soundcard."); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "Enable the caiaq soundcard."); - -enum { - SAMPLERATE_44100 = 0, - SAMPLERATE_48000 = 1, - SAMPLERATE_96000 = 2, - SAMPLERATE_192000 = 3, - SAMPLERATE_88200 = 4, - SAMPLERATE_INVALID = 0xff -}; - -enum { - DEPTH_NONE = 0, - DEPTH_16 = 1, - DEPTH_24 = 2, - DEPTH_32 = 3 -}; - -static struct usb_device_id snd_usb_id_table[] = { - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_RIGKONTROL2 - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_RIGKONTROL3 - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_KORECONTROLLER - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_KORECONTROLLER2 - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_AK1 - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_AUDIO8DJ - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_SESSIONIO - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_GUITARRIGMOBILE - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = USB_VID_NATIVEINSTRUMENTS, - .idProduct = USB_PID_AUDIO4DJ - }, - { /* terminator */ } -}; - -static void usb_ep1_command_reply_dispatch (struct urb* urb) -{ - int ret; - struct snd_usb_caiaqdev *dev = urb->context; - unsigned char *buf = urb->transfer_buffer; - - if (urb->status || !dev) { - log("received EP1 urb->status = %i\n", urb->status); - return; - } - - switch(buf[0]) { - case EP1_CMD_GET_DEVICE_INFO: - memcpy(&dev->spec, buf+1, sizeof(struct caiaq_device_spec)); - dev->spec.fw_version = le16_to_cpu(dev->spec.fw_version); - debug("device spec (firmware %d): audio: %d in, %d out, " - "MIDI: %d in, %d out, data alignment %d\n", - dev->spec.fw_version, - dev->spec.num_analog_audio_in, - dev->spec.num_analog_audio_out, - dev->spec.num_midi_in, - dev->spec.num_midi_out, - dev->spec.data_alignment); - - dev->spec_received++; - wake_up(&dev->ep1_wait_queue); - break; - case EP1_CMD_AUDIO_PARAMS: - dev->audio_parm_answer = buf[1]; - wake_up(&dev->ep1_wait_queue); - break; - case EP1_CMD_MIDI_READ: - snd_usb_caiaq_midi_handle_input(dev, buf[1], buf + 3, buf[2]); - break; - case EP1_CMD_READ_IO: - if (dev->chip.usb_id == - USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) { - if (urb->actual_length > sizeof(dev->control_state)) - urb->actual_length = sizeof(dev->control_state); - memcpy(dev->control_state, buf + 1, urb->actual_length); - wake_up(&dev->ep1_wait_queue); - break; - } -#ifdef CONFIG_SND_USB_CAIAQ_INPUT - case EP1_CMD_READ_ERP: - case EP1_CMD_READ_ANALOG: - snd_usb_caiaq_input_dispatch(dev, buf, urb->actual_length); -#endif - break; - } - - dev->ep1_in_urb.actual_length = 0; - ret = usb_submit_urb(&dev->ep1_in_urb, GFP_ATOMIC); - if (ret < 0) - log("unable to submit urb. OOM!?\n"); -} - -int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev, - unsigned char command, - const unsigned char *buffer, - int len) -{ - int actual_len; - struct usb_device *usb_dev = dev->chip.dev; - - if (!usb_dev) - return -EIO; - - if (len > EP1_BUFSIZE - 1) - len = EP1_BUFSIZE - 1; - - if (buffer && len > 0) - memcpy(dev->ep1_out_buf+1, buffer, len); - - dev->ep1_out_buf[0] = command; - return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1), - dev->ep1_out_buf, len+1, &actual_len, 200); -} - -int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, - int rate, int depth, int bpp) -{ - int ret; - char tmp[5]; - - switch (rate) { - case 44100: tmp[0] = SAMPLERATE_44100; break; - case 48000: tmp[0] = SAMPLERATE_48000; break; - case 88200: tmp[0] = SAMPLERATE_88200; break; - case 96000: tmp[0] = SAMPLERATE_96000; break; - case 192000: tmp[0] = SAMPLERATE_192000; break; - default: return -EINVAL; - } - - switch (depth) { - case 16: tmp[1] = DEPTH_16; break; - case 24: tmp[1] = DEPTH_24; break; - default: return -EINVAL; - } - - tmp[2] = bpp & 0xff; - tmp[3] = bpp >> 8; - tmp[4] = 1; /* packets per microframe */ - - debug("setting audio params: %d Hz, %d bits, %d bpp\n", - rate, depth, bpp); - - dev->audio_parm_answer = -1; - ret = snd_usb_caiaq_send_command(dev, EP1_CMD_AUDIO_PARAMS, - tmp, sizeof(tmp)); - - if (ret) - return ret; - - if (!wait_event_timeout(dev->ep1_wait_queue, - dev->audio_parm_answer >= 0, HZ)) - return -EPIPE; - - if (dev->audio_parm_answer != 1) - debug("unable to set the device's audio params\n"); - else - dev->bpp = bpp; - - return dev->audio_parm_answer == 1 ? 0 : -EINVAL; -} - -int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, - int digital, int analog, int erp) -{ - char tmp[3] = { digital, analog, erp }; - return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG, - tmp, sizeof(tmp)); -} - -static void __devinit setup_card(struct snd_usb_caiaqdev *dev) -{ - int ret; - char val[4]; - - /* device-specific startup specials */ - switch (dev->chip.usb_id) { - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): - /* RigKontrol2 - display centered dash ('-') */ - val[0] = 0x00; - val[1] = 0x00; - val[2] = 0x01; - snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 3); - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): - /* RigKontrol2 - display two centered dashes ('--') */ - val[0] = 0x00; - val[1] = 0x40; - val[2] = 0x40; - val[3] = 0x00; - snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 4); - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): - /* Audio Kontrol 1 - make USB-LED stop blinking */ - val[0] = 0x00; - snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 1); - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): - /* Audio 8 DJ - trigger read of current settings */ - dev->control_state[0] = 0xff; - snd_usb_caiaq_set_auto_msg(dev, 1, 0, 0); - snd_usb_caiaq_send_command(dev, EP1_CMD_READ_IO, NULL, 0); - - if (!wait_event_timeout(dev->ep1_wait_queue, - dev->control_state[0] != 0xff, HZ)) - return; - - /* fix up some defaults */ - if ((dev->control_state[1] != 2) || - (dev->control_state[2] != 3) || - (dev->control_state[4] != 2)) { - dev->control_state[1] = 2; - dev->control_state[2] = 3; - dev->control_state[4] = 2; - snd_usb_caiaq_send_command(dev, - EP1_CMD_WRITE_IO, dev->control_state, 6); - } - - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): - /* Audio 4 DJ - default input mode to phono */ - dev->control_state[0] = 2; - snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, - dev->control_state, 1); - break; - } - - if (dev->spec.num_analog_audio_out + - dev->spec.num_analog_audio_in + - dev->spec.num_digital_audio_out + - dev->spec.num_digital_audio_in > 0) { - ret = snd_usb_caiaq_audio_init(dev); - if (ret < 0) - log("Unable to set up audio system (ret=%d)\n", ret); - } - - if (dev->spec.num_midi_in + - dev->spec.num_midi_out > 0) { - ret = snd_usb_caiaq_midi_init(dev); - if (ret < 0) - log("Unable to set up MIDI system (ret=%d)\n", ret); - } - -#ifdef CONFIG_SND_USB_CAIAQ_INPUT - ret = snd_usb_caiaq_input_init(dev); - if (ret < 0) - log("Unable to set up input system (ret=%d)\n", ret); -#endif - - /* finally, register the card and all its sub-instances */ - ret = snd_card_register(dev->chip.card); - if (ret < 0) { - log("snd_card_register() returned %d\n", ret); - snd_card_free(dev->chip.card); - } - - ret = snd_usb_caiaq_control_init(dev); - if (ret < 0) - log("Unable to set up control system (ret=%d)\n", ret); -} - -static int create_card(struct usb_device* usb_dev, struct snd_card **cardp) -{ - int devnum; - int err; - struct snd_card *card; - struct snd_usb_caiaqdev *dev; - - for (devnum = 0; devnum < SNDRV_CARDS; devnum++) - if (enable[devnum] && !snd_card_used[devnum]) - break; - - if (devnum >= SNDRV_CARDS) - return -ENODEV; - - err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, - sizeof(struct snd_usb_caiaqdev), &card); - if (err < 0) - return err; - - dev = caiaqdev(card); - dev->chip.dev = usb_dev; - dev->chip.card = card; - dev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), - le16_to_cpu(usb_dev->descriptor.idProduct)); - spin_lock_init(&dev->spinlock); - snd_card_set_dev(card, &usb_dev->dev); - - *cardp = card; - return 0; -} - -static int __devinit init_card(struct snd_usb_caiaqdev *dev) -{ - char *c; - struct usb_device *usb_dev = dev->chip.dev; - struct snd_card *card = dev->chip.card; - int err, len; - - if (usb_set_interface(usb_dev, 0, 1) != 0) { - log("can't set alt interface.\n"); - return -EIO; - } - - usb_init_urb(&dev->ep1_in_urb); - usb_init_urb(&dev->midi_out_urb); - - usb_fill_bulk_urb(&dev->ep1_in_urb, usb_dev, - usb_rcvbulkpipe(usb_dev, 0x1), - dev->ep1_in_buf, EP1_BUFSIZE, - usb_ep1_command_reply_dispatch, dev); - - usb_fill_bulk_urb(&dev->midi_out_urb, usb_dev, - usb_sndbulkpipe(usb_dev, 0x1), - dev->midi_out_buf, EP1_BUFSIZE, - snd_usb_caiaq_midi_output_done, dev); - - init_waitqueue_head(&dev->ep1_wait_queue); - init_waitqueue_head(&dev->prepare_wait_queue); - - if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0) - return -EIO; - - err = snd_usb_caiaq_send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0); - if (err) - return err; - - if (!wait_event_timeout(dev->ep1_wait_queue, dev->spec_received, HZ)) - return -ENODEV; - - usb_string(usb_dev, usb_dev->descriptor.iManufacturer, - dev->vendor_name, CAIAQ_USB_STR_LEN); - - usb_string(usb_dev, usb_dev->descriptor.iProduct, - dev->product_name, CAIAQ_USB_STR_LEN); - - usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, - dev->serial, CAIAQ_USB_STR_LEN); - - /* terminate serial string at first white space occurence */ - c = strchr(dev->serial, ' '); - if (c) - *c = '\0'; - - strcpy(card->driver, MODNAME); - strcpy(card->shortname, dev->product_name); - - len = snprintf(card->longname, sizeof(card->longname), - "%s %s (serial %s, ", - dev->vendor_name, dev->product_name, dev->serial); - - if (len < sizeof(card->longname) - 2) - len += usb_make_path(usb_dev, card->longname + len, - sizeof(card->longname) - len); - - card->longname[len++] = ')'; - card->longname[len] = '\0'; - setup_card(dev); - return 0; -} - -static int __devinit snd_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - int ret; - struct snd_card *card; - struct usb_device *device = interface_to_usbdev(intf); - - ret = create_card(device, &card); - - if (ret < 0) - return ret; - - usb_set_intfdata(intf, card); - ret = init_card(caiaqdev(card)); - if (ret < 0) { - log("unable to init card! (ret=%d)\n", ret); - snd_card_free(card); - return ret; - } - - return 0; -} - -static void snd_disconnect(struct usb_interface *intf) -{ - struct snd_usb_caiaqdev *dev; - struct snd_card *card = usb_get_intfdata(intf); - - debug("%s(%p)\n", __func__, intf); - - if (!card) - return; - - dev = caiaqdev(card); - snd_card_disconnect(card); - -#ifdef CONFIG_SND_USB_CAIAQ_INPUT - snd_usb_caiaq_input_free(dev); -#endif - snd_usb_caiaq_audio_free(dev); - - usb_kill_urb(&dev->ep1_in_urb); - usb_kill_urb(&dev->midi_out_urb); - - snd_card_free(card); - usb_reset_device(interface_to_usbdev(intf)); -} - - -MODULE_DEVICE_TABLE(usb, snd_usb_id_table); -static struct usb_driver snd_usb_driver = { - .name = MODNAME, - .probe = snd_probe, - .disconnect = snd_disconnect, - .id_table = snd_usb_id_table, -}; - -static int __init snd_module_init(void) -{ - return usb_register(&snd_usb_driver); -} - -static void __exit snd_module_exit(void) -{ - usb_deregister(&snd_usb_driver); -} - -module_init(snd_module_init) -module_exit(snd_module_exit) - diff --git a/sound/usb/caiaq/caiaq-device.h b/sound/usb/caiaq/caiaq-device.h deleted file mode 100644 index 4cce1ad7493d..000000000000 --- a/sound/usb/caiaq/caiaq-device.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef CAIAQ_DEVICE_H -#define CAIAQ_DEVICE_H - -#include "../usbaudio.h" - -#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_AUDIO4DJ 0x0839 -#define USB_PID_AUDIO8DJ 0x1978 -#define USB_PID_SESSIONIO 0x1915 -#define USB_PID_GUITARRIGMOBILE 0x0d8d - -#define EP1_BUFSIZE 64 -#define CAIAQ_USB_STR_LEN 0xff -#define MAX_STREAMS 32 - -//#define SND_USB_CAIAQ_DEBUG - -#define MODNAME "snd-usb-caiaq" -#define log(x...) snd_printk(KERN_WARNING MODNAME" log: " x) - -#ifdef SND_USB_CAIAQ_DEBUG -#define debug(x...) snd_printk(KERN_WARNING MODNAME " debug: " x) -#else -#define debug(x...) do { } while(0) -#endif - -#define EP1_CMD_GET_DEVICE_INFO 0x1 -#define EP1_CMD_READ_ERP 0x2 -#define EP1_CMD_READ_ANALOG 0x3 -#define EP1_CMD_READ_IO 0x4 -#define EP1_CMD_WRITE_IO 0x5 -#define EP1_CMD_MIDI_READ 0x6 -#define EP1_CMD_MIDI_WRITE 0x7 -#define EP1_CMD_AUDIO_PARAMS 0x9 -#define EP1_CMD_AUTO_MSG 0xb -#define EP1_CMD_DIMM_LEDS 0xc - -struct caiaq_device_spec { - unsigned short fw_version; - unsigned char hw_subtype; - unsigned char num_erp; - unsigned char num_analog_in; - unsigned char num_digital_in; - unsigned char num_digital_out; - unsigned char num_analog_audio_out; - unsigned char num_analog_audio_in; - unsigned char num_digital_audio_out; - unsigned char num_digital_audio_in; - unsigned char num_midi_out; - unsigned char num_midi_in; - unsigned char data_alignment; -} __attribute__ ((packed)); - -struct snd_usb_caiaq_cb_info; - -struct snd_usb_caiaqdev { - struct snd_usb_audio chip; - - struct urb ep1_in_urb; - struct urb midi_out_urb; - struct urb **data_urbs_in; - struct urb **data_urbs_out; - struct snd_usb_caiaq_cb_info *data_cb_info; - - unsigned char ep1_in_buf[EP1_BUFSIZE]; - unsigned char ep1_out_buf[EP1_BUFSIZE]; - unsigned char midi_out_buf[EP1_BUFSIZE]; - - struct caiaq_device_spec spec; - spinlock_t spinlock; - wait_queue_head_t ep1_wait_queue; - wait_queue_head_t prepare_wait_queue; - int spec_received, audio_parm_answer; - int midi_out_active; - - char vendor_name[CAIAQ_USB_STR_LEN]; - char product_name[CAIAQ_USB_STR_LEN]; - char serial[CAIAQ_USB_STR_LEN]; - - int n_streams, n_audio_in, n_audio_out; - int streaming, first_packet, output_running; - int audio_in_buf_pos[MAX_STREAMS]; - int audio_out_buf_pos[MAX_STREAMS]; - int period_in_count[MAX_STREAMS]; - int period_out_count[MAX_STREAMS]; - int input_panic, output_panic, warned; - char *audio_in_buf, *audio_out_buf; - unsigned int samplerates, bpp; - - struct snd_pcm_substream *sub_playback[MAX_STREAMS]; - struct snd_pcm_substream *sub_capture[MAX_STREAMS]; - - /* Controls */ - unsigned char control_state[64]; - - /* Linux input */ -#ifdef CONFIG_SND_USB_CAIAQ_INPUT - struct input_dev *input_dev; - char phys[64]; /* physical device path */ - unsigned short keycode[64]; -#endif - - /* ALSA */ - struct snd_pcm *pcm; - struct snd_pcm_hardware pcm_info; - struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *midi_receive_substream; - struct snd_rawmidi_substream *midi_out_substream; -}; - -struct snd_usb_caiaq_cb_info { - struct snd_usb_caiaqdev *dev; - int index; -}; - -#define caiaqdev(c) ((struct snd_usb_caiaqdev*)(c)->private_data) - -int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, int rate, int depth, int bbp); -int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, int digital, int analog, int erp); -int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev, - unsigned char command, - const unsigned char *buffer, - int len); - -#endif /* CAIAQ_DEVICE_H */ diff --git a/sound/usb/caiaq/caiaq-input.c b/sound/usb/caiaq/caiaq-input.c deleted file mode 100644 index 4451775f82e6..000000000000 --- a/sound/usb/caiaq/caiaq-input.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (c) 2006,2007 Daniel Mack, Tim Ruetz - * - * 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 -*/ - -#include -#include -#include -#include -#include "caiaq-device.h" -#include "caiaq-input.h" - -static unsigned short keycode_ak1[] = { KEY_C, KEY_B, KEY_A }; -static unsigned short keycode_rk2[] = { KEY_1, KEY_2, KEY_3, KEY_4, - KEY_5, KEY_6, KEY_7 }; -static unsigned short keycode_rk3[] = { KEY_1, KEY_2, KEY_3, KEY_4, - KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 }; - -static unsigned short keycode_kore[] = { - KEY_FN_F1, /* "menu" */ - KEY_FN_F7, /* "lcd backlight */ - KEY_FN_F2, /* "control" */ - KEY_FN_F3, /* "enter" */ - KEY_FN_F4, /* "view" */ - KEY_FN_F5, /* "esc" */ - KEY_FN_F6, /* "sound" */ - KEY_FN_F8, /* array spacer, never triggered. */ - KEY_RIGHT, - KEY_DOWN, - KEY_UP, - KEY_LEFT, - KEY_SOUND, /* "listen" */ - KEY_RECORD, - KEY_PLAYPAUSE, - KEY_STOP, - BTN_4, /* 8 softkeys */ - BTN_3, - BTN_2, - BTN_1, - BTN_8, - BTN_7, - BTN_6, - BTN_5, - KEY_BRL_DOT4, /* touch sensitive knobs */ - KEY_BRL_DOT3, - KEY_BRL_DOT2, - KEY_BRL_DOT1, - KEY_BRL_DOT8, - KEY_BRL_DOT7, - KEY_BRL_DOT6, - KEY_BRL_DOT5 -}; - -#define DEG90 (range / 2) -#define DEG180 (range) -#define DEG270 (DEG90 + DEG180) -#define DEG360 (DEG180 * 2) -#define HIGH_PEAK (268) -#define LOW_PEAK (-7) - -/* some of these devices have endless rotation potentiometers - * built in which use two tapers, 90 degrees phase shifted. - * this algorithm decodes them to one single value, ranging - * from 0 to 999 */ -static unsigned int decode_erp(unsigned char a, unsigned char b) -{ - int weight_a, weight_b; - int pos_a, pos_b; - int ret; - int range = HIGH_PEAK - LOW_PEAK; - int mid_value = (HIGH_PEAK + LOW_PEAK) / 2; - - weight_b = abs(mid_value - a) - (range / 2 - 100) / 2; - - if (weight_b < 0) - weight_b = 0; - - if (weight_b > 100) - weight_b = 100; - - weight_a = 100 - weight_b; - - if (a < mid_value) { - /* 0..90 and 270..360 degrees */ - pos_b = b - LOW_PEAK + DEG270; - if (pos_b >= DEG360) - pos_b -= DEG360; - } else - /* 90..270 degrees */ - pos_b = HIGH_PEAK - b + DEG90; - - - if (b > mid_value) - /* 0..180 degrees */ - pos_a = a - LOW_PEAK; - else - /* 180..360 degrees */ - pos_a = HIGH_PEAK - a + DEG180; - - /* interpolate both slider values, depending on weight factors */ - /* 0..99 x DEG360 */ - ret = pos_a * weight_a + pos_b * weight_b; - - /* normalize to 0..999 */ - ret *= 10; - ret /= DEG360; - - if (ret < 0) - ret += 1000; - - if (ret >= 1000) - ret -= 1000; - - return ret; -} - -#undef DEG90 -#undef DEG180 -#undef DEG270 -#undef DEG360 -#undef HIGH_PEAK -#undef LOW_PEAK - - -static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, - const unsigned char *buf, - unsigned int len) -{ - struct input_dev *input_dev = dev->input_dev; - - switch (dev->chip.usb_id) { - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): - input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]); - input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]); - input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]); - input_sync(input_dev); - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): - input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]); - input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]); - 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_KORECONTROLLER): - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): - input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]); - input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]); - input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]); - input_sync(input_dev); - break; - } -} - -static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, - const char *buf, unsigned int len) -{ - struct input_dev *input_dev = dev->input_dev; - int i; - - switch (dev->chip.usb_id) { - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): - i = decode_erp(buf[0], buf[1]); - input_report_abs(input_dev, ABS_X, i); - input_sync(input_dev); - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): - i = decode_erp(buf[7], buf[5]); - input_report_abs(input_dev, ABS_HAT0X, i); - i = decode_erp(buf[12], buf[14]); - input_report_abs(input_dev, ABS_HAT0Y, i); - i = decode_erp(buf[15], buf[13]); - input_report_abs(input_dev, ABS_HAT1X, i); - i = decode_erp(buf[0], buf[2]); - input_report_abs(input_dev, ABS_HAT1Y, i); - i = decode_erp(buf[3], buf[1]); - input_report_abs(input_dev, ABS_HAT2X, i); - i = decode_erp(buf[8], buf[10]); - input_report_abs(input_dev, ABS_HAT2Y, i); - i = decode_erp(buf[11], buf[9]); - input_report_abs(input_dev, ABS_HAT3X, i); - i = decode_erp(buf[4], buf[6]); - input_report_abs(input_dev, ABS_HAT3Y, i); - input_sync(input_dev); - break; - } -} - -static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, - char *buf, unsigned int len) -{ - struct input_dev *input_dev = dev->input_dev; - unsigned short *keycode = input_dev->keycode; - int i; - - if (!keycode) - return; - - if (input_dev->id.product == USB_PID_RIGKONTROL2) - for (i = 0; i < len; i++) - buf[i] = ~buf[i]; - - for (i = 0; i < input_dev->keycodemax && i < len * 8; i++) - 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)) - input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]); - - input_sync(input_dev); -} - -void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, - char *buf, - unsigned int len) -{ - if (!dev->input_dev || len < 1) - return; - - switch (buf[0]) { - case EP1_CMD_READ_ANALOG: - snd_caiaq_input_read_analog(dev, buf + 1, len - 1); - break; - case EP1_CMD_READ_ERP: - snd_caiaq_input_read_erp(dev, buf + 1, len - 1); - break; - case EP1_CMD_READ_IO: - snd_caiaq_input_read_io(dev, buf + 1, len - 1); - break; - } -} - -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; - - input = input_allocate_device(); - if (!input) - return -ENOMEM; - - usb_make_path(usb_dev, dev->phys, sizeof(dev->phys)); - strlcat(dev->phys, "/input0", sizeof(dev->phys)); - - input->name = dev->product_name; - input->phys = dev->phys; - usb_to_input_id(usb_dev, &input->id); - input->dev.parent = &usb_dev->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) | - BIT_MASK(ABS_Z); - BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk2)); - memcpy(dev->keycode, keycode_rk2, sizeof(keycode_rk2)); - input->keycodemax = ARRAY_SIZE(keycode_rk2); - input_set_abs_params(input, ABS_X, 0, 4096, 0, 10); - input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10); - input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10); - snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0); - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): - input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | - BIT_MASK(ABS_Z); - BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk3)); - memcpy(dev->keycode, keycode_rk3, sizeof(keycode_rk3)); - input->keycodemax = ARRAY_SIZE(keycode_rk3); - input_set_abs_params(input, ABS_X, 0, 1024, 0, 10); - input_set_abs_params(input, ABS_Y, 0, 1024, 0, 10); - input_set_abs_params(input, ABS_Z, 0, 1024, 0, 10); - snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0); - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): - input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input->absbit[0] = BIT_MASK(ABS_X); - BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_ak1)); - memcpy(dev->keycode, keycode_ak1, sizeof(keycode_ak1)); - input->keycodemax = ARRAY_SIZE(keycode_ak1); - input_set_abs_params(input, ABS_X, 0, 999, 0, 10); - snd_usb_caiaq_set_auto_msg(dev, 1, 0, 5); - break; - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): - case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): - 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) < sizeof(keycode_kore)); - memcpy(dev->keycode, keycode_kore, sizeof(keycode_kore)); - input->keycodemax = ARRAY_SIZE(keycode_kore); - 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_X, 0, 4096, 0, 10); - input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10); - input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10); - input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1); - 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; - } - - 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; - } - - dev->input_dev = input; - return 0; -} - -void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev) -{ - if (!dev || !dev->input_dev) - return; - - input_unregister_device(dev->input_dev); - dev->input_dev = NULL; -} - diff --git a/sound/usb/caiaq/caiaq-input.h b/sound/usb/caiaq/caiaq-input.h deleted file mode 100644 index ced535577864..000000000000 --- a/sound/usb/caiaq/caiaq-input.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CAIAQ_INPUT_H -#define CAIAQ_INPUT_H - -void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, char *buf, unsigned int len); -int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev); -void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev); - -#endif diff --git a/sound/usb/caiaq/caiaq-midi.c b/sound/usb/caiaq/caiaq-midi.c deleted file mode 100644 index 79424c198912..000000000000 --- a/sound/usb/caiaq/caiaq-midi.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2006,2007 Daniel Mack - * - * 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 -*/ - -#include -#include -#include -#include - -#include "caiaq-device.h" -#include "caiaq-midi.h" - -static int snd_usb_caiaq_midi_input_open(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static int snd_usb_caiaq_midi_input_close(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static void snd_usb_caiaq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct snd_usb_caiaqdev *dev = substream->rmidi->private_data; - - if (!dev) - return; - - dev->midi_receive_substream = up ? substream : NULL; -} - - -static int snd_usb_caiaq_midi_output_open(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substream) -{ - struct snd_usb_caiaqdev *dev = substream->rmidi->private_data; - if (dev->midi_out_active) { - usb_kill_urb(&dev->midi_out_urb); - dev->midi_out_active = 0; - } - return 0; -} - -static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *dev, - struct snd_rawmidi_substream *substream) -{ - int len, ret; - - dev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE; - dev->midi_out_buf[1] = 0; /* port */ - len = snd_rawmidi_transmit(substream, dev->midi_out_buf + 3, - EP1_BUFSIZE - 3); - - if (len <= 0) - return; - - dev->midi_out_buf[2] = len; - dev->midi_out_urb.transfer_buffer_length = len+3; - - ret = usb_submit_urb(&dev->midi_out_urb, GFP_ATOMIC); - if (ret < 0) - log("snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed," - "ret=%d, len=%d\n", - substream, ret, len); - else - dev->midi_out_active = 1; -} - -static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct snd_usb_caiaqdev *dev = substream->rmidi->private_data; - - if (up) { - dev->midi_out_substream = substream; - if (!dev->midi_out_active) - snd_usb_caiaq_midi_send(dev, substream); - } else { - dev->midi_out_substream = NULL; - } -} - - -static struct snd_rawmidi_ops snd_usb_caiaq_midi_output = -{ - .open = snd_usb_caiaq_midi_output_open, - .close = snd_usb_caiaq_midi_output_close, - .trigger = snd_usb_caiaq_midi_output_trigger, -}; - -static struct snd_rawmidi_ops snd_usb_caiaq_midi_input = -{ - .open = snd_usb_caiaq_midi_input_open, - .close = snd_usb_caiaq_midi_input_close, - .trigger = snd_usb_caiaq_midi_input_trigger, -}; - -void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, - int port, const char *buf, int len) -{ - if (!dev->midi_receive_substream) - return; - - snd_rawmidi_receive(dev->midi_receive_substream, buf, len); -} - -int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device) -{ - int ret; - struct snd_rawmidi *rmidi; - - ret = snd_rawmidi_new(device->chip.card, device->product_name, 0, - device->spec.num_midi_out, - device->spec.num_midi_in, - &rmidi); - - if (ret < 0) - return ret; - - strcpy(rmidi->name, device->product_name); - - rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX; - rmidi->private_data = device; - - if (device->spec.num_midi_out > 0) { - rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &snd_usb_caiaq_midi_output); - } - - if (device->spec.num_midi_in > 0) { - rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &snd_usb_caiaq_midi_input); - } - - device->rmidi = rmidi; - - return 0; -} - -void snd_usb_caiaq_midi_output_done(struct urb* urb) -{ - struct snd_usb_caiaqdev *dev = urb->context; - - dev->midi_out_active = 0; - if (urb->status != 0) - return; - - if (!dev->midi_out_substream) - return; - - snd_usb_caiaq_midi_send(dev, dev->midi_out_substream); -} - diff --git a/sound/usb/caiaq/caiaq-midi.h b/sound/usb/caiaq/caiaq-midi.h deleted file mode 100644 index 9d16db027fc3..000000000000 --- a/sound/usb/caiaq/caiaq-midi.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CAIAQ_MIDI_H -#define CAIAQ_MIDI_H - -int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *dev); -void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, int port, const char *buf, int len); -void snd_usb_caiaq_midi_output_done(struct urb* urb); - -#endif /* CAIAQ_MIDI_H */ diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c new file mode 100644 index 000000000000..537102ba6b9d --- /dev/null +++ b/sound/usb/caiaq/control.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2007 Daniel Mack + * friendly supported by NI. + * + * 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 + */ + +#include +#include +#include +#include +#include + +#include "device.h" +#include "control.h" + +#define CNT_INTVAL 0x10000 + +static int control_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_usb_audio *chip = snd_kcontrol_chip(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; + + 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; + } + + 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; + } + + if (is_intval) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 64; + } else { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + } + + return 0; +} + +static int control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); + struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); + int pos = kcontrol->private_value; + + if (dev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)) { + /* A4DJ has only one control */ + /* do not expose hardware input mode 0 */ + ucontrol->value.integer.value[0] = dev->control_state[0] - 1; + return 0; + } + + if (pos & CNT_INTVAL) + ucontrol->value.integer.value[0] + = dev->control_state[pos & ~CNT_INTVAL]; + else + ucontrol->value.integer.value[0] + = !!(dev->control_state[pos / 8] & (1 << pos % 8)); + + return 0; +} + +static int control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); + struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); + int pos = kcontrol->private_value; + + if (dev->chip.usb_id == + 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; + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, + dev->control_state, sizeof(dev->control_state)); + return 1; + } + + 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, + dev->control_state, sizeof(dev->control_state)); + } else { + if (ucontrol->value.integer.value[0]) + dev->control_state[pos / 8] |= 1 << (pos % 8); + else + dev->control_state[pos / 8] &= ~(1 << (pos % 8)); + + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, + dev->control_state, sizeof(dev->control_state)); + } + + return 1; +} + +static struct snd_kcontrol_new kcontrol_template __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .index = 0, + .info = control_info, + .get = control_get, + .put = control_put, + /* name and private_value filled later */ +}; + +struct caiaq_controller { + char *name; + int index; +}; + +static struct caiaq_controller ak1_controller[] = { + { "LED left", 2 }, + { "LED middle", 1 }, + { "LED right", 0 }, + { "LED ring", 3 } +}; + +static struct caiaq_controller rk2_controller[] = { + { "LED 1", 5 }, + { "LED 2", 4 }, + { "LED 3", 3 }, + { "LED 4", 2 }, + { "LED 5", 1 }, + { "LED 6", 0 }, + { "LED pedal", 6 }, + { "LED 7seg_1b", 8 }, + { "LED 7seg_1c", 9 }, + { "LED 7seg_2a", 10 }, + { "LED 7seg_2b", 11 }, + { "LED 7seg_2c", 12 }, + { "LED 7seg_2d", 13 }, + { "LED 7seg_2e", 14 }, + { "LED 7seg_2f", 15 }, + { "LED 7seg_2g", 16 }, + { "LED 7seg_3a", 17 }, + { "LED 7seg_3b", 18 }, + { "LED 7seg_3c", 19 }, + { "LED 7seg_3d", 20 }, + { "LED 7seg_3e", 21 }, + { "LED 7seg_3f", 22 }, + { "LED 7seg_3g", 23 } +}; + +static struct caiaq_controller rk3_controller[] = { + { "LED 7seg_1a", 0 + 0 }, + { "LED 7seg_1b", 0 + 1 }, + { "LED 7seg_1c", 0 + 2 }, + { "LED 7seg_1d", 0 + 3 }, + { "LED 7seg_1e", 0 + 4 }, + { "LED 7seg_1f", 0 + 5 }, + { "LED 7seg_1g", 0 + 6 }, + { "LED 7seg_1p", 0 + 7 }, + + { "LED 7seg_2a", 8 + 0 }, + { "LED 7seg_2b", 8 + 1 }, + { "LED 7seg_2c", 8 + 2 }, + { "LED 7seg_2d", 8 + 3 }, + { "LED 7seg_2e", 8 + 4 }, + { "LED 7seg_2f", 8 + 5 }, + { "LED 7seg_2g", 8 + 6 }, + { "LED 7seg_2p", 8 + 7 }, + + { "LED 7seg_3a", 16 + 0 }, + { "LED 7seg_3b", 16 + 1 }, + { "LED 7seg_3c", 16 + 2 }, + { "LED 7seg_3d", 16 + 3 }, + { "LED 7seg_3e", 16 + 4 }, + { "LED 7seg_3f", 16 + 5 }, + { "LED 7seg_3g", 16 + 6 }, + { "LED 7seg_3p", 16 + 7 }, + + { "LED 7seg_4a", 24 + 0 }, + { "LED 7seg_4b", 24 + 1 }, + { "LED 7seg_4c", 24 + 2 }, + { "LED 7seg_4d", 24 + 3 }, + { "LED 7seg_4e", 24 + 4 }, + { "LED 7seg_4f", 24 + 5 }, + { "LED 7seg_4g", 24 + 6 }, + { "LED 7seg_4p", 24 + 7 }, + + { "LED 1", 32 + 0 }, + { "LED 2", 32 + 1 }, + { "LED 3", 32 + 2 }, + { "LED 4", 32 + 3 }, + { "LED 5", 32 + 4 }, + { "LED 6", 32 + 5 }, + { "LED 7", 32 + 6 }, + { "LED 8", 32 + 7 }, + { "LED pedal", 32 + 8 } +}; + +static struct caiaq_controller kore_controller[] = { + { "LED F1", 8 | CNT_INTVAL }, + { "LED F2", 12 | CNT_INTVAL }, + { "LED F3", 0 | CNT_INTVAL }, + { "LED F4", 4 | CNT_INTVAL }, + { "LED F5", 11 | CNT_INTVAL }, + { "LED F6", 15 | CNT_INTVAL }, + { "LED F7", 3 | CNT_INTVAL }, + { "LED F8", 7 | CNT_INTVAL }, + { "LED touch1", 10 | CNT_INTVAL }, + { "LED touch2", 14 | CNT_INTVAL }, + { "LED touch3", 2 | CNT_INTVAL }, + { "LED touch4", 6 | CNT_INTVAL }, + { "LED touch5", 9 | CNT_INTVAL }, + { "LED touch6", 13 | CNT_INTVAL }, + { "LED touch7", 1 | CNT_INTVAL }, + { "LED touch8", 5 | CNT_INTVAL }, + { "LED left", 18 | CNT_INTVAL }, + { "LED right", 22 | CNT_INTVAL }, + { "LED up", 16 | CNT_INTVAL }, + { "LED down", 20 | CNT_INTVAL }, + { "LED stop", 23 | CNT_INTVAL }, + { "LED play", 21 | CNT_INTVAL }, + { "LED record", 19 | CNT_INTVAL }, + { "LED listen", 17 | CNT_INTVAL }, + { "LED lcd", 30 | CNT_INTVAL }, + { "LED menu", 28 | CNT_INTVAL }, + { "LED sound", 31 | CNT_INTVAL }, + { "LED esc", 29 | CNT_INTVAL }, + { "LED view", 27 | CNT_INTVAL }, + { "LED enter", 24 | CNT_INTVAL }, + { "LED control", 26 | CNT_INTVAL } +}; + +static struct caiaq_controller a8dj_controller[] = { + { "Current input mode", 0 | CNT_INTVAL }, + { "GND lift for TC Vinyl mode", 24 + 0 }, + { "GND lift for TC CD/Line mode", 24 + 1 }, + { "GND lift for phono mode", 24 + 2 }, + { "Software lock", 40 } +}; + +static struct caiaq_controller a4dj_controller[] = { + { "Current input mode", 0 | CNT_INTVAL } +}; + +static int __devinit add_controls(struct caiaq_controller *c, int num, + struct snd_usb_caiaqdev *dev) +{ + int i, ret; + struct snd_kcontrol *kc; + + for (i = 0; i < num; i++, c++) { + kcontrol_template.name = c->name; + kcontrol_template.private_value = c->index; + kc = snd_ctl_new1(&kcontrol_template, dev); + ret = snd_ctl_add(dev->chip.card, kc); + if (ret < 0) + return ret; + } + + return 0; +} + +int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev) +{ + int ret = 0; + + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): + ret = add_controls(ak1_controller, + ARRAY_SIZE(ak1_controller), dev); + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): + ret = add_controls(rk2_controller, + ARRAY_SIZE(rk2_controller), dev); + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): + ret = add_controls(rk3_controller, + ARRAY_SIZE(rk3_controller), dev); + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + ret = add_controls(kore_controller, + ARRAY_SIZE(kore_controller), dev); + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): + 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; + } + + return ret; +} + diff --git a/sound/usb/caiaq/control.h b/sound/usb/caiaq/control.h new file mode 100644 index 000000000000..2e7ab1aa4fb3 --- /dev/null +++ b/sound/usb/caiaq/control.h @@ -0,0 +1,6 @@ +#ifndef CAIAQ_CONTROL_H +#define CAIAQ_CONTROL_H + +int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev); + +#endif /* CAIAQ_CONTROL_H */ diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c new file mode 100644 index 000000000000..6d517705da0e --- /dev/null +++ b/sound/usb/caiaq/device.c @@ -0,0 +1,521 @@ +/* + * caiaq.c: ALSA driver for caiaq/NativeInstruments devices + * + * Copyright (c) 2007 Daniel Mack + * Karsten Wiese + * + * 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "device.h" +#include "audio.h" +#include "midi.h" +#include "control.h" +#include "input.h" + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("caiaq USB audio, version 1.3.13"); +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 4 DJ}," + "{Native Instruments, Audio 8 DJ}," + "{Native Instruments, Session I/O}," + "{Native Instruments, GuitarRig mobile}"); + +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 */ +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"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for the caiaq soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable the caiaq soundcard."); + +enum { + SAMPLERATE_44100 = 0, + SAMPLERATE_48000 = 1, + SAMPLERATE_96000 = 2, + SAMPLERATE_192000 = 3, + SAMPLERATE_88200 = 4, + SAMPLERATE_INVALID = 0xff +}; + +enum { + DEPTH_NONE = 0, + DEPTH_16 = 1, + DEPTH_24 = 2, + DEPTH_32 = 3 +}; + +static struct usb_device_id snd_usb_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_RIGKONTROL2 + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_RIGKONTROL3 + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_KORECONTROLLER + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_KORECONTROLLER2 + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_AK1 + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_AUDIO8DJ + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_SESSIONIO + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_GUITARRIGMOBILE + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_AUDIO4DJ + }, + { /* terminator */ } +}; + +static void usb_ep1_command_reply_dispatch (struct urb* urb) +{ + int ret; + struct snd_usb_caiaqdev *dev = urb->context; + unsigned char *buf = urb->transfer_buffer; + + if (urb->status || !dev) { + log("received EP1 urb->status = %i\n", urb->status); + return; + } + + switch(buf[0]) { + case EP1_CMD_GET_DEVICE_INFO: + memcpy(&dev->spec, buf+1, sizeof(struct caiaq_device_spec)); + dev->spec.fw_version = le16_to_cpu(dev->spec.fw_version); + debug("device spec (firmware %d): audio: %d in, %d out, " + "MIDI: %d in, %d out, data alignment %d\n", + dev->spec.fw_version, + dev->spec.num_analog_audio_in, + dev->spec.num_analog_audio_out, + dev->spec.num_midi_in, + dev->spec.num_midi_out, + dev->spec.data_alignment); + + dev->spec_received++; + wake_up(&dev->ep1_wait_queue); + break; + case EP1_CMD_AUDIO_PARAMS: + dev->audio_parm_answer = buf[1]; + wake_up(&dev->ep1_wait_queue); + break; + case EP1_CMD_MIDI_READ: + snd_usb_caiaq_midi_handle_input(dev, buf[1], buf + 3, buf[2]); + break; + case EP1_CMD_READ_IO: + if (dev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) { + if (urb->actual_length > sizeof(dev->control_state)) + urb->actual_length = sizeof(dev->control_state); + memcpy(dev->control_state, buf + 1, urb->actual_length); + wake_up(&dev->ep1_wait_queue); + break; + } +#ifdef CONFIG_SND_USB_CAIAQ_INPUT + case EP1_CMD_READ_ERP: + case EP1_CMD_READ_ANALOG: + snd_usb_caiaq_input_dispatch(dev, buf, urb->actual_length); +#endif + break; + } + + dev->ep1_in_urb.actual_length = 0; + ret = usb_submit_urb(&dev->ep1_in_urb, GFP_ATOMIC); + if (ret < 0) + log("unable to submit urb. OOM!?\n"); +} + +int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev, + unsigned char command, + const unsigned char *buffer, + int len) +{ + int actual_len; + struct usb_device *usb_dev = dev->chip.dev; + + if (!usb_dev) + return -EIO; + + if (len > EP1_BUFSIZE - 1) + len = EP1_BUFSIZE - 1; + + if (buffer && len > 0) + memcpy(dev->ep1_out_buf+1, buffer, len); + + dev->ep1_out_buf[0] = command; + return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1), + dev->ep1_out_buf, len+1, &actual_len, 200); +} + +int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, + int rate, int depth, int bpp) +{ + int ret; + char tmp[5]; + + switch (rate) { + case 44100: tmp[0] = SAMPLERATE_44100; break; + case 48000: tmp[0] = SAMPLERATE_48000; break; + case 88200: tmp[0] = SAMPLERATE_88200; break; + case 96000: tmp[0] = SAMPLERATE_96000; break; + case 192000: tmp[0] = SAMPLERATE_192000; break; + default: return -EINVAL; + } + + switch (depth) { + case 16: tmp[1] = DEPTH_16; break; + case 24: tmp[1] = DEPTH_24; break; + default: return -EINVAL; + } + + tmp[2] = bpp & 0xff; + tmp[3] = bpp >> 8; + tmp[4] = 1; /* packets per microframe */ + + debug("setting audio params: %d Hz, %d bits, %d bpp\n", + rate, depth, bpp); + + dev->audio_parm_answer = -1; + ret = snd_usb_caiaq_send_command(dev, EP1_CMD_AUDIO_PARAMS, + tmp, sizeof(tmp)); + + if (ret) + return ret; + + if (!wait_event_timeout(dev->ep1_wait_queue, + dev->audio_parm_answer >= 0, HZ)) + return -EPIPE; + + if (dev->audio_parm_answer != 1) + debug("unable to set the device's audio params\n"); + else + dev->bpp = bpp; + + return dev->audio_parm_answer == 1 ? 0 : -EINVAL; +} + +int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, + int digital, int analog, int erp) +{ + char tmp[3] = { digital, analog, erp }; + return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG, + tmp, sizeof(tmp)); +} + +static void __devinit setup_card(struct snd_usb_caiaqdev *dev) +{ + int ret; + char val[4]; + + /* device-specific startup specials */ + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): + /* RigKontrol2 - display centered dash ('-') */ + val[0] = 0x00; + val[1] = 0x00; + val[2] = 0x01; + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 3); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): + /* RigKontrol2 - display two centered dashes ('--') */ + val[0] = 0x00; + val[1] = 0x40; + val[2] = 0x40; + val[3] = 0x00; + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 4); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): + /* Audio Kontrol 1 - make USB-LED stop blinking */ + val[0] = 0x00; + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 1); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): + /* Audio 8 DJ - trigger read of current settings */ + dev->control_state[0] = 0xff; + snd_usb_caiaq_set_auto_msg(dev, 1, 0, 0); + snd_usb_caiaq_send_command(dev, EP1_CMD_READ_IO, NULL, 0); + + if (!wait_event_timeout(dev->ep1_wait_queue, + dev->control_state[0] != 0xff, HZ)) + return; + + /* fix up some defaults */ + if ((dev->control_state[1] != 2) || + (dev->control_state[2] != 3) || + (dev->control_state[4] != 2)) { + dev->control_state[1] = 2; + dev->control_state[2] = 3; + dev->control_state[4] = 2; + snd_usb_caiaq_send_command(dev, + EP1_CMD_WRITE_IO, dev->control_state, 6); + } + + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): + /* Audio 4 DJ - default input mode to phono */ + dev->control_state[0] = 2; + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, + dev->control_state, 1); + break; + } + + if (dev->spec.num_analog_audio_out + + dev->spec.num_analog_audio_in + + dev->spec.num_digital_audio_out + + dev->spec.num_digital_audio_in > 0) { + ret = snd_usb_caiaq_audio_init(dev); + if (ret < 0) + log("Unable to set up audio system (ret=%d)\n", ret); + } + + if (dev->spec.num_midi_in + + dev->spec.num_midi_out > 0) { + ret = snd_usb_caiaq_midi_init(dev); + if (ret < 0) + log("Unable to set up MIDI system (ret=%d)\n", ret); + } + +#ifdef CONFIG_SND_USB_CAIAQ_INPUT + ret = snd_usb_caiaq_input_init(dev); + if (ret < 0) + log("Unable to set up input system (ret=%d)\n", ret); +#endif + + /* finally, register the card and all its sub-instances */ + ret = snd_card_register(dev->chip.card); + if (ret < 0) { + log("snd_card_register() returned %d\n", ret); + snd_card_free(dev->chip.card); + } + + ret = snd_usb_caiaq_control_init(dev); + if (ret < 0) + log("Unable to set up control system (ret=%d)\n", ret); +} + +static int create_card(struct usb_device* usb_dev, struct snd_card **cardp) +{ + int devnum; + int err; + struct snd_card *card; + struct snd_usb_caiaqdev *dev; + + for (devnum = 0; devnum < SNDRV_CARDS; devnum++) + if (enable[devnum] && !snd_card_used[devnum]) + break; + + if (devnum >= SNDRV_CARDS) + return -ENODEV; + + err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, + sizeof(struct snd_usb_caiaqdev), &card); + if (err < 0) + return err; + + dev = caiaqdev(card); + dev->chip.dev = usb_dev; + dev->chip.card = card; + dev->chip.usb_id = USB_ID(le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct)); + spin_lock_init(&dev->spinlock); + snd_card_set_dev(card, &usb_dev->dev); + + *cardp = card; + return 0; +} + +static int __devinit init_card(struct snd_usb_caiaqdev *dev) +{ + char *c; + struct usb_device *usb_dev = dev->chip.dev; + struct snd_card *card = dev->chip.card; + int err, len; + + if (usb_set_interface(usb_dev, 0, 1) != 0) { + log("can't set alt interface.\n"); + return -EIO; + } + + usb_init_urb(&dev->ep1_in_urb); + usb_init_urb(&dev->midi_out_urb); + + usb_fill_bulk_urb(&dev->ep1_in_urb, usb_dev, + usb_rcvbulkpipe(usb_dev, 0x1), + dev->ep1_in_buf, EP1_BUFSIZE, + usb_ep1_command_reply_dispatch, dev); + + usb_fill_bulk_urb(&dev->midi_out_urb, usb_dev, + usb_sndbulkpipe(usb_dev, 0x1), + dev->midi_out_buf, EP1_BUFSIZE, + snd_usb_caiaq_midi_output_done, dev); + + init_waitqueue_head(&dev->ep1_wait_queue); + init_waitqueue_head(&dev->prepare_wait_queue); + + if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0) + return -EIO; + + err = snd_usb_caiaq_send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0); + if (err) + return err; + + if (!wait_event_timeout(dev->ep1_wait_queue, dev->spec_received, HZ)) + return -ENODEV; + + usb_string(usb_dev, usb_dev->descriptor.iManufacturer, + dev->vendor_name, CAIAQ_USB_STR_LEN); + + usb_string(usb_dev, usb_dev->descriptor.iProduct, + dev->product_name, CAIAQ_USB_STR_LEN); + + usb_string(usb_dev, usb_dev->descriptor.iSerialNumber, + dev->serial, CAIAQ_USB_STR_LEN); + + /* terminate serial string at first white space occurence */ + c = strchr(dev->serial, ' '); + if (c) + *c = '\0'; + + strcpy(card->driver, MODNAME); + strcpy(card->shortname, dev->product_name); + + len = snprintf(card->longname, sizeof(card->longname), + "%s %s (serial %s, ", + dev->vendor_name, dev->product_name, dev->serial); + + if (len < sizeof(card->longname) - 2) + len += usb_make_path(usb_dev, card->longname + len, + sizeof(card->longname) - len); + + card->longname[len++] = ')'; + card->longname[len] = '\0'; + setup_card(dev); + return 0; +} + +static int __devinit snd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + struct snd_card *card; + struct usb_device *device = interface_to_usbdev(intf); + + ret = create_card(device, &card); + + if (ret < 0) + return ret; + + usb_set_intfdata(intf, card); + ret = init_card(caiaqdev(card)); + if (ret < 0) { + log("unable to init card! (ret=%d)\n", ret); + snd_card_free(card); + return ret; + } + + return 0; +} + +static void snd_disconnect(struct usb_interface *intf) +{ + struct snd_usb_caiaqdev *dev; + struct snd_card *card = usb_get_intfdata(intf); + + debug("%s(%p)\n", __func__, intf); + + if (!card) + return; + + dev = caiaqdev(card); + snd_card_disconnect(card); + +#ifdef CONFIG_SND_USB_CAIAQ_INPUT + snd_usb_caiaq_input_free(dev); +#endif + snd_usb_caiaq_audio_free(dev); + + usb_kill_urb(&dev->ep1_in_urb); + usb_kill_urb(&dev->midi_out_urb); + + snd_card_free(card); + usb_reset_device(interface_to_usbdev(intf)); +} + + +MODULE_DEVICE_TABLE(usb, snd_usb_id_table); +static struct usb_driver snd_usb_driver = { + .name = MODNAME, + .probe = snd_probe, + .disconnect = snd_disconnect, + .id_table = snd_usb_id_table, +}; + +static int __init snd_module_init(void) +{ + return usb_register(&snd_usb_driver); +} + +static void __exit snd_module_exit(void) +{ + usb_deregister(&snd_usb_driver); +} + +module_init(snd_module_init) +module_exit(snd_module_exit) + diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h new file mode 100644 index 000000000000..4cce1ad7493d --- /dev/null +++ b/sound/usb/caiaq/device.h @@ -0,0 +1,131 @@ +#ifndef CAIAQ_DEVICE_H +#define CAIAQ_DEVICE_H + +#include "../usbaudio.h" + +#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_AUDIO4DJ 0x0839 +#define USB_PID_AUDIO8DJ 0x1978 +#define USB_PID_SESSIONIO 0x1915 +#define USB_PID_GUITARRIGMOBILE 0x0d8d + +#define EP1_BUFSIZE 64 +#define CAIAQ_USB_STR_LEN 0xff +#define MAX_STREAMS 32 + +//#define SND_USB_CAIAQ_DEBUG + +#define MODNAME "snd-usb-caiaq" +#define log(x...) snd_printk(KERN_WARNING MODNAME" log: " x) + +#ifdef SND_USB_CAIAQ_DEBUG +#define debug(x...) snd_printk(KERN_WARNING MODNAME " debug: " x) +#else +#define debug(x...) do { } while(0) +#endif + +#define EP1_CMD_GET_DEVICE_INFO 0x1 +#define EP1_CMD_READ_ERP 0x2 +#define EP1_CMD_READ_ANALOG 0x3 +#define EP1_CMD_READ_IO 0x4 +#define EP1_CMD_WRITE_IO 0x5 +#define EP1_CMD_MIDI_READ 0x6 +#define EP1_CMD_MIDI_WRITE 0x7 +#define EP1_CMD_AUDIO_PARAMS 0x9 +#define EP1_CMD_AUTO_MSG 0xb +#define EP1_CMD_DIMM_LEDS 0xc + +struct caiaq_device_spec { + unsigned short fw_version; + unsigned char hw_subtype; + unsigned char num_erp; + unsigned char num_analog_in; + unsigned char num_digital_in; + unsigned char num_digital_out; + unsigned char num_analog_audio_out; + unsigned char num_analog_audio_in; + unsigned char num_digital_audio_out; + unsigned char num_digital_audio_in; + unsigned char num_midi_out; + unsigned char num_midi_in; + unsigned char data_alignment; +} __attribute__ ((packed)); + +struct snd_usb_caiaq_cb_info; + +struct snd_usb_caiaqdev { + struct snd_usb_audio chip; + + struct urb ep1_in_urb; + struct urb midi_out_urb; + struct urb **data_urbs_in; + struct urb **data_urbs_out; + struct snd_usb_caiaq_cb_info *data_cb_info; + + unsigned char ep1_in_buf[EP1_BUFSIZE]; + unsigned char ep1_out_buf[EP1_BUFSIZE]; + unsigned char midi_out_buf[EP1_BUFSIZE]; + + struct caiaq_device_spec spec; + spinlock_t spinlock; + wait_queue_head_t ep1_wait_queue; + wait_queue_head_t prepare_wait_queue; + int spec_received, audio_parm_answer; + int midi_out_active; + + char vendor_name[CAIAQ_USB_STR_LEN]; + char product_name[CAIAQ_USB_STR_LEN]; + char serial[CAIAQ_USB_STR_LEN]; + + int n_streams, n_audio_in, n_audio_out; + int streaming, first_packet, output_running; + int audio_in_buf_pos[MAX_STREAMS]; + int audio_out_buf_pos[MAX_STREAMS]; + int period_in_count[MAX_STREAMS]; + int period_out_count[MAX_STREAMS]; + int input_panic, output_panic, warned; + char *audio_in_buf, *audio_out_buf; + unsigned int samplerates, bpp; + + struct snd_pcm_substream *sub_playback[MAX_STREAMS]; + struct snd_pcm_substream *sub_capture[MAX_STREAMS]; + + /* Controls */ + unsigned char control_state[64]; + + /* Linux input */ +#ifdef CONFIG_SND_USB_CAIAQ_INPUT + struct input_dev *input_dev; + char phys[64]; /* physical device path */ + unsigned short keycode[64]; +#endif + + /* ALSA */ + struct snd_pcm *pcm; + struct snd_pcm_hardware pcm_info; + struct snd_rawmidi *rmidi; + struct snd_rawmidi_substream *midi_receive_substream; + struct snd_rawmidi_substream *midi_out_substream; +}; + +struct snd_usb_caiaq_cb_info { + struct snd_usb_caiaqdev *dev; + int index; +}; + +#define caiaqdev(c) ((struct snd_usb_caiaqdev*)(c)->private_data) + +int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, int rate, int depth, int bbp); +int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, int digital, int analog, int erp); +int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev, + unsigned char command, + const unsigned char *buffer, + int len); + +#endif /* CAIAQ_DEVICE_H */ diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c new file mode 100644 index 000000000000..a48d309bd94c --- /dev/null +++ b/sound/usb/caiaq/input.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2006,2007 Daniel Mack, Tim Ruetz + * + * 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 +*/ + +#include +#include +#include +#include + +#include "device.h" +#include "input.h" + +static unsigned short keycode_ak1[] = { KEY_C, KEY_B, KEY_A }; +static unsigned short keycode_rk2[] = { KEY_1, KEY_2, KEY_3, KEY_4, + KEY_5, KEY_6, KEY_7 }; +static unsigned short keycode_rk3[] = { KEY_1, KEY_2, KEY_3, KEY_4, + KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 }; + +static unsigned short keycode_kore[] = { + KEY_FN_F1, /* "menu" */ + KEY_FN_F7, /* "lcd backlight */ + KEY_FN_F2, /* "control" */ + KEY_FN_F3, /* "enter" */ + KEY_FN_F4, /* "view" */ + KEY_FN_F5, /* "esc" */ + KEY_FN_F6, /* "sound" */ + KEY_FN_F8, /* array spacer, never triggered. */ + KEY_RIGHT, + KEY_DOWN, + KEY_UP, + KEY_LEFT, + KEY_SOUND, /* "listen" */ + KEY_RECORD, + KEY_PLAYPAUSE, + KEY_STOP, + BTN_4, /* 8 softkeys */ + BTN_3, + BTN_2, + BTN_1, + BTN_8, + BTN_7, + BTN_6, + BTN_5, + KEY_BRL_DOT4, /* touch sensitive knobs */ + KEY_BRL_DOT3, + KEY_BRL_DOT2, + KEY_BRL_DOT1, + KEY_BRL_DOT8, + KEY_BRL_DOT7, + KEY_BRL_DOT6, + KEY_BRL_DOT5 +}; + +#define DEG90 (range / 2) +#define DEG180 (range) +#define DEG270 (DEG90 + DEG180) +#define DEG360 (DEG180 * 2) +#define HIGH_PEAK (268) +#define LOW_PEAK (-7) + +/* some of these devices have endless rotation potentiometers + * built in which use two tapers, 90 degrees phase shifted. + * this algorithm decodes them to one single value, ranging + * from 0 to 999 */ +static unsigned int decode_erp(unsigned char a, unsigned char b) +{ + int weight_a, weight_b; + int pos_a, pos_b; + int ret; + int range = HIGH_PEAK - LOW_PEAK; + int mid_value = (HIGH_PEAK + LOW_PEAK) / 2; + + weight_b = abs(mid_value - a) - (range / 2 - 100) / 2; + + if (weight_b < 0) + weight_b = 0; + + if (weight_b > 100) + weight_b = 100; + + weight_a = 100 - weight_b; + + if (a < mid_value) { + /* 0..90 and 270..360 degrees */ + pos_b = b - LOW_PEAK + DEG270; + if (pos_b >= DEG360) + pos_b -= DEG360; + } else + /* 90..270 degrees */ + pos_b = HIGH_PEAK - b + DEG90; + + + if (b > mid_value) + /* 0..180 degrees */ + pos_a = a - LOW_PEAK; + else + /* 180..360 degrees */ + pos_a = HIGH_PEAK - a + DEG180; + + /* interpolate both slider values, depending on weight factors */ + /* 0..99 x DEG360 */ + ret = pos_a * weight_a + pos_b * weight_b; + + /* normalize to 0..999 */ + ret *= 10; + ret /= DEG360; + + if (ret < 0) + ret += 1000; + + if (ret >= 1000) + ret -= 1000; + + return ret; +} + +#undef DEG90 +#undef DEG180 +#undef DEG270 +#undef DEG360 +#undef HIGH_PEAK +#undef LOW_PEAK + + +static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, + const unsigned char *buf, + unsigned int len) +{ + struct input_dev *input_dev = dev->input_dev; + + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): + input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]); + input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]); + input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]); + input_sync(input_dev); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): + input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]); + input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]); + 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_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]); + input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]); + input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]); + input_sync(input_dev); + break; + } +} + +static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, + const char *buf, unsigned int len) +{ + struct input_dev *input_dev = dev->input_dev; + int i; + + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): + i = decode_erp(buf[0], buf[1]); + input_report_abs(input_dev, ABS_X, i); + input_sync(input_dev); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + i = decode_erp(buf[7], buf[5]); + input_report_abs(input_dev, ABS_HAT0X, i); + i = decode_erp(buf[12], buf[14]); + input_report_abs(input_dev, ABS_HAT0Y, i); + i = decode_erp(buf[15], buf[13]); + input_report_abs(input_dev, ABS_HAT1X, i); + i = decode_erp(buf[0], buf[2]); + input_report_abs(input_dev, ABS_HAT1Y, i); + i = decode_erp(buf[3], buf[1]); + input_report_abs(input_dev, ABS_HAT2X, i); + i = decode_erp(buf[8], buf[10]); + input_report_abs(input_dev, ABS_HAT2Y, i); + i = decode_erp(buf[11], buf[9]); + input_report_abs(input_dev, ABS_HAT3X, i); + i = decode_erp(buf[4], buf[6]); + input_report_abs(input_dev, ABS_HAT3Y, i); + input_sync(input_dev); + break; + } +} + +static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, + char *buf, unsigned int len) +{ + struct input_dev *input_dev = dev->input_dev; + unsigned short *keycode = input_dev->keycode; + int i; + + if (!keycode) + return; + + if (input_dev->id.product == USB_PID_RIGKONTROL2) + for (i = 0; i < len; i++) + buf[i] = ~buf[i]; + + for (i = 0; i < input_dev->keycodemax && i < len * 8; i++) + 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)) + input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]); + + input_sync(input_dev); +} + +void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, + char *buf, + unsigned int len) +{ + if (!dev->input_dev || len < 1) + return; + + switch (buf[0]) { + case EP1_CMD_READ_ANALOG: + snd_caiaq_input_read_analog(dev, buf + 1, len - 1); + break; + case EP1_CMD_READ_ERP: + snd_caiaq_input_read_erp(dev, buf + 1, len - 1); + break; + case EP1_CMD_READ_IO: + snd_caiaq_input_read_io(dev, buf + 1, len - 1); + break; + } +} + +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; + + input = input_allocate_device(); + if (!input) + return -ENOMEM; + + usb_make_path(usb_dev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + input->name = dev->product_name; + input->phys = dev->phys; + usb_to_input_id(usb_dev, &input->id); + input->dev.parent = &usb_dev->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) | + BIT_MASK(ABS_Z); + BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk2)); + memcpy(dev->keycode, keycode_rk2, sizeof(keycode_rk2)); + input->keycodemax = ARRAY_SIZE(keycode_rk2); + input_set_abs_params(input, ABS_X, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10); + snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | + BIT_MASK(ABS_Z); + BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk3)); + memcpy(dev->keycode, keycode_rk3, sizeof(keycode_rk3)); + input->keycodemax = ARRAY_SIZE(keycode_rk3); + input_set_abs_params(input, ABS_X, 0, 1024, 0, 10); + input_set_abs_params(input, ABS_Y, 0, 1024, 0, 10); + input_set_abs_params(input, ABS_Z, 0, 1024, 0, 10); + snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->absbit[0] = BIT_MASK(ABS_X); + BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_ak1)); + memcpy(dev->keycode, keycode_ak1, sizeof(keycode_ak1)); + input->keycodemax = ARRAY_SIZE(keycode_ak1); + input_set_abs_params(input, ABS_X, 0, 999, 0, 10); + snd_usb_caiaq_set_auto_msg(dev, 1, 0, 5); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + 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) < sizeof(keycode_kore)); + memcpy(dev->keycode, keycode_kore, sizeof(keycode_kore)); + input->keycodemax = ARRAY_SIZE(keycode_kore); + 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_X, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1); + 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; + } + + 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; + } + + dev->input_dev = input; + return 0; +} + +void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev) +{ + if (!dev || !dev->input_dev) + return; + + input_unregister_device(dev->input_dev); + dev->input_dev = NULL; +} + diff --git a/sound/usb/caiaq/input.h b/sound/usb/caiaq/input.h new file mode 100644 index 000000000000..ced535577864 --- /dev/null +++ b/sound/usb/caiaq/input.h @@ -0,0 +1,8 @@ +#ifndef CAIAQ_INPUT_H +#define CAIAQ_INPUT_H + +void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, char *buf, unsigned int len); +int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev); +void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev); + +#endif diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c new file mode 100644 index 000000000000..8fa8cd88d763 --- /dev/null +++ b/sound/usb/caiaq/midi.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2006,2007 Daniel Mack + * + * 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 +*/ + +#include +#include +#include +#include + +#include "device.h" +#include "midi.h" + +static int snd_usb_caiaq_midi_input_open(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static int snd_usb_caiaq_midi_input_close(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static void snd_usb_caiaq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct snd_usb_caiaqdev *dev = substream->rmidi->private_data; + + if (!dev) + return; + + dev->midi_receive_substream = up ? substream : NULL; +} + + +static int snd_usb_caiaq_midi_output_open(struct snd_rawmidi_substream *substream) +{ + return 0; +} + +static int snd_usb_caiaq_midi_output_close(struct snd_rawmidi_substream *substream) +{ + struct snd_usb_caiaqdev *dev = substream->rmidi->private_data; + if (dev->midi_out_active) { + usb_kill_urb(&dev->midi_out_urb); + dev->midi_out_active = 0; + } + return 0; +} + +static void snd_usb_caiaq_midi_send(struct snd_usb_caiaqdev *dev, + struct snd_rawmidi_substream *substream) +{ + int len, ret; + + dev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE; + dev->midi_out_buf[1] = 0; /* port */ + len = snd_rawmidi_transmit(substream, dev->midi_out_buf + 3, + EP1_BUFSIZE - 3); + + if (len <= 0) + return; + + dev->midi_out_buf[2] = len; + dev->midi_out_urb.transfer_buffer_length = len+3; + + ret = usb_submit_urb(&dev->midi_out_urb, GFP_ATOMIC); + if (ret < 0) + log("snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed," + "ret=%d, len=%d\n", + substream, ret, len); + else + dev->midi_out_active = 1; +} + +static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct snd_usb_caiaqdev *dev = substream->rmidi->private_data; + + if (up) { + dev->midi_out_substream = substream; + if (!dev->midi_out_active) + snd_usb_caiaq_midi_send(dev, substream); + } else { + dev->midi_out_substream = NULL; + } +} + + +static struct snd_rawmidi_ops snd_usb_caiaq_midi_output = +{ + .open = snd_usb_caiaq_midi_output_open, + .close = snd_usb_caiaq_midi_output_close, + .trigger = snd_usb_caiaq_midi_output_trigger, +}; + +static struct snd_rawmidi_ops snd_usb_caiaq_midi_input = +{ + .open = snd_usb_caiaq_midi_input_open, + .close = snd_usb_caiaq_midi_input_close, + .trigger = snd_usb_caiaq_midi_input_trigger, +}; + +void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, + int port, const char *buf, int len) +{ + if (!dev->midi_receive_substream) + return; + + snd_rawmidi_receive(dev->midi_receive_substream, buf, len); +} + +int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device) +{ + int ret; + struct snd_rawmidi *rmidi; + + ret = snd_rawmidi_new(device->chip.card, device->product_name, 0, + device->spec.num_midi_out, + device->spec.num_midi_in, + &rmidi); + + if (ret < 0) + return ret; + + strcpy(rmidi->name, device->product_name); + + rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = device; + + if (device->spec.num_midi_out > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_usb_caiaq_midi_output); + } + + if (device->spec.num_midi_in > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &snd_usb_caiaq_midi_input); + } + + device->rmidi = rmidi; + + return 0; +} + +void snd_usb_caiaq_midi_output_done(struct urb* urb) +{ + struct snd_usb_caiaqdev *dev = urb->context; + + dev->midi_out_active = 0; + if (urb->status != 0) + return; + + if (!dev->midi_out_substream) + return; + + snd_usb_caiaq_midi_send(dev, dev->midi_out_substream); +} + diff --git a/sound/usb/caiaq/midi.h b/sound/usb/caiaq/midi.h new file mode 100644 index 000000000000..9d16db027fc3 --- /dev/null +++ b/sound/usb/caiaq/midi.h @@ -0,0 +1,8 @@ +#ifndef CAIAQ_MIDI_H +#define CAIAQ_MIDI_H + +int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *dev); +void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, int port, const char *buf, int len); +void snd_usb_caiaq_midi_output_done(struct urb* urb); + +#endif /* CAIAQ_MIDI_H */