Writing an audio device driver for NetBSD

This tutorial is a work in progress.

Of course, this tutorial applies to a great extent to other kinds of device drivers for NetBSD.

Last modification: Sun Jun 10 14:58:06 CEST 2007

1. Background information

1.1 Pre-requisites

To be able to follow this document, and eventually write your own driver, there are a few things that you better already know, others that you will need to get familiar with (if not already), and some that you will need. The most obvious are:

1.2 The kernel

To ease things for everyone, it is better to work with the latest version of the kernel source tree. As a driver developer you will probably get access to the best version of the relevant API, plus more accurate support of the kernel development team if required. You will still be able to port your driver to your actual, stable kernel version if you want.

In NetBSD the most recent version of the sources tree is called "-current". You want to have a copy of the full repository somewhere on your system. You may choose to download them in /usr/src, with the permission to write and compile them. You can refer to the relevant section of the NetBSD guide to get detailed instructions about this process. The following should also work:

$ su -
Terminal type is xterm.
# mkdir /usr/src /usr/obj /usr/tools
# chgrp wsrc /usr/src /usr/obj /usr/tools
# chmod g+w /usr/src /usr/obj /usr/tools
# exit
$ cd /usr
$ cvs -z3 -d:pserver:anoncvs@anoncvs.netbsd.org:/cvsroot login
Logging in to :pserver:anoncvs@anoncvs.netbsd.org:2401/cvsroot
CVS password: an empty password should work, otherwise try "anoncvs"
$ cvs -z3 -d:pserver:anoncvs@anoncvs.netbsd.org:/cvsroot co src
This will take a while, I recommend fetching daily source tarballs on an FTP
mirror instead, and only from there update to the latest.
$ cd src
$ ./build.sh -O /usr/obj -T /usr/tools -U -u tools kernel=GENERIC
This will take a while too.
===> Kernels built from GENERIC:

At this point you will want to install this kernel somewhere you can boot it, with the possibility to boot a stable kernel in case things break.

If anything went wrong when compiling or using the -current kernel, you will want to consult the corresponding mailing-list archives: current-users@netbsd.org.

1.3 The device

When booting your new kernel you should notice the presence of your device. If you're unsure of this, run the following command:

$ /sbin/dmesg | grep 'not configured'
Aureal Semiconductor product 0x0002 (audio multimedia, revision 0xfe) at pci0
dev 12 function 0 not configured

As it is the case here, the kernel may not even know the name of your card (or even the vendor). The only thing that the system sees is a pair of two 16-bit integers, respectively called vendor and device ID. The kernel has its own internal list of known values, which can be found in many places on Internet.

The card on which this tutorial is based is an Aureal Vortex 3D sound card. It is unfortunately a very good example of why open source drivers are necessary: this vendor is dead. Needless to say, specifications were not released. Luckily in this case, some people have already reverse-engineered the binary drivers that were released for this card, and shared their results publicly.

This is a good moment to check through the documentation available for the device. The more advanced, low-level and detailed, the better. Something about registers, addresses, with lots of hexadecimal values is probably what you are looking for. Ideally, you obtained the actual instructions for the device driver implementation, in which case you can really consider yourself lucky.

Due to the nature of the device documented here, this tutorial will detail how to talk to the NetBSD audio sub-system as an example. Its API, and the other kernel APIs are documented in the section 9 of the manual.

2. The implementation

2.1 Register the device vendor and product ID

This part is really straight-forward: if not already, you want to give your device a better name than just 0xdead. The relevant list for PCI devices is maintained in the file src/sys/dev/pci/pcidevs. Its syntax should be self-explanatory: there is first a list of vendors, and then a list of products. As with Aureal your vendor may already be known, otherwise append a line like this in the section with all the vendors, preferably in ascending order of vendor ID, like so:

vendor AUREAL		0x12eb	Aureal Semiconductor

Repeat with the device name, in the product list:

/* Aureal Semiconductor */
product AUREAL AU8820		0x0001	AU8820 Vortex Digital Audio Processor
product AUREAL AU8830		0x0002	AU8830 Vortex 3D Digital Audio Processor
product AUREAL AU8810		0x0003	AU8810 Vortex Digital Audio Processor

You want to re-generate the corresponding database:

$ cd /usr/src
$ (cd sys/dev/pci && make -f Makefile.pcidevs)

2.2 Register the new driver

Very much like just done, a plain text file must be modified to tell the kernel build process that you are adding a driver. You are probably looking for src/sys/dev/pci/files.pci. The syntax used in this file reflects the autoconf device configuration framework used in NetBSD. Even if you do not know about it, you should be just fine imitating a similar entry for your device. Here is mine:

# Aureal Vortex Digital Audio Processor
device	vortex: audiobus, auconv, ac97
attach	vortex at pci
file	dev/pci/vortex.c		vortex

Hopefully you just passed one of the most difficult parts of the driver: finding a proper name. It is always possible to change it before the actual submission, be prepared that you may be asked to change it for a number of reasons.

Now your driver is in the build process, but it is only going to be built if you configure your kernel to. Supposing you are using the "GENERIC" kernel as a base, you need to add a line to its configuration file as well. For the i386 architecture (or "port") it is found in src/sys/arch/i386/conf/GENERIC, for other ports just replace "i386" with the name of the port you are using. You typically need to attach your driver to its bus:

vortex*	at pci? dev ? function ?	# Aureal Vortex Audio Processor

Now is also a good time to enable additional sanity checks in the kernel, by enabling (un-commenting out) the "DIAGNOSTIC" directive in the same file. Note that at this stage the kernel will not compile, because the driver file does not exist yet.