FreeBSD provides an object-oriented mechanism for requesting resources from a parent bus. Almost all devices will be a child member of some sort of bus (PCI, ISA, USB, SCSI, etc) and these devices need to acquire resources from their parent bus (such as memory segments, interrupt lines, or DMA channels).
To do anything particularly useful with a PCI device you
will need to obtain the Base Address
Registers (BARs) from the PCI Configuration space.
The PCI-specific details of obtaining the BAR are abstracted in
the bus_alloc_resource()
function.
For example, a typical driver might have something similar
to this in the attach()
function:
Handles for each base address register are kept in the softc structure so that they can be used to write to the device later.
These handles can then be used to read or write from the
device registers with the bus_space_*
functions. For example, a driver might contain a shorthand
function to read from a board specific register like this:
Similarly, one could write to the registers with:
These functions exist in 8bit, 16bit, and 32bit versions
and you should use
bus_space_{read|write}_{1|2|4}
accordingly.
In FreeBSD 7.0 and later, you can use the
bus_*
functions instead of
bus_space_*
. The
bus_*
functions take a struct
resource * pointer instead of a bus tag and handle.
Thus, you could drop the bus tag and bus handle members from
the softc and rewrite the
board_read()
function as:
Interrupts are allocated from the object-oriented bus code in a way similar to the memory resources. First an IRQ resource must be allocated from the parent bus, and then the interrupt handler must be set up to deal with this IRQ.
Again, a sample from a device
attach()
function says more than
words.
Some care must be taken in the detach routine of the
driver. You must quiesce the device's interrupt stream, and
remove the interrupt handler. Once
bus_teardown_intr()
has returned, you
know that your interrupt handler will no longer be called and
that all threads that might have been executing this interrupt handler
have returned. Since this function can sleep, you must not hold
any mutexes when calling this function.
This section is obsolete, and present only for historical
reasons. The proper methods for dealing with these issues is to
use the bus_space_dma*()
functions instead.
This paragraph can be removed when this section is updated to reflect
that usage. However, at the moment, the API is in a bit of
flux, so once that settles down, it would be good to update this
section to reflect that.
On the PC, peripherals that want to do bus-mastering DMA
must deal with physical addresses. This is a problem since
FreeBSD uses virtual memory and deals almost exclusively with
virtual addresses. Fortunately, there is a function,
vtophys()
to help.
The solution is a bit different on the alpha however, and
what we really want is a function called
vtobus()
.
This, and other documents, can be downloaded from http://ftp.FreeBSD.org/pub/FreeBSD/doc/
For questions about FreeBSD, read the
documentation before
contacting <questions@FreeBSD.org>.
For questions about this documentation, e-mail <doc@FreeBSD.org>.