[[!toc startlevel=2 levels=2]]
## Introduction
### Why was this tutorial created?
- Introductory-level documentation is scarce
- Writing device drivers is often considered black magic
- Reading the man pages won’t give you the big picture
- BSD systems are always in need of new drivers
- Device drivers are fun
### What won’t be covered here?
We don’t have much time, so several ~~advanced~~ topics were omitted:
- Interrupt handling
- Direct Memory Access and the bus\_dma framework
- Power management
- Driver detachment
- Drivers as kernel modules
- Examples for buses other than PCI
- Pretty much everything else...
However, once you finish this tutorial, you should be able to pursue
this knowledge yourself.
### What is a driver anyway?
- The interface between user space and hardware, implemented as a part
of the kernel
- The NetBSD drivers are written mostly in C
- Sometimes they have machine dependent assembler parts, but this is a
rare case
### What do you need to write a driver?
- C programming skills
- Hardware documentation (or the ability to reverse engineer the
hardware)
- A reference driver implementation will help but is not essential
- A NetBSD installation and kernel source, or a cross-build
environment (the latter is usually preferred for development of
drivers)
- A lot of time, coffee and patience
### Why is writing the device drivers considered difficult?
- It’s not as difficult as you may expect, in fact during this
tutorial we’ll prove that it’s quite easy
- You need to think on a very low level
- Good understanding of computer architecture is a must
- Often documentation is the main problem – writing the driver is not
possible if you don’t understand how the device works
- No access to documentation (uncooperative hardware vendors,
vendors out of business)
- Documentation is incomplete or plain wrong
- Reverse engineering can solve these problems but it’s a very
time consuming process
## The NetBSD driver model
### The NetBSD kernel basics
- NetBSD has a classic monolithic UNIX-like kernel - all drivers are
running in the same address space
- Thanks to the above, communication between drivers and other kernel
layers is simple
- However, it also means that one badly written driver can affect the
whole kernel
- Numerous in-kernel frameworks standardise the way drivers are
written (bus\_space, autoconf, etc.)
### The NetBSD source directory structure
- We’ll only cover parts interesting for a device driver programmer
- src/sys/
- kernel source directory
- src/sys/dev/
- machine-independent device drivers
- src/sys/arch/
- port-specific or architecture-specific parts (such as the
low-level system initialisation procedures or machine-dependent
drivers)
- src/sys/arch/$PORTNAME/conf/
- kernel configuration files for a given port
### Kernel autoconfiguration framework - autoconf(9)
- Autoconfiguration is the process of matching hardware devices with
an appropriate device driver
- The kernel message buffer (dmesg) contains information about
autoconfiguration of devices
- driver0 at bus0: Foo hardware
- Instance 0 of the driver has attached to instance 0 of the
particular bus
- Such messages often carry additional bus-specific information
about the exact location of the device (like the device and
function number on the PCI bus)
- driver0: some message
- Additional information about the driver state or device
configuration
### Autoconfiguration as seen in the dmesg
NetBSD 6.99.12 (GENERIC) #7: Fri Oct 5 18:43:21 CEST 2012
rkujawa@saiko.local:/Users/rkujawa/netbsd-eurobsdcon2012/src/sys/arch/cobalt/compile/obj/GENERIC
Cobalt Qube 2
total memory = 32768 KB
avail memory = 27380 KB
mainbus0 (root)
com0 at mainbus0 addr 0x1c800000 level 3: ns16550a, working fifo
com0: console
cpu0 at mainbus0: QED RM5200 CPU (0x28a0) Rev. 10.0 with built-in FPU Rev. 1.0
cpu0: 48 TLB entries, 256MB max page size
cpu0: 32KB/32B 2-way set-associative L1 instruction cache
cpu0: 32KB/32B 2-way set-associative write-back L1 data cache
mcclock0 at mainbus0 addr 0x10000070: mc146818 compatible time-of-day clock
panel0 at mainbus0 addr 0x1f000000
gt0 at mainbus0 addr 0x14000000
pci0 at gt0
pchb0 at pci0 dev 0 function 0: Galileo GT-64011 System Controller, rev 1
pcib0 at pci0 dev 9 function 0
pcib0: VIA Technologies VT82C586 PCI-ISA Bridge, rev 57
viaide0 at pci0 dev 9 function 1
viaide0: VIA Technologies VT82C586 (Apollo VP) ATA33 controller
viaide0: primary channel interrupting at irq 14
atabus0 at viaide0 channel 0
viaide0: secondary channel interrupting at irq 15
atabus1 at viaide0 channel 1
wd0 at atabus0 drive 0
wd0:
wd0: 750 MB, 1524 cyl, 16 head, 63 sec, 512 bytes/sect x 1536192 sectors
### Autoconfiguration as seen in the dmesg
![image](img_cobaltdevices.png)
### The bus\_space(9) framework
- “The goal of the bus\_space functions is to allow a single driver
source file to manipulate a set of devices on different system
architectures, and to allow a single driver object file to
manipulate a set of devices on multiple bus types on a single
architecture.”
- Provides a set of functions implementing common operations on the
bus like mapping, reading, writing, copying, etc.
- The bus\_space(9) is implemented at the machine-dependent level
(typically it’s a part of architecture-specific code), but all
implementations present the same interface
> At least they should, some functions are missing on less popular ports
### Machine independent drivers
- If possible drivers should work on any hardware platform
- High quality, machine-independent (MI) drivers are an important
factor that adds to NetBSD portability
- Some drivers are completely MI, some have MD or bus dependent
attachments and some are completely MD
- A driver for a typical PCI card will be completely MI
- A driver for the components of a SoC will usually be completely
MD
- The bus\_space abstraction helps to achieve portability,
transparently handling endianness issues and hiding bus
implementation details from the device driver
- Even if we have MI drivers, writing the drivers is always
significant part of effort needed to port NetBSD to new hardware
## Example driver from scratch
### Development environment
- Out of scope of this course, but very well documented
- Cross compiling is an easy task with the build.sh script
- Described in [Part V of the NetBSD
Guide](http://www.netbsd.org/docs/guide/en/part-compile.html)
- Check out the NetBSD sources
- $ build.sh -m cobalt tools
will build compiler, assembler, linker, etc. for cobalt port
- $ build.sh -m cobalt kernel=GENERIC
will build the GENERIC kernel for cobalt
- Call build.sh with a -u parameter to update (won’t rebuilding
everything)
- build.sh
is calling nbconfig and nbmake tools, no magic involved
### Quick introduction to GXemul
- A framework for full-system computer architecture emulation,
excellent for educational purposes
- Capable of emulating several real machines supported by NetBSD
- We’ll emulate a [Cobalt](http://en.wikipedia.org/wiki/Cobalt_Qube),
MIPS-based micro server with PCI bus
- I’ve modified GXemul and implemented an emulation of an additional
PCI device
- It will be used to show (almost) a real-life example of the driver
development process
### Our hardware - functional description
- Business applications often use arithmetic operations like addition
- Fake Cards Inc. responded to market needs and created a new product,
Advanced Addition Accelerator
- Pointy Haired Bosses will certainly buy it to accelerate their
business applications, so let’s create a driver for NetBSD!
### Our hardware - technical details
- Overview
- Implemented as a PCI device
- Arithmetic unit capable of addition of two numbers
- Four registers in the PCI memory space
> Only three of these registers are of any importance for us at this moment
- PCI configuration space
- Identified by the PCI vendor ID 0xfabc and product ID 0x0001
- Base Address Register 0x10 used to configure the engine address
- 4 x 32-bit registers = 16 bytes
- Other configuration registers irrelevant
### Our hardware - technical details (memory mapped register set)
- Advanced Addition Acceleration registers

Register Name

Offset

Description

COMMAND

0x4

Register used to issue commands to the engine

DATA

0x8

Register used to load data to internal engine registers

RESULT

0xC

Register used to store the result of arithmetic operation

- COMMAND register

Bit

R/W

Description

0

W

Execute ADD operation on values loaded into internal register A and B

1

R/W

Select internal register A for access through DATA register

2

R/W

Select internal register B for access through DATA register

- Selecting internal register A and B at the same time will lead to
undefined behaviour
### Our hardware - technical details (memory mapped register set)
- DATA register