1
0
Fork 0

ALSA: doc: Brush up the old writing-an-alsa-driver

Slightly brushing up and throw the old dust away from my ancient
writing-an-alsa-driver document.  The contents aren't changed so much
but the obsoleted parts are dropped.

Also, remove the date and the version number.  It's useless.

Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
hifive-unleashed-5.1
Takashi Iwai 2018-09-20 16:42:09 +02:00
parent 5cb6b5fc01
commit f90afe7955
1 changed files with 149 additions and 158 deletions

View File

@ -3,8 +3,6 @@ Writing an ALSA Driver
======================
:Author: Takashi Iwai <tiwai@suse.de>
:Date: Oct 15, 2007
:Edition: 0.3.7
Preface
=======
@ -21,11 +19,6 @@ explain the general topic of linux kernel coding and doesn't cover
low-level driver implementation details. It only describes the standard
way to write a PCI sound driver on ALSA.
If you are already familiar with the older ALSA ver.0.5.x API, you can
check the drivers such as ``sound/pci/es1938.c`` or
``sound/pci/maestro3.c`` which have also almost the same code-base in
the ALSA 0.5.x tree, so you can compare the differences.
This document is still a draft version. Any feedback and corrections,
please!!
@ -35,24 +28,7 @@ File Tree Structure
General
-------
The ALSA drivers are provided in two ways.
One is the trees provided as a tarball or via cvs from the ALSA's ftp
site, and another is the 2.6 (or later) Linux kernel tree. To
synchronize both, the ALSA driver tree is split into two different
trees: alsa-kernel and alsa-driver. The former contains purely the
source code for the Linux 2.6 (or later) tree. This tree is designed
only for compilation on 2.6 or later environment. The latter,
alsa-driver, contains many subtle files for compiling ALSA drivers
outside of the Linux kernel tree, wrapper functions for older 2.2 and
2.4 kernels, to adapt the latest kernel API, and additional drivers
which are still in development or in tests. The drivers in alsa-driver
tree will be moved to alsa-kernel (and eventually to the 2.6 kernel
tree) when they are finished and confirmed to work fine.
The file tree structure of ALSA driver is depicted below. Both
alsa-kernel and alsa-driver have almost the same file structure, except
for “core” directory. It's named as “acore” in alsa-driver tree.
The file tree structure of ALSA driver is depicted below.
::
@ -61,14 +37,11 @@ for “core” directory. It's named as “acore” in alsa-driver tree.
/oss
/seq
/oss
/instr
/ioctl32
/include
/drivers
/mpu401
/opl3
/i2c
/l3
/synth
/emux
/pci
@ -80,6 +53,7 @@ for “core” directory. It's named as “acore” in alsa-driver tree.
/sparc
/usb
/pcmcia /(cards)
/soc
/oss
@ -99,13 +73,6 @@ directory. The rawmidi OSS emulation is included in the ALSA rawmidi
code since it's quite small. The sequencer code is stored in
``core/seq/oss`` directory (see `below <#core-seq-oss>`__).
core/ioctl32
~~~~~~~~~~~~
This directory contains the 32bit-ioctl wrappers for 64bit architectures
such like x86-64, ppc64 and sparc64. For 32bit and alpha architectures,
these are not compiled.
core/seq
~~~~~~~~
@ -119,11 +86,6 @@ core/seq/oss
This contains the OSS sequencer emulation codes.
core/seq/instr
~~~~~~~~~~~~~~
This directory contains the modules for the sequencer instrument layer.
include directory
-----------------
@ -161,11 +123,6 @@ Although there is a standard i2c layer on Linux, ALSA has its own i2c
code for some cards, because the soundcard needs only a simple operation
and the standard i2c API is too complicated for such a purpose.
i2c/l3
~~~~~~
This is a sub-directory for ARM L3 i2c.
synth directory
---------------
@ -209,11 +166,19 @@ The PCMCIA, especially PCCard drivers will go here. CardBus drivers will
be in the pci directory, because their API is identical to that of
standard PCI cards.
soc directory
-------------
This directory contains the codes for ASoC (ALSA System on Chip)
layer including ASoC core, codec and machine drivers.
oss directory
-------------
The OSS/Lite source files are stored here in Linux 2.6 (or later) tree.
In the ALSA driver tarball, this directory is empty, of course :)
Here contains OSS/Lite codes.
All codes have been deprecated except for dmasound on m68k as of
writing this.
Basic Flow for PCI Drivers
==========================
@ -352,10 +317,8 @@ to details explained in the following section.
/* (3) */
err = snd_mychip_create(card, pci, &chip);
if (err < 0) {
snd_card_free(card);
return err;
}
if (err < 0)
goto error;
/* (4) */
strcpy(card->driver, "My Chip");
@ -368,22 +331,23 @@ to details explained in the following section.
/* (6) */
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
if (err < 0)
goto error;
/* (7) */
pci_set_drvdata(pci, card);
dev++;
return 0;
error:
snd_card_free(card);
return err;
}
/* destructor -- see the "Destructor" sub-section */
static void snd_mychip_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
@ -445,14 +409,26 @@ In this part, the PCI resources are allocated.
struct mychip *chip;
....
err = snd_mychip_create(card, pci, &chip);
if (err < 0) {
snd_card_free(card);
return err;
}
if (err < 0)
goto error;
The details will be explained in the section `PCI Resource
Management`_.
When something goes wrong, the probe function needs to deal with the
error. In this example, we have a single error handling path placed
at the end of the function.
::
error:
snd_card_free(card);
return err;
Since each component can be properly freed, the single
:c:func:`snd_card_free()` call should suffice in most cases.
4) Set the driver ID and name strings.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -486,10 +462,8 @@ too.
::
err = snd_card_register(card);
if (err < 0) {
snd_card_free(card);
return err;
}
if (err < 0)
goto error;
Will be explained in the section `Management of Cards and
Components`_, too.
@ -513,14 +487,13 @@ The destructor, remove callback, simply releases the card instance. Then
the ALSA middle layer will release all the attached components
automatically.
It would be typically like the following:
It would be typically just :c:func:`calling snd_card_free()`:
::
static void snd_mychip_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
@ -546,7 +519,7 @@ in the source file. If the code is split into several files, the files
without module options don't need them.
In addition to these headers, you'll need ``<linux/interrupt.h>`` for
interrupt handling, and ``<asm/io.h>`` for I/O access. If you use the
interrupt handling, and ``<linux/io.h>`` for I/O access. If you use the
:c:func:`mdelay()` or :c:func:`udelay()` functions, you'll need
to include ``<linux/delay.h>`` too.
@ -720,6 +693,13 @@ function, which will call the real destructor.
where :c:func:`snd_mychip_free()` is the real destructor.
The demerit of this method is the obviously more amount of codes.
The merit is, however, you can trigger the own callback at registering
and disconnecting the card via setting in snd_device_ops.
About the registering and disconnecting the card, see the subsections
below.
Registration and Release
------------------------
@ -905,10 +885,8 @@ Resource Allocation
-------------------
The allocation of I/O ports and irqs is done via standard kernel
functions. Unlike ALSA ver.0.5.x., there are no helpers for that. And
these resources must be released in the destructor function (see below).
Also, on ALSA 0.9.x, you don't need to allocate (pseudo-)DMA for PCI
like in ALSA 0.5.x.
functions. These resources must be released in the destructor
function (see below).
Now assume that the PCI device has an I/O port with 8 bytes and an
interrupt. Then :c:type:`struct mychip <mychip>` will have the
@ -1064,7 +1042,8 @@ and the allocation would be like below:
::
if ((err = pci_request_regions(pci, "My Chip")) < 0) {
err = pci_request_regions(pci, "My Chip");
if (err < 0) {
kfree(chip);
return err;
}
@ -1086,6 +1065,21 @@ and the corresponding destructor would be:
....
}
Of course, a modern way with :c:func:`pci_iomap()` will make things a
bit easier, too.
::
err = pci_request_regions(pci, "My Chip");
if (err < 0) {
kfree(chip);
return err;
}
chip->iobase_virt = pci_iomap(pci, 0, 0);
which is paired with :c:func:`pci_iounmap()` at destructor.
PCI Entries
-----------
@ -1154,13 +1148,6 @@ And at last, the module entries:
Note that these module entries are tagged with ``__init`` and ``__exit``
prefixes.
Oh, one thing was forgotten. If you have no exported symbols, you need
to declare it in 2.2 or 2.4 kernels (it's not necessary in 2.6 kernels).
::
EXPORT_NO_SYMBOLS;
That's all!
PCM Interface
@ -2113,6 +2100,16 @@ non-contiguous buffers. The mmap calls this callback to get the page
address. Some examples will be explained in the later section `Buffer
and Memory Management`_, too.
mmap calllback
~~~~~~~~~~~~~~
This is another optional callback for controlling mmap behavior.
Once when defined, PCM core calls this callback when a page is
memory-mapped instead of dealing via the standard helper.
If you need special handling (due to some architecture or
device-specific issues), implement everything here as you like.
PCM Interrupt Handler
---------------------
@ -2370,6 +2367,27 @@ to define the inverse rule:
hw_rule_format_by_channels, NULL,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
One typical usage of the hw constraints is to align the buffer size
with the period size. As default, ALSA PCM core doesn't enforce the
buffer size to be aligned with the period size. For example, it'd be
possible to have a combination like 256 period bytes with 999 buffer
bytes.
Many device chips, however, require the buffer to be a multiple of
periods. In such a case, call
:c:func:`snd_pcm_hw_constraint_integer()` for
``SNDRV_PCM_HW_PARAM_PERIODS``.
::
snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
This assures that the number of periods is integer, hence the buffer
size is aligned with the period size.
The hw constraint is a very much powerful mechanism to define the
preferred PCM configuration, and there are relevant helpers.
I won't give more details here, rather I would like to say, “Luke, use
the source.”
@ -3712,7 +3730,14 @@ example, for an intermediate buffer. Since the allocated pages are not
contiguous, you need to set the ``page`` callback to obtain the physical
address at every offset.
The implementation of ``page`` callback would be like this:
The easiest way to achieve it would be to use
:c:func:`snd_pcm_lib_alloc_vmalloc_buffer()` for allocating the buffer
via :c:func:`vmalloc()`, and set :c:func:`snd_pcm_sgbuf_ops_page()` to
the ``page`` callback. At release, you need to call
:c:func:`snd_pcm_lib_free_vmalloc_buffer()`.
If you want to implementation the ``page`` manually, it would be like
this:
::
@ -3848,7 +3873,9 @@ Power Management
If the chip is supposed to work with suspend/resume functions, you need
to add power-management code to the driver. The additional code for
power-management should be ifdef-ed with ``CONFIG_PM``.
power-management should be ifdef-ed with ``CONFIG_PM``, or annotated
with __maybe_unused attribute; otherwise the compiler will complain
you.
If the driver *fully* supports suspend/resume that is, the device can be
properly resumed to its state when suspend was called, you can set the
@ -3879,18 +3906,16 @@ the case of PCI drivers, the callbacks look like below:
::
#ifdef CONFIG_PM
static int snd_my_suspend(struct pci_dev *pci, pm_message_t state)
static int __maybe_unused snd_my_suspend(struct device *dev)
{
.... /* do things for suspend */
return 0;
}
static int snd_my_resume(struct pci_dev *pci)
static int __maybe_unused snd_my_resume(struct device *dev)
{
.... /* do things for suspend */
return 0;
}
#endif
The scheme of the real suspend job is as follows.
@ -3909,18 +3934,14 @@ The scheme of the real suspend job is as follows.
6. Stop the hardware if necessary.
7. Disable the PCI device by calling
:c:func:`pci_disable_device()`. Then, call
:c:func:`pci_save_state()` at last.
A typical code would be like:
::
static int mychip_suspend(struct pci_dev *pci, pm_message_t state)
static int __maybe_unused mychip_suspend(struct device *dev)
{
/* (1) */
struct snd_card *card = pci_get_drvdata(pci);
struct snd_card *card = dev_get_drvdata(dev);
struct mychip *chip = card->private_data;
/* (2) */
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
@ -3932,9 +3953,6 @@ A typical code would be like:
snd_mychip_save_registers(chip);
/* (6) */
snd_mychip_stop_hardware(chip);
/* (7) */
pci_disable_device(pci);
pci_save_state(pci);
return 0;
}
@ -3943,44 +3961,35 @@ The scheme of the real resume job is as follows.
1. Retrieve the card and the chip data.
2. Set up PCI. First, call :c:func:`pci_restore_state()`. Then
enable the pci device again by calling
:c:func:`pci_enable_device()`. Call
:c:func:`pci_set_master()` if necessary, too.
2. Re-initialize the chip.
3. Re-initialize the chip.
3. Restore the saved registers if necessary.
4. Restore the saved registers if necessary.
4. Resume the mixer, e.g. calling :c:func:`snd_ac97_resume()`.
5. Resume the mixer, e.g. calling :c:func:`snd_ac97_resume()`.
5. Restart the hardware (if any).
6. Restart the hardware (if any).
7. Call :c:func:`snd_power_change_state()` with
6. Call :c:func:`snd_power_change_state()` with
``SNDRV_CTL_POWER_D0`` to notify the processes.
A typical code would be like:
::
static int mychip_resume(struct pci_dev *pci)
static int __maybe_unused mychip_resume(struct pci_dev *pci)
{
/* (1) */
struct snd_card *card = pci_get_drvdata(pci);
struct snd_card *card = dev_get_drvdata(dev);
struct mychip *chip = card->private_data;
/* (2) */
pci_restore_state(pci);
pci_enable_device(pci);
pci_set_master(pci);
/* (3) */
snd_mychip_reinit_chip(chip);
/* (4) */
/* (3) */
snd_mychip_restore_registers(chip);
/* (5) */
/* (4) */
snd_ac97_resume(chip->ac97);
/* (6) */
/* (5) */
snd_mychip_restart_chip(chip);
/* (7) */
/* (6) */
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
@ -4046,15 +4055,14 @@ And next, set suspend/resume callbacks to the pci_driver.
::
static SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume);
static struct pci_driver driver = {
.name = KBUILD_MODNAME,
.id_table = snd_my_ids,
.probe = snd_my_probe,
.remove = snd_my_remove,
#ifdef CONFIG_PM
.suspend = snd_my_suspend,
.resume = snd_my_resume,
#endif
.driver.pm = &snd_my_pm_ops,
};
Module Parameters
@ -4078,7 +4086,7 @@ variables, instead. ``enable`` option is not always necessary in this
case, but it would be better to have a dummy option for compatibility.
The module parameters must be declared with the standard
``module_param()()``, ``module_param_array()()`` and
``module_param()``, ``module_param_array()`` and
:c:func:`MODULE_PARM_DESC()` macros.
The typical coding would be like below:
@ -4094,15 +4102,14 @@ The typical coding would be like below:
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
Also, don't forget to define the module description, classes, license
and devices. Especially, the recent modprobe requires to define the
Also, don't forget to define the module description and the license.
Especially, the recent modprobe requires to define the
module license as GPL, etc., otherwise the system is shown as “tainted”.
::
MODULE_DESCRIPTION("My Chip");
MODULE_DESCRIPTION("Sound driver for My Chip");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Vendor,My Chip Name}}");
How To Put Your Driver Into ALSA Tree
@ -4117,21 +4124,17 @@ a question now: how to put my own driver into the ALSA driver tree? Here
Suppose that you create a new PCI driver for the card “xyz”. The card
module name would be snd-xyz. The new driver is usually put into the
alsa-driver tree, ``alsa-driver/pci`` directory in the case of PCI
cards. Then the driver is evaluated, audited and tested by developers
and users. After a certain time, the driver will go to the alsa-kernel
tree (to the corresponding directory, such as ``alsa-kernel/pci``) and
eventually will be integrated into the Linux 2.6 tree (the directory
would be ``linux/sound/pci``).
alsa-driver tree, ``sound/pci`` directory in the case of PCI
cards.
In the following sections, the driver code is supposed to be put into
alsa-driver tree. The two cases are covered: a driver consisting of a
Linux kernel tree. The two cases are covered: a driver consisting of a
single source file and one consisting of several source files.
Driver with A Single Source File
--------------------------------
1. Modify alsa-driver/pci/Makefile
1. Modify sound/pci/Makefile
Suppose you have a file xyz.c. Add the following two lines
@ -4160,52 +4163,43 @@ Driver with A Single Source File
For the details of Kconfig script, refer to the kbuild documentation.
3. Run cvscompile script to re-generate the configure script and build
the whole stuff again.
Drivers with Several Source Files
---------------------------------
Suppose that the driver snd-xyz have several source files. They are
located in the new subdirectory, pci/xyz.
located in the new subdirectory, sound/pci/xyz.
1. Add a new directory (``xyz``) in ``alsa-driver/pci/Makefile`` as
below
1. Add a new directory (``sound/pci/xyz``) in ``sound/pci/Makefile``
as below
::
obj-$(CONFIG_SND) += xyz/
obj-$(CONFIG_SND) += sound/pci/xyz/
2. Under the directory ``xyz``, create a Makefile
2. Under the directory ``sound/pci/xyz``, create a Makefile
::
ifndef SND_TOPDIR
SND_TOPDIR=../..
endif
include $(SND_TOPDIR)/toplevel.config
include $(SND_TOPDIR)/Makefile.conf
snd-xyz-objs := xyz.o abc.o def.o
obj-$(CONFIG_SND_XYZ) += snd-xyz.o
include $(SND_TOPDIR)/Rules.make
3. Create the Kconfig entry
This procedure is as same as in the last section.
4. Run cvscompile script to re-generate the configure script and build
the whole stuff again.
Useful Functions
================
:c:func:`snd_printk()` and friends
---------------------------------------
----------------------------------
.. note:: This subsection describes a few helper functions for
decorating a bit more on the standard :c:func:`printk()` & co.
However, in general, the use of such helpers is no longer recommended.
If possible, try to stick with the standard functions like
:c:func:`dev_err()` or :c:func:`pr_err()`.
ALSA provides a verbose version of the :c:func:`printk()` function.
If a kernel config ``CONFIG_SND_VERBOSE_PRINTK`` is set, this function
@ -4221,13 +4215,10 @@ just like :c:func:`snd_printk()`. If the ALSA is compiled without
the debugging flag, it's ignored.
:c:func:`snd_printdd()` is compiled in only when
``CONFIG_SND_DEBUG_VERBOSE`` is set. Please note that
``CONFIG_SND_DEBUG_VERBOSE`` is not set as default even if you configure
the alsa-driver with ``--with-debug=full`` option. You need to give
explicitly ``--with-debug=detect`` option instead.
``CONFIG_SND_DEBUG_VERBOSE`` is set.
:c:func:`snd_BUG()`
------------------------
-------------------
It shows the ``BUG?`` message and stack trace as well as
:c:func:`snd_BUG_ON()` at the point. It's useful to show that a
@ -4236,7 +4227,7 @@ fatal error happens there.
When no debug flag is set, this macro is ignored.
:c:func:`snd_BUG_ON()`
----------------------------
----------------------
:c:func:`snd_BUG_ON()` macro is similar with
:c:func:`WARN_ON()` macro. For example, snd_BUG_ON(!pointer); or