usbaudio.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,293 行 · 第 1/5 页
C
2,293 行
* 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 0; 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; else u->transfer_flags &= ~URB_ASYNC_UNLINK; usb_unlink_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; else u->transfer_flags &= ~URB_ASYNC_UNLINK; usb_unlink_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; 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 */static void release_substream_urbs(snd_usb_substream_t *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]); if (subs->tmpbuf) { kfree(subs->tmpbuf); subs->tmpbuf = NULL; } subs->nurbs = 0;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?