Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 6532
b: refs/heads/master
c: 7efd8bc
h: refs/heads/master
v: v3
  • Loading branch information
Clemens Ladisch authored and Jaroslav Kysela committed Aug 30, 2005
1 parent b4195fb commit 2c34320
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 87 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: b263a9bdf9394062a4fc4272ebed60de331c5490
refs/heads/master: 7efd8bc800324a967a37e8a425433468b7f06adb
151 changes: 65 additions & 86 deletions trunk/sound/usb/usbaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#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>
Expand Down Expand Up @@ -129,8 +130,6 @@ struct snd_urb_ctx {
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 {
Expand Down Expand Up @@ -168,9 +167,7 @@ struct snd_usb_substream {

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 */
Expand All @@ -179,12 +176,12 @@ struct snd_usb_substream {
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 */

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) */
};
Expand Down Expand Up @@ -320,7 +317,6 @@ static int prepare_capture_urb(snd_usb_substream_t *subs,
urb->iso_frame_desc[i].length = subs->curpacksize;
offs += subs->curpacksize;
}
urb->transfer_buffer = ctx->buf;
urb->transfer_buffer_length = offs;
urb->number_of_packets = ctx->packets;
#if 0 // for check
Expand Down Expand Up @@ -482,12 +478,10 @@ static int retire_playback_sync_urb_hs(snd_usb_substream_t *subs,
/*
* 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,
Expand All @@ -496,6 +490,7 @@ static int prepare_playback_urb(snd_usb_substream_t *subs,
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;
Expand All @@ -520,80 +515,81 @@ static int prepare_playback_urb(snd_usb_substream_t *subs,
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;
}
}
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);
}


/*
*/
Expand Down Expand Up @@ -848,11 +844,10 @@ static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
static void release_urb_ctx(snd_urb_ctx_t *u)
{
if (u->urb) {
kfree(u->urb->transfer_buffer);
usb_free_urb(u->urb);
u->urb = NULL;
}
kfree(u->buf);
u->buf = NULL;
}

/*
Expand All @@ -870,8 +865,6 @@ static void release_substream_urbs(snd_usb_substream_t *subs, int force)
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;
subs->nurbs = 0;
}

Expand Down Expand Up @@ -923,24 +916,15 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
urb_packs = 1;
urb_packs *= 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;
}
}

/* decide how many packets to be used */
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 25% lower */
/* with sync from device, assume it can be 12% lower */
if (subs->syncpipe)
minsize -= minsize >> 2;
minsize -= minsize >> 3;
minsize = max(minsize, 1u);
total_packs = (period_bytes + minsize - 1) / minsize;
/* round up to multiple of packs_per_ms */
Expand Down Expand Up @@ -989,27 +973,22 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
snd_urb_ctx_t *u = &subs->dataurb[i];
u->index = i;
u->subs = subs;
u->transfer = 0;
u->packets = npacks[i];
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;
u->urb->transfer_buffer = kmalloc(maxsize * u->packets,
GFP_KERNEL);
if (! u->urb->transfer_buffer) {
release_substream_urbs(subs, 0);
return -ENOMEM;
}
u->urb->pipe = subs->datapipe;
u->urb->transfer_flags = URB_ISO_ASAP;
u->urb->number_of_packets = u->packets;
u->urb->interval = 1 << subs->datainterval;
u->urb->context = u;
u->urb->complete = snd_usb_complete_callback(snd_complete_urb);
Expand All @@ -1029,7 +1008,6 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
}
u->urb->transfer_buffer = subs->syncbuf + 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->number_of_packets = 1;
Expand Down Expand Up @@ -1386,9 +1364,7 @@ static int snd_usb_pcm_prepare(snd_pcm_substream_t *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;

Expand Down Expand Up @@ -2035,6 +2011,9 @@ static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat

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;
Expand Down

0 comments on commit 2c34320

Please sign in to comment.