aboutsummaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/usb
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/Kconfig32
-rw-r--r--sound/usb/Makefile12
-rw-r--r--sound/usb/usbaudio.c3337
-rw-r--r--sound/usb/usbaudio.h251
-rw-r--r--sound/usb/usbmidi.c1564
-rw-r--r--sound/usb/usbmixer.c1545
-rw-r--r--sound/usb/usbmixer_maps.c135
-rw-r--r--sound/usb/usbquirks.h1202
-rw-r--r--sound/usb/usx2y/Makefile3
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c280
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.h6
-rw-r--r--sound/usb/usx2y/usbus428ctldefs.h108
-rw-r--r--sound/usb/usx2y/usbusx2y.c461
-rw-r--r--sound/usb/usx2y/usbusx2y.h84
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c1026
-rw-r--r--sound/usb/usx2y/usx2y.h51
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c807
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.h21
18 files changed, 10925 insertions, 0 deletions
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
new file mode 100644
index 00000000000..9329e992c84
--- /dev/null
+++ b/sound/usb/Kconfig
@@ -0,0 +1,32 @@
+# ALSA USB drivers
+
+menu "USB devices"
+ depends on SND!=n && USB!=n
+
+config SND_USB_AUDIO
+ tristate "USB Audio/MIDI driver"
+ depends on SND && USB
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say Y here to include support for USB audio and USB MIDI
+ devices.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-usb-audio.
+
+config SND_USB_USX2Y
+ tristate "Tascam US-122, US-224 and US-428 USB driver"
+ depends on SND && USB && (X86 || PPC || ALPHA)
+ select SND_HWDEP
+ select SND_RAWMIDI
+ select SND_PCM
+ help
+ Say Y here to include support for Tascam USB Audio/MIDI
+ interfaces or controllers US-122, US-224 and US-428.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-usb-usx2y.
+
+endmenu
+
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
new file mode 100644
index 00000000000..2c1dc11a72e
--- /dev/null
+++ b/sound/usb/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for ALSA
+#
+
+snd-usb-audio-objs := usbaudio.o usbmixer.o
+snd-usb-lib-objs := usbmidi.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o
+obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o
+
+obj-$(CONFIG_SND) += usx2y/
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
new file mode 100644
index 00000000000..84b0bbddbd2
--- /dev/null
+++ b/sound/usb/usbaudio.c
@@ -0,0 +1,3337 @@
+/*
+ * (Tentative) USB Audio Driver for ALSA
+ *
+ * Main and PCM part
+ *
+ * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ * Many codes borrowed from audio.c by
+ * Alan Cox (alan@lxorguk.ukuu.org.uk)
+ * Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *
+ * 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
+ *
+ *
+ * NOTES:
+ *
+ * - async unlink should be used for avoiding the sleep inside lock.
+ * 2.4.22 usb-uhci seems buggy for async unlinking and results in
+ * oops. in such a cse, pass async_unlink=0 option.
+ * - the linked URBs would be preferred but not used so far because of
+ * the instability of unlinking.
+ * - type II is not supported properly. there is no device which supports
+ * this type *correctly*. SB extigy looks as if it supports, but it's
+ * indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream).
+ */
+
+
+#include <sound/driver.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+
+#include "usbaudio.h"
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("USB Audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}");
+
+
+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 vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */
+static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */
+static int nrpacks = 4; /* max. number of packets per urb */
+static int async_unlink = 1;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the USB audio adapter.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable USB audio adapter.");
+module_param_array(vid, int, NULL, 0444);
+MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
+module_param_array(pid, int, NULL, 0444);
+MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
+module_param(nrpacks, int, 0444);
+MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");
+module_param(async_unlink, bool, 0444);
+MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
+
+
+/*
+ * debug the h/w constraints
+ */
+/* #define HW_CONST_DEBUG */
+
+
+/*
+ *
+ */
+
+#define MAX_PACKS 10
+#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */
+#define MAX_URBS 5 /* max. 20ms long packets */
+#define SYNC_URBS 2 /* always two urbs for sync */
+#define MIN_PACKS_URB 1 /* minimum 1 packet per urb */
+
+typedef struct snd_usb_substream snd_usb_substream_t;
+typedef struct snd_usb_stream snd_usb_stream_t;
+typedef struct snd_urb_ctx snd_urb_ctx_t;
+
+struct audioformat {
+ struct list_head list;
+ snd_pcm_format_t format; /* format type */
+ unsigned int channels; /* # channels */
+ unsigned int fmt_type; /* USB audio format type (1-3) */
+ unsigned int frame_size; /* samples per frame for non-audio */
+ int iface; /* interface number */
+ unsigned char altsetting; /* corresponding alternate setting */
+ unsigned char altset_idx; /* array index of altenate setting */
+ unsigned char attributes; /* corresponding attributes of cs endpoint */
+ unsigned char endpoint; /* endpoint */
+ unsigned char ep_attr; /* endpoint attributes */
+ unsigned int maxpacksize; /* max. packet size */
+ unsigned int rates; /* rate bitmasks */
+ unsigned int rate_min, rate_max; /* min/max rates */
+ unsigned int nr_rates; /* number of rate table entries */
+ unsigned int *rate_table; /* rate table */
+};
+
+struct snd_urb_ctx {
+ struct urb *urb;
+ snd_usb_substream_t *subs;
+ int index; /* index for urb array */
+ int packets; /* number of packets per urb */
+ int transfer; /* transferred size */
+ char *buf; /* buffer for capture */
+};
+
+struct snd_urb_ops {
+ int (*prepare)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);
+ int (*retire)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);
+ int (*prepare_sync)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);
+ int (*retire_sync)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);
+};
+
+struct snd_usb_substream {
+ snd_usb_stream_t *stream;
+ struct usb_device *dev;
+ snd_pcm_substream_t *pcm_substream;
+ int direction; /* playback or capture */
+ int interface; /* current interface */
+ int endpoint; /* assigned endpoint */
+ struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */
+ unsigned int cur_rate; /* current rate (for hw_params callback) */
+ unsigned int period_bytes; /* current period bytes (for hw_params callback) */
+ unsigned int format; /* USB data format */
+ unsigned int datapipe; /* the data i/o pipe */
+ unsigned int syncpipe; /* 1 - async out or adaptive in */
+ unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */
+ unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */
+ unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */
+ unsigned int freqmax; /* maximum sampling rate, used for buffer management */
+ unsigned int phase; /* phase accumulator */
+ unsigned int maxpacksize; /* max packet size in bytes */
+ unsigned int maxframesize; /* max packet size in frames */
+ unsigned int curpacksize; /* current packet size in bytes (for capture) */
+ unsigned int curframesize; /* current packet size in frames (for capture) */
+ unsigned int fill_max: 1; /* fill max packet size always */
+ unsigned int fmt_type; /* USB audio format type (1-3) */
+
+ unsigned int running: 1; /* running status */
+
+ unsigned int hwptr; /* free frame position in the buffer (only for playback) */
+ unsigned int hwptr_done; /* processed frame position in the buffer */
+ unsigned int transfer_sched; /* scheduled frames since last period (for playback) */
+ unsigned int transfer_done; /* processed frames since last period update */
+ unsigned long active_mask; /* bitmask of active urbs */
+ unsigned long unlink_mask; /* bitmask of unlinked urbs */
+
+ unsigned int nurbs; /* # urbs */
+ snd_urb_ctx_t dataurb[MAX_URBS]; /* data urb table */
+ snd_urb_ctx_t syncurb[SYNC_URBS]; /* sync urb table */
+ char syncbuf[SYNC_URBS * MAX_PACKS * 4]; /* sync buffer; it's so small - let's get static */
+ char *tmpbuf; /* temporary buffer for playback */
+
+ u64 formats; /* format bitmasks (all or'ed) */
+ unsigned int num_formats; /* number of supported audio formats (list) */
+ struct list_head fmt_list; /* format list */
+ spinlock_t lock;
+
+ struct snd_urb_ops ops; /* callbacks (must be filled at init) */
+};
+
+
+struct snd_usb_stream {
+ snd_usb_audio_t *chip;
+ snd_pcm_t *pcm;
+ int pcm_index;
+ unsigned int fmt_type; /* USB audio format type (1-3) */
+ snd_usb_substream_t substream[2];
+ struct list_head list;
+};
+
+
+/*
+ * we keep the snd_usb_audio_t instances by ourselves for merging
+ * the all interfaces on the same card as one sound device.
+ */
+
+static DECLARE_MUTEX(register_mutex);
+static snd_usb_audio_t *usb_chip[SNDRV_CARDS];
+
+
+/*
+ * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
+ * this will overflow at approx 524 kHz
+ */
+inline static 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
+ */
+inline static unsigned get_usb_high_speed_rate(unsigned int rate)
+{
+ return ((rate << 10) + 62) / 125;
+}
+
+/* convert our full speed USB rate into sampling rate in Hz */
+inline static unsigned get_full_speed_hz(unsigned int usb_rate)
+{
+ return (usb_rate * 125 + (1 << 12)) >> 13;
+}
+
+/* convert our high speed USB rate into sampling rate in Hz */
+inline static unsigned get_high_speed_hz(unsigned int usb_rate)
+{
+ return (usb_rate * 125 + (1 << 9)) >> 10;
+}
+
+
+/*
+ * 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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ unsigned char *cp = urb->transfer_buffer;
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+ int i, offs;
+
+ urb->number_of_packets = ctx->packets;
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) {
+ urb->iso_frame_desc[i].length = 3;
+ urb->iso_frame_desc[i].offset = offs;
+ 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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ unsigned char *cp = urb->transfer_buffer;
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+ int i, offs;
+
+ urb->number_of_packets = ctx->packets;
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) {
+ urb->iso_frame_desc[i].length = 4;
+ urb->iso_frame_desc[i].offset = offs;
+ 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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ int i, offs;
+ unsigned long flags;
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+ offs = 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++) {
+ urb->iso_frame_desc[i].offset = offs;
+ urb->iso_frame_desc[i].length = subs->curpacksize;
+ offs += subs->curpacksize;
+ urb->number_of_packets++;
+ subs->transfer_sched += subs->curframesize;
+ if (subs->transfer_sched >= runtime->period_size) {
+ subs->transfer_sched -= runtime->period_size;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&subs->lock, flags);
+ urb->transfer_buffer = ctx->buf;
+ urb->transfer_buffer_length = offs;
+#if 0 // for check
+ if (! urb->bandwidth) {
+ int bustime;
+ bustime = usb_check_bandwidth(urb->dev, urb);
+ if (bustime < 0)
+ return bustime;
+ printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets);
+ usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+ }
+#endif // for check
+ 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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ unsigned long flags;
+ unsigned char *cp;
+ int i;
+ unsigned int stride, len, oldptr;
+
+ 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;
+ }
+ len = urb->iso_frame_desc[i].actual_length / stride;
+ if (! len)
+ continue;
+ /* update the current pointer */
+ spin_lock_irqsave(&subs->lock, flags);
+ oldptr = subs->hwptr_done;
+ subs->hwptr_done += len;
+ if (subs->hwptr_done >= runtime->buffer_size)
+ subs->hwptr_done -= runtime->buffer_size;
+ subs->transfer_done += len;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ /* copy a data chunk */
+ if (oldptr + len > runtime->buffer_size) {
+ unsigned int cnt = runtime->buffer_size - oldptr;
+ unsigned int blen = cnt * stride;
+ memcpy(runtime->dma_area + oldptr * stride, cp, blen);
+ memcpy(runtime->dma_area, cp + blen, len * stride - blen);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
+ }
+ /* update the pointer, call callback if necessary */
+ spin_lock_irqsave(&subs->lock, flags);
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ snd_pcm_period_elapsed(subs->pcm_substream);
+ } else
+ spin_unlock_irqrestore(&subs->lock, flags);
+ }
+ 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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ int i, offs;
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+ urb->number_of_packets = ctx->packets;
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) {
+ urb->iso_frame_desc[i].length = 3;
+ urb->iso_frame_desc[i].offset = offs;
+ }
+ 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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ int i, offs;
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+ urb->number_of_packets = ctx->packets;
+ urb->dev = ctx->subs->dev; /* we need to set this at each time */
+ for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) {
+ urb->iso_frame_desc[i].length = 4;
+ urb->iso_frame_desc[i].offset = offs;
+ }
+ 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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ int i;
+ unsigned int f, found;
+ unsigned char *cp = urb->transfer_buffer;
+ unsigned long flags;
+
+ found = 0;
+ for (i = 0; i < urb->number_of_packets; i++, cp += 4) {
+ if (urb->iso_frame_desc[i].status ||
+ urb->iso_frame_desc[i].actual_length < 3)
+ continue;
+ f = combine_triple(cp) << 2;
+#if 0
+ if (f < subs->freqn - (subs->freqn>>3) || f > subs->freqmax) {
+ snd_printd(KERN_WARNING "requested frequency %d (%u,%03uHz) out of range (current nominal %d (%u,%03uHz))\n",
+ f, f >> 14, (f & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1),
+ subs->freqn, subs->freqn >> 14, (subs->freqn & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1));
+ continue;
+ }
+#endif
+ found = f;
+ }
+ if (found) {
+ spin_lock_irqsave(&subs->lock, flags);
+ subs->freqm = found;
+ 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(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ int i;
+ unsigned int found;
+ unsigned char *cp = urb->transfer_buffer;
+ unsigned long flags;
+
+ found = 0;
+ for (i = 0; i < urb->number_of_packets; i++, cp += 4) {
+ if (urb->iso_frame_desc[i].status ||
+ urb->iso_frame_desc[i].actual_length < 4)
+ continue;
+ found = combine_quad(cp) & 0x0fffffff;
+ }
+ if (found) {
+ spin_lock_irqsave(&subs->lock, flags);
+ subs->freqm = found;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ }
+
+ return 0;
+}
+
+/*
+ * prepare urb for playback data pipe
+ *
+ * we copy the data directly from the pcm buffer.
+ * the current position to be copied is held in hwptr field.
+ * since a urb can handle only a single linear buffer, if the total
+ * transferred area overflows the buffer boundary, we cannot send
+ * it directly from the buffer. thus the data is once copied to
+ * a temporary buffer and urb points to that.
+ */
+static int prepare_playback_urb(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ int i, stride, offs;
+ unsigned int counts;
+ unsigned long flags;
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+ stride = runtime->frame_bits >> 3;
+
+ offs = 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++) {
+ /* calculate the size of a packet */
+ if (subs->fill_max)
+ counts = subs->maxframesize; /* fixed */
+ else {
+ subs->phase = (subs->phase & 0xffff) + subs->freqm;
+ counts = subs->phase >> 16;
+ if (counts > subs->maxframesize)
+ counts = subs->maxframesize;
+ }
+ /* set up descriptor */
+ urb->iso_frame_desc[i].offset = offs * stride;
+ urb->iso_frame_desc[i].length = counts * stride;
+ offs += counts;
+ urb->number_of_packets++;
+ subs->transfer_sched += counts;
+ if (subs->transfer_sched >= runtime->period_size) {
+ subs->transfer_sched -= runtime->period_size;
+ if (subs->fmt_type == USB_FORMAT_TYPE_II) {
+ if (subs->transfer_sched > 0) {
+ /* FIXME: fill-max mode is not supported yet */
+ offs -= subs->transfer_sched;
+ counts -= subs->transfer_sched;
+ urb->iso_frame_desc[i].length = counts * stride;
+ subs->transfer_sched = 0;
+ }
+ i++;
+ if (i < ctx->packets) {
+ /* add a transfer delimiter */
+ urb->iso_frame_desc[i].offset = offs * stride;
+ urb->iso_frame_desc[i].length = 0;
+ urb->number_of_packets++;
+ }
+ }
+ break;
+ }
+ }
+ if (subs->hwptr + offs > runtime->buffer_size) {
+ /* err, the transferred area goes over buffer boundary.
+ * copy the data to the temp buffer.
+ */
+ int len;
+ len = runtime->buffer_size - subs->hwptr;
+ urb->transfer_buffer = subs->tmpbuf;
+ memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * stride, len * stride);
+ memcpy(subs->tmpbuf + len * stride, runtime->dma_area, (offs - len) * stride);
+ subs->hwptr += offs;
+ subs->hwptr -= runtime->buffer_size;
+ } else {
+ /* set the buffer pointer */
+ urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride;
+ subs->hwptr += offs;
+ }
+ spin_unlock_irqrestore(&subs->lock, flags);
+ urb->transfer_buffer_length = offs * stride;
+ ctx->transfer = offs;
+
+ return 0;
+}
+
+/*
+ * process after playback data complete
+ *
+ * update the current position and call callback if a period is processed.
+ */
+static int retire_playback_urb(snd_usb_substream_t *subs,
+ snd_pcm_runtime_t *runtime,
+ struct urb *urb)
+{
+ unsigned long flags;
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+ spin_lock_irqsave(&subs->lock, flags);
+ subs->transfer_done += ctx->transfer;
+ subs->hwptr_done += ctx->transfer;
+ ctx->transfer = 0;
+ if (subs->hwptr_done >= runtime->buffer_size)
+ subs->hwptr_done -= runtime->buffer_size;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ spin_unlock_irqrestore(&subs->lock, flags);
+ snd_pcm_period_elapsed(subs->pcm_substream);
+ } else
+ spin_unlock_irqrestore(&subs->lock, flags);
+ return 0;
+}
+
+
+/*
+ */
+static struct snd_urb_ops audio_urb_ops[2] = {
+ {
+ .prepare = prepare_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_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,
+ },
+};
+
+/*
+ * complete callback from data urb
+ */
+static void snd_complete_urb(struct urb *urb, struct pt_regs *regs)
+{
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+ snd_usb_substream_t *subs = ctx->subs;
+ snd_pcm_substream_t *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 pt_regs *regs)
+{
+ snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+ snd_usb_substream_t *subs = ctx->subs;
+ snd_pcm_substream_t *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);
+ }
+ }
+}
+
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(snd_usb_substream_t *subs, int force, int can_sleep)
+{
+ unsigned int i;
+ int async;
+
+ subs->running = 0;
+
+ if (!force && subs->stream->chip->shutdown) /* to be sure... */
+ return -EBADFD;
+
+ async = !can_sleep && 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) {
+ u->transfer_flags |= URB_ASYNC_UNLINK;
+ 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) {
+ u->transfer_flags |= URB_ASYNC_UNLINK;
+ usb_unlink_urb(u);
+ } else
+ usb_kill_urb(u);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * set up and start data/sync urbs
+ */
+static int start_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
+{
+ unsigned int i;
+ int err;
+
+ if (subs->stream->chip->shutdown)
+ return -EBADFD;
+
+ for (i = 0; i < subs->nurbs; i++) {
+ snd_assert(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++) {
+ snd_assert(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++) {
+ if ((err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC)) < 0) {
+ snd_printk(KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err);
+ goto __error;
+ }
+ set_bit(i, &subs->active_mask);
+ }
+ if (subs->syncpipe) {
+ for (i = 0; i < SYNC_URBS; i++) {
+ if ((err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC)) < 0) {
+ snd_printk(KERN_ERR "cannot submit syncpipe for urb %d, err = %d\n", i, 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;
+}
+
+
+/*
+ * wait until all urbs are processed.
+ */
+static int wait_clear_urbs(snd_usb_substream_t *subs)
+{
+ int timeout = HZ;
+ 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;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ } while (--timeout > 0);
+ if (alive)
+ snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+ return 0;
+}
+
+
+/*
+ * return the current pcm pointer. just return the hwptr_done value.
+ */
+static snd_pcm_uframes_t snd_usb_pcm_pointer(snd_pcm_substream_t *substream)
+{
+ snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data;
+ return subs->hwptr_done;
+}
+
+
+/*
+ * start/stop substream
+ */
+static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+ snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data;
+ int err;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err = start_urbs(subs, substream->runtime);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ err = deactivate_urbs(subs, 0, 0);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err < 0 ? err : 0;
+}
+
+
+/*
+ * release a urb data
+ */
+static void release_urb_ctx(snd_urb_ctx_t *u)
+{
+ if (u->urb) {
+ usb_free_urb(u->urb);
+ u->urb = NULL;
+ }
+ if (u->buf) {
+ kfree(u->buf);
+ u->buf = NULL;
+ }
+}
+
+/*
+ * release a substream
+ */<