diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2005-08-15 02:24:44 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-08-30 02:45:43 -0400 |
commit | 7efd8bc800324a967a37e8a425433468b7f06adb (patch) | |
tree | 9366f8d7b709cca8fa243708910783b8977919b7 /sound/usb/usbaudio.c | |
parent | b263a9bdf9394062a4fc4272ebed60de331c5490 (diff) |
[ALSA] usb-audio: double-buffer all playback data
USB generic driver
We always had to use double buffering when capturing, and when playback
data for one URB crosses a buffer boundary. The latter would make hwptr
updates less precise because the double-buffered data is read from the
buffer much earlier than the other data is read by the host controller.
Double-buffering all data allows to update hwptr immediately after the
data was copied to the USB buffer(s), which has the additional benefit
of avoiding the latency imposed by the host controller's delay of up to
one frame when interrupting.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Diffstat (limited to 'sound/usb/usbaudio.c')
-rw-r--r-- | sound/usb/usbaudio.c | 151 |
1 files changed, 65 insertions, 86 deletions
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index a703d96bfcb4..2b4f916a0a9a 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <sound/driver.h> | 41 | #include <sound/driver.h> |
42 | #include <linux/bitops.h> | 42 | #include <linux/bitops.h> |
43 | #include <linux/init.h> | 43 | #include <linux/init.h> |
44 | #include <linux/interrupt.h> | ||
44 | #include <linux/list.h> | 45 | #include <linux/list.h> |
45 | #include <linux/slab.h> | 46 | #include <linux/slab.h> |
46 | #include <linux/string.h> | 47 | #include <linux/string.h> |
@@ -129,8 +130,6 @@ struct snd_urb_ctx { | |||
129 | snd_usb_substream_t *subs; | 130 | snd_usb_substream_t *subs; |
130 | int index; /* index for urb array */ | 131 | int index; /* index for urb array */ |
131 | int packets; /* number of packets per urb */ | 132 | int packets; /* number of packets per urb */ |
132 | int transfer; /* transferred size */ | ||
133 | char *buf; /* buffer for capture */ | ||
134 | }; | 133 | }; |
135 | 134 | ||
136 | struct snd_urb_ops { | 135 | struct snd_urb_ops { |
@@ -168,9 +167,7 @@ struct snd_usb_substream { | |||
168 | 167 | ||
169 | unsigned int running: 1; /* running status */ | 168 | unsigned int running: 1; /* running status */ |
170 | 169 | ||
171 | unsigned int hwptr; /* free frame position in the buffer (only for playback) */ | ||
172 | unsigned int hwptr_done; /* processed frame position in the buffer */ | 170 | unsigned int hwptr_done; /* processed frame position in the buffer */ |
173 | unsigned int transfer_sched; /* scheduled frames since last period (for playback) */ | ||
174 | unsigned int transfer_done; /* processed frames since last period update */ | 171 | unsigned int transfer_done; /* processed frames since last period update */ |
175 | unsigned long active_mask; /* bitmask of active urbs */ | 172 | unsigned long active_mask; /* bitmask of active urbs */ |
176 | unsigned long unlink_mask; /* bitmask of unlinked urbs */ | 173 | unsigned long unlink_mask; /* bitmask of unlinked urbs */ |
@@ -179,12 +176,12 @@ struct snd_usb_substream { | |||
179 | snd_urb_ctx_t dataurb[MAX_URBS]; /* data urb table */ | 176 | snd_urb_ctx_t dataurb[MAX_URBS]; /* data urb table */ |
180 | snd_urb_ctx_t syncurb[SYNC_URBS]; /* sync urb table */ | 177 | snd_urb_ctx_t syncurb[SYNC_URBS]; /* sync urb table */ |
181 | char syncbuf[SYNC_URBS * 4]; /* sync buffer; it's so small - let's get static */ | 178 | char syncbuf[SYNC_URBS * 4]; /* sync buffer; it's so small - let's get static */ |
182 | char *tmpbuf; /* temporary buffer for playback */ | ||
183 | 179 | ||
184 | u64 formats; /* format bitmasks (all or'ed) */ | 180 | u64 formats; /* format bitmasks (all or'ed) */ |
185 | unsigned int num_formats; /* number of supported audio formats (list) */ | 181 | unsigned int num_formats; /* number of supported audio formats (list) */ |
186 | struct list_head fmt_list; /* format list */ | 182 | struct list_head fmt_list; /* format list */ |
187 | spinlock_t lock; | 183 | spinlock_t lock; |
184 | struct tasklet_struct start_period_elapsed; /* for start trigger */ | ||
188 | 185 | ||
189 | struct snd_urb_ops ops; /* callbacks (must be filled at init) */ | 186 | struct snd_urb_ops ops; /* callbacks (must be filled at init) */ |
190 | }; | 187 | }; |
@@ -320,7 +317,6 @@ static int prepare_capture_urb(snd_usb_substream_t *subs, | |||
320 | urb->iso_frame_desc[i].length = subs->curpacksize; | 317 | urb->iso_frame_desc[i].length = subs->curpacksize; |
321 | offs += subs->curpacksize; | 318 | offs += subs->curpacksize; |
322 | } | 319 | } |
323 | urb->transfer_buffer = ctx->buf; | ||
324 | urb->transfer_buffer_length = offs; | 320 | urb->transfer_buffer_length = offs; |
325 | urb->number_of_packets = ctx->packets; | 321 | urb->number_of_packets = ctx->packets; |
326 | #if 0 // for check | 322 | #if 0 // for check |
@@ -482,12 +478,10 @@ static int retire_playback_sync_urb_hs(snd_usb_substream_t *subs, | |||
482 | /* | 478 | /* |
483 | * prepare urb for playback data pipe | 479 | * prepare urb for playback data pipe |
484 | * | 480 | * |
485 | * we copy the data directly from the pcm buffer. | 481 | * Since a URB can handle only a single linear buffer, we must use double |
486 | * the current position to be copied is held in hwptr field. | 482 | * buffering when the data to be transferred overflows the buffer boundary. |
487 | * since a urb can handle only a single linear buffer, if the total | 483 | * To avoid inconsistencies when updating hwptr_done, we use double buffering |
488 | * transferred area overflows the buffer boundary, we cannot send | 484 | * for all URBs. |
489 | * it directly from the buffer. thus the data is once copied to | ||
490 | * a temporary buffer and urb points to that. | ||
491 | */ | 485 | */ |
492 | static int prepare_playback_urb(snd_usb_substream_t *subs, | 486 | static int prepare_playback_urb(snd_usb_substream_t *subs, |
493 | snd_pcm_runtime_t *runtime, | 487 | snd_pcm_runtime_t *runtime, |
@@ -496,6 +490,7 @@ static int prepare_playback_urb(snd_usb_substream_t *subs, | |||
496 | int i, stride, offs; | 490 | int i, stride, offs; |
497 | unsigned int counts; | 491 | unsigned int counts; |
498 | unsigned long flags; | 492 | unsigned long flags; |
493 | int period_elapsed = 0; | ||
499 | snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; | 494 | snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; |
500 | 495 | ||
501 | stride = runtime->frame_bits >> 3; | 496 | stride = runtime->frame_bits >> 3; |
@@ -520,21 +515,25 @@ static int prepare_playback_urb(snd_usb_substream_t *subs, | |||
520 | urb->iso_frame_desc[i].length = counts * stride; | 515 | urb->iso_frame_desc[i].length = counts * stride; |
521 | offs += counts; | 516 | offs += counts; |
522 | urb->number_of_packets++; | 517 | urb->number_of_packets++; |
523 | subs->transfer_sched += counts; | 518 | subs->transfer_done += counts; |
524 | if (subs->transfer_sched >= runtime->period_size) { | 519 | if (subs->transfer_done >= runtime->period_size) { |
525 | subs->transfer_sched -= runtime->period_size; | 520 | subs->transfer_done -= runtime->period_size; |
521 | period_elapsed = 1; | ||
526 | if (subs->fmt_type == USB_FORMAT_TYPE_II) { | 522 | if (subs->fmt_type == USB_FORMAT_TYPE_II) { |
527 | if (subs->transfer_sched > 0) { | 523 | if (subs->transfer_done > 0) { |
528 | /* FIXME: fill-max mode is not supported yet */ | 524 | /* FIXME: fill-max mode is not |
529 | offs -= subs->transfer_sched; | 525 | * supported yet */ |
530 | counts -= subs->transfer_sched; | 526 | offs -= subs->transfer_done; |
531 | urb->iso_frame_desc[i].length = counts * stride; | 527 | counts -= subs->transfer_done; |
532 | subs->transfer_sched = 0; | 528 | urb->iso_frame_desc[i].length = |
529 | counts * stride; | ||
530 | subs->transfer_done = 0; | ||
533 | } | 531 | } |
534 | i++; | 532 | i++; |
535 | if (i < ctx->packets) { | 533 | if (i < ctx->packets) { |
536 | /* add a transfer delimiter */ | 534 | /* add a transfer delimiter */ |
537 | urb->iso_frame_desc[i].offset = offs * stride; | 535 | urb->iso_frame_desc[i].offset = |
536 | offs * stride; | ||
538 | urb->iso_frame_desc[i].length = 0; | 537 | urb->iso_frame_desc[i].length = 0; |
539 | urb->number_of_packets++; | 538 | urb->number_of_packets++; |
540 | } | 539 | } |
@@ -542,58 +541,55 @@ static int prepare_playback_urb(snd_usb_substream_t *subs, | |||
542 | break; | 541 | break; |
543 | } | 542 | } |
544 | } | 543 | } |
545 | if (subs->hwptr + offs > runtime->buffer_size) { | 544 | if (subs->hwptr_done + offs > runtime->buffer_size) { |
546 | /* err, the transferred area goes over buffer boundary. | 545 | /* err, the transferred area goes over buffer boundary. */ |
547 | * copy the data to the temp buffer. | 546 | unsigned int len = runtime->buffer_size - subs->hwptr_done; |
548 | */ | 547 | memcpy(urb->transfer_buffer, |
549 | int len; | 548 | runtime->dma_area + subs->hwptr_done * stride, |
550 | len = runtime->buffer_size - subs->hwptr; | 549 | len * stride); |
551 | urb->transfer_buffer = subs->tmpbuf; | 550 | memcpy(urb->transfer_buffer + len * stride, |
552 | memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * stride, len * stride); | 551 | runtime->dma_area, |
553 | memcpy(subs->tmpbuf + len * stride, runtime->dma_area, (offs - len) * stride); | 552 | (offs - len) * stride); |
554 | subs->hwptr += offs; | ||
555 | subs->hwptr -= runtime->buffer_size; | ||
556 | } else { | 553 | } else { |
557 | /* set the buffer pointer */ | 554 | memcpy(urb->transfer_buffer, |
558 | urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride; | 555 | runtime->dma_area + subs->hwptr_done * stride, |
559 | subs->hwptr += offs; | 556 | offs * stride); |
560 | if (subs->hwptr == runtime->buffer_size) | ||
561 | subs->hwptr = 0; | ||
562 | } | 557 | } |
558 | subs->hwptr_done += offs; | ||
559 | if (subs->hwptr_done >= runtime->buffer_size) | ||
560 | subs->hwptr_done -= runtime->buffer_size; | ||
563 | spin_unlock_irqrestore(&subs->lock, flags); | 561 | spin_unlock_irqrestore(&subs->lock, flags); |
564 | urb->transfer_buffer_length = offs * stride; | 562 | urb->transfer_buffer_length = offs * stride; |
565 | ctx->transfer = offs; | 563 | if (period_elapsed) { |
566 | 564 | if (likely(subs->running)) | |
565 | snd_pcm_period_elapsed(subs->pcm_substream); | ||
566 | else | ||
567 | tasklet_hi_schedule(&subs->start_period_elapsed); | ||
568 | } | ||
567 | return 0; | 569 | return 0; |
568 | } | 570 | } |
569 | 571 | ||
570 | /* | 572 | /* |
571 | * process after playback data complete | 573 | * process after playback data complete |
572 | * | 574 | * - nothing to do |
573 | * update the current position and call callback if a period is processed. | ||
574 | */ | 575 | */ |
575 | static int retire_playback_urb(snd_usb_substream_t *subs, | 576 | static int retire_playback_urb(snd_usb_substream_t *subs, |
576 | snd_pcm_runtime_t *runtime, | 577 | snd_pcm_runtime_t *runtime, |
577 | struct urb *urb) | 578 | struct urb *urb) |
578 | { | 579 | { |
579 | unsigned long flags; | ||
580 | snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; | ||
581 | |||
582 | spin_lock_irqsave(&subs->lock, flags); | ||
583 | subs->transfer_done += ctx->transfer; | ||
584 | subs->hwptr_done += ctx->transfer; | ||
585 | ctx->transfer = 0; | ||
586 | if (subs->hwptr_done >= runtime->buffer_size) | ||
587 | subs->hwptr_done -= runtime->buffer_size; | ||
588 | if (subs->transfer_done >= runtime->period_size) { | ||
589 | subs->transfer_done -= runtime->period_size; | ||
590 | spin_unlock_irqrestore(&subs->lock, flags); | ||
591 | snd_pcm_period_elapsed(subs->pcm_substream); | ||
592 | } else | ||
593 | spin_unlock_irqrestore(&subs->lock, flags); | ||
594 | return 0; | 580 | return 0; |
595 | } | 581 | } |
596 | 582 | ||
583 | /* | ||
584 | * Delay the snd_pcm_period_elapsed() call until after the start trigger | ||
585 | * callback so that we're not longer in the substream's lock. | ||
586 | */ | ||
587 | static void start_period_elapsed(unsigned long data) | ||
588 | { | ||
589 | snd_usb_substream_t *subs = (snd_usb_substream_t *)data; | ||
590 | snd_pcm_period_elapsed(subs->pcm_substream); | ||
591 | } | ||
592 | |||
597 | 593 | ||
598 | /* | 594 | /* |
599 | */ | 595 | */ |
@@ -848,11 +844,10 @@ static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd) | |||
848 | static void release_urb_ctx(snd_urb_ctx_t *u) | 844 | static void release_urb_ctx(snd_urb_ctx_t *u) |
849 | { | 845 | { |
850 | if (u->urb) { | 846 | if (u->urb) { |
847 | kfree(u->urb->transfer_buffer); | ||
851 | usb_free_urb(u->urb); | 848 | usb_free_urb(u->urb); |
852 | u->urb = NULL; | 849 | u->urb = NULL; |
853 | } | 850 | } |
854 | kfree(u->buf); | ||
855 | u->buf = NULL; | ||
856 | } | 851 | } |
857 | 852 | ||
858 | /* | 853 | /* |
@@ -870,8 +865,6 @@ static void release_substream_urbs(snd_usb_substream_t *subs, int force) | |||
870 | release_urb_ctx(&subs->dataurb[i]); | 865 | release_urb_ctx(&subs->dataurb[i]); |
871 | for (i = 0; i < SYNC_URBS; i++) | 866 | for (i = 0; i < SYNC_URBS; i++) |
872 | release_urb_ctx(&subs->syncurb[i]); | 867 | release_urb_ctx(&subs->syncurb[i]); |
873 | kfree(subs->tmpbuf); | ||
874 | subs->tmpbuf = NULL; | ||
875 | subs->nurbs = 0; | 868 | subs->nurbs = 0; |
876 | } | 869 | } |
877 | 870 | ||
@@ -923,24 +916,15 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by | |||
923 | urb_packs = 1; | 916 | urb_packs = 1; |
924 | urb_packs *= packs_per_ms; | 917 | urb_packs *= packs_per_ms; |
925 | 918 | ||
926 | /* allocate a temporary buffer for playback */ | ||
927 | if (is_playback) { | ||
928 | subs->tmpbuf = kmalloc(maxsize * urb_packs, GFP_KERNEL); | ||
929 | if (! subs->tmpbuf) { | ||
930 | snd_printk(KERN_ERR "cannot malloc tmpbuf\n"); | ||
931 | return -ENOMEM; | ||
932 | } | ||
933 | } | ||
934 | |||
935 | /* decide how many packets to be used */ | 919 | /* decide how many packets to be used */ |
936 | if (is_playback) { | 920 | if (is_playback) { |
937 | unsigned int minsize; | 921 | unsigned int minsize; |
938 | /* determine how small a packet can be */ | 922 | /* determine how small a packet can be */ |
939 | minsize = (subs->freqn >> (16 - subs->datainterval)) | 923 | minsize = (subs->freqn >> (16 - subs->datainterval)) |
940 | * (frame_bits >> 3); | 924 | * (frame_bits >> 3); |
941 | /* with sync from device, assume it can be 25% lower */ | 925 | /* with sync from device, assume it can be 12% lower */ |
942 | if (subs->syncpipe) | 926 | if (subs->syncpipe) |
943 | minsize -= minsize >> 2; | 927 | minsize -= minsize >> 3; |
944 | minsize = max(minsize, 1u); | 928 | minsize = max(minsize, 1u); |
945 | total_packs = (period_bytes + minsize - 1) / minsize; | 929 | total_packs = (period_bytes + minsize - 1) / minsize; |
946 | /* round up to multiple of packs_per_ms */ | 930 | /* round up to multiple of packs_per_ms */ |
@@ -989,27 +973,22 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by | |||
989 | snd_urb_ctx_t *u = &subs->dataurb[i]; | 973 | snd_urb_ctx_t *u = &subs->dataurb[i]; |
990 | u->index = i; | 974 | u->index = i; |
991 | u->subs = subs; | 975 | u->subs = subs; |
992 | u->transfer = 0; | ||
993 | u->packets = npacks[i]; | 976 | u->packets = npacks[i]; |
994 | if (subs->fmt_type == USB_FORMAT_TYPE_II) | 977 | if (subs->fmt_type == USB_FORMAT_TYPE_II) |
995 | u->packets++; /* for transfer delimiter */ | 978 | u->packets++; /* for transfer delimiter */ |
996 | if (! is_playback) { | ||
997 | /* allocate a capture buffer per urb */ | ||
998 | u->buf = kmalloc(maxsize * u->packets, GFP_KERNEL); | ||
999 | if (! u->buf) { | ||
1000 | release_substream_urbs(subs, 0); | ||
1001 | return -ENOMEM; | ||
1002 | } | ||
1003 | } | ||
1004 | u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); | 979 | u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); |
1005 | if (! u->urb) { | 980 | if (! u->urb) { |
1006 | release_substream_urbs(subs, 0); | 981 | release_substream_urbs(subs, 0); |
1007 | return -ENOMEM; | 982 | return -ENOMEM; |
1008 | } | 983 | } |
1009 | u->urb->dev = subs->dev; | 984 | u->urb->transfer_buffer = kmalloc(maxsize * u->packets, |
985 | GFP_KERNEL); | ||
986 | if (! u->urb->transfer_buffer) { | ||
987 | release_substream_urbs(subs, 0); | ||
988 | return -ENOMEM; | ||
989 | } | ||
1010 | u->urb->pipe = subs->datapipe; | 990 | u->urb->pipe = subs->datapipe; |
1011 | u->urb->transfer_flags = URB_ISO_ASAP; | 991 | u->urb->transfer_flags = URB_ISO_ASAP; |
1012 | u->urb->number_of_packets = u->packets; | ||
1013 | u->urb->interval = 1 << subs->datainterval; | 992 | u->urb->interval = 1 << subs->datainterval; |
1014 | u->urb->context = u; | 993 | u->urb->context = u; |
1015 | u->urb->complete = snd_usb_complete_callback(snd_complete_urb); | 994 | u->urb->complete = snd_usb_complete_callback(snd_complete_urb); |
@@ -1029,7 +1008,6 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by | |||
1029 | } | 1008 | } |
1030 | u->urb->transfer_buffer = subs->syncbuf + i * 4; | 1009 | u->urb->transfer_buffer = subs->syncbuf + i * 4; |
1031 | u->urb->transfer_buffer_length = 4; | 1010 | u->urb->transfer_buffer_length = 4; |
1032 | u->urb->dev = subs->dev; | ||
1033 | u->urb->pipe = subs->syncpipe; | 1011 | u->urb->pipe = subs->syncpipe; |
1034 | u->urb->transfer_flags = URB_ISO_ASAP; | 1012 | u->urb->transfer_flags = URB_ISO_ASAP; |
1035 | u->urb->number_of_packets = 1; | 1013 | u->urb->number_of_packets = 1; |
@@ -1386,9 +1364,7 @@ static int snd_usb_pcm_prepare(snd_pcm_substream_t *substream) | |||
1386 | subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); | 1364 | subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); |
1387 | 1365 | ||
1388 | /* reset the pointer */ | 1366 | /* reset the pointer */ |
1389 | subs->hwptr = 0; | ||
1390 | subs->hwptr_done = 0; | 1367 | subs->hwptr_done = 0; |
1391 | subs->transfer_sched = 0; | ||
1392 | subs->transfer_done = 0; | 1368 | subs->transfer_done = 0; |
1393 | subs->phase = 0; | 1369 | subs->phase = 0; |
1394 | 1370 | ||
@@ -2035,6 +2011,9 @@ static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat | |||
2035 | 2011 | ||
2036 | INIT_LIST_HEAD(&subs->fmt_list); | 2012 | INIT_LIST_HEAD(&subs->fmt_list); |
2037 | spin_lock_init(&subs->lock); | 2013 | spin_lock_init(&subs->lock); |
2014 | if (stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
2015 | tasklet_init(&subs->start_period_elapsed, start_period_elapsed, | ||
2016 | (unsigned long)subs); | ||
2038 | 2017 | ||
2039 | subs->stream = as; | 2018 | subs->stream = as; |
2040 | subs->direction = stream; | 2019 | subs->direction = stream; |