ALSA: doc: Update copy_user, copy_kernel and fill_silence PCM ops

Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2017-06-01 22:36:02 +02:00
parent fed5794fcc
commit f7a478178a

View File

@ -2080,8 +2080,8 @@ sleeping poll threads, etc.
This callback is also atomic as default.
copy and silence callbacks
~~~~~~~~~~~~~~~~~~~~~~~~~~
copy_user, copy_kernel and fill_silence ops
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These callbacks are not mandatory, and can be omitted in most cases.
These callbacks are used when the hardware buffer cannot be in the
@ -3532,8 +3532,9 @@ external hardware buffer in interrupts (or in tasklets, preferably).
The first case works fine if the external hardware buffer is large
enough. This method doesn't need any extra buffers and thus is more
effective. You need to define the ``copy`` and ``silence`` callbacks
for the data transfer. However, there is a drawback: it cannot be
effective. You need to define the ``copy_user`` and ``copy_kernel``
callbacks for the data transfer, in addition to ``fill_silence``
callback for playback. However, there is a drawback: it cannot be
mmapped. The examples are GUS's GF1 PCM or emu8000's wavetable PCM.
The second case allows for mmap on the buffer, although you have to
@ -3545,30 +3546,34 @@ Another case is when the chip uses a PCI memory-map region for the
buffer instead of the host memory. In this case, mmap is available only
on certain architectures like the Intel one. In non-mmap mode, the data
cannot be transferred as in the normal way. Thus you need to define the
``copy`` and ``silence`` callbacks as well, as in the cases above. The
examples are found in ``rme32.c`` and ``rme96.c``.
``copy_user``, ``copy_kernel`` and ``fill_silence`` callbacks as well,
as in the cases above. The examples are found in ``rme32.c`` and
``rme96.c``.
The implementation of the ``copy`` and ``silence`` callbacks depends
upon whether the hardware supports interleaved or non-interleaved
samples. The ``copy`` callback is defined like below, a bit
differently depending whether the direction is playback or capture:
The implementation of the ``copy_user``, ``copy_kernel`` and
``silence`` callbacks depends upon whether the hardware supports
interleaved or non-interleaved samples. The ``copy_user`` callback is
defined like below, a bit differently depending whether the direction
is playback or capture:
::
static int playback_copy(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count);
static int capture_copy(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count);
static int playback_copy_user(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
void __user *src, unsigned long count);
static int capture_copy_user(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
void __user *dst, unsigned long count);
In the case of interleaved samples, the second argument (``channel``) is
not used. The third argument (``pos``) points the current position
offset in frames.
offset in bytes.
The meaning of the fourth argument is different between playback and
capture. For playback, it holds the source data pointer, and for
capture, it's the destination data pointer.
The last argument is the number of frames to be copied.
The last argument is the number of bytes to be copied.
What you have to do in this callback is again different between playback
and capture directions. In the playback case, you copy the given amount
@ -3578,8 +3583,7 @@ way, the copy would be like:
::
my_memcpy(my_buffer + frames_to_bytes(runtime, pos), src,
frames_to_bytes(runtime, count));
my_memcpy_from_user(my_buffer + pos, src, count);
For the capture direction, you copy the given amount of data (``count``)
at the specified offset (``pos``) on the hardware buffer to the
@ -3587,31 +3591,68 @@ specified pointer (``dst``).
::
my_memcpy(dst, my_buffer + frames_to_bytes(runtime, pos),
frames_to_bytes(runtime, count));
my_memcpy_to_user(dst, my_buffer + pos, count);
Note that both the position and the amount of data are given in frames.
Here the functions are named as ``from_user`` and ``to_user`` because
it's the user-space buffer that is passed to these callbacks. That
is, the callback is supposed to copy from/to the user-space data
directly to/from the hardware buffer.
Careful readers might notice that these callbacks receive the
arguments in bytes, not in frames like other callbacks. It's because
it would make coding easier like the examples above, and also it makes
easier to unify both the interleaved and non-interleaved cases, as
explained in the following.
In the case of non-interleaved samples, the implementation will be a bit
more complicated.
more complicated. The callback is called for each channel, passed by
the second argument, so totally it's called for N-channels times per
transfer.
You need to check the channel argument, and if it's -1, copy the whole
channels. Otherwise, you have to copy only the specified channel. Please
check ``isa/gus/gus_pcm.c`` as an example.
The meaning of other arguments are almost same as the interleaved
case. The callback is supposed to copy the data from/to the given
user-space buffer, but only for the given channel. For the detailed
implementations, please check ``isa/gus/gus_pcm.c`` or
"pci/rme9652/rme9652.c" as examples.
The ``silence`` callback is also implemented in a similar way
The above callbacks are the copy from/to the user-space buffer. There
are some cases where we want copy from/to the kernel-space buffer
instead. In such a case, ``copy_kernel`` callback is called. It'd
look like:
::
static int playback_copy_kernel(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
void *src, unsigned long count);
static int capture_copy_kernel(struct snd_pcm_substream *substream,
int channel, unsigned long pos,
void *dst, unsigned long count);
As found easily, the only difference is that the buffer pointer is
without ``__user`` prefix; that is, a kernel-buffer pointer is passed
in the fourth argument. Correspondingly, the implementation would be
a version without the user-copy, such as:
::
my_memcpy(my_buffer + pos, src, count);
Usually for the playback, another callback ``fill_silence`` is
defined. It's implemented in a similar way as the copy callbacks
above:
::
static int silence(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
unsigned long pos, unsigned long count);
The meanings of arguments are the same as in the ``copy`` callback,
although there is no ``src/dst`` argument. In the case of interleaved
samples, the channel argument has no meaning, as well as on ``copy``
callback.
The meanings of arguments are the same as in the ``copy_user`` and
``copy_kernel`` callbacks, although there is no buffer pointer
argument. In the case of interleaved samples, the channel argument has
no meaning, as well as on ``copy_*`` callbacks.
The role of ``silence`` callback is to set the given amount
The role of ``fill_silence`` callback is to set the given amount
(``count``) of silence data at the specified offset (``pos``) on the
hardware buffer. Suppose that the data format is signed (that is, the
silent-data is 0), and the implementation using a memset-like function
@ -3619,11 +3660,11 @@ would be like:
::
my_memcpy(my_buffer + frames_to_bytes(runtime, pos), 0,
frames_to_bytes(runtime, count));
my_memset(my_buffer + pos, 0, count);
In the case of non-interleaved samples, again, the implementation
becomes a bit more complicated. See, for example, ``isa/gus/gus_pcm.c``.
becomes a bit more complicated, as it's called N-times per transfer
for each channel. See, for example, ``isa/gus/gus_pcm.c``.
Non-Contiguous Buffers
----------------------