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
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):
- build and install the ports mentioned above. In the directory for linux-kmod-compat
also build the pwcview program.
- run "kldload gspca.ko" (or the other modules you want) and look at /var/log/messages
for errors or warnings, e.g. unresolved symbols etc.
- plug in (or unplug and replug) your webcam and see if it is detected - if successful,
you should see an "ldev0: ... message in /var/log/messages" and /dev/video0 should
- To display images, just run "pwcview -s cif" (or use other formats e.g. sif, qcif,
vga if your camera supports them)
back to Luigi Rizzo's home
This project is made of several components, not all of them
needed in all drivers, nor all implemented so far. These are:
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.
- 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,
- 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.
- 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.
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
- 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.