Inside the Driver

The Linux kernel sees device drivers as either block or character
drivers. A block device hosts a filesystem; thus, the drivers for your
IDE disks and your tape drives are all block devices. Character
devices are accessed like files, and the character device driver
implements the system's I/O calls (open/close, read/write) on the
requested device. Drivers are compiled into the kernel or as modules
to be dynamically loaded into the kernel space. Once a module occupies
kernel space its services are available exactly as any other available
system function. Applications can then access those services by
reading and writing to the special files found in the
/dev directory (e.g. /dev/dsp or
/dev/audio).

Device drivers may be very simple or very complex. Writing a Linux
soundcard device driver is a relatively complex task, hence the need
for complete technical documentation and specifications from the card
manufacturer. Happily some manufacturers have taken an open attitude
about supplying that information to driver developers, and you can
view the source code for the resulting kernel sound modules in
/usr/src/linux/drivers/sound. Recommended modules for
study include sound_core.c (the top level handler for the
Linux sound system) and opl3sa.c (the driver for the
Yamaha YMF701B, also known as the OPL3-SA chipset).

Source Code

The source code for the OPL3-SA offers an example of a simple kernel
sound driver. The basic routines found in opl3sa.c include
detection/initialization, data read/write operations, and an
exit/cleanup function. The following abstract demonstrates the code's
adherence to the typical structure of a Linux character device driver:

True to form, we see the initialization, read/write, and detection
routines for the chipset, load/unload routines for the WSS (Windows
Sound System) and MPU (external MIDI interface) functions of the
chipset, and the mandatory exit/cleanup routine.

Interested readers should consult the complete OPL3-SA driver source code for a
more detailed understanding. See also the Resources
listings at the end of this article for guides to more information on
writing Linux device drivers. In particular, Alessandro Rubini and
Jonathan Corbet's book (Linux Device
Drivers) is highly recommended to anyone thinking about
writing a Linux driver.

Programming the Device: The OSS/Free and OSS/Linux APIs

As mentioned earlier, Linux applications do not normally access
hardware directly; thus, applications developers do not usually write
code to directly access and control a device such as a
soundcard. Instead, an application programming interface (API)
provides the developer with a hardware-independent set of I/O controls
over the device's services.

The OSS/Free API (at
/usr/src/linux/include/linux/soundcard.h) is the default
programming interface for the kernel sound modules. The OSS/Linux API
is an enhanced and expanded version of the OSS/Free programming
interface; it is normally found at
/usr/lib/oss/soundcard.h (the OSS/Linux default
installation path).

The following code fragment illustrates how to control a soundcard
by programming with the OSS/Free API:

Note that the device interface code is hardware-independent. It
does not need to know what particular soundcard you have: the API
provides a generalized control interface that lets the application
developer ignore the hardware specifics and write only to the device
file (/dev/dsp in this example). Manipulating the bits in
the hardware registers of the card is left to the driver for that
card. For instance, in the fragment above, we see no indication that
my soundcard is a SoundBlaster; however, my kernel sound driver is
indeed the SoundBlaster module, and when an audio service is requested
that module does its duty, translating the request into my
SoundBlaster's unique command set.

The OSS/Linux applications interface is an enhanced and expanded
version of OSS/Free. You can obtain the OSS/Linux API in
PDF format from 4Front's site, along with some examples of coding for
PCM audio, the soundcard mixer, and MIDI. The API covers a greater
range of cards, and an accordingly broader range of functions, but the
basic programming style is similar to OSS/Free.

Programming the Device: The ALSA API

The Advanced Linux Sound Architecture (ALSA) goes beyond the
capabilities of the OSS/Free API and is generally regarided as the
likely successor to OSS/Free as the kernel sound API. The ALSA driver
can be compiled with an OSS/Free emulation mode completely compatible
with the existing kernel sound API; however, given the truly advanced
nature of the ALSA driver, I urge developers to write their new audio
applications using ALSA in its native mode (i.e., leaving behind
OSS/Free legacy code). The next code fragment shows how to do the same
thing as the OSS/Free fragment, but in native ALSA mode.

As you can see, the ALSA API permits more robust control over the
device, yet retains the hardware independence necessary for a
generalized programming interface. ALSA offers many amenities to audio
developers, particularly for professional applications (such as
multitrack, hard disk recorders) that demand higher performance from
the entire system. Developers do not write directly against kernel
ioctls(), and higher level support is available for PCM
plugins and transparent network audio. Readers interested in the
details of the ALSA API should consult asoundlib.h in the
alsa-lib-x.x.x/include directory.

Andy wrote this code for ALSA 0.9.0beta, but it is likely to remain
valid for the 1.0 release. The stabilization of the API will be a
long-awaited achievement, one that promises a great future for the
development of serious and professional Linux audio
applications. There is widespread hope that the ALSA programming
interface will enter the kernel sources, eventually replacing OSS/Free
and providing the kernel with a more modern and more flexible sound
system.