#include <sound/driver.h>
#include <linux/bitops.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/usb.h>
+#include <linux/vmalloc.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/info.h>
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_param(nrpacks, int, 0644);
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.");
#define MAX_PACKS 10
#define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */
-#define MAX_URBS 5 /* max. 20ms long packets */
+#define MAX_URBS 8
#define SYNC_URBS 4 /* always four urbs for sync */
#define MIN_PACKS_URB 1 /* minimum 1 packet per urb */
struct snd_urb_ctx {
struct urb *urb;
+ unsigned int buffer_size; /* size of data buffer, if data 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 {
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 datainterval; /* log_2 of data packet interval */
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 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 packs_per_ms; /* packets per millisecond (for playback) */
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 * 4]; /* sync buffer; it's so small - let's get static */
- char *tmpbuf; /* temporary buffer for playback */
+ char *syncbuf; /* sync buffer for all sync URBs */
+ dma_addr_t sync_dma; /* DMA address of syncbuf */
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 tasklet_struct start_period_elapsed; /* for start trigger */
struct snd_urb_ops ops; /* callbacks (must be filled at init) */
};
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;
+ urb->number_of_packets = ctx->packets;
#if 0 // for check
if (! urb->bandwidth) {
int bustime;
unsigned char *cp;
int i;
unsigned int stride, len, oldptr;
+ int period_elapsed = 0;
stride = runtime->frame_bits >> 3;
if (subs->hwptr_done >= runtime->buffer_size)
subs->hwptr_done -= runtime->buffer_size;
subs->transfer_done += len;
+ 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 + len > runtime->buffer_size) {
} 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);
}
+ if (period_elapsed)
+ snd_pcm_period_elapsed(subs->pcm_substream);
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.
+ * 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(snd_usb_substream_t *subs,
snd_pcm_runtime_t *runtime,
int i, stride, offs;
unsigned int counts;
unsigned long flags;
+ int period_elapsed = 0;
snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
stride = runtime->frame_bits >> 3;
if (subs->fill_max)
counts = subs->maxframesize; /* fixed */
else {
- subs->phase = (subs->phase & 0xffff) + subs->freqm;
+ subs->phase = (subs->phase & 0xffff)
+ + (subs->freqm << subs->datainterval);
counts = subs->phase >> 16;
if (counts > subs->maxframesize)
counts = subs->maxframesize;
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;
+ subs->transfer_done += counts;
+ if (subs->transfer_done >= runtime->period_size) {
+ subs->transfer_done -= runtime->period_size;
+ period_elapsed = 1;
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;
+ if (subs->transfer_done > 0) {
+ /* FIXME: fill-max mode is not
+ * supported yet */
+ offs -= 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 = offs * stride;
+ urb->iso_frame_desc[i].offset =
+ offs * stride;
urb->iso_frame_desc[i].length = 0;
urb->number_of_packets++;
}
+ break;
}
- break;
}
+ /* finish at the frame boundary at/after the period boundary */
+ if (period_elapsed &&
+ (i & (subs->packs_per_ms - 1)) == subs->packs_per_ms - 1)
+ 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;
+ if (subs->hwptr_done + offs > runtime->buffer_size) {
+ /* err, the transferred area goes over buffer boundary. */
+ unsigned int len = runtime->buffer_size - subs->hwptr_done;
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done * stride,
+ len * stride);
+ memcpy(urb->transfer_buffer + len * stride,
+ runtime->dma_area,
+ (offs - len) * stride);
} else {
- /* set the buffer pointer */
- urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride;
- subs->hwptr += offs;
- if (subs->hwptr == runtime->buffer_size)
- subs->hwptr = 0;
+ memcpy(urb->transfer_buffer,
+ runtime->dma_area + subs->hwptr_done * stride,
+ offs * stride);
}
+ subs->hwptr_done += offs;
+ if (subs->hwptr_done >= runtime->buffer_size)
+ subs->hwptr_done -= runtime->buffer_size;
spin_unlock_irqrestore(&subs->lock, flags);
urb->transfer_buffer_length = offs * stride;
- ctx->transfer = offs;
-
+ if (period_elapsed) {
+ if (likely(subs->running))
+ snd_pcm_period_elapsed(subs->pcm_substream);
+ else
+ tasklet_hi_schedule(&subs->start_period_elapsed);
+ }
return 0;
}
/*
* process after playback data complete
- *
- * update the current position and call callback if a period is processed.
+ * - nothing to do
*/
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;
}
+/*
+ * Delay the snd_pcm_period_elapsed() call until after the start trigger
+ * callback so that we're not longer in the substream's lock.
+ */
+static void start_period_elapsed(unsigned long data)
+{
+ snd_usb_substream_t *subs = (snd_usb_substream_t *)data;
+ snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
/*
*/
}
+/* get the physical page pointer at the given offset */
+static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+/* allocate virtual buffer; may be called more than once */
+static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size)
+{
+ snd_pcm_runtime_t *runtime = subs->runtime;
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes >= size)
+ return 0; /* already large enough */
+ vfree_nocheck(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc_nocheck(size);
+ if (! runtime->dma_area)
+ return -ENOMEM;
+ runtime->dma_bytes = size;
+ return 0;
+}
+
+/* free virtual buffer; may be called more than once */
+static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs)
+{
+ snd_pcm_runtime_t *runtime = subs->runtime;
+ if (runtime->dma_area) {
+ vfree_nocheck(runtime->dma_area);
+ runtime->dma_area = NULL;
+ }
+ return 0;
+}
+
+
/*
* unlink active urbs.
*/
*/
static int wait_clear_urbs(snd_usb_substream_t *subs)
{
- int timeout = HZ;
+ unsigned long end_time = jiffies + msecs_to_jiffies(1000);
unsigned int i;
int alive;
break;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
- } while (--timeout > 0);
+ } while (time_before(jiffies, end_time));
if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
return 0;
*/
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;
+ snd_usb_substream_t *subs;
+ snd_pcm_uframes_t hwptr_done;
+
+ subs = (snd_usb_substream_t *)substream->runtime->private_data;
+ spin_lock(&subs->lock);
+ hwptr_done = subs->hwptr_done;
+ spin_unlock(&subs->lock);
+ return hwptr_done;
}
static void release_urb_ctx(snd_urb_ctx_t *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;
}
- kfree(u->buf);
- u->buf = NULL;
}
/*
release_urb_ctx(&subs->dataurb[i]);
for (i = 0; i < SYNC_URBS; i++)
release_urb_ctx(&subs->syncurb[i]);
- kfree(subs->tmpbuf);
- subs->tmpbuf = NULL;
+ usb_buffer_free(subs->dev, SYNC_URBS * 4,
+ subs->syncbuf, subs->sync_dma);
+ subs->syncbuf = NULL;
subs->nurbs = 0;
}
{
unsigned int maxsize, n, i;
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
- unsigned int npacks[MAX_URBS], urb_packs, total_packs;
+ unsigned int npacks[MAX_URBS], urb_packs, total_packs, packs_per_ms;
/* calculate the frequency in 16.16 format */
if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
else
subs->freqn = get_usb_high_speed_rate(rate);
subs->freqm = subs->freqn;
- subs->freqmax = subs->freqn + (subs->freqn >> 2); /* max. allowed frequency */
- subs->phase = 0;
-
- /* calculate the max. size of packet */
- maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) >> 16;
- if (subs->maxpacksize && maxsize > subs->maxpacksize) {
- //snd_printd(KERN_DEBUG "maxsize %d is greater than defined size %d\n",
- // maxsize, subs->maxpacksize);
+ /* 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_FULL)
- urb_packs = nrpacks;
+ if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+ packs_per_ms = 8 >> subs->datainterval;
else
- urb_packs = nrpacks * 8;
+ packs_per_ms = 1;
+ subs->packs_per_ms = packs_per_ms;
- /* allocate a temporary buffer for playback */
if (is_playback) {
- subs->tmpbuf = kmalloc(maxsize * urb_packs, GFP_KERNEL);
- if (! subs->tmpbuf) {
- snd_printk(KERN_ERR "cannot malloc tmpbuf\n");
- return -ENOMEM;
- }
- }
+ urb_packs = nrpacks;
+ urb_packs = max(urb_packs, (unsigned int)MIN_PACKS_URB);
+ urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
+ } else
+ urb_packs = 1;
+ urb_packs *= packs_per_ms;
/* decide how many packets to be used */
- total_packs = (period_bytes + maxsize - 1) / maxsize;
- if (total_packs < 2 * MIN_PACKS_URB)
- total_packs = 2 * MIN_PACKS_URB;
+ if (is_playback) {
+ unsigned int minsize;
+ /* 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;
+ /* round up to multiple of packs_per_ms */
+ total_packs = (total_packs + packs_per_ms - 1)
+ & ~(packs_per_ms - 1);
+ /* we need at least two URBs for queueing */
+ if (total_packs < 2 * MIN_PACKS_URB * packs_per_ms)
+ total_packs = 2 * MIN_PACKS_URB * packs_per_ms;
+ } else {
+ total_packs = MAX_URBS * urb_packs;
+ }
subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
if (subs->nurbs > MAX_URBS) {
/* too much... */
subs->nurbs = 2;
npacks[0] = (total_packs + 1) / 2;
npacks[1] = total_packs - npacks[0];
- } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB) {
+ } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB * packs_per_ms) {
/* the last packet is too small.. */
if (subs->nurbs > 2) {
/* merge to the first one */
snd_urb_ctx_t *u = &subs->dataurb[i];
u->index = i;
u->subs = subs;
- u->transfer = 0;
u->packets = npacks[i];
+ u->buffer_size = maxsize * u->packets;
if (subs->fmt_type == USB_FORMAT_TYPE_II)
u->packets++; /* for transfer delimiter */
- if (! is_playback) {
- /* allocate a capture buffer per urb */
- u->buf = kmalloc(maxsize * u->packets, GFP_KERNEL);
- if (! u->buf) {
- release_substream_urbs(subs, 0);
- return -ENOMEM;
- }
- }
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
- if (! u->urb) {
- release_substream_urbs(subs, 0);
- return -ENOMEM;
- }
- u->urb->dev = subs->dev;
+ 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;
- u->urb->number_of_packets = u->packets;
- u->urb->interval = 1;
+ 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_usb_complete_callback(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++) {
snd_urb_ctx_t *u = &subs->syncurb[i];
u->index = i;
u->subs = subs;
u->packets = 1;
u->urb = usb_alloc_urb(1, GFP_KERNEL);
- if (! u->urb) {
- release_substream_urbs(subs, 0);
- return -ENOMEM;
- }
+ 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->dev = subs->dev;
u->urb->pipe = subs->syncpipe;
- u->urb->transfer_flags = URB_ISO_ASAP;
+ 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;
}
}
return 0;
+
+out_of_memory:
+ release_substream_urbs(subs, 0);
+ return -ENOMEM;
}
subs->datapipe = usb_sndisocpipe(dev, ep);
else
subs->datapipe = usb_rcvisocpipe(dev, ep);
+ if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH &&
+ get_endpoint(alts, 0)->bInterval >= 1 &&
+ get_endpoint(alts, 0)->bInterval <= 4)
+ subs->datainterval = get_endpoint(alts, 0)->bInterval - 1;
+ else
+ subs->datainterval = 0;
subs->syncpipe = subs->syncinterval = 0;
subs->maxpacksize = fmt->maxpacksize;
subs->fill_max = 0;
unsigned int channels, rate, format;
int ret, changed;
- ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
subs->cur_rate = 0;
subs->period_bytes = 0;
release_substream_urbs(subs, 0);
- return snd_pcm_lib_free_pages(substream);
+ return snd_pcm_free_vmalloc_buffer(substream);
}
/*
subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
/* reset the pointer */
- subs->hwptr = 0;
subs->hwptr_done = 0;
- subs->transfer_sched = 0;
subs->transfer_done = 0;
subs->phase = 0;
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
- .buffer_bytes_max = (128*1024),
+ .buffer_bytes_max = (256*1024),
.period_bytes_min = 64,
.period_bytes_max = (128*1024),
.periods_min = 2,
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
- .buffer_bytes_max = (128*1024),
+ .buffer_bytes_max = (256*1024),
.period_bytes_min = 64,
.period_bytes_max = (128*1024),
.periods_min = 2,
.prepare = snd_usb_pcm_prepare,
.trigger = snd_usb_pcm_trigger,
.pointer = snd_usb_pcm_pointer,
+ .page = snd_pcm_get_vmalloc_page,
};
static snd_pcm_ops_t snd_usb_capture_ops = {
.prepare = snd_usb_pcm_prepare,
.trigger = snd_usb_pcm_trigger,
.pointer = snd_usb_pcm_pointer,
+ .page = snd_pcm_get_vmalloc_page,
};
INIT_LIST_HEAD(&subs->fmt_list);
spin_lock_init(&subs->lock);
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tasklet_init(&subs->start_period_elapsed, start_period_elapsed,
+ (unsigned long)subs);
subs->stream = as;
subs->direction = stream;
subs->ops = audio_urb_ops[stream];
else
subs->ops = audio_urb_ops_high_speed[stream];
- snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 64 * 1024, 128 * 1024);
snd_pcm_set_ops(as->pcm, stream,
stream == SNDRV_PCM_STREAM_PLAYBACK ?
&snd_usb_playback_ops : &snd_usb_capture_ops);
snd_usb_stream_t *stream = pcm->private_data;
if (stream) {
stream->pcm = NULL;
- snd_pcm_lib_preallocate_free_for_all(pcm);
snd_usb_audio_stream_free(stream);
}
}
if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
chip->usb_id == USB_ID(0x041e, 0x3020)) {
if (fmt[3] == USB_FORMAT_TYPE_I &&
- stream == SNDRV_PCM_STREAM_PLAYBACK &&
fp->rates != SNDRV_PCM_RATE_48000 &&
fp->rates != SNDRV_PCM_RATE_96000)
- return -1; /* use 48k only */
+ return -1;
}
#endif
return 0;
fp->altset_idx = i;
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
- /* FIXME: decode wMaxPacketSize of high bandwith endpoints */
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+ if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
+ fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
+ * (fp->maxpacksize & 0x7ff);
fp->attributes = csep[3];
/* some quirks for attributes here */
* to detect the sample rate is by looking at wMaxPacketSize.
*/
static int create_ua700_ua25_quirk(snd_usb_audio_t *chip,
- struct usb_interface *iface)
+ struct usb_interface *iface,
+ const snd_usb_audio_quirk_t *quirk)
{
static const struct audioformat ua_format = {
.format = SNDRV_PCM_FORMAT_S24_3LE,
/*
* Create a stream for an Edirol UA-1000 interface.
*/
-static int create_ua1000_quirk(snd_usb_audio_t *chip, struct usb_interface *iface)
+static int create_ua1000_quirk(snd_usb_audio_t *chip,
+ struct usb_interface *iface,
+ const snd_usb_audio_quirk_t *quirk)
{
static const struct audioformat ua1000_format = {
.format = SNDRV_PCM_FORMAT_S32_LE,
return 0;
}
+static int ignore_interface_quirk(snd_usb_audio_t *chip,
+ struct usb_interface *iface,
+ const snd_usb_audio_quirk_t *quirk)
+{
+ return 0;
+}
+
/*
* boot quirks
static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
{
-#if 0
- /* TODO: enable this when high speed synchronization actually works */
u8 buf = 1;
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
1, 2000, NULL, 0, 1000);
return -ENODEV;
}
-#endif
return 0;
}
struct usb_interface *iface,
const snd_usb_audio_quirk_t *quirk)
{
- switch (quirk->type) {
- case QUIRK_MIDI_FIXED_ENDPOINT:
- case QUIRK_MIDI_YAMAHA:
- case QUIRK_MIDI_MIDIMAN:
- case QUIRK_MIDI_NOVATION:
- case QUIRK_MIDI_MOTU:
- case QUIRK_MIDI_EMAGIC:
- return snd_usb_create_midi_interface(chip, iface, quirk);
- case QUIRK_COMPOSITE:
- return create_composite_quirk(chip, iface, quirk);
- case QUIRK_AUDIO_FIXED_ENDPOINT:
- return create_fixed_stream_quirk(chip, iface, quirk);
- case QUIRK_AUDIO_STANDARD_INTERFACE:
- case QUIRK_MIDI_STANDARD_INTERFACE:
- return create_standard_interface_quirk(chip, iface, quirk);
- case QUIRK_AUDIO_EDIROL_UA700_UA25:
- return create_ua700_ua25_quirk(chip, iface);
- case QUIRK_AUDIO_EDIROL_UA1000:
- return create_ua1000_quirk(chip, iface);
- case QUIRK_IGNORE_INTERFACE:
- return 0;
- default:
+ typedef int (*quirk_func_t)(snd_usb_audio_t *, struct usb_interface *,
+ const snd_usb_audio_quirk_t *);
+ static const quirk_func_t quirk_funcs[] = {
+ [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
+ [QUIRK_COMPOSITE] = create_composite_quirk,
+ [QUIRK_MIDI_STANDARD_INTERFACE] = snd_usb_create_midi_interface,
+ [QUIRK_MIDI_FIXED_ENDPOINT] = snd_usb_create_midi_interface,
+ [QUIRK_MIDI_YAMAHA] = snd_usb_create_midi_interface,
+ [QUIRK_MIDI_MIDIMAN] = snd_usb_create_midi_interface,
+ [QUIRK_MIDI_NOVATION] = snd_usb_create_midi_interface,
+ [QUIRK_MIDI_RAW] = snd_usb_create_midi_interface,
+ [QUIRK_MIDI_EMAGIC] = snd_usb_create_midi_interface,
+ [QUIRK_MIDI_MIDITECH] = snd_usb_create_midi_interface,
+ [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_interface_quirk,
+ [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
+ [QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,
+ [QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
+ };
+
+ if (quirk->type < QUIRK_TYPE_COUNT) {
+ return quirk_funcs[quirk->type](chip, iface, quirk);
+ } else {
snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
return -ENXIO;
}