When the PCM substream is opened, a PCM runtime instance is
allocated and assigned to the substream. This pointer is
accessible via substream->runtime
.
This runtime pointer holds most information you need
to control the PCM: the copy of hw_params and sw_params configurations, the buffer
pointers, mmap records, spinlocks, etc.
The definition of runtime instance is found in
<sound/pcm.h>
. Here are
the contents of this file:
struct _snd_pcm_runtime { /* -- Status -- */ struct snd_pcm_substream *trigger_master; snd_timestamp_t trigger_tstamp; /* trigger timestamp */ int overrange; snd_pcm_uframes_t avail_max; snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/ /* -- HW params -- */ snd_pcm_access_t access; /* access mode */ snd_pcm_format_t format; /* SNDRV_PCM_FORMAT_* */ snd_pcm_subformat_t subformat; /* subformat */ unsigned int rate; /* rate in Hz */ unsigned int channels; /* channels */ snd_pcm_uframes_t period_size; /* period size */ unsigned int periods; /* periods */ snd_pcm_uframes_t buffer_size; /* buffer size */ unsigned int tick_time; /* tick time */ snd_pcm_uframes_t min_align; /* Min alignment for the format */ size_t byte_align; unsigned int frame_bits; unsigned int sample_bits; unsigned int info; unsigned int rate_num; unsigned int rate_den; /* -- SW params -- */ struct timespec tstamp_mode; /* mmap timestamp is updated */ unsigned int period_step; unsigned int sleep_min; /* min ticks to sleep */ snd_pcm_uframes_t start_threshold; snd_pcm_uframes_t stop_threshold; snd_pcm_uframes_t silence_threshold; /* Silence filling happens when noise is nearest than this */ snd_pcm_uframes_t silence_size; /* Silence filling size */ snd_pcm_uframes_t boundary; /* pointers wrap point */ snd_pcm_uframes_t silenced_start; snd_pcm_uframes_t silenced_size; snd_pcm_sync_id_t sync; /* hardware synchronization ID */ /* -- mmap -- */ volatile struct snd_pcm_mmap_status *status; volatile struct snd_pcm_mmap_control *control; atomic_t mmap_count; /* -- locking / scheduling -- */ spinlock_t lock; wait_queue_head_t sleep; struct timer_list tick_timer; struct fasync_struct *fasync; /* -- private section -- */ void *private_data; void (*private_free)(struct snd_pcm_runtime *runtime); /* -- hardware description -- */ struct snd_pcm_hardware hw; struct snd_pcm_hw_constraints hw_constraints; /* -- interrupt callbacks -- */ void (*transfer_ack_begin)(struct snd_pcm_substream *substream); void (*transfer_ack_end)(struct snd_pcm_substream *substream); /* -- timer -- */ unsigned int timer_resolution; /* timer resolution */ /* -- DMA -- */ unsigned char *dma_area; /* DMA area */ dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ size_t dma_bytes; /* size of DMA area */ struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; #endif };
For the operators (callbacks) of each sound driver, most of
these records are supposed to be read-only. Only the PCM
middle-layer changes / updates them. The exceptions are
the hardware description (hw), interrupt callbacks
(transfer_ack_xxx), DMA buffer information, and the private
data. Besides, if you use the standard buffer allocation
method via snd_pcm_lib_malloc_pages()
,
you don't need to set the DMA buffer information by yourself.
In the sections below, important records are explained.
The hardware descriptor (struct snd_pcm_hardware)
contains the definitions of the fundamental hardware
configuration. Above all, you'll need to define this in
the open callback.
Note that the runtime instance holds the copy of the
descriptor, not the pointer to the existing descriptor. That
is, in the open callback, you can modify the copied descriptor
(runtime->hw
) as you need. For example, if the maximum
number of channels is 1 only on some chip models, you can
still use the same hardware descriptor and change the
channels_max later:
struct snd_pcm_runtime *runtime = substream->runtime; ... runtime->hw = snd_mychip_playback_hw; /* common definition */ if (chip->model == VERY_OLD_ONE) runtime->hw.channels_max = 1;
Typically, you'll have a hardware descriptor as below:
static struct snd_pcm_hardware snd_mychip_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 4096, .period_bytes_max = 32768, .periods_min = 1, .periods_max = 1024, };
The info
field contains the type and
capabilities of this pcm. The bit flags are defined in
<sound/asound.h>
as
SNDRV_PCM_INFO_XXX
. Here, at least, you
have to specify whether the mmap is supported and which
interleaved format is supported.
When the is supported, add the
SNDRV_PCM_INFO_MMAP
flag here. When the
hardware supports the interleaved or the non-interleaved
formats, SNDRV_PCM_INFO_INTERLEAVED
or
SNDRV_PCM_INFO_NONINTERLEAVED
flag must
be set, respectively. If both are supported, you can set both,
too.
In the above example, MMAP_VALID
and
BLOCK_TRANSFER
are specified for the OSS mmap
mode. Usually both are set. Of course,
MMAP_VALID
is set only if the mmap is
really supported.
The other possible flags are
SNDRV_PCM_INFO_PAUSE
and
SNDRV_PCM_INFO_RESUME
. The
PAUSE
bit means that the pcm supports the
“pause” operation, while the
RESUME
bit means that the pcm supports
the full “suspend/resume” operation.
If the PAUSE
flag is set,
the trigger
callback below
must handle the corresponding (pause push/release) commands.
The suspend/resume trigger commands can be defined even without
the RESUME
flag. See
Power Management section for details.
When the PCM substreams can be synchronized (typically,
synchronized start/stop of a playback and a capture streams),
you can give SNDRV_PCM_INFO_SYNC_START
,
too. In this case, you'll need to check the linked-list of
PCM substreams in the trigger callback. This will be
described in the later section.
formats
field contains the bit-flags
of supported formats (SNDRV_PCM_FMTBIT_XXX
).
If the hardware supports more than one format, give all or'ed
bits. In the example above, the signed 16bit little-endian
format is specified.
rates
field contains the bit-flags of
supported rates (SNDRV_PCM_RATE_XXX
).
When the chip supports continuous rates, pass
CONTINUOUS
bit additionally.
The pre-defined rate bits are provided only for typical
rates. If your chip supports unconventional rates, you need to add
the KNOT
bit and set up the hardware
constraint manually (explained later).
rate_min
and
rate_max
define the minimum and
maximum sample rate. This should correspond somehow to
rates
bits.
channel_min
and
channel_max
define, as you might already expected, the minimum and maximum
number of channels.
buffer_bytes_max
defines the
maximum buffer size in bytes. There is no
buffer_bytes_min
field, since
it can be calculated from the minimum period size and the
minimum number of periods.
Meanwhile, period_bytes_min
and
define the minimum and maximum size of the period in bytes.
periods_max
and
periods_min
define the maximum and
minimum number of periods in the buffer.
The “period” is a term that corresponds to a fragment in the OSS world. The period defines the size at which a PCM interrupt is generated. This size strongly depends on the hardware. Generally, the smaller period size will give you more interrupts, that is, more controls. In the case of capture, this size defines the input latency. On the other hand, the whole buffer size defines the output latency for the playback direction.
There is also a field fifo_size
.
This specifies the size of the hardware FIFO, but currently it
is neither used in the driver nor in the alsa-lib. So, you
can ignore this field.
Ok, let's go back again to the PCM runtime records.
The most frequently referred records in the runtime instance are
the PCM configurations.
The PCM configurations are stored in the runtime instance
after the application sends hw_params data via
alsa-lib. There are many fields copied from hw_params and
sw_params structs. For example,
format
holds the format type
chosen by the application. This field contains the enum value
SNDRV_PCM_FORMAT_XXX
.
One thing to be noted is that the configured buffer and period
sizes are stored in “frames” in the runtime.
In the ALSA world, 1 frame = channels * samples-size.
For conversion between frames and bytes, you can use the
frames_to_bytes()
and
bytes_to_frames()
helper functions.
period_bytes = frames_to_bytes(runtime, runtime->period_size);
Also, many software parameters (sw_params) are stored in frames, too. Please check the type of the field. snd_pcm_uframes_t is for the frames as unsigned integer while snd_pcm_sframes_t is for the frames as signed integer.
The DMA buffer is defined by the following four fields,
dma_area
,
dma_addr
,
dma_bytes
and
dma_private
.
The dma_area
holds the buffer
pointer (the logical address). You can call
memcpy
from/to
this pointer. Meanwhile, dma_addr
holds the physical address of the buffer. This field is
specified only when the buffer is a linear buffer.
dma_bytes
holds the size of buffer
in bytes. dma_private
is used for
the ALSA DMA allocator.
If you use a standard ALSA function,
snd_pcm_lib_malloc_pages()
, for
allocating the buffer, these fields are set by the ALSA middle
layer, and you should not change them by
yourself. You can read them but not write them.
On the other hand, if you want to allocate the buffer by
yourself, you'll need to manage it in hw_params callback.
At least, dma_bytes
is mandatory.
dma_area
is necessary when the
buffer is mmapped. If your driver doesn't support mmap, this
field is not necessary. dma_addr
is also optional. You can use
dma_private
as you like, too.
The running status can be referred via runtime->status
.
This is the pointer to the struct snd_pcm_mmap_status
record. For example, you can get the current DMA hardware
pointer via runtime->status->hw_ptr
.
The DMA application pointer can be referred via
runtime->control
, which points to the
struct snd_pcm_mmap_control record.
However, accessing directly to this value is not recommended.
You can allocate a record for the substream and store it in
runtime->private_data
. Usually, this
is done in
the open callback.
Don't mix this with pcm->private_data
.
The pcm->private_data
usually points to the
chip instance assigned statically at the creation of PCM, while the
runtime->private_data
points to a dynamic
data structure created at the PCM open callback.
static int snd_xxx_open(struct snd_pcm_substream *substream) { struct my_pcm_data *data; .... data = kmalloc(sizeof(*data), GFP_KERNEL); substream->runtime->private_data = data; .... }
The allocated object must be released in the close callback.