summaryrefslogtreecommitdiffstats
path: root/sound/usb/urb.c
diff options
context:
space:
mode:
authorDaniel Mack <daniel@caiaq.de>2010-03-04 19:46:13 +0100
committerTakashi Iwai <tiwai@suse.de>2010-03-05 08:17:14 +0100
commite5779998bf8b70e48a6cc208c8b61b33bd6117ea (patch)
tree512568f0fc4b81eac8019522c10df5b81483bcca /sound/usb/urb.c
parent3e1aebef6fb55e35668d2d7cf608cf03f30c904f (diff)
downloadlinux-e5779998bf8b70e48a6cc208c8b61b33bd6117ea.tar.bz2
ALSA: usb-audio: refactor code
Clean up the usb audio driver by factoring out a lot of functions to separate files. Code for procfs, quirks, urbs, format parsers etc all got a new home now. Moved almost all special quirk handling to quirks.c and introduced new generic functions to handle them, so the exceptions do not pollute the whole driver. Renamed usbaudio.c to card.c because this is what it actually does now. Renamed usbmidi.c to midi.c for namespace clarity. Removed more things from usbaudio.h. The non-standard drivers were adopted accordingly. Signed-off-by: Daniel Mack <daniel@caiaq.de> Cc: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/urb.c')
-rw-r--r--sound/usb/urb.c989
1 files changed, 989 insertions, 0 deletions
diff --git a/sound/usb/urb.c b/sound/usb/urb.c
new file mode 100644
index 000000000000..e9c339f75861
--- /dev/null
+++ b/sound/usb/urb.c
@@ -0,0 +1,989 @@
+/*
+ * 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/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "helper.h"
+#include "card.h"
+#include "urb.h"
+#include "pcm.h"
+
+/*
+ * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
+ * this will overflow at approx 524 kHz
+ */
+static inline unsigned get_usb_full_speed_rate(unsigned int rate)
+{
+ return ((rate << 13) + 62) / 125;
+}
+
+/*
+ * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
+ * this will overflow at approx 4 MHz
+ */
+static inline unsigned get_usb_high_speed_rate(unsigned int rate)
+{
+ return ((rate << 10) + 62) / 125;
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+{
+ struct snd_usb_audio *chip = subs->stream->chip;
+ unsigned int i;
+ int async;
+
+ subs->running = 0;
+
+ if (!force && subs->stream->chip->shutdown) /* to be sure... */
+ return -EBADFD;
+
+ async = !can_sleep && chip->async_unlink;
+
+ if (!async && in_interrupt())
+ return 0;
+
+ for (i = 0; i < subs->nurbs; i++) {
+ if (test_bit(i, &subs->active_mask)) {
+ if (!test_and_set_bit(i, &subs->unlink_mask)) {
+ struct urb *u = subs->dataurb[i].urb;
+ if (async)
+ usb_unlink_urb(u);
+ else
+ usb_kill_urb(u);
+ }
+ }
+ }
+ if (subs->syncpipe) {
+ for (i = 0; i < SYNC_URBS; i++) {
+ if (test_bit(i+16, &subs->active_mask)) {
+ if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
+ struct urb *u = subs->syncurb[i].urb;
+ if (async)
+ usb_unlink_urb(u);
+ else
+ usb_kill_urb(u);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * release a urb data
+ */
+static void release_urb_ctx(struct snd_urb_ctx *u)
+{
+ if (u->urb) {
+ if (u->buffer_size)
+ usb_buffer_free(u->subs->dev, u->buffer_size,
+ u->urb->transfer_buffer,
+ u->urb->transfer_dma);
+ usb_free_urb(u->urb);
+ u->urb = NULL;
+ }
+}
+
+/*
+ * wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_substream *subs)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+ unsigned int i;
+ int alive;
+
+ do {
+ alive = 0;
+ for (i = 0; i < subs->nurbs; i++) {
+ if (test_bit(i, &subs->active_mask))
+ alive++;
+ }
+ if (subs->syncpipe) {
+ for (i = 0; i < SYNC_URBS; i++) {
+ if (test_bit(i + 16, &subs->active_mask))
+ alive++;
+ }
+ }
+ if (! alive)
+ break;
+ schedule_timeout_uninterruptible(1);
+ } while (time_before(jiffies, end_time));
+ if (alive)
+ snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+ return 0;
+}
+
+/*
+ * release a substream
+ */
+void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
+{
+ int i;
+
+ /* stop urbs (to be sure) */
+ deactivate_urbs(subs, force, 1);
+ wait_clear_urbs(subs);
+
+ for (i = 0; i < MAX_URBS; i++)
+ release_urb_ctx(&subs->dataurb[i]);
+ for (i = 0; i < SYNC_URBS; i++)
+ release_urb_ctx(&subs->syncurb[i]);
+ usb_buffer_free(subs->dev, SYNC_URBS * 4,
+ subs->syncbuf, subs->sync_dma);
+ subs->syncbuf = NULL;
+ subs->nurbs = 0;
+}
+
+/*
+ * complete callback from data urb
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+ struct snd_urb_ctx *ctx = urb->context;
+ struct snd_usb_substream *subs = ctx->subs;
+ struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
+ int err = 0;
+
+ if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
+ !subs->running || /* can be stopped during retire callback */
+ (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
+ (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ clear_bit(ctx->index, &subs->active_mask);
+ if (err < 0) {
+ snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ }
+ }
+}
+
+
+/*
+ * complete callback from sync urb
+ */
+static void snd_complete_sync_urb(struct urb *urb)
+{
+ struct snd_urb_ctx *ctx = urb->context;
+ struct snd_usb_substream *subs = ctx->subs;
+ struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
+ int err = 0;
+
+ if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
+ !subs->running || /* can be stopped during retire callback */
+ (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
+ (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ clear_bit(ctx->index + 16, &subs->active_mask);
+ if (err < 0) {
+ snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ }
+ }
+}
+
+
+/*
+ * initialize a substream for plaback/capture
+ */
+int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
+ unsigned int period_bytes,
+ unsigned int rate,
+ unsigned int frame_bits)
+{
+ unsigned int maxsize, i;
+ int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+ unsigned int urb_packs, total_packs, packs_per_ms;
+ struct snd_usb_audio *chip = subs->stream->chip;
+
+ /* calculate the frequency in 16.16 format */
+ if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
+ subs->freqn = get_usb_full_speed_rate(rate);
+ else
+ subs->freqn = get_usb_high_speed_rate(rate);
+ subs->freqm = subs->freqn;
+ /* calculate max. frequency */
+ if (subs->maxpacksize) {
+ /* whatever fits into a max. size packet */
+ maxsize = subs->maxpacksize;
+ subs->freqmax = (maxsize / (frame_bits >> 3))
+ << (16 - subs->datainterval);
+ } else {
+ /* no max. packet size: just take 25% higher than nominal */
+ subs->freqmax = subs->freqn + (subs->freqn >> 2);
+ maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
+ >> (16 - subs->datainterval);
+ }
+ subs->phase = 0;
+
+ if (subs->fill_max)
+ subs->curpacksize = subs->maxpacksize;
+ else
+ subs->curpacksize = maxsize;
+
+ if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+ packs_per_ms = 8 >> subs->datainterval;
+ else
+ packs_per_ms = 1;
+
+ if (is_playback) {
+ urb_packs = max(chip->nrpacks, 1);
+ urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
+ } else
+ urb_packs = 1;
+ urb_packs *= packs_per_ms;
+ if (subs->syncpipe)
+ urb_packs = min(urb_packs, 1U << subs->syncinterval);
+
+ /* decide how many packets to be used */
+ if (is_playback) {
+ unsigned int minsize, maxpacks;
+ /* determine how small a packet can be */
+ minsize = (subs->freqn >> (16 - subs->datainterval))
+ * (frame_bits >> 3);
+ /* with sync from device, assume it can be 12% lower */
+ if (subs->syncpipe)
+ minsize -= minsize >> 3;
+ minsize = max(minsize, 1u);
+ total_packs = (period_bytes + minsize - 1) / minsize;
+ /* we need at least two URBs for queueing */
+ if (total_packs < 2) {
+ total_packs = 2;
+ } else {
+ /* and we don't want too long a queue either */
+ maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+ total_packs = min(total_packs, maxpacks);
+ }
+ } else {
+ while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+ urb_packs >>= 1;
+ total_packs = MAX_URBS * urb_packs;
+ }
+ subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+ if (subs->nurbs > MAX_URBS) {
+ /* too much... */
+ subs->nurbs = MAX_URBS;
+ total_packs = MAX_URBS * urb_packs;
+ } else if (subs->nurbs < 2) {
+ /* too little - we need at least two packets
+ * to ensure contiguous playback/capture
+ */
+ subs->nurbs = 2;
+ }
+
+ /* allocate and initialize data urbs */
+ for (i = 0; i < subs->nurbs; i++) {
+ struct snd_urb_ctx *u = &subs->dataurb[i];
+ u->index = i;
+ u->subs = subs;
+ u->packets = (i + 1) * total_packs / subs->nurbs
+ - i * total_packs / subs->nurbs;
+ u->buffer_size = maxsize * u->packets;
+ if (subs->fmt_type == UAC_FORMAT_TYPE_II)
+ u->packets++; /* for transfer delimiter */
+ u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+ if (!u->urb)
+ goto out_of_memory;
+ u->urb->transfer_buffer =
+ usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,
+ &u->urb->transfer_dma);
+ if (!u->urb->transfer_buffer)
+ goto out_of_memory;
+ u->urb->pipe = subs->datapipe;
+ u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ u->urb->interval = 1 << subs->datainterval;
+ u->urb->context = u;
+ u->urb->complete = snd_complete_urb;
+ }
+
+ if (subs->syncpipe) {
+ /* allocate and initialize sync urbs */
+ subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,
+ GFP_KERNEL, &subs->sync_dma);
+ if (!subs->syncbuf)
+ goto out_of_memory;
+ for (i = 0; i < SYNC_URBS; i++) {
+ struct snd_urb_ctx *u = &subs->syncurb[i];
+ u->index = i;
+ u->subs = subs;
+ u->packets = 1;
+ u->urb = usb_alloc_urb(1, GFP_KERNEL);
+ if (!u->urb)
+ goto out_of_memory;
+ u->urb->transfer_buffer = subs->syncbuf + i * 4;
+ u->urb->transfer_dma = subs->sync_dma + i * 4;
+ u->urb->transfer_buffer_length = 4;
+ u->urb->pipe = subs->syncpipe;
+ u->urb->transfer_flags = URB_ISO_ASAP |
+ URB_NO_TRANSFER_DMA_MAP;
+ u->urb->number_of_packets = 1;
+ u->urb->interval = 1 << subs->syncinterval;
+ u->urb->context = u;
+ u->urb->complete = snd_complete_sync_urb;
+ }
+ }
+ return 0;
+
+out_of_memory:
+ snd_usb_release_substream_urbs(subs, 0);
+ return -ENOMEM;
+}
+
+/*
+ * prepare urb for full speed capture sync pipe
+ *
+ * fill the length and offset of each urb descriptor.
+ * the fixed 10.14 frequency is passed through the pipe.
+ */
+static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ unsigned char *cp = urb->transfer_buffer;
+ struct snd_urb_ctx *ctx = urb->context;
+
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ urb->iso_frame_desc[0].length = 3;
+ urb->iso_frame_desc[0].offset = 0;
+ cp[0] = subs->freqn >> 2;
+ cp[1] = subs->freqn >> 10;
+ cp[2] = subs->freqn >> 18;
+ return 0;
+}
+
+/*
+ * prepare urb for high speed capture sync pipe
+ *
+ * fill the length and offset of each urb descriptor.
+ * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+ */
+static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ unsigned char *cp = urb->transfer_buffer;
+ struct snd_urb_ctx *ctx = urb->context;
+
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ urb->iso_frame_desc[0].length = 4;
+ urb->iso_frame_desc[0].offset = 0;
+ cp[0] = subs->freqn;
+ cp[1] = subs->freqn >> 8;
+ cp[2] = subs->freqn >> 16;
+ cp[3] = subs->freqn >> 24;
+ return 0;
+}
+
+/*
+ * process after capture sync complete
+ * - nothing to do
+ */
+static int retire_capture_sync_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ return 0;
+}
+
+/*
+ * prepare urb for capture data pipe
+ *
+ * fill the offset and length of each descriptor.
+ *
+ * we use a temporary buffer to write the captured data.
+ * since the length of written data is determined by host, we cannot
+ * write onto the pcm buffer directly... the data is thus copied
+ * later at complete callback to the global buffer.
+ */
+static int prepare_capture_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ int i, offs;
+ struct snd_urb_ctx *ctx = urb->context;
+
+ offs = 0;
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ for (i = 0; i < ctx->packets; i++) {
+ urb->iso_frame_desc[i].offset = offs;
+ urb->iso_frame_desc[i].length = subs->curpacksize;
+ offs += subs->curpacksize;
+ }
+ urb->transfer_buffer_length = offs;
+ urb->number_of_packets = ctx->packets;
+ return 0;
+}
+
+/*
+ * process after capture complete
+ *
+ * copy the data from each desctiptor to the pcm buffer, and
+ * update the current position.
+ */
+static int retire_capture_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ unsigned long flags;
+ unsigned char *cp;
+ int i;
+ unsigned int stride, frames, bytes, oldptr;
+ int period_elapsed = 0;
+
+ stride = runtime->frame_bits >> 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ if (urb->iso_frame_desc[i].status) {
+ snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
+ // continue;
+ }
+ bytes = urb->iso_frame_desc[i].actual_length;
+ frames = bytes / stride;
+ if (!subs->txfr_quirk)
+ bytes = frames * stride;
+ if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int oldbytes = bytes;
+#endif
+ bytes = frames * stride;
+ snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
+ oldbytes, bytes);
+ }
+ /* update the current pointer */
+ spin_lock_irqsave(&subs->lock, flags);
+ oldptr = subs->hwptr_done;
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+ frames = (bytes + (oldptr % stride)) / stride;
+ subs->transfer_done += frames;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ period_elapsed = 1;
+ }
+ spin_unlock_irqrestore(&subs->lock, flags);
+ /* copy a data chunk */
+ if (oldptr + bytes > runtime->buffer_size * stride) {
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - oldptr;
+ memcpy(runtime->dma_area + oldptr, cp, bytes1);
+ memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
+ } else {
+ memcpy(runtime->dma_area + oldptr, cp, bytes);
+ }
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(subs->pcm_substream);
+ return 0;
+}
+
+/*
+ * Process after capture complete when paused. Nothing to do.
+ */
+static int retire_paused_capture_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ return 0;
+}
+
+
+/*
+ * prepare urb for full speed playback sync pipe
+ *
+ * set up the offset and length to receive the current frequency.
+ */
+
+static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ struct snd_urb_ctx *ctx = urb->context;
+
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ urb->iso_frame_desc[0].length = 3;
+ urb->iso_frame_desc[0].offset = 0;
+ return 0;
+}
+
+/*
+ * prepare urb for high speed playback sync pipe
+ *
+ * set up the offset and length to receive the current frequency.
+ */
+
+static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ struct snd_urb_ctx *ctx = urb->context;
+
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ urb->iso_frame_desc[0].length = 4;
+ urb->iso_frame_desc[0].offset = 0;
+ return 0;
+}
+
+/*
+ * process after full speed playback sync complete
+ *
+ * retrieve the current 10.14 frequency from pipe, and set it.
+ * the value is referred in prepare_playback_urb().
+ */
+static int retire_playback_sync_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ unsigned int f;
+ unsigned long flags;
+
+ if (urb->iso_frame_desc[0].status == 0 &&
+ urb->iso_frame_desc[0].actual_length == 3) {
+ f = combine_triple((u8*)urb->transfer_buffer) << 2;
+ if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+ spin_lock_irqsave(&subs->lock, flags);
+ subs->freqm = f;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * process after high speed playback sync complete
+ *
+ * retrieve the current 12.13 frequency from pipe, and set it.
+ * the value is referred in prepare_playback_urb().
+ */
+static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ unsigned int f;
+ unsigned long flags;
+
+ if (urb->iso_frame_desc[0].status == 0 &&
+ urb->iso_frame_desc[0].actual_length == 4) {
+ f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
+ if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+ spin_lock_irqsave(&subs->lock, flags);
+ subs->freqm = f;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
+ *
+ * These devices return the number of samples per packet instead of the number
+ * of samples per microframe.
+ */
+static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ unsigned int f;
+ unsigned long flags;
+
+ if (urb->iso_frame_desc[0].status == 0 &&
+ urb->iso_frame_desc[0].actual_length == 4) {
+ f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
+ f >>= subs->datainterval;
+ if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+ spin_lock_irqsave(&subs->lock, flags);
+ subs->freqm = f;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ }
+ }
+
+ return 0;
+}
+
+/* determine the number of frames in the next packet */
+static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
+{
+ if (subs->fill_max)
+ return subs->maxframesize;
+ else {
+ subs->phase = (subs->phase & 0xffff)
+ + (subs->freqm << subs->datainterval);
+ return min(subs->phase >> 16, subs->maxframesize);
+ }
+}
+
+/*
+ * Prepare urb for streaming before playback starts or when paused.
+ *
+ * We don't have any data, so we send silence.
+ */
+static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ unsigned int i, offs, counts;
+ struct snd_urb_ctx *ctx = urb->context;
+ int stride = runtime->frame_bits >> 3;
+
+ offs = 0;
+ urb->dev = ctx->subs->dev;
+ for (i = 0; i < ctx->packets; ++i) {
+ counts = snd_usb_audio_next_packet_size(subs);
+ urb->iso_frame_desc[i].offset = offs * stride;
+ urb->iso_frame_desc[i].length = counts * stride;
+ offs += counts;
+ }
+ urb->number_of_packets = ctx->packets;
+ urb->transfer_buffer_length = offs * stride;
+ memset(urb->transfer_buffer,
+ subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
+ offs * stride);
+ return 0;
+}
+
+/*
+ * prepare urb for playback data pipe
+ *
+ * Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
+ */
+static int prepare_playback_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ int i, stride;
+ unsigned int counts, frames, bytes;
+ unsigned long flags;
+ int period_elapsed = 0;
+ struct snd_urb_ctx *ctx = urb->context;
+
+ stride = runtime->frame_bits >> 3;
+
+ frames = 0;
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ urb->number_of_packets = 0;
+ spin_lock_irqsave(&subs->lock, flags);
+ for (i = 0; i < ctx->packets; i++) {
+ counts = snd_usb_audio_next_packet_size(subs);
+ /* set up descriptor */
+ urb->iso_frame_desc[i].offset = frames * stride;
+ urb->iso_frame_desc[i].length = counts * stride;
+ frames += counts;
+ urb->number_of_packets++;
+ subs->transfer_done += counts;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ period_elapsed = 1;
+ if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+ if (subs->transfer_done > 0) {
+ /* FIXME: fill-max mode is not
+ * supported yet */
+ frames -= subs->transfer_done;
+ counts -= subs->transfer_done;
+ urb->iso_frame_desc[i].length =
+ counts * stride;
+ subs->transfer_done = 0;
+ }
+ i++;
+ if (i < ctx->packets) {
+ /* add a transfer delimiter */
+ urb->iso_frame_desc[i].offset =
+ frames * stride;
+ urb->iso_frame_desc[i].length = 0;
+ urb->number_of_packets++;
+ }
+ break;
+ }
+ }
+ if (period_elapsed) /* finish at the period boundary */
+ break;
+ }
+ bytes = frames * stride;
+ if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+ /* err, the transferred area goes over buffer boundary. */
+ unsigned int bytes1 =
+ runtime->buffer_size * stride - subs->hwptr_done;
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done, bytes1);
+ memcpy(urb->transfer_buffer + bytes1,
+ runtime->dma_area, bytes - bytes1);
+ } else {
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done, bytes);
+ }
+ subs->hwptr_done += bytes;
+ if (subs->hwptr_done >= runtime->buffer_size * stride)
+ subs->hwptr_done -= runtime->buffer_size * stride;
+ runtime->delay += frames;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ urb->transfer_buffer_length = bytes;
+ if (period_elapsed)
+ snd_pcm_period_elapsed(subs->pcm_substream);
+ return 0;
+}
+
+/*
+ * process after playback data complete
+ * - decrease the delay count again
+ */
+static int retire_playback_urb(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime,
+ struct urb *urb)
+{
+ unsigned long flags;
+ int stride = runtime->frame_bits >> 3;
+ int processed = urb->transfer_buffer_length / stride;
+
+ spin_lock_irqsave(&subs->lock, flags);
+ if (processed > runtime->delay)
+ runtime->delay = 0;
+ else
+ runtime->delay -= processed;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ return 0;
+}
+
+static const char *usb_error_string(int err)
+{
+ switch (err) {
+ case -ENODEV:
+ return "no device";
+ case -ENOENT:
+ return "endpoint not enabled";
+ case -EPIPE:
+ return "endpoint stalled";
+ case -ENOSPC:
+ return "not enough bandwidth";
+ case -ESHUTDOWN:
+ return "device disabled";
+ case -EHOSTUNREACH:
+ return "device suspended";
+ case -EINVAL:
+ case -EAGAIN:
+ case -EFBIG:
+ case -EMSGSIZE:
+ return "internal error";
+ default:
+ return "unknown error";
+ }
+}
+
+/*
+ * set up and start data/sync urbs
+ */
+static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
+{
+ unsigned int i;
+ int err;
+
+ if (subs->stream->chip->shutdown)
+ return -EBADFD;
+
+ for (i = 0; i < subs->nurbs; i++) {
+ if (snd_BUG_ON(!subs->dataurb[i].urb))
+ return -EINVAL;
+ if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
+ snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
+ goto __error;
+ }
+ }
+ if (subs->syncpipe) {
+ for (i = 0; i < SYNC_URBS; i++) {
+ if (snd_BUG_ON(!subs->syncurb[i].urb))
+ return -EINVAL;
+ if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
+ snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
+ goto __error;
+ }
+ }
+ }
+
+ subs->active_mask = 0;
+ subs->unlink_mask = 0;
+ subs->running = 1;
+ for (i = 0; i < subs->nurbs; i++) {
+ err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
+ if (err < 0) {
+ snd_printk(KERN_ERR "cannot submit datapipe "
+ "for urb %d, error %d: %s\n",
+ i, err, usb_error_string(err));
+ goto __error;
+ }
+ set_bit(i, &subs->active_mask);
+ }
+ if (subs->syncpipe) {
+ for (i = 0; i < SYNC_URBS; i++) {
+ err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
+ if (err < 0) {
+ snd_printk(KERN_ERR "cannot submit syncpipe "
+ "for urb %d, error %d: %s\n",
+ i, err, usb_error_string(err));
+ goto __error;
+ }
+ set_bit(i + 16, &subs->active_mask);
+ }
+ }
+ return 0;
+
+ __error:
+ // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
+ deactivate_urbs(subs, 0, 0);
+ return -EPIPE;
+}
+
+
+/*
+ */
+static struct snd_urb_ops audio_urb_ops[2] = {
+ {
+ .prepare = prepare_nodata_playback_urb,
+ .retire = retire_playback_urb,
+ .prepare_sync = prepare_playback_sync_urb,
+ .retire_sync = retire_playback_sync_urb,
+ },
+ {
+ .prepare = prepare_capture_urb,
+ .retire = retire_capture_urb,
+ .prepare_sync = prepare_capture_sync_urb,
+ .retire_sync = retire_capture_sync_urb,
+ },
+};
+
+static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
+ {
+ .prepare = prepare_nodata_playback_urb,
+ .retire = retire_playback_urb,
+ .prepare_sync = prepare_playback_sync_urb_hs,
+ .retire_sync = retire_playback_sync_urb_hs,
+ },
+ {
+ .prepare = prepare_capture_urb,
+ .retire = retire_capture_urb,
+ .prepare_sync = prepare_capture_sync_urb_hs,
+ .retire_sync = retire_capture_sync_urb,
+ },
+};
+
+/*
+ * initialize the substream instance.
+ */
+
+void snd_usb_init_substream(struct snd_usb_stream *as,
+ int stream, struct audioformat *fp)
+{
+ struct snd_usb_substream *subs = &as->substream[stream];
+
+ INIT_LIST_HEAD(&subs->fmt_list);
+ spin_lock_init(&subs->lock);
+
+ subs->stream = as;
+ subs->direction = stream;
+ subs->dev = as->chip->dev;
+ subs->txfr_quirk = as->chip->txfr_quirk;
+ if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
+ subs->ops = audio_urb_ops[stream];
+ } else {
+ subs->ops = audio_urb_ops_high_speed[stream];
+ switch (as->chip->usb_id) {
+ case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
+ case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
+ case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
+ subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
+ break;
+ }
+ }
+
+ snd_usb_set_pcm_ops(as->pcm, stream);
+
+ list_add_tail(&fp->list, &subs->fmt_list);
+ subs->formats |= 1ULL << fp->format;
+ subs->endpoint = fp->endpoint;
+ subs->num_formats++;
+ subs->fmt_type = fp->fmt_type;
+}
+
+int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_usb_substream *subs = substream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ subs->ops.prepare = prepare_playback_urb;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ return deactivate_urbs(subs, 0, 0);
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->ops.prepare = prepare_nodata_playback_urb;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_usb_substream *subs = substream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ subs->ops.retire = retire_capture_urb;
+ return start_urbs(subs, substream->runtime);
+ case SNDRV_PCM_TRIGGER_STOP:
+ return deactivate_urbs(subs, 0, 0);
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ subs->ops.retire = retire_paused_capture_urb;
+ return 0;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ subs->ops.retire = retire_capture_urb;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int snd_usb_substream_prepare(struct snd_usb_substream *subs,
+ struct snd_pcm_runtime *runtime)
+{
+ /* clear urbs (to be sure) */
+ deactivate_urbs(subs, 0, 1);
+ wait_clear_urbs(subs);
+
+ /* for playback, submit the URBs now; otherwise, the first hwptr_done
+ * updates for all URBs would happen at the same time when starting */
+ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ subs->ops.prepare = prepare_nodata_playback_urb;
+ return start_urbs(subs, runtime);
+ }
+
+ return 0;
+}
+