Building Linux Device Drivers on FreeBSD

Linux has a large amount of device drivers for hardware not supported on FreeBSD, especially USB devices (see here for a related discussion). Not rarely, such drivers have been written based on information derived by protocol sniffing, reverse engineering and the like. This makes the code highly undocumented, and renders the porting effort extremely error prone.

To help with this task, I decided to start working on an emulation layer that would let us recompile the linux source code on FreeBSD, and provide a sufficiently complete emulation of the kernel APIs so that device drivers (or at least certain classes) could be used without modifications to their source code.

The methodology is not new - FreeBSD has always offered emulation of different APIs at the syscall level, and also some emulation of the Windows API is available for network device drivers. So I am just repplying the concept to another area which is currently lacking native support. My initial focus was on usb webcam drivers, and so this emulation layer contains enough to create a character driver using the services of the USB stack.

back to Luigi Rizzo's home

Current status (updated 18 Nov. 2008): The project has reached a usable form with three webcam drivers ('gspca', 'spca', 'ov') rebuilt under FreeBSD and working. What remains to do is some cleanup of the locking, and add support for BULK and other transfer types as the need arises. The most recent version of the code is now available as three Freebsd ports:

Note that the emulation code in linux-kmod-compat is rewritten from scratch and under a BSD license, should you need it.
The source for the linux drivers is usually distributed under GPL.

Trying the drivers

In order to try them you should do the following (as root):

DOCUMENTATION

Emulation strategy
This project is made of several components, not all of them needed in all drivers, nor all implemented so far. These are:
  1. Header files
    One thing that all linux drivers need is a suitable set of headers to provide all the definitions that are available in the original header files. Some of them are just empty placeholders, some are partial replicas of the corresponding linux headers, and some more are almost completely rewritten to remap linux structs and constants to FreeBSD types.

    The headers are under linux_compat/ in this archive,

  2. Generic kernel functions.
    There are some library functions that basically all kernel modules need. Some are already available on FreeBSD as libkern, others (e.g. printk and kmalloc etc.) are remapped using macros, some more are implemented by C functions.
  3. Specific kernel functions.
    These functions emulate the API of some kernel subsystems, e.g. mbufs, sockets, usb... They are implemented as the need arises, and the way to do it differs depending on the circumstances.
Emulation of kernel functions is trivial when there is a one-to-one mapping to FreeBSD calls. Otherwise, e.g. when data structures differ, it may be necessary to put small wrappers around the FreeBSD calls, doing the necessary massaging of data - copying, remapping values, etc.

Finally, some cases are even harder because the equivalence is not on single functions but on sets of them - in which case we need to record the sequence of calls done by the linux driver, and when we have enough information to perform the equivalent FreeBSD functions, issue a number of FreeBSD calls to perform the task, possibly storing the results and returning them to the linux driver a few at a time. We had to follow this approach when emulating the functions of the USB stack. The details are described in linux/usb.h and in the source file (at the moment, linux_compat.c)

Common porting problems
The problems in building a linux driver on FreeBSD are described in more detail in the README included in the tarball. However some of the problem you may encounter are:
  • missing header files. Try create an empty file to resolve the missing dependency and see if compilation goes further.
  • missing functions. You can detect this at build time, usually because of 'missing prototype' warnings from the compiler. Or, there is a chance that the warning will be ignored and you will find that the module fails to load reporting (in /var/log/messages) an unresolved symbol. The fix for this can be from trivial (just write the prototype and a stub for the function that calls the FreeBSD equivalent) to complex (e.g. if you need to emulate a subsystem such as the 'urb' in the usb stack).
  • excessive compiler warning. You can disable them by setting WERROR= (leave it empty) in the Makefile.kld. However i do not recommend doing that, because these warning are a precious help in finding out if something is wrong in the emulation code, or even in the original driver itself.
  • USB specific - if you get this error in /var/log/messages: usb1: *** WARNING: opening low/full speed device, this does not work yet. it is because you are attaching a USB1 device to a USB2 hub, and trying to do an ISOChronous transfer (cameras do that) which is something that the ehci.c driver (the driver, not the hardware) does not support.
    A workaround is to connect the device directly to the PC, so that the USB1 device talks to a USB1 controller and you avoid the ehci driver.
  • USB specific - I spent a lot of time debugging overruns in the isocronous transfers with web cameras. Turns out that most Linux video drivers work well with only two ISOC transfers, whereas the FreeBSD usb driver (at least uhci) seems to need at least 3 ISOC transfers to keep streaming. The fix is relatively easy, most drivers have a constant in their main headers to set the number of pending ISOC transfers, and you should be able to bump it up from 2 to 3 or higher.
back to Luigi Rizzo's home