📄 usbaudio.c
字号:
* * 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;}/* 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 a frame of 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; urb->number_of_packets = subs->packs_per_ms; for (i = 0; i < subs->packs_per_ms; ++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->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, offs; unsigned int counts; unsigned long flags; int period_elapsed = 0; struct snd_urb_ctx *ctx = 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++) { counts = snd_usb_audio_next_packet_size(subs); /* 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_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_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].length = 0; urb->number_of_packets++; } 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_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 { 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; if (period_elapsed) snd_pcm_period_elapsed(subs->pcm_substream); return 0;}/* * process after playback data complete * - nothing to do */static int retire_playback_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *urb){ return 0;}/* */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, },};/* * 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); } }}/* get the physical page pointer at the given offset */static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *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(struct snd_pcm_substream *subs, size_t size){ struct snd_pcm_runtime *runtime = subs->runtime; if (runtime->dma_area) { if (runtime->dma_bytes >= size) return 0; /* already large enough */ vfree(runtime->dma_area); } runtime->dma_area = vmalloc(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(struct snd_pcm_substream *subs){ struct snd_pcm_runtime *runtime = subs->runtime; vfree(runtime->dma_area); runtime->dma_area = NULL; return 0;}/* * unlink active urbs. */static int deactivate_urbs(struct snd_usb_substream *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) 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;}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";#ifndef CONFIG_USB_EHCI_SPLIT_ISO case -ENOSYS: return "enable CONFIG_USB_EHCI_SPLIT_ISO to play through a hub";#endif 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++) { 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++) { 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;}/* * 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;}/* * return the current pcm pointer. just return the hwptr_done value. */static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream){ struct snd_usb_substream *subs; snd_pcm_uframes_t hwptr_done; subs = (struct snd_usb_substream *)substream->runtime->private_data; spin_lock(&subs->lock); hwptr_done = subs->hwptr_done; spin_unlock(&subs->lock); return hwptr_done;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -