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