linux/sound/usb/line6/capture.c
<<
>>
Prefs
   1/*
   2 * Line 6 Linux USB driver
   3 *
   4 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
   5 *
   6 *      This program is free software; you can redistribute it and/or
   7 *      modify it under the terms of the GNU General Public License as
   8 *      published by the Free Software Foundation, version 2.
   9 *
  10 */
  11
  12#include <linux/slab.h>
  13#include <sound/core.h>
  14#include <sound/pcm.h>
  15#include <sound/pcm_params.h>
  16
  17#include "capture.h"
  18#include "driver.h"
  19#include "pcm.h"
  20
  21/*
  22        Find a free URB and submit it.
  23        must be called in line6pcm->in.lock context
  24*/
  25static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
  26{
  27        int index;
  28        int i, urb_size;
  29        int ret;
  30        struct urb *urb_in;
  31
  32        index = find_first_zero_bit(&line6pcm->in.active_urbs,
  33                                    line6pcm->line6->iso_buffers);
  34
  35        if (index < 0 || index >= line6pcm->line6->iso_buffers) {
  36                dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
  37                return -EINVAL;
  38        }
  39
  40        urb_in = line6pcm->in.urbs[index];
  41        urb_size = 0;
  42
  43        for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
  44                struct usb_iso_packet_descriptor *fin =
  45                    &urb_in->iso_frame_desc[i];
  46                fin->offset = urb_size;
  47                fin->length = line6pcm->max_packet_size_in;
  48                urb_size += line6pcm->max_packet_size_in;
  49        }
  50
  51        urb_in->transfer_buffer =
  52            line6pcm->in.buffer +
  53            index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in;
  54        urb_in->transfer_buffer_length = urb_size;
  55        urb_in->context = line6pcm;
  56
  57        ret = usb_submit_urb(urb_in, GFP_ATOMIC);
  58
  59        if (ret == 0)
  60                set_bit(index, &line6pcm->in.active_urbs);
  61        else
  62                dev_err(line6pcm->line6->ifcdev,
  63                        "URB in #%d submission failed (%d)\n", index, ret);
  64
  65        return 0;
  66}
  67
  68/*
  69        Submit all currently available capture URBs.
  70        must be called in line6pcm->in.lock context
  71*/
  72int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
  73{
  74        int ret = 0, i;
  75
  76        for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
  77                ret = submit_audio_in_urb(line6pcm);
  78                if (ret < 0)
  79                        break;
  80        }
  81
  82        return ret;
  83}
  84
  85/*
  86        Copy data into ALSA capture buffer.
  87*/
  88void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
  89{
  90        struct snd_pcm_substream *substream =
  91            get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
  92        struct snd_pcm_runtime *runtime = substream->runtime;
  93        const int bytes_per_frame =
  94                line6pcm->properties->bytes_per_channel *
  95                line6pcm->properties->capture_hw.channels_max;
  96        int frames = fsize / bytes_per_frame;
  97
  98        if (runtime == NULL)
  99                return;
 100
 101        if (line6pcm->in.pos_done + frames > runtime->buffer_size) {
 102                /*
 103                   The transferred area goes over buffer boundary,
 104                   copy two separate chunks.
 105                 */
 106                int len;
 107
 108                len = runtime->buffer_size - line6pcm->in.pos_done;
 109
 110                if (len > 0) {
 111                        memcpy(runtime->dma_area +
 112                               line6pcm->in.pos_done * bytes_per_frame, fbuf,
 113                               len * bytes_per_frame);
 114                        memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,
 115                               (frames - len) * bytes_per_frame);
 116                } else {
 117                        /* this is somewhat paranoid */
 118                        dev_err(line6pcm->line6->ifcdev,
 119                                "driver bug: len = %d\n", len);
 120                }
 121        } else {
 122                /* copy single chunk */
 123                memcpy(runtime->dma_area +
 124                       line6pcm->in.pos_done * bytes_per_frame, fbuf, fsize);
 125        }
 126
 127        line6pcm->in.pos_done += frames;
 128        if (line6pcm->in.pos_done >= runtime->buffer_size)
 129                line6pcm->in.pos_done -= runtime->buffer_size;
 130}
 131
 132void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length)
 133{
 134        struct snd_pcm_substream *substream =
 135            get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
 136
 137        line6pcm->in.bytes += length;
 138        if (line6pcm->in.bytes >= line6pcm->in.period) {
 139                line6pcm->in.bytes %= line6pcm->in.period;
 140                spin_unlock(&line6pcm->in.lock);
 141                snd_pcm_period_elapsed(substream);
 142                spin_lock(&line6pcm->in.lock);
 143        }
 144}
 145
 146/*
 147 * Callback for completed capture URB.
 148 */
 149static void audio_in_callback(struct urb *urb)
 150{
 151        int i, index, length = 0, shutdown = 0;
 152        unsigned long flags;
 153
 154        struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
 155
 156        line6pcm->in.last_frame = urb->start_frame;
 157
 158        /* find index of URB */
 159        for (index = 0; index < line6pcm->line6->iso_buffers; ++index)
 160                if (urb == line6pcm->in.urbs[index])
 161                        break;
 162
 163        spin_lock_irqsave(&line6pcm->in.lock, flags);
 164
 165        for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
 166                char *fbuf;
 167                int fsize;
 168                struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i];
 169
 170                if (fin->status == -EXDEV) {
 171                        shutdown = 1;
 172                        break;
 173                }
 174
 175                fbuf = urb->transfer_buffer + fin->offset;
 176                fsize = fin->actual_length;
 177
 178                if (fsize > line6pcm->max_packet_size_in) {
 179                        dev_err(line6pcm->line6->ifcdev,
 180                                "driver and/or device bug: packet too large (%d > %d)\n",
 181                                fsize, line6pcm->max_packet_size_in);
 182                }
 183
 184                length += fsize;
 185
 186                BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1,
 187                        "The following code assumes LINE6_ISO_PACKETS == 1");
 188                /* TODO:
 189                 * Also, if iso_buffers != 2, the prev frame is almost random at
 190                 * playback side.
 191                 * This needs to be redesigned. It should be "stable", but we may
 192                 * experience sync problems on such high-speed configs.
 193                 */
 194
 195                line6pcm->prev_fbuf = fbuf;
 196                line6pcm->prev_fsize = fsize /
 197                        (line6pcm->properties->bytes_per_channel *
 198                        line6pcm->properties->capture_hw.channels_max);
 199
 200                if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
 201                    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
 202                    fsize > 0)
 203                        line6_capture_copy(line6pcm, fbuf, fsize);
 204        }
 205
 206        clear_bit(index, &line6pcm->in.active_urbs);
 207
 208        if (test_and_clear_bit(index, &line6pcm->in.unlink_urbs))
 209                shutdown = 1;
 210
 211        if (!shutdown) {
 212                submit_audio_in_urb(line6pcm);
 213
 214                if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
 215                    test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))
 216                        line6_capture_check_period(line6pcm, length);
 217        }
 218
 219        spin_unlock_irqrestore(&line6pcm->in.lock, flags);
 220}
 221
 222/* open capture callback */
 223static int snd_line6_capture_open(struct snd_pcm_substream *substream)
 224{
 225        int err;
 226        struct snd_pcm_runtime *runtime = substream->runtime;
 227        struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
 228
 229        err = snd_pcm_hw_constraint_ratdens(runtime, 0,
 230                                            SNDRV_PCM_HW_PARAM_RATE,
 231                                            &line6pcm->properties->rates);
 232        if (err < 0)
 233                return err;
 234
 235        line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false);
 236
 237        runtime->hw = line6pcm->properties->capture_hw;
 238        return 0;
 239}
 240
 241/* close capture callback */
 242static int snd_line6_capture_close(struct snd_pcm_substream *substream)
 243{
 244        struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
 245
 246        line6_pcm_release(line6pcm, LINE6_STREAM_CAPTURE_HELPER);
 247        return 0;
 248}
 249
 250/* capture operators */
 251const struct snd_pcm_ops snd_line6_capture_ops = {
 252        .open = snd_line6_capture_open,
 253        .close = snd_line6_capture_close,
 254        .ioctl = snd_pcm_lib_ioctl,
 255        .hw_params = snd_line6_hw_params,
 256        .hw_free = snd_line6_hw_free,
 257        .prepare = snd_line6_prepare,
 258        .trigger = snd_line6_trigger,
 259        .pointer = snd_line6_pointer,
 260};
 261
 262int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
 263{
 264        struct usb_line6 *line6 = line6pcm->line6;
 265        int i;
 266
 267        line6pcm->in.urbs = kcalloc(line6->iso_buffers, sizeof(struct urb *),
 268                                    GFP_KERNEL);
 269        if (line6pcm->in.urbs == NULL)
 270                return -ENOMEM;
 271
 272        /* create audio URBs and fill in constant values: */
 273        for (i = 0; i < line6->iso_buffers; ++i) {
 274                struct urb *urb;
 275
 276                /* URB for audio in: */
 277                urb = line6pcm->in.urbs[i] =
 278                    usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
 279
 280                if (urb == NULL)
 281                        return -ENOMEM;
 282
 283                urb->dev = line6->usbdev;
 284                urb->pipe =
 285                    usb_rcvisocpipe(line6->usbdev,
 286                                    line6->properties->ep_audio_r &
 287                                    USB_ENDPOINT_NUMBER_MASK);
 288                urb->transfer_flags = URB_ISO_ASAP;
 289                urb->start_frame = -1;
 290                urb->number_of_packets = LINE6_ISO_PACKETS;
 291                urb->interval = LINE6_ISO_INTERVAL;
 292                urb->error_count = 0;
 293                urb->complete = audio_in_callback;
 294        }
 295
 296        return 0;
 297}
 298