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, NULL };
- 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
- Name:
- 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 theB_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
or0xff
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()