diff options
author | Takashi Iwai <tiwai@suse.de> | 2017-06-19 16:39:18 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2017-06-23 09:39:47 -0400 |
commit | 42f945970af9df6216e3d771b4df371d02d8742c (patch) | |
tree | e0737461cdf10f91602f80eea364b7f6fd542c27 | |
parent | c2d6af53a43fd8bb528eac8f31ffb666e9c74cf7 (diff) |
ALSA: pcm: Add the explicit appl_ptr sync support
Currently x86 platforms use the PCM status/control mmaps for
transferring the PCM status and appl_ptr between kernel and
user-spaces. The mmap is a most efficient way of communication, but
it has a drawback per its nature, namely, it can't notify the change
explicitly to kernel.
The lack of appl_ptr update notification is a problem on a few
existing drivers, but it's mostly a small issue and negligible.
However, a new type of driver that uses DSP for a deep buffer
management requires the exact position of appl_ptr for calculating the
buffer prefetch size, and the asynchronous appl_ptr update between
kernel and user-spaces becomes a significant problem for it.
How can we enforce user-space to report the appl_ptr update? The way
is relatively simple. Just by disabling the PCM control mmap, the
user-space is supposed to fall back to the mode using SYNC_PTR ioctl,
and the kernel gets control over that. This fallback mode is used in
all non-x86 platforms as default, and also in the 32bit compatible
model on all platforms including x86. It's been implemented already
over a decade, so we can say it's fairly safe and stably working.
With the help of the knowledge above, this patch introduces a new PCM
info flag SNDRV_PCM_INFO_SYNC_APPLPTR for achieving the appl_ptr sync
from user-space. When a driver sets this flag at open, the PCM status
/ control mmap is disabled, which effectively switches to SYNC_PTR
mode in user-space side.
In this version, both PCM status and control mmaps are disabled
although only the latter, control mmap, is the target. It's because
the current alsa-lib implementation supposes that both status and
control mmaps are always coupled, thus it handles a fatal error when
only one of them fails.
Of course, the disablement of the status/control mmaps may bring a
slight performance overhead. Thus, as of now, this should be used
only for the dedicated devices that deserves.
Note that the disablement of mmap is a sort of workaround. In the
later patch, we'll introduce the way to identify the protocol version
alsa-lib supports, and keep mmap working while the sync_ptr is
performed together.
Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | include/uapi/sound/asound.h | 1 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 23 |
2 files changed, 22 insertions, 2 deletions
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index fd41697cb4d3..7eee52eb7462 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h | |||
@@ -268,6 +268,7 @@ typedef int __bitwise snd_pcm_subformat_t; | |||
268 | #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ | 268 | #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ |
269 | #define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */ | 269 | #define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */ |
270 | #define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */ | 270 | #define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */ |
271 | #define SNDRV_PCM_INFO_SYNC_APPLPTR 0x00000020 /* need the explicit sync of appl_ptr update */ | ||
271 | #define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */ | 272 | #define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */ |
272 | #define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */ | 273 | #define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */ |
273 | #define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */ | 274 | #define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */ |
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index d35c6614fdab..9ade0c8b54a3 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c | |||
@@ -3376,10 +3376,29 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file | |||
3376 | area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; | 3376 | area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; |
3377 | return 0; | 3377 | return 0; |
3378 | } | 3378 | } |
3379 | |||
3380 | static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file) | ||
3381 | { | ||
3382 | if (pcm_file->no_compat_mmap) | ||
3383 | return false; | ||
3384 | /* Disallow the status/control mmap when SYNC_APPLPTR flag is set; | ||
3385 | * it enforces the user-space to fall back to snd_pcm_sync_ptr(), | ||
3386 | * thus it effectively assures the manual update of appl_ptr. | ||
3387 | * In theory, it should be enough to disallow only PCM control mmap, | ||
3388 | * but since the current alsa-lib implementation requires both status | ||
3389 | * and control mmaps always paired, we have to disable both of them. | ||
3390 | */ | ||
3391 | if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR) | ||
3392 | return false; | ||
3393 | return true; | ||
3394 | } | ||
3395 | |||
3379 | #else /* ! coherent mmap */ | 3396 | #else /* ! coherent mmap */ |
3380 | /* | 3397 | /* |
3381 | * don't support mmap for status and control records. | 3398 | * don't support mmap for status and control records. |
3382 | */ | 3399 | */ |
3400 | #define pcm_status_mmap_allowed(pcm_file) false | ||
3401 | |||
3383 | static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, | 3402 | static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, |
3384 | struct vm_area_struct *area) | 3403 | struct vm_area_struct *area) |
3385 | { | 3404 | { |
@@ -3563,11 +3582,11 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) | |||
3563 | offset = area->vm_pgoff << PAGE_SHIFT; | 3582 | offset = area->vm_pgoff << PAGE_SHIFT; |
3564 | switch (offset) { | 3583 | switch (offset) { |
3565 | case SNDRV_PCM_MMAP_OFFSET_STATUS: | 3584 | case SNDRV_PCM_MMAP_OFFSET_STATUS: |
3566 | if (pcm_file->no_compat_mmap) | 3585 | if (!pcm_status_mmap_allowed(pcm_file)) |
3567 | return -ENXIO; | 3586 | return -ENXIO; |
3568 | return snd_pcm_mmap_status(substream, file, area); | 3587 | return snd_pcm_mmap_status(substream, file, area); |
3569 | case SNDRV_PCM_MMAP_OFFSET_CONTROL: | 3588 | case SNDRV_PCM_MMAP_OFFSET_CONTROL: |
3570 | if (pcm_file->no_compat_mmap) | 3589 | if (!pcm_status_mmap_allowed(pcm_file)) |
3571 | return -ENXIO; | 3590 | return -ENXIO; |
3572 | return snd_pcm_mmap_control(substream, file, area); | 3591 | return snd_pcm_mmap_control(substream, file, area); |
3573 | default: | 3592 | default: |