Commit f90afe79 authored by Takashi Iwai's avatar Takashi Iwai

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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 5cb6b5fc
...@@ -3,8 +3,6 @@ Writing an ALSA Driver ...@@ -3,8 +3,6 @@ Writing an ALSA Driver
====================== ======================
:Author: Takashi Iwai <tiwai@suse.de> :Author: Takashi Iwai <tiwai@suse.de>
:Date: Oct 15, 2007
:Edition: 0.3.7
Preface Preface
======= =======
...@@ -21,11 +19,6 @@ explain the general topic of linux kernel coding and doesn't cover ...@@ -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 low-level driver implementation details. It only describes the standard
way to write a PCI sound driver on ALSA. 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, This document is still a draft version. Any feedback and corrections,
please!! please!!
...@@ -35,24 +28,7 @@ File Tree Structure ...@@ -35,24 +28,7 @@ File Tree Structure
General General
------- -------
The ALSA drivers are provided in two ways. The file tree structure of ALSA driver is depicted below.
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.
:: ::
...@@ -61,14 +37,11 @@ for “core” directory. It's named as “acore” in alsa-driver tree. ...@@ -61,14 +37,11 @@ for “core” directory. It's named as “acore” in alsa-driver tree.
/oss /oss
/seq /seq
/oss /oss
/instr
/ioctl32
/include /include
/drivers /drivers
/mpu401 /mpu401
/opl3 /opl3
/i2c /i2c
/l3
/synth /synth
/emux /emux
/pci /pci
...@@ -80,6 +53,7 @@ for “core” directory. It's named as “acore” in alsa-driver tree. ...@@ -80,6 +53,7 @@ for “core” directory. It's named as “acore” in alsa-driver tree.
/sparc /sparc
/usb /usb
/pcmcia /(cards) /pcmcia /(cards)
/soc
/oss /oss
...@@ -99,13 +73,6 @@ directory. The rawmidi OSS emulation is included in the ALSA rawmidi ...@@ -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 code since it's quite small. The sequencer code is stored in
``core/seq/oss`` directory (see `below <#core-seq-oss>`__). ``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 core/seq
~~~~~~~~ ~~~~~~~~
...@@ -119,11 +86,6 @@ core/seq/oss ...@@ -119,11 +86,6 @@ core/seq/oss
This contains the OSS sequencer emulation codes. This contains the OSS sequencer emulation codes.
core/seq/instr
~~~~~~~~~~~~~~
This directory contains the modules for the sequencer instrument layer.
include directory include directory
----------------- -----------------
...@@ -161,11 +123,6 @@ Although there is a standard i2c layer on Linux, ALSA has its own i2c ...@@ -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 code for some cards, because the soundcard needs only a simple operation
and the standard i2c API is too complicated for such a purpose. 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 synth directory
--------------- ---------------
...@@ -209,11 +166,19 @@ The PCMCIA, especially PCCard drivers will go here. CardBus drivers will ...@@ -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 be in the pci directory, because their API is identical to that of
standard PCI cards. 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 oss directory
------------- -------------
The OSS/Lite source files are stored here in Linux 2.6 (or later) tree. Here contains OSS/Lite codes.
In the ALSA driver tarball, this directory is empty, of course :) All codes have been deprecated except for dmasound on m68k as of
writing this.
Basic Flow for PCI Drivers Basic Flow for PCI Drivers
========================== ==========================
...@@ -352,10 +317,8 @@ to details explained in the following section. ...@@ -352,10 +317,8 @@ to details explained in the following section.
/* (3) */ /* (3) */
err = snd_mychip_create(card, pci, &chip); err = snd_mychip_create(card, pci, &chip);
if (err < 0) { if (err < 0)
snd_card_free(card); goto error;
return err;
}
/* (4) */ /* (4) */
strcpy(card->driver, "My Chip"); strcpy(card->driver, "My Chip");
...@@ -368,22 +331,23 @@ to details explained in the following section. ...@@ -368,22 +331,23 @@ to details explained in the following section.
/* (6) */ /* (6) */
err = snd_card_register(card); err = snd_card_register(card);
if (err < 0) { if (err < 0)
snd_card_free(card); goto error;
return err;
}
/* (7) */ /* (7) */
pci_set_drvdata(pci, card); pci_set_drvdata(pci, card);
dev++; dev++;
return 0; return 0;
error:
snd_card_free(card);
return err;
} }
/* destructor -- see the "Destructor" sub-section */ /* destructor -- see the "Destructor" sub-section */
static void snd_mychip_remove(struct pci_dev *pci) static void snd_mychip_remove(struct pci_dev *pci)
{ {
snd_card_free(pci_get_drvdata(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. ...@@ -445,14 +409,26 @@ In this part, the PCI resources are allocated.
struct mychip *chip; struct mychip *chip;
.... ....
err = snd_mychip_create(card, pci, &chip); err = snd_mychip_create(card, pci, &chip);
if (err < 0) { if (err < 0)
snd_card_free(card); goto error;
return err;
}
The details will be explained in the section `PCI Resource The details will be explained in the section `PCI Resource
Management`_. 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. 4) Set the driver ID and name strings.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -486,10 +462,8 @@ too. ...@@ -486,10 +462,8 @@ too.
:: ::
err = snd_card_register(card); err = snd_card_register(card);
if (err < 0) { if (err < 0)
snd_card_free(card); goto error;
return err;
}
Will be explained in the section `Management of Cards and Will be explained in the section `Management of Cards and
Components`_, too. Components`_, too.
...@@ -513,14 +487,13 @@ The destructor, remove callback, simply releases the card instance. Then ...@@ -513,14 +487,13 @@ The destructor, remove callback, simply releases the card instance. Then
the ALSA middle layer will release all the attached components the ALSA middle layer will release all the attached components
automatically. 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) static void snd_mychip_remove(struct pci_dev *pci)
{ {
snd_card_free(pci_get_drvdata(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 ...@@ -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. without module options don't need them.
In addition to these headers, you'll need ``<linux/interrupt.h>`` for 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 :c:func:`mdelay()` or :c:func:`udelay()` functions, you'll need
to include ``<linux/delay.h>`` too. to include ``<linux/delay.h>`` too.
...@@ -720,6 +693,13 @@ function, which will call the real destructor. ...@@ -720,6 +693,13 @@ function, which will call the real destructor.
where :c:func:`snd_mychip_free()` is 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 Registration and Release
------------------------ ------------------------
...@@ -905,10 +885,8 @@ Resource Allocation ...@@ -905,10 +885,8 @@ Resource Allocation
------------------- -------------------
The allocation of I/O ports and irqs is done via standard kernel 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 functions. These resources must be released in the destructor
these resources must be released in the destructor function (see below). 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.
Now assume that the PCI device has an I/O port with 8 bytes and an 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 interrupt. Then :c:type:`struct mychip <mychip>` will have the
...@@ -1064,7 +1042,8 @@ and the allocation would be like below: ...@@ -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); kfree(chip);
return err; return err;
} }
...@@ -1086,6 +1065,21 @@ and the corresponding destructor would be: ...@@ -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 PCI Entries
----------- -----------
...@@ -1154,13 +1148,6 @@ And at last, the module entries: ...@@ -1154,13 +1148,6 @@ And at last, the module entries:
Note that these module entries are tagged with ``__init`` and ``__exit`` Note that these module entries are tagged with ``__init`` and ``__exit``
prefixes. 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! That's all!
PCM Interface PCM Interface
...@@ -2113,6 +2100,16 @@ non-contiguous buffers. The mmap calls this callback to get the page ...@@ -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 address. Some examples will be explained in the later section `Buffer
and Memory Management`_, too. 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 PCM Interrupt Handler
--------------------- ---------------------
...@@ -2370,6 +2367,27 @@ to define the inverse rule: ...@@ -2370,6 +2367,27 @@ to define the inverse rule:
hw_rule_format_by_channels, NULL, hw_rule_format_by_channels, NULL,
SNDRV_PCM_HW_PARAM_CHANNELS, -1); 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 I won't give more details here, rather I would like to say, “Luke, use
the source.” the source.”
...@@ -3712,7 +3730,14 @@ example, for an intermediate buffer. Since the allocated pages are not ...@@ -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 contiguous, you need to set the ``page`` callback to obtain the physical
address at every offset. 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 ...@@ -3848,7 +3873,9 @@ Power Management
If the chip is supposed to work with suspend/resume functions, you need 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 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 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 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: ...@@ -3879,18 +3906,16 @@ the case of PCI drivers, the callbacks look like below:
:: ::
#ifdef CONFIG_PM static int __maybe_unused snd_my_suspend(struct device *dev)
static int snd_my_suspend(struct pci_dev *pci, pm_message_t state)
{ {
.... /* do things for suspend */ .... /* do things for suspend */
return 0; 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 */ .... /* do things for suspend */
return 0; return 0;
} }
#endif
The scheme of the real suspend job is as follows. The scheme of the real suspend job is as follows.
...@@ -3909,18 +3934,14 @@ 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. 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: 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) */ /* (1) */
struct snd_card *card = pci_get_drvdata(pci); struct snd_card *card = dev_get_drvdata(dev);
struct mychip *chip = card->private_data; struct mychip *chip = card->private_data;
/* (2) */ /* (2) */
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
...@@ -3932,9 +3953,6 @@ A typical code would be like: ...@@ -3932,9 +3953,6 @@ A typical code would be like:
snd_mychip_save_registers(chip); snd_mychip_save_registers(chip);
/* (6) */ /* (6) */
snd_mychip_stop_hardware(chip); snd_mychip_stop_hardware(chip);
/* (7) */
pci_disable_device(pci);
pci_save_state(pci);
return 0; return 0;
} }
...@@ -3943,44 +3961,35 @@ The scheme of the real resume job is as follows. ...@@ -3943,44 +3961,35 @@ The scheme of the real resume job is as follows.
1. Retrieve the card and the chip data. 1. Retrieve the card and the chip data.
2. Set up PCI. First, call :c:func:`pci_restore_state()`. Then 2. Re-initialize the chip.
enable the pci device again by calling
:c:func:`pci_enable_device()`. Call
:c:func:`pci_set_master()` if necessary, too.
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). 6. Call :c:func:`snd_power_change_state()` with
7. Call :c:func:`snd_power_change_state()` with
``SNDRV_CTL_POWER_D0`` to notify the processes. ``SNDRV_CTL_POWER_D0`` to notify the processes.
A typical code would be like: 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) */ /* (1) */
struct snd_card *card = pci_get_drvdata(pci); struct snd_card *card = dev_get_drvdata(dev);
struct mychip *chip = card->private_data; struct mychip *chip = card->private_data;
/* (2) */ /* (2) */
pci_restore_state(pci);
pci_enable_device(pci);
pci_set_master(pci);
/* (3) */
snd_mychip_reinit_chip(chip); snd_mychip_reinit_chip(chip);
/* (4) */ /* (3) */
snd_mychip_restore_registers(chip); snd_mychip_restore_registers(chip);
/* (5) */ /* (4) */
snd_ac97_resume(chip->ac97); snd_ac97_resume(chip->ac97);
/* (6) */ /* (5) */
snd_mychip_restart_chip(chip); snd_mychip_restart_chip(chip);
/* (7) */ /* (6) */
snd_power_change_state(card, SNDRV_CTL_POWER_D0); snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0; return 0;
} }
...@@ -4046,15 +4055,14 @@ And next, set suspend/resume callbacks to the pci_driver. ...@@ -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 = { static struct pci_driver driver = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
.id_table = snd_my_ids, .id_table = snd_my_ids,
.probe = snd_my_probe, .probe = snd_my_probe,
.remove = snd_my_remove, .remove = snd_my_remove,
#ifdef CONFIG_PM .driver.pm = &snd_my_pm_ops,
.suspend = snd_my_suspend,
.resume = snd_my_resume,
#endif
}; };
Module Parameters Module Parameters
...@@ -4078,7 +4086,7 @@ variables, instead. ``enable`` option is not always necessary in this ...@@ -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. case, but it would be better to have a dummy option for compatibility.
The module parameters must be declared with the standard 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. :c:func:`MODULE_PARM_DESC()` macros.
The typical coding would be like below: The typical coding would be like below:
...@@ -4094,15 +4102,14 @@ 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_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
Also, don't forget to define the module description, classes, license Also, don't forget to define the module description and the license.
and devices. Especially, the recent modprobe requires to define the Especially, the recent modprobe requires to define the
module license as GPL, etc., otherwise the system is shown as “tainted”. 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_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Vendor,My Chip Name}}");
How To Put Your Driver Into ALSA Tree 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 ...@@ -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 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 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 alsa-driver tree, ``sound/pci`` directory in the case of PCI
cards. Then the driver is evaluated, audited and tested by developers cards.
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``).
In the following sections, the driver code is supposed to be put into 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. single source file and one consisting of several source files.
Driver with A Single Source File 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 Suppose you have a file xyz.c. Add the following two lines
...@@ -4160,52 +4163,43 @@ Driver with A Single Source File ...@@ -4160,52 +4163,43 @@ Driver with A Single Source File
For the details of Kconfig script, refer to the kbuild documentation. 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 Drivers with Several Source Files
--------------------------------- ---------------------------------
Suppose that the driver snd-xyz have several source files. They are 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 1. Add a new directory (``sound/pci/xyz``) in ``sound/pci/Makefile``
below 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 snd-xyz-objs := xyz.o abc.o def.o
obj-$(CONFIG_SND_XYZ) += snd-xyz.o obj-$(CONFIG_SND_XYZ) += snd-xyz.o
include $(SND_TOPDIR)/Rules.make
3. Create the Kconfig entry 3. Create the Kconfig entry
This procedure is as same as in the last section. 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 Useful Functions
================ ================
:c:func:`snd_printk()` and friends :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. ALSA provides a verbose version of the :c:func:`printk()` function.
If a kernel config ``CONFIG_SND_VERBOSE_PRINTK`` is set, this 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 ...@@ -4221,13 +4215,10 @@ just like :c:func:`snd_printk()`. If the ALSA is compiled without
the debugging flag, it's ignored. the debugging flag, it's ignored.
:c:func:`snd_printdd()` is compiled in only when :c:func:`snd_printdd()` is compiled in only when
``CONFIG_SND_DEBUG_VERBOSE`` is set. Please note that ``CONFIG_SND_DEBUG_VERBOSE`` is set.
``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.
:c:func:`snd_BUG()` :c:func:`snd_BUG()`
------------------------ -------------------
It shows the ``BUG?`` message and stack trace as well as 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 :c:func:`snd_BUG_ON()` at the point. It's useful to show that a
...@@ -4236,7 +4227,7 @@ fatal error happens there. ...@@ -4236,7 +4227,7 @@ fatal error happens there.
When no debug flag is set, this macro is ignored. When no debug flag is set, this macro is ignored.
:c:func:`snd_BUG_ON()` :c:func:`snd_BUG_ON()`
---------------------------- ----------------------
:c:func:`snd_BUG_ON()` macro is similar with :c:func:`snd_BUG_ON()` macro is similar with
:c:func:`WARN_ON()` macro. For example, snd_BUG_ON(!pointer); or :c:func:`WARN_ON()` macro. For example, snd_BUG_ON(!pointer); or
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment