The interface between the pcm
core
and the sound drivers is defined in terms of kernel objects.
There are two main interfaces that a sound driver will usually provide: CHANNEL and either MIXER or AC97.
The AC97 interface is a very small
hardware access (register read/write) interface, implemented by
drivers for hardware with an AC97 codec. In this case, the
actual MIXER interface is provided by the shared AC97 code in
pcm
.
Sound drivers usually have a private data structure to describe their device, and one structure for each play and record data channel that it supports.
For all CHANNEL interface functions, the first parameter is an opaque pointer.
The second parameter is a pointer to the private
channel data structure, except for
channel_init()
which has a pointer to
the private device structure (and returns the channel
pointer for further use by
pcm
).
For sound data transfers, the
pcm
core and the sound drivers
communicate through a shared memory area, described by a
struct
snd_dbuf
.
struct snd_dbuf
is
private to pcm
, and sound drivers
obtain values of interest by calls to accessor functions
(sndbuf_getxxx()
).
The shared memory area has a size of
sndbuf_getsize()
and is divided into
fixed size blocks of sndbuf_getblksz()
bytes.
When playing, the general transfer mechanism is as follows (reverse the idea for recording):
pcm
initially fills up the
buffer, then calls the sound driver's
xxxchannel_trigger()
function with a parameter of PCMTRIG_START.
The sound driver then arranges to repeatedly
transfer the whole memory area
(sndbuf_getbuf()
,
sndbuf_getsize()
) to the device, in
blocks of sndbuf_getblksz()
bytes.
It calls back the chn_intr()
pcm
function for each
transferred block (this will typically happen at
interrupt time).
chn_intr()
arranges to copy new
data to the area that was transferred to the device (now
free), and make appropriate updates to the snd_dbuf
structure.
xxxchannel_init()
is called to
initialize each of the play or record channels. The calls
are initiated from the sound driver attach routine. (See
the probe and attach
section).
static void * xxxchannel_init(kobj_t obj, void *data, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct xxx_info *sc = data; struct xxx_chinfo *ch; ... return ch; }
| |
The function should return a pointer to the private area used to control this channel. This will be passed as a parameter to other channel interface calls. |
xxxchannel_setformat()
should set
up the hardware for the specified channel for the specified
sound format.
static int xxxchannel_setformat(kobj_t obj, void *data, u_int32_t format) { struct xxx_chinfo *ch = data; ... return 0; }
xxxchannel_setspeed()
sets up the
channel hardware for the specified sampling speed, and
returns the possibly adjusted speed.
static int xxxchannel_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct xxx_chinfo *ch = data; ... return speed; }
xxxchannel_setblocksize()
sets the
block size, which is the size of unit transactions between
pcm
and the sound driver, and
between the sound driver and the device. Typically, this
would be the number of bytes transferred before an interrupt
occurs. During a transfer, the sound driver should call
pcm
's
chn_intr()
every time this size has
been transferred.
Most sound drivers only take note of the block size here, to be used when an actual transfer will be started.
static int xxxchannel_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct xxx_chinfo *ch = data; ... return blocksize; }
xxxchannel_trigger()
is called by
pcm
to control data transfer
operations in the driver.
static int xxxchannel_trigger(kobj_t obj, void *data, int go) { struct xxx_chinfo *ch = data; ... return 0; }
|
If the driver uses ISA DMA,
sndbuf_isadma()
should be called
before performing actions on the device, and will take
care of the DMA chip side of things.
xxxchannel_getptr()
returns the
current offset in the transfer buffer. This will typically
be called by chn_intr()
, and this is
how pcm
knows where it can transfer
new data.
xxxchannel_free()
is called to free
up channel resources, for example when the driver is
unloaded, and should be implemented if the channel data
structures are dynamically allocated or if
sndbuf_alloc()
was not used for buffer
allocation.
channel_reset()
,
channel_resetdone()
, and
channel_notify()
are for special
purposes and should not be implemented in a driver without
discussing it on the FreeBSD multimedia mailing list.
channel_setdir()
is
deprecated.
xxxmixer_init()
initializes the
hardware and tells pcm
what mixer
devices are available for playing and recording
static int xxxmixer_init(struct snd_mixer *m) { struct xxx_info *sc = mix_getdevinfo(m); u_int32_t v; [Initialize hardware] [Set appropriate bits in v for play mixers] mix_setdevs(m, v); [Set appropriate bits in v for record mixers] mix_setrecdevs(m, v) return 0; }
Set bits in an integer value and call
|
Mixer bits definitions can be found in
soundcard.h
(SOUND_MASK_XXX
values and
SOUND_MIXER_XXX
bit shifts).
xxxmixer_set()
sets the volume
level for one mixer device.
static int xxxmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct sc_info *sc = mix_getdevinfo(m); [set volume level] return left | (right << 8); }
The device is specified as a
The volume values are specified in range [0-100]. A value of zero should mute the device. | |
As the hardware levels probably will not match the input scale, and some rounding will occur, the routine returns the actual level values (in range 0-100) as shown. |
The AC97 interface is implemented by drivers with an AC97 codec. It only has three methods:
xxxac97_init()
returns the number
of ac97 codecs found.
ac97_read()
and
ac97_write()
read or write a
specified register.
The AC97 interface is used by the
AC97 code in pcm
to perform higher
level operations. Look at
sound/pci/maestro3.c
or many others under
sound/pci/
for an example.
All FreeBSD documents are available for download at https://download.freebsd.org/ftp/doc/
Questions that are not answered by the
documentation may be
sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.