Driver Development Presentation at WalterCon 2006

1. Device File System

Weakly typed directory hierarchy:

  • there is no /dev/eth0, instead, all network devices are in /dev/net/
  • all drivers in a subdirectory usually follow the same API, however this is not enforced
  • drivers can support multiple APIs by publishing more than one device
  • the name of the driver does not need to have any relation to where or what is published, even though you shouldn't abuse this to avoid confusion

For development:

  • use rescan <driver name> to make sure the system reloads your driver after a recompilation
  • for the rescanning to work as expected, the driver must be located in the drivers/bin/ directory - this is the only reason why it's usually not a good idea to put the driver into drivers/dev/
  • Rescanning will only work if no device published by the driver is currently in use

System boot in BeOS:

  • The boot loader needs to load all drivers needed to let the kernel access the boot file system
  • Only the drivers found in certain well known directories are loaded:
    • drivers/dev/
    • drivers/dev/disk/
  • All other drivers are loaded on demand: an application (for example, the media server) looks for devices in /dev/audio, and the devfs will then load all of the drivers it finds in that directory.

System boot in Haiku:

  • It's yet to be determined how exactly the boot loader will do its job
  • If possible, it will use hardware detection to identify which driver need to be loaded so that the kernel can boot further

2. Modules

  • BeOS supports 3 different types of kernel add-ons:
    • drivers
    • file systems
    • modules
  • Haiku's file systems are standard modules; BeOS file systems are not supported
  • Haiku's new device system uses modules only, too, but BeOS drivers are still supported for compatibility
  • There can be several exported modules per add-on:
    	module_info *modules[] = {
    		(module_info *)&scsi_for_sim_module,
    		(module_info *)&scsi_bus_module,
    		(module_info *)&scsi_device_module,
    		(module_info *)&scsi_bus_raw_module,
  • The module name is determined by its path - plus an additional arbitrary identifier:
    • Name: "bus_managers/scsi/bus/v1"
    • Path: bus_managers/scsi
    • Free Identifier: bus/v1
  • To use a module, you have to get it first:
    	device_manager_info *gManager;
    	status_t status = get_module(B_DEVICE_MANAGER_MODULE_NAME,
    		(module_info **)&gManager);
    	if (status < B_OK) {
  • The gManager variable points to the requested module if the above call was successful; the module's exported functions can be used
  • After usage, you have to put the module away again using put_module() so that the system knows that it may unload the module again to save memory
  • Haiku extension: Dependencies
    	locked_pool_interface *gLockedPool;
    	device_manager_info *gManager;
    	module_dependency module_dependencies[] = {
    		{B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&gManager},
    		{LOCKED_POOL_MODULE_NAME, (module_info **)&gLockedPool},
  • These dependencies are automatically resolved when loading the module - the referenced modules are directly available via the provided variables
  • They will be put away after your module has been uninitialized

3. Device Types

The sub directory determines the function and API of a device:

  • network drivers are in /dev/net
  • disk drivers are in /dev/disk/
  • audio drivers are in /dev/audio/; there are further sub directories which specify the exact function and API, for example old for the R3 style sound interface, or multi for the never finished multi audio interface
  • graphics drivers are in /dev/graphics/, TV and similar drivers can be found in /dev/video/

API? What API?

  • all devices export the same C API
  • the special type depending API is completely exported via the general purpose ioctl() call

ioctl() pitfalls

  • You have to make sure the passed in arguments are valid!
  • You have to make sure any pointers are valid - and stay valid during execution
  • There is no publicly documented way to do the above in BeOS; in Haiku, you have to use user_memcpy() to copy any incoming data before use in the kernel
  • Any application can send any control codes - make extra double sure you can't cause crashes this way
  • BeOS does not preserve the optional length argument when given

Network device drivers

  • Private definitions in ether_driver.h
  • The ETHER_INIT parameters can safely be ignored (they are always 0 for Haiku)
  • The BONE extensions ETHER_GETIFTYPE, and ETHER_GETLINKSTATE will be supported by Haiku as well
  • ETHER_HASIOVECS might not be supported - in that case, a replacement for this mechanism will be implemented (important for speedy Gigabit ethernet)
  • More or less good example implementations: rtl8169 for a Gigabit driver, sis900 for a 10/100MBit driver

Audio device drivers

  • Obsolete R3 definitions in sound.h - this is not supported by Haiku yet
  • R5's multi audio interface was never completely finished or documented (for example, recording never worked)
  • Haiku's current multi audio implementation is not completely compatible, and is therefore called hmulti_audio for the time being
  • Example for the old R3 API: sonic_vibes, usb_audio, sis7018
  • Example for Be's multi audio API: ich97 - this will likely be converted to the new Haiku API, though
  • Example for Haiku's current multi audio API would be: auich

Graphics device drivers (1/2)

  • Special breed because the interface is mostly free
  • The only call is B_GET_ACCELERANT_SIGNATURE to identify the accelerant to be used by the app_server
  • The real app_server API to be implemented is defined in add-ons/graphics/Accelerant.h
  • The interface as defined in GraphicsCard.h is deprecated and won't be supported in Haiku

Graphics device drivers (2/2)

  • Haiku will extend the existing API (in a back- and upwards compatible way), but it's not yet finalized. For example, 32 bit mouse cursors with transparency will be used in Haiku
  • Example implementation to look at: intel_extreme - the exception here is the B_PROPOSE_MODE hook which should be better be used from a different driver at the time of this writing

4. Interrupts

  • An interrupt will be enabled when you install a dedicated handler:
    	status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line,
    		&my_interrupt_handler, (void *)&info, 0);
  • When your driver is closed, you have to remove that handler again using remove_io_interrupt_handler()
  • You get the interrupt line either from the PCI device info structure, or (like PS/2, and other legacy devices) you have a documented fixed line that you have to use

Things to know about interrupt handlers (1/4)

  • You may need to share your interrupt line with another driver; the first thing you should do in that handler is to test that your hardware device generated the interrupt
  • Since a handler is called exclusively on one CPU, it should execute in very little time: the whole CPU is paused for other things while it's executing your code

Things to know about interrupt handlers (2/4)

  • The handler is called with interrupts turned off - no new interrupt can be generated while your handler is on it, at least that's the case for non-legacy edge-triggered interrupts
  • A direct consequence of the two items above: you must not wait in your driver

Things to know about interrupt handlers (3/4)

  • The only real API you can use while in a handler is:
    • release_sem_etc(..., B_DO_NOT_RESCHEDULE)
    • acquire_spinlock()/release_spinlock()
    • get_memory_map() can be currently used, but this may not be possible on all architectures, and may also be deprecated in the future
  • Timer handlers are very similar than interrupts in practice, and the same restrictions apply

Things to know about interrupt handlers (4/4)

  • Sometimes, your BIOS fails to assign an interrupt to your (PCI) device; this is a problem of BeOS that Haiku probably will overcome, but doesn't yet do so; if that happens the interrupt line will be 0x0 or 0xff

5. Haiku Specials

devfs extensions (not necessarily in R1):

  • driver will be able to publish attributes with their devices where, for example, the network MAC address can be shown
  • query support will be implemented as well
  • Also, device writers are encouraged to place unique (serial) identifiers into the attributes - this will allow querying for your particular USB printer (and its settings), no matter where you attached it

New device system (1/2):

  • A device tree is built after the hardware found in the computer
  • The function and device IDs can identify the driver to be used, the system can find the right driver faster
  • Therefore, the system knows which device belongs to which driver; if your sound card already has a driver, it's not needed to look for any other drivers if that's the only sound card in your computer

New device system (2/2):

  • No driver can mess with a device that already has a driver
  • Dynamic publishing and unpublishing of drivers at any time; it's not necessary to pre-publish any devices to make it available (for example, for USB mass storage devices you may put into your computer)
  • You know if your driver was called from within the kernel or from userland
  • Additional read()/write() calls that directly let you access ranges of physical memory

Extended stability and security:

  • New area flag B_USER_CLONEABLE_AREA prevents user code from cloning and inspecting kernel or driver data
  • Defined interface to access user memory:
    • user_memcpy()
    • user_strlcpy()
    • user_memset()