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.