| /* |
| * 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 <linux/spinlock.h> |
| #include <linux/slab.h> |
| #include <linux/init.h> |
| #include <linux/usb.h> |
| #include <sound/core.h> |
| #include <sound/pcm.h> |
| |
| #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) |
| { |
| spin_lock(&dev->spinlock); |
| |
| if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| dev->sub_playback[sub->number] = sub; |
| else |
| dev->sub_capture[sub->number] = sub; |
| |
| spin_unlock(&dev->spinlock); |
| } |
| |
| 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 = 4; |
| 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) { |
| int out_pos; |
| |
| switch (dev->spec.data_alignment) { |
| case 0: |
| case 2: |
| out_pos = BYTES_PER_SAMPLE + 1; |
| break; |
| case 3: |
| default: |
| out_pos = 0; |
| break; |
| } |
| |
| dev->period_out_count[index] = out_pos; |
| dev->audio_out_buf_pos[index] = out_pos; |
| } else { |
| int in_pos; |
| |
| switch (dev->spec.data_alignment) { |
| case 0: |
| in_pos = BYTES_PER_SAMPLE + 2; |
| break; |
| case 2: |
| in_pos = BYTES_PER_SAMPLE; |
| break; |
| case 3: |
| default: |
| in_pos = 0; |
| break; |
| } |
| |
| dev->period_in_count[index] = in_pos; |
| dev->audio_in_buf_pos[index] = in_pos; |
| } |
| |
| if (dev->streaming) |
| 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); |
| |
| debug("%s(%p) cmd %d\n", __func__, sub, cmd); |
| |
| 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); |
| snd_pcm_uframes_t ptr; |
| |
| spin_lock(&dev->spinlock); |
| |
| if (dev->input_panic || dev->output_panic) |
| ptr = SNDRV_PCM_POS_XRUN; |
| |
| if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| ptr = bytes_to_frames(sub->runtime, |
| dev->audio_out_buf_pos[index]); |
| else |
| ptr = bytes_to_frames(sub->runtime, |
| dev->audio_in_buf_pos[index]); |
| |
| spin_unlock(&dev->spinlock); |
| return ptr; |
| } |
| |
| /* 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 = snd_pcm_lib_period_bytes(sub); |
| 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_mode3(struct snd_usb_caiaqdev *dev, |
| const struct urb *urb, |
| const struct usb_iso_packet_descriptor *iso) |
| { |
| unsigned char *usb_buf = urb->transfer_buffer + iso->offset; |
| int stream, i; |
| |
| /* paranoia check */ |
| if (iso->actual_length % (BYTES_PER_SAMPLE_USB * CHANNELS_PER_STREAM)) |
| return; |
| |
| for (i = 0; i < iso->actual_length;) { |
| for (stream = 0; stream < dev->n_streams; stream++) { |
| struct snd_pcm_substream *sub = dev->sub_capture[stream]; |
| char *audio_buf = NULL; |
| int c, n, sz = 0; |
| |
| if (sub && !dev->input_panic) { |
| struct snd_pcm_runtime *rt = sub->runtime; |
| audio_buf = rt->dma_area; |
| sz = frames_to_bytes(rt, rt->buffer_size); |
| } |
| |
| for (c = 0; c < CHANNELS_PER_STREAM; c++) { |
| /* 3 audio data bytes, followed by 1 check byte */ |
| if (audio_buf) { |
| for (n = 0; n < BYTES_PER_SAMPLE; n++) { |
| audio_buf[dev->audio_in_buf_pos[stream]++] = usb_buf[i+n]; |
| |
| if (dev->audio_in_buf_pos[stream] == sz) |
| dev->audio_in_buf_pos[stream] = 0; |
| } |
| |
| dev->period_in_count[stream] += BYTES_PER_SAMPLE; |
| } |
| |
| i += BYTES_PER_SAMPLE; |
| |
| if (usb_buf[i] != ((stream << 1) | c) && |
| !dev->first_packet) { |
| if (!dev->input_panic) |
| printk(" EXPECTED: %02x got %02x, c %d, stream %d, i %d\n", |
| ((stream << 1) | c), usb_buf[i], c, stream, i); |
| dev->input_panic = 1; |
| } |
| |
| i++; |
| } |
| } |
| } |
| |
| if (dev->first_packet > 0) |
| dev->first_packet--; |
| } |
| |
| static void read_in_urb(struct snd_usb_caiaqdev *dev, |
| const struct urb *urb, |
| const struct usb_iso_packet_descriptor *iso) |
| { |
| 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; |
| case 3: |
| read_in_urb_mode3(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_mode_0(struct snd_usb_caiaqdev *dev, |
| struct urb *urb, |
| const struct usb_iso_packet_descriptor *iso) |
| { |
| unsigned char *usb_buf = urb->transfer_buffer + iso->offset; |
| struct snd_pcm_substream *sub; |
| 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 fill_out_urb_mode_3(struct snd_usb_caiaqdev *dev, |
| struct urb *urb, |
| const struct usb_iso_packet_descriptor *iso) |
| { |
| unsigned char *usb_buf = urb->transfer_buffer + iso->offset; |
| int stream, i; |
| |
| for (i = 0; i < iso->length;) { |
| for (stream = 0; stream < dev->n_streams; stream++) { |
| struct snd_pcm_substream *sub = dev->sub_playback[stream]; |
| char *audio_buf = NULL; |
| int c, n, sz = 0; |
| |
| if (sub) { |
| struct snd_pcm_runtime *rt = sub->runtime; |
| audio_buf = rt->dma_area; |
| sz = frames_to_bytes(rt, rt->buffer_size); |
| } |
| |
| for (c = 0; c < CHANNELS_PER_STREAM; c++) { |
| for (n = 0; n < BYTES_PER_SAMPLE; n++) { |
| if (audio_buf) { |
| usb_buf[i+n] = audio_buf[dev->audio_out_buf_pos[stream]++]; |
| |
| if (dev->audio_out_buf_pos[stream] == sz) |
| dev->audio_out_buf_pos[stream] = 0; |
| } else { |
| usb_buf[i+n] = 0; |
| } |
| } |
| |
| if (audio_buf) |
| dev->period_out_count[stream] += BYTES_PER_SAMPLE; |
| |
| i += BYTES_PER_SAMPLE; |
| |
| /* fill in the check byte pattern */ |
| usb_buf[i++] = (stream << 1) | c; |
| } |
| } |
| } |
| } |
| |
| static inline void fill_out_urb(struct snd_usb_caiaqdev *dev, |
| struct urb *urb, |
| const struct usb_iso_packet_descriptor *iso) |
| { |
| switch (dev->spec.data_alignment) { |
| case 0: |
| case 2: |
| fill_out_urb_mode_0(dev, urb, iso); |
| break; |
| case 3: |
| fill_out_urb_mode_3(dev, urb, iso); |
| break; |
| } |
| } |
| |
| 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_AUDIO2DJ): |
| 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); |
| } |
| |