The information that a user enters into the kernel
configuration file is processed and passed to the kernel as
configuration resources. This information is parsed by the bus
configuration code and transformed into a value of structure
device_t and the bus resources associated with it. The drivers
may access the configuration resources directly using
functions resource_*
for more complex cases of
configuration. However, generally this is neither needed nor recommended,
so this issue is not discussed further here.
The bus resources are associated with each device. They are identified by type and number within the type. For the ISA bus the following types are defined:
SYS_RES_IRQ - interrupt number
SYS_RES_DRQ - ISA DMA channel number
SYS_RES_MEMORY - range of device memory mapped into the system memory space
SYS_RES_IOPORT - range of device I/O registers
The enumeration within types starts from 0, so if a device
has two memory regions it would have resources of type
SYS_RES_MEMORY
numbered 0 and 1. The resource type has
nothing to do with the C language type, all the resource
values have the C language type unsigned long
and must be
cast as necessary. The resource numbers do not have to be
contiguous, although for ISA they normally would be. The
permitted resource numbers for ISA devices are:
IRQ: 0-1 DRQ: 0-1 MEMORY: 0-3 IOPORT: 0-7
All the resources are represented as ranges, with a start value and count. For IRQ and DRQ resources the count would normally be equal to 1. The values for memory refer to the physical addresses.
Three types of activities can be performed on resources:
set/get
allocate/release
activate/deactivate
Setting sets the range used by the resource. Allocation reserves the requested range that no other driver would be able to reserve it (and checking that no other driver reserved this range already). Activation makes the resource accessible to the driver by doing whatever is necessary for that (for example, for memory it would be mapping into the kernel virtual address space).
The functions to manipulate resources are:
int bus_set_resource(device_t dev, int type,
int rid, u_long start, u_long count)
Set a range for a resource. Returns 0 if successful,
error code otherwise. Normally, this function will
return an error only if one of type
,
rid
, start
or
count
has a value that falls out of the
permitted range.
dev - driver's device
type - type of resource, SYS_RES_*
rid - resource number (ID) within type
start, count - resource range
int bus_get_resource(device_t dev, int type,
int rid, u_long *startp, u_long *countp)
Get the range of resource. Returns 0 if successful, error code if the resource is not defined yet.
u_long bus_get_resource_start(device_t dev,
int type, int rid) u_long bus_get_resource_count (device_t
dev, int type, int rid)
Convenience functions to get only the start or count. Return 0 in case of error, so if the resource start has 0 among the legitimate values it would be impossible to tell if the value is 0 or an error occurred. Luckily, no ISA resources for add-on drivers may have a start value equal to 0.
void bus_delete_resource(device_t dev, int
type, int rid)
Delete a resource, make it undefined.
struct resource *
bus_alloc_resource(device_t dev, int type, int *rid,
u_long start, u_long end, u_long count, u_int
flags)
Allocate a resource as a range of count values not
allocated by anyone else, somewhere between start and
end. Alas, alignment is not supported. If the resource
was not set yet it is automatically created. The special
values of start 0 and end ~0 (all ones) means that the
fixed values previously set by
bus_set_resource()
must be used
instead: start and count as themselves and
end=(start+count), in this case if the resource was not
defined before then an error is returned. Although rid is
passed by reference it is not set anywhere by the resource
allocation code of the ISA bus. (The other buses may use a
different approach and modify it).
Flags are a bitmap, the flags interesting for the caller are:
RF_ACTIVE - causes the resource to be automatically activated after allocation.
RF_SHAREABLE - resource may be shared at the same time by multiple drivers.
RF_TIMESHARE - resource may be time-shared by multiple drivers, i.e., allocated at the same time by many but activated only by one at any given moment of time.
Returns 0 on error. The allocated values may be
obtained from the returned handle using methods
rhand_*()
.
int bus_release_resource(device_t dev, int
type, int rid, struct resource *r)
Release the resource, r is the handle returned by
bus_alloc_resource()
. Returns 0 on
success, error code otherwise.
int bus_activate_resource(device_t dev, int
type, int rid, struct resource *r)
int bus_deactivate_resource(device_t dev, int
type, int rid, struct resource *r)
Activate or deactivate resource. Return 0 on success,
error code otherwise. If the resource is time-shared and
currently activated by another driver then EBUSY
is
returned.
int bus_setup_intr(device_t dev, struct
resource *r, int flags, driver_intr_t *handler, void *arg,
void **cookiep)
int
bus_teardown_intr(device_t dev, struct resource *r, void
*cookie)
Associate or de-associate the interrupt handler with a device. Return 0 on success, error code otherwise.
r - the activated resource handler describing the IRQ
flags - the interrupt priority level, one of:
INTR_TYPE_TTY
- terminals and
other likewise character-type devices. To mask them
use spltty()
.
(INTR_TYPE_TTY |
INTR_TYPE_FAST)
- terminal type devices
with small input buffer, critical to the data loss on
input (such as the old-fashioned serial ports). To
mask them use spltty()
.
INTR_TYPE_BIO
- block-type
devices, except those on the CAM controllers. To mask
them use splbio()
.
INTR_TYPE_CAM
- CAM (Common
Access Method) bus controllers. To mask them use
splcam()
.
INTR_TYPE_NET
- network
interface controllers. To mask them use
splimp()
.
INTR_TYPE_MISC
-
miscellaneous devices. There is no other way to mask
them than by splhigh()
which
masks all interrupts.
When an interrupt handler executes all the other interrupts matching its priority level will be masked. The only exception is the MISC level for which no other interrupts are masked and which is not masked by any other interrupt.
handler - pointer to the handler
function, the type driver_intr_t is defined as void
driver_intr_t(void *)
arg - the argument passed to the handler to identify this particular device. It is cast from void* to any real type by the handler. The old convention for the ISA interrupt handlers was to use the unit number as argument, the new (recommended) convention is using a pointer to the device softc structure.
cookie[p] - the value received
from setup()
is used to identify the
handler when passed to
teardown()
A number of methods are defined to operate on the resource handlers (struct resource *). Those of interest to the device driver writers are:
u_long rman_get_start(r) u_long
rman_get_end(r)
Get the start and end of
allocated resource range.
void *rman_get_virtual(r)
Get
the virtual address of activated memory resource.
All FreeBSD documents are available for download at https://download.freebsd.org/ftp/doc/
Questions that are not answered by the
documentation may be
sent to <freebsd-questions@FreeBSD.org>.
Send questions about this document to <freebsd-doc@FreeBSD.org>.