6 VMEbus Device Driver Example
This chapter provides you with an opportunity to study a VMEbus device driver
called
/dev/dmaex.
Although this driver does not operate on a real device, you can use it
as the basis for writing your own working VMEbus device drivers.
The
/dev/dmaex
device driver is a simple direct memory access (DMA) interface that
uses the 32-bit VMEbus.
Table 6-1
lists the parts of the
/dev/dmaex
device driver and the sections of the chapter where each is described.
Table 6-1: Parts of the /dev/dmaex Device Driver
The source code uses the following convention:
#define IE 0001 [1]
-
Numbers appear after some line or lines of code in the
/dev/dmaex
device driver example.
Following the example, a corresponding number appears that contains an
explanation for the associated line or lines.
The source code does not contain any inline comments.
If you prefer to read the
/dev/dmaex
driver source code in its entirety with the inline comments, see
Appendix B.
[Return to example]
6.1 The dmaexreg.h Header File
The following
dmaexreg.h
file is the device register header file for the
/dev/dmaex
device driver.
It contains public declarations and the device register structure for
the device.
#define DMAEX_CSR_OFF 0 [1]
#define DMAEX_COUNT_OFF 1 [2]
#define DMAEX_ADDR_OFF 4 [3]
#define DMAEX_CSR_SIZE sizeof(char) [4]
#define DMAEX_COUNT_SIZE sizeof(short)
#define DMAEX_ADDR_SIZE sizeof(vme_addr_t)
#define IE 0001 [5]
#define DMA_GO 0002 [6]
#define RESET 0010 [7]
#define ERROR 0020 [8]
#define READ 0040 [9]
-
Defines a device register byte offset that specifies a 1-byte control status
register (CSR).
[Return to example]
-
Defines a device register byte offset that specifies a 16-bit byte count.
[Return to example]
-
Defines a device register byte offset that specifies the 32-bit VMEbus
transfer address.
[Return to example]
-
Defines a convenience constant for a call to the
sizeof
operator.
The next two lines define similar convenience constants.
The
/dev/dmaex
device driver passes these constants to a variety of kernel interfaces.
[Return to example]
-
Defines a constant that represents the interrupt enable bit.
Section 6.7
shows how the
dmaexstrategy
interface uses this bit to set up the device for transfer of data.
[Return to example]
-
Defines a constant that represents the start DMA bit.
Section 6.4
shows how the
dmaexprobe
interface uses this bit to reset the device.
[Return to example]
-
Defines a constant that represents the reset bit.
Section 6.4
shows how the
dmaexprobe
interface uses this bit to instruct the device to reset itself in
preparation for data transfer operations.
[Return to example]
-
Defines a constant that represents the error bit.
Section 6.4
shows how the
dmaexprobe
interface uses this bit to indicate that the device is broken.
[Return to example]
-
Defines a constant that represents the data-transfer-is-read/write bit.
Section 6.7
shows how the
dmaexstrategy
interface uses this bit to indicate a read operation.
[Return to example]
6.2 Include Files Section
The following code shows the Include Files Section for the
/dev/dmaex
device driver:
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/map.h>
#include <sys/buf.h>
#include <sys/vm.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/devio.h>
#include <hal/cpuconf.h>
#include <sys/exec.h>
#include <io/common/devdriver.h>
#include <io/common/handler.h>
#include <io/dec/vme/vbareg.h> [1]
#include <sys/sysconfig.h>
#include <io/dec/tc/tc.h>
#include <machine/cpu.h>
#include <io/dec/vme/dmaexreg.h> [2]
#include <dmaex.h> [3]
-
Includes the
/usr/sys/include/io/dec/vme/vbareg.h
file, which is specific to VMEbus device drivers.
This file contains definitions for the different VMEbus adapters.
For a summary description of this header file, see
Section A.2.
[Return to example]
-
Includes the device register header file, which contains the device
register offset definitions that describe the
DMAEX
device.
This device register header file is the one created for the
DMAEX
device.
Section 6.1
shows the device offset definitions for this device.
Writing Device Drivers: Reference
provides reference (man) page-style descriptions of the header files
most commonly used by Digital UNIX device drivers.
[Return to example]
-
Includes the
dmaex.h
file, which is the device driver header file that
config
creates for the
DMAEX
device.
The
dmaex.h
file contains
#define
statements for the number of
DMAEX
devices configured into the system.
See
Writing Device Drivers: Tutorial
for more information on the device driver header file.
[Return to example]
6.3 Declarations Section
The following code shows the Declarations Section for the
/dev/dmaex
device driver:
int dmaexprobe(), dmaexminphys, dmaexopen(), dmaexclose(),
dmaexread(), dmaexwrite(), dmaexstrategy(),
dmaexintr1(), dmaexintr2(); [1]
struct buf dmaexbuf[NDMAEX]; [2]
struct controller *dmaexinfo[NDMAEX]; [3]
io_handle_t reg[NDMAEX]; [4]
struct driver dmaexdriver = {
dmaexprobe,
0,
0,
0,
0,
0,
0,
0,
"dmaex",
dmaexinfo,
0,
0x8,
VME_A32_UDATA_D32,
0,
0,
0,
0
}; [5]
#define DMAEXOPEN 1
#define DMAEXCLOSE 0
#define EACCFAULT 200
#define ENOMAPREG 201
#define EBUFTOOBIG 202
struct dmaex_softc {
int sc_open;
int sc_error;
struct buf *bp;
dma_handle_t sc_dma_handle;
} dmaex_softc[NDMAEX]; [6]
#define NINTS_PER_DMAEX 2 [7]
ihandler_id_t *dmaex_id_t[NDMAEX][NINTS_PER_DMAEX]; [8]
int dmaex_int_wait=1; [9]
int dmaex_timeout(); [10]
extern void timeout();
extern void untimeout();
extern int hz; [11]
-
Declares the driver interfaces for the
/dev/dmaex
device driver.
[Return to example]
-
Declares an array of
buf
structures and calls it
dmaexbuf.
This array is
referenced by the driver's
dmaexread
and
dmaexwrite
interfaces, which are described in
Section 6.6.1
and
Section 6.6.2.
The
NDMAEX
constant represents the maximum number of
DMAEX
devices.
The
/dev/dmaex
driver uses this number to size the array of
buf
structures.
Thus, there is one
buf
structure per
DMAEX
device.
[Return to example]
-
Declares an array of pointers to
controller
structures and calls it
dmaexinfo.
This array
is referenced by the driver's
dmaexopen,
dmaexclose,
dmaexstrategy,
and
dmaexintr
interfaces, which are described in
Section 6.5.1,
Section 6.5.2,
Section 6.7,
and
Section 6.8.
Note the use of the
NDMAEX
constant to size the array.
[Return to example]
-
Declares an array of I/O handles and calls it
reg.
There is one I/O handle for each
DMAEX
device.
[Return to example]
-
Declares and initializes the
driver
structure and calls it
dmaexdriver.
The value zero (0) indicates that the
/dev/dmaex
driver does not make use of a specific member of the
driver
structure.
The following list describes those members initialized to a nonzero
value:
-
dmaexprobe,
the driver's
probe
interface.
-
dmaex,
the device name.
-
dmaexinfo,
which references the array of pointers to the previously declared
controller
structures.
You index this array with the controller number as specified in the
ctlr_num
member of the
controller
structure.
-
0x8,
which indicates the size (in bytes) of the first CSR area.
-
VME_A32_UDATA_D32,
which indicates the address space (A32) and data size
(D32) of the first CSR area.
[Return to example]
-
Allows the
/dev/dmaex
device driver's interfaces to share data.
The driver interfaces that reference this structure are
dmaexopen,
dmaexclose,
dmaexstrategy,
and
dmaexintr.
Again, note that the constant
NDMAEX
is used to size the array.
The following list describes the members contained in this structure:
-
sc_open
Stores the constant
DMAEXOPEN
or
DMAEXCLOSE
to indicate that the specified
DMAEX
device was opened or closed.
-
sc_error
Stores one of the error constants
EACCFAULT
(device access violation),
ENOMAPREG
(no mapping registers),
or
EBUFTOOBIG
(buffer too big).
-
bp
Stores a pointer to a
buf
structure.
-
sc_dma_handle
Stores the DMA handle that the
dma_map_alloc
and
dma_map_load
interfaces use.
[Return to example]
-
Defines a constant used to size the multidimensional array.
[Return to example]
-
Declares a two-dimensional array to store an opaque
ihandler_id_t
key, which is a unique number that identifies the interrupt service
interfaces to be acted on by subsequent calls to
handler_del
and
handler_enable.
Section 6.4
shows that the
dmaexprobe
interfaces passes these opaque keys to the
handler_del
and
handler_enable
interfaces.
[Return to example]
-
Declares and initializes a variable called
dmaex_int_wait.
The
/dev/dmaex
driver passes this variable in a call to the
timeout
interface.
[Return to example]
-
Declares an internal interface called
dmaex_timeout
that the
/dev/dmaex
driver uses to handle timeouts.
Section 6.9
describes the tasks that this interface performs.
The
/dev/dmaex
driver passes a pointer to this interface call to the
timeout
interface.
[Return to example]
-
Declares the global variable
hz
to store the number of clock ticks per second.
The
hz
global variable is typically used with the
timeout
kernel interface to schedule interfaces to be run at the time stored in
the variable.
[Return to example]
6.4 Autoconfiguration Support Section
The following code shows the Autoconfiguration Support Section for the
/dev/dmaex
device driver:
dmaexprobe(addr, ctlr)
io_handle_t addr; [1]
struct controller *ctlr; [2]
{
ihandler_t handler; [3]
struct vme_handler_info info; [4]
int unit; [5]
char csr; [6]
u_long phys_addr; [7]
caddr_t sva; [8]
unit = ctlr->ctlr_num; [9]
reg[unit] = addr; [10]
phys_addr = iohandle_to_phys(reg[unit] + DMAEX_CSR_OFF,
HANDLE_BYTE | HANDLE_SPARSE_SPACE); [11]
sva = (caddr_t)PHYS_TO_KSEG((vm_offset_t)phys_addr); [12]
if (BADADDR(sva, DMAEX_CSR_SIZE, ctlr))
return(0); [13]
write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE,
0, RESET); [14]
mb(); [15]
DELAY(10000); [16]
csr = (char) read_io_port (reg[unit] + DMAEX_CSR_OFF,
DMAEX_CSR_SIZE, 0); [17]
if (csr & ERROR)
return(0); [18]
write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE,
0, 0); [19]
mb(); [20]
info.gen_intr_info.configuration_st = (caddr_t) ctlr; [21]
info.gen_intr_info.intr = dmaexintr1; [22]
info.gen_intr_info.param = (caddr_t) unit; [23]
info.gen_intr_info.config_type = CONTROLLER_CONFIG_TYPE; [24]
info.vec = ctlr->ivnum; [25]
info.irq = ctlr->bus_priority; [26]
handler.ih_bus = ctlr->bus_hd; [27]
handler.ih_bus_info = (char *) &info; [28]
dmaex_id_t[unit][0] = handler_add (&handler); [29]
if (dmaex_id_t[unit][0] == (ihandler_id_t *) NULL) {
return (0); [30]
}
if (handler_enable (dmaex_id_t[unit][0]) != 0) { [31]
handler_del (dmaex_id_t[unit][0]);
return (0); [32]
}
info.gen_intr_info.intr = dmaexintr2; [33]
info.vec = ctlr->ivnum + 1; [34]
dmaex_id_t[unit][1] = handler_add (&handler); [35]
if (dmaex_id_t[unit][1] == (ihandler_id_t *) NULL)
return (0); [36]
if (handler_enable (dmaex_id_t[unit][1]) != 0) {
handler_del (dmaex_id_t[unit][1]);
return (0);
} [37]
return (1); [38]
}
-
Declares an I/O handle for the
DMAEX
device.
For the VMEbus, this I/O handle specifies
an I/O handle that you can use to reference a device register located in
the VMEbus address space.
The VMEbus configuration code passes this I/O handle to the device driver's
dmaexprobe
interface during device autoconfiguration.
You can perform standard C mathematical operations (addition and
subtraction only) on the I/O handle.
For example, you can add an offset to or subtract an offset from the
I/O handle.
This I/O handle corresponds to the first CSR address that you specified in
the system configuration file.
[Return to example]
-
Declares a pointer to the
controller
structure associated with this
DMAEX
device.
The
dmaexprobe
interface use the
ctlr_num
member of the
controller
structure pointer associated with a specific
DMAEX
device to obtain the device's unit number.
[Return to example]
-
Declares an
ihandler_t
data structure and calls it
handler.
The
ihandler_t
structure contains information associated with device driver interrupt
handling.
[Return to example]
-
Declares a
vme_handler_info
data structure and calls it
info.
The
vme_handler_info
structure contains interrupt handler information for device controllers
connected to a VMEbus.
This structure uses the generic
handler_intr_info
structure that contains all of the necessary information to add an
interrupt handler for any bus.
It also contains some VMEbus-specific members.
[Return to example]
-
Declares a
unit
variable to store the unit number for this
DMAEX
device.
The
dmaexprobe
interface obtains this unit number from the
ctlr_num
member of the
controller
structure pointer.
[Return to example]
-
Declares a variable called
csr
to store the return value from the
read_io_port
interface.
[Return to example]
-
Declares a variable called
phys_addr
to store the return value from the
iohandle_to_phys
interface.
[Return to example]
-
Declares a variable called
sva
to store the return value from the
PHYS_TO_KSEG
interface.
[Return to example]
-
Stores the controller number for this
DMAEX
device in the
unit
variable.
[Return to example]
-
Stores the I/O handle for this
DMAEX
device in the
reg
array.
The
example uses the
unit
variable as an index into the array of I/O handles for this
DMAEX
device.
[Return to example]
-
Calls the
iohandle_to_phys
interface to convert an I/O handle to a valid system physical address.
It is necessary to call this interface and
PHYS_TO_KSEG
to pass a system virtual address to the
BADADDR
interface.
For this version of Digital UNIX,
BADADDR
cannot use I/O handles.
The
iohandle_to_phys
interface takes two arguments:
-
The first argument specifies
an I/O handle that you can use to reference a device register or memory
located in bus address space (either I/O space or memory space).
This is the I/O handle to be translated into an appropriate address.
You can perform standard C mathematical operations (addition and
subtraction only) on the I/O handle.
For example, you can add an offset to or subtract an offset from the
I/O handle.
In this call,
dmaexprobe
passes the result of adding the I/O handle for this device with the
1-byte control/status register.
-
The second argument specifies
a flag that indicates the conversion type and data size.
This flag is the bitwise inclusive OR of a valid conversion type
value and a valid data size value.
In this call,
dmaexprobe
ORs the flags
HANDLE_BYTE
(converts the I/O handle to a byte)
and
HANDLE_SPARSE_SPACE
(converts the I/O handle to a sparse space address).
[Return to example]
-
Calls the
PHYS_TO_KSEG
interface to convert the physical address returned by
iohandle_to_phys
to a kernel-unmapped virtual address.
The
PHYS_TO_KSEG
interface takes one argument:
the physical address to convert to a kernel-unmapped virtual address.
In this call,
dmaexprobe
passes
phys_addr,
which contains the valid physical address associated with the I/O
handle.
[Return to example]
-
Calls the
BADADDR
interface to determine if the device is present.
The
BADADDR
interface takes three arguments.
-
The first argument specifies
the address of the device registers or memory.
In this call,
dmaexprobe
passes the kernel-unmapped virtual address returned by
PHYS_TO_KSEG
in the
sva
variable.
-
The second argument specifies
the length (in bytes) of the data to be checked.
Valid values are 1, 2, and 4 for device controllers connected to the
VMEbus.
In this call, the length is the value returned by the
sizeof
operator, in this case the number of bytes needed to contain a value of
type
char.
Note that the constant
DMAEX_CSR_SIZE
represents a call to the
sizeof
operator.
Section 6.1
shows the definition of this constant.
-
The third argument specifies
a pointer to a
bus_ctlr_common
structure.
You cast this argument as a pointer to either a
bus
or
controller
structure.
In this call, the example passes the
controller
structure pointer associated with this
DMAEX
device.
For VMEbus device drivers, you should always cast this argument as a
pointer to a
controller
structure.
If a device is not present,
dmaexprobe
returns the value zero (0).
[Return to example]
-
Calls the
write_io_port
interface to write a byte to the VMEbus.
The
write_io_port
interface takes four arguments:
-
The first argument specifies
an I/O handle that you can use to reference a device register or memory
located in bus address space (either I/O space or memory space).
This I/O handle references a device register in the bus address space
where the write operation occurs.
In this call, the example passes the result of the I/O handle stored in the
reg
array with the 1-byte control/status register.
-
The second argument specifies
the width (in bytes) of the data to be written.
Valid values are 1, 2, 3, 4, and 8.
Not all CPU platforms or bus adapters support all of these values.
In this call, the example passes the constant
DMAEX_CSR_SIZE,
which represents a call to the
sizeof
operator.
Section 6.1
shows the definition of this constant.
-
The third argument specifies
flags to indicate special processing requests.
Currently, no flags are used.
To indicate that no flags are used, the example passes the value zero
(0).
-
The fourth argument specifies
the data to be written to the specified device register in bus address
space.
In this call, the example passes the
RESET
bit.
This bit instructs the device to reset itself in preparation for data
transfer operations.
[Return to example]
-
Calls the
mb
interface to perform a memory barrier.
You call
mb
in a device driver under the following circumstances:
-
To force a barrier between load/store operations
-
After the CPU has prepared a data buffer in memory and before the device
driver
tries to perform a DMA out of the buffer
-
Before attempting to read any device CSRs after taking a device
interrupt
-
Between writes
[Return to example]
-
Calls the
DELAY
interface to delay the calling interface 10-milliseconds.
The
DELAY
interface takes one argument: the number of microseconds for the calling
process to spin.
In this call, the example passes the value
10000
to achieve a 10-millisecond delay.
[Return to example]
-
Calls the
read_io_port
interface to read a byte from the VMEbus.
The
read_io_port
interface takes three arguments:
-
The first argument specifies
an I/O handle that you can use to reference a device register or memory
located in bus address space (either I/O space or memory space).
This I/O handle references a device register in the bus address space
where the read operation originates.
In this call, the example passes the result of the I/O handle stored in the
reg
array with the 1-byte control/status register.
-
The second argument specifies
an I/O handle that you can use to reference a device register or memory
located in bus address space (either I/O space or memory space).
Valid values are 1, 2, 3, 4, and 8.
Not all CPU platforms or bus adapters support all of these values.
In this call, the example passes the constant
DMAEX_CSR_SIZE,
which represents a call to the
sizeof
operator.
Section 6.1
shows the definition of this constant.
-
The third argument specifies
flags to indicate special processing requests.
Currently, no flags are used.
To indicate that no flags are used, the example passes the value zero
(0).
[Return to example]
-
If the result of the bitwise AND operation produces a nonzero value
(that is, the error bit is set), then
dmaexprobe
returns the value zero (0) to the configuration code to indicate that the
device is not responding.
If the result of the bitwise AND operation produces a zero value (that
is the error bit is not set), then
dmaexprobe
calls the
write_io_port
interface to write a byte to the VMEbus.
[Return to example]
-
Calls the
write_io_port
interface to write a byte to the VMEbus.
The example passes the same values as in a previous call to
write_io_port
except for the fourth argument.
The fourth argument specifies
the data to be written to the specified device register in bus address
space.
In this call, the example passes the value zero (0).
[Return to example]
-
Calls the
mb
interface to perform a memory barrier.
[Return to example]
-
Sets the
configuration_st
member of the
info
data structure to the pointer to the
controller
structure associated with this
DMAEX
device.
This
controller
structure is the one for which an associated interrupt will be written.
This line also performs a type-casting operation that converts
ctlr
(which is of type pointer to a
controller
structure)
to be of type
caddr_t,
the type of the
configuration_st
member.
[Return to example]
-
Sets the
intr
member of the
info
data structure
to
dmaexintr1,
the
/dev/dmaex
device driver's first interrupt service interface.
[Return to example]
-
Sets the
param
member of the
info
data structure to the controller number for the
controller
structure associated with this
/dev/dmaex
device.
This line also performs a type-casting operation that converts
unit
(which is of type
int)
to be of type
caddr_t,
the type of the
param
member.
[Return to example]
-
Sets the
config_type
member of the
info
data structure to the constant
CONTROLLER_CONFIG_TYPE,
which identifies the
/dev/dmaex
driver type as a controller.
[Return to example]
-
Sets the
vec
member of the
info
data structure to the interrupt vector number for this
/dev/dmaex
device.
[Return to example]
-
Sets the
irq
member of the
info
data structure to the configured VMEbus priority level for this
/dev/dmaex
device.
[Return to example]
-
Registers the interrupt handlers by setting up the
interrupt handler structure.
This line specifies the bus that this controller is attached to.
The
bus_hd
member of the
controller
structure contains a pointer to the
bus
structure that this controller is connected to.
After the initialization,
the
ih_bus
member of the
ihandler_t
structure
contains the pointer to the
bus
structure associated with the
/dev/dmaex
device driver.
[Return to example]
-
Sets the
ih_bus_info
member of the
handler
data structure to the address of the bus-specific information structure,
info.
This line also performs a type-casting operation that converts
info
(which is of type
ihandler_t)
to be of type
char *,
the type of the
ih_bus_info
member.
[Return to example]
-
Calls the
handler_add
interface and saves its return value for use later by the
handler_del
interface.
The
handler_add
interface takes one argument:
a pointer to an
ihandler_t
data structure.
In this call, the example passes the address of the initialized
handler
structure.
Upon successful completion,
the
handler_add
interface returns an opaque
ihandler_id_t
key, which is a unique number that identifies the ISIs
to be acted on by subsequent calls to
handler_del,
handler_disable,
and
handler_enable.
To implement this
ihandler_id_t
key, each call to
handler_add
causes the
handler_key
data structure to be allocated.
The
handler_add
interface returns the value NULL if it cannot allocate the appropriate
resources or if it detected an error.
In this call, the example uses the array of
ihandler_id_t
structures to store the opaque
ihandler_id_t
keys.
[Return to example]
-
Returns the value zero (0) to indicate that
dmaexprobe
did not complete the probe operation if the value returned by
handler_add
is NULL.
[Return to example]
-
If
handler_enable
returns a nonzero value, the attempt to enable the previously registered
interrupt service interface,
dmaexintr1,
failed.
The
dmaexprobe
interface calls
handler_del
to deregister
dmaexintr1.
Both
the
handler_enable
and
handler_del
interfaces take one argument:
a pointer to the interrupt handler's entry in the interrupt table.
The
dmaexprobe
interface returns the value zero to indicate that the
/dev/dmaex
device driver did not complete the probe operation.
[Return to example]
-
Returns to the VMEbus configuration code a zero value to indicate
that the device is not present.
[Return to example]
-
Sets the
intr
member of the
info
data structure to
dmaexintr2,
the
/dev/dmaex
device driver's second interrupt service interface.
[Return to example]
-
Sets the
vec
member of the
info
data structure to the second interrupt vector number for this
/dev/dmaex
device.
Typically, multiple interrupt vectors for VMEbus controllers are
contiguous.
[Return to example]
-
Calls the
handler_add
interface and saves its return value for use later by the
handler_del
interface.
The
handler_add
interface takes one argument:
a pointer to an
ihandler_t
data structure.
As in the previous call, the example passes the address of the initialized
handler
structure.
[Return to example]
-
Returns the value zero (0) to indicate that the
/dev/dmaex
device driver did not complete the probe operation.
This occurs if
handler_add
returns the value NULL, indicating that it could not allocate the
appropriate resources or it detected an error.
[Return to example]
-
If
handler_enable
returns a nonzero value, the attempt to enable the previously registered
interrupt service interface,
dmaexintr2,
failed.
The
dmaexprobe
interface calls
handler_del
to deregister
dmaexintr1.
Both
the
handler_enable
and
handler_del
interfaces take one argument:
a pointer to the interrupt handler's entry in the interrupt table.
The
dmaexprobe
interface returns the value zero to indicate that the
/dev/dmaex
device driver did not complete the probe operation.
[Return to example]
-
Returns to the VMEbus configuration code a nonzero value to indicate
that the device is present.
[Return to example]
6.5 Open and Close Device Section
Table 6-2
lists the two interfaces implemented as part of the
Open and Close Device Section
along with the sections in the book where each is described.
Table 6-2: Interfaces Implemented as Part of the Open and Close Device Section
6.5.1 Implementing the dmaexopen Interface
The following code shows the implementation of the
dmaexopen
interface:
dmaexopen(dev, flag, format)
dev_t dev; [1]
int flag; [2]
int format; [3]
{
register int unit = minor(dev); [4]
register struct controller *ctlr; [5]
register struct dmaex_softc *sc; [6]
if ((unit >= NDMAEX ) || (unit <= 0))
return (EIO); [7]
sc = &dmaex_softc[unit]; [8]
if (sc == (struct dmaex_softc *)NULL)
return (ENODEV); [9]
if (sc->sc_open == DMAEXOPEN)
return (EBUSY); [10]
ctlr = dmaexinfo[unit]; [11]
if ((ctlr) && (ctlr->alive == ALV_ALIVE)) [12]
{
sc->sc_open = DMAEXOPEN;
return(0);
}
else return(ENXIO); [13]
}
-
Declares an argument that specifies the major and minor numbers for a
specific
DMAEX
device.
The minor device number is used to determine the logical unit number for
the
DMAEX
device that is to be opened.
[Return to example]
-
Declares an argument to contain flag bits from the
file
/usr/sys/include/sys/file.h.
These flags indicate whether the device is being opened for reading,
writing, or both.
The
dmaexopen
interface does not use this argument.
[Return to example]
-
Declares an argument that specifies the format of the special device to
be opened.
The
format
argument is used by a driver that has both block and character
interfaces and that uses the same
open
interface in both the
dsent
and
dsent
tables.
The driver uses this argument to distinguish the type of device being
opened.
The
dmaexopen
interface does not use this argument.
[Return to example]
-
Declares a
unit
variable and initializes it to the device minor number.
Note the use of the
minor
interface to obtain the device minor number.
The
minor
interface takes one argument: the number of the device for which an
associated device minor number will be obtained.
The minor number is encoded in the
dev
argument, which is of type
dev_t.
[Return to example]
-
Declares a pointer to the
controller
structure associated with this
DMAEX
device.
[Return to example]
-
Declares a pointer to the
dmaex_softc
structure associated with this
DMAEX
device.
Section 6.3
defines this structure.
[Return to example]
-
If the device minor number stored in
unit
is greater than or equal to the number of devices configured by the
system or the device minor number is less than zero (0), returns the error code
EIO,
which indicates an I/O error.
This error code is defined in
/usr/sys/include/sys/errno.h.
[Return to example]
-
Initializes
sc
to the address of the
dmaex_softc
structure associated with this
DMAEX
device.
The minor device number (stored in the
unit
variable)
is used as an index into the array of
dmaex_softc
structures to determine which
dmaex_softc
structure is associated with this
DMAEX
device.
[Return to example]
-
If the
sc
pointer equals NULL, returns the error code
ENODEV,
which indicates no such device.
This error code is defined in
/usr/sys/include/sys/errno.h.
[Return to example]
-
If the
sc_open
member of the
sc
pointer is equal to
DMAEXOPEN,
returns the error code
EBUSY,
which indicates that the
DMAEX
device has already been opened.
This error code is also defined in
/usr/sys/include/sys/errno.h.
[Return to example]
-
Sets the pointer to the
controller
structure to its associated
DMAEX
device.
Note that
unit,
which now contains this
DMAEX
device's minor device number,
is used as an index into the array of
controller
structures to obtain the
controller
structure associated with this
DMAEX
device.
[Return to example]
-
If
the
ctlr
pointer exists and the
alive
member of
ctlr
is equal to
ALV_ALIVE,
then the device exists.
If this is the case, the
dmaexopen
interface
sets the
sc_open
member of the
sc
pointer to the open bit
DMAEXOPEN
and returns the value zero (0) to indicate a successful open.
[Return to example]
-
If the device does not exist,
dmaexopen
returns the error code
ENXIO,
which indicates that the device does not exist.
This error code is defined in
/usr/sys/include/sys/errno.h.
[Return to example]
6.5.2 Implementing the dmaexclose Interface
The following code shows the implementation of the
dmaexclose
interface:
dmaexclose(dev, flag, format)
dev_t dev; [1]
int flag; [2]
int format; [3]
{
register int unit = minor(dev); [4]
register struct controller *ctlr; [5]
register struct dmaex_softc *sc; [6]
sc = &dmaex_softc[unit]; [7]
if (sc == (struct dmaex_softc *)NULL)
return (ENODEV); [8]
sc->sc_open = DMAEXCLOSE; [9]
ctlr = dmaexinfo[unit]; [10]
if (!(ctlr))
return (ENODEV); [11]
if (reg[unit] == (io_handle_t)0)
return (ENODEV); [12]
write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE,
0, 0); [13]
return(0); [14]
}
-
Declares an argument that specifies the major and minor numbers for a
specific
DMAEX
device.
The minor device number is used to determine the logical unit number for
the
DMAEX
device that is to be closed.
[Return to example]
-
Declares an argument to contain flag bits from the
file
/usr/sys/include/sys/file.h.
These flags indicate whether the device is being opened for reading,
writing, or both.
The
dmaexclose
interface does not use this argument.
[Return to example]
-
Declares an argument that specifies the format of the special device to
be closed.
The
format
argument is used by a driver that has both block and character
interfaces and that uses the same
close
interface in both the
dsent
and
dsent
tables.
The driver uses this argument to distinguish the type of device being
closed.
The
dmaexclose
interface does not use this argument.
[Return to example]
-
Declares a
unit
variable and initializes it to the device minor number.
Note the use of the
minor
interface to obtain the device minor number.
The
minor
interface takes one argument: the number of the device for which an
associated device minor number will be obtained.
The minor number is encoded in the
dev
argument, which is of type
dev_t.
[Return to example]
-
Declares a pointer to the
controller
structure associated with this
DMAEX
device.
[Return to example]
-
Declares a pointer to the
dmaex_softc
structure associated with this
DMAEX
device.
[Return to example]
-
Initializes
sc
to the address of the
dmaex_softc
structure associated with this
DMAEX
device.
The minor device number (stored in the
unit
variable)
is used as an index into the array of
dmaex_softc
structures to determine which
dmaex_softc
structure is associated with this
DMAEX
device.
[Return to example]
-
If the
sc
pointer equals NULL, returns the error code
ENODEV,
which indicates no such device.
This error code is defined in
/usr/sys/include/sys/errno.h.
[Return to example]
-
Turns off the open bit by setting the
sc_open
member of the
sc
pointer to the close bit
DMAEXCLOSE.
[Return to example]
-
Sets the pointer to the
controller
structure to its associated
DMAEX
device.
Note that
unit,
which now contains this
DMAEX
device's minor device number,
is used as an index into the array of
controller
structures to obtain the
controller
structure associated with this
DMAEX
device.
[Return to example]
-
If the
controller
structure pointer does not exist, returns the error code
ENODEV,
which indicates no such device.
This error code is defined in
/usr/sys/include/sys/errno.h.
[Return to example]
-
If the I/O handle stored in the
reg
array equals zero (0),
returns the error code
ENODEV,
which indicates no such device.
[Return to example]
-
Calls the
write_io_port
interface to write a byte to the VMEbus.
The
write_io_port
interface takes four arguments:
-
The first argument specifies
an I/O handle that you can use to reference a device register or memory
located in bus address space (either I/O space or memory space).
This I/O handle references a device register in the bus address space
where the write operation occurs.
In this call, the example passes the result of the I/O handle stored in the
reg
array and the 1-byte control/status register.
-
The second argument specifies
the width (in bytes) of the data to be written.
Valid values are 1, 2, 3, 4, and 8.
Not all CPU platforms or bus adapters support all of these values.
In this call, the example passes the constant
DMAEX_CSR_SIZE,
which represents a call to the
sizeof
operator.
Section 6.1
shows the definition of this constant.
-
The third argument specifies
flags to indicate special processing requests.
Currently, no flags are used.
To indicate that no flags are used, the example passes the value zero
(0).
-
The fourth argument specifies
the data to be written to the specified device register in bus address
space.
In this call, the example passes the value zero (0).
[Return to example]
-
Returns the value zero (0) to the
close
system call to indicate a successful close of the
DMAEX
device.
[Return to example]
6.6 Read and Write Device Section
Table 6-3
lists the two interfaces implemented as part of the Read and Write
Device Section for the
/dev/dmaex
device driver
along with the sections in the book where each is described.
Table 6-3: Interfaces Implemented as Part of the Read and Write Device Section
6.6.1 Implementing the dmaexread Interface
The following code shows the implementation of the
dmaexread
interface:
dmaexread(dev, uio, flag)
dev_t dev; [1]
struct uio *uio; [2]
int flag; [3]
{
register int unit = minor(dev); [4]
if ((unit >= NDMAEX ) || (unit < 0))
return (EIO); [5]
return (physio(dmaexstrategy, &dmaexbuf[unit], dev, B_READ,
dmaexminphys, uio)); [6]
}
-
Declares an argument that specifies the major and minor numbers for a
specific
DMAEX
device.
The minor device number is used to determine the logical unit
number for the device on which the read operation is performed.
[Return to example]
-
Declares a pointer to a
uio
structure.
This structure contains the information for transferring data to and
from the address space of the user's process.
You typically
pass this pointer unmodified to the
uiomove
or
physio
kernel interface.
This driver passes the pointer to
physio.
[Return to example]
-
Declares an argument that specifies the access mode of the device.
The access modes are represented by a bit mask of flags defined in
/usr/sys/include/sys/fcntl.h.
The
dmaexread
interface does not use this argument.
[Return to example]
-
Declares a
unit
variable and initializes it to the device minor number.
Note the use of the
minor
interface to obtain the device minor number.
The
minor
interface takes one argument: the number of the device for which an
associated device minor number will be obtained.
The minor number is encoded in the
dev
argument, which is of type
dev_t.
[Return to example]
-
If the device minor number is greater than the number of devices on the
system, returns the error constant
EIO
to indicate an I/O error.
[Return to example]
-
Calls the
physio
kernel interface to implement raw I/O.
The
physio
interface takes six arguments:
-
The first argument specifies a pointer to the driver's
strategy
interface.
In this call, the driver's
strategy
interface is
dmaexstrategy.
Section 6.7
discusses
dmaexstrategy.
-
The second argument specifies a pointer to a
buf
structure.
In this call, the
buf
structure is the one associated with this
DMAEX
device.
This structure contains information such as the binary status flags, the
major/minor device numbers, and the address of the associated buffer.
This buffer is always a special buffer header owned
exclusively by the device for handling I/O requests.
The minor device number (stored in the
unit
variable)
is used as an index into the array of
buf
structures to determine the buffer associated with this
DMAEX
device.
-
The third argument specifies the device number, which in this call is
contained in the
dev
argument.
-
The fourth argument specifies the read/write flag.
In this call, the read/write flag is the constant
B_READ.
This bit indicates this is a read operation.
-
The fifth argument specifies a pointer to a
minphys
interface.
A device driver can call the
minphys
kernel interface, which bounds the data transfer size.
You can also provide your own
minphys
interface.
In this call, the example passes a driver-specific
minphys
interface called
dmaexminphys.
-
The sixth argument specifies a pointer to a
uio
structure.
[Return to example]
6.6.2 Implementing the dmaexwrite Interface
The following code shows the implementation of the
dmaexwrite
interface:
dmaexwrite(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
register int unit = minor(dev);
if ((unit >= NDMAEX) || (unit <= 0))
return (EIO);
return (physio(dmaexstrategy, &dmaexbuf[unit], dev, B_WRITE,
dmaexminphys, uio)); [1]
}
-
The
dmaexwrite
interface
is almost identical to the
dmaexread
interface.
The only difference is that
dmaexwrite
uses the
B_WRITE
bit instead of the
B_READ
bit for the read/write flag to indicate that this is a write operation.
[Return to example]
6.6.3 Implementing the dmaexminphys Interface
The following code shows the implementation of the
dmaexminphys
interface:
dmaexminphys (bp)
register struct buf *bp; [1]
{
#define dmaexMAXPHYS (64*1024) [2]
if (bp->b_bcount > dmaexMAXPHYS)
bp->b_bcount = dmaexMAXPHYS;
return;
} [3]
-
Declares a pointer to the
structure associated with this
DMAEX
device.
[Return to example]
-
Defines the maximum size of the data for the DMA transfer operation.
[Return to example]
-
Defines the maximum size of the data for the DMA transfer operation for this
DMAEX
device.
The
b_bcount
member of the
buf
structure pointer specifies
the size of the requested transfer (in bytes).
This is the
buf
structure pointer associated with this
DMAEX
device.
After setting the maximum size of the data for the DMA transfer
operation,
dmaexminphys
returns to the
physio
interface.
This operation occurs until the DMA data transfer is completed.
[Return to example]
6.7 Strategy Section
The following code shows the Strategy Section for the
/dev/dmaex
device driver:
dmaexstrategy(bp)
struct buf *bp; [1]
{
register int unit = minor(bp->b_dev); [2]
register struct controller *ctlr = dmaexinfo[unit]; [3]
register struct dmaex_softc *sc = &dmaex_softc[unit]; [4]
short csr; [5]
sg_entry_t dmaex_sg; [6]
/***************************************************
* Check the validity of the unit number and the *
* pointers. *
***************************************************/
if ( (!ctlr) || (!sc) )
return (EINVAL); [7]
sc->bp = bp; [8]
sc->sc_dma_handle = (dma_handle_t) NULL; [9]
if (dma_map_load ((unsigned long) bp->b_bcount,
(vm_offset_t) bp->b_un.b_addr, bp->b_proc, ctlr,
&sc->sc_dma_handle, 0, VME_A32_UDATA_D32 | VME_BS_NOSWAP |
DMA_GUARD_UPPER | DMA_ALL) == 0) { [10]
bp->b_error = EIO; [11]
sc->sc_error = bp->b_error; [12]
bp->b_flags |= B_ERROR; [13]
iodone(bp); [14]
return; [15]
}
dmaex_sg = dma_get_curr_sgentry (sc->sc_dma_handle); [16]
write_io_port (reg[unit] + DMAEX_COUNT_OFF, DMAEX_COUNT_SIZE, 0,
dmaex_sg->bc); [17]
mb(); [18]
write_io_port (reg[unit] + DMAEX_ADDR_OFF, DMAEX_ADDR_SIZE, 0,
(long)dmaex_sg->ba); [19]
mb(); [20]
if (bp->b_flags & B_READ)
csr = READ | IE; [21]
else
csr = IE; [22]
write_io_port (reg[unit] + DMAEX_CSR_OFF,
DMAEX_CSR_SIZE, 0, (csr | DMA_GO)); [23]
mb(); [24]
timeout(dmaex_timeout, (caddr_t) unit,
dmaex_int_wait*hz); [25]
sleep(&dmaex_softc[unit], PRIBIO); [26]
dma_map_unload (DMA_DEALLOC, sc->sc_dma_handle); [27]
iodone(bp); [28]
return; [29]
}
-
Declares a pointer to a
buf
structure and calls it
bp.
The
dmaexstrategy
interface uses the following members of the
buf
structure:
b_dev,
b_addr,
b_bcount,
b_flags,
and
b_error.
[Return to example]
-
Declares a
unit
variable and initializes it to the device minor number.
The
minor
kernel interface is used to obtain the minor device number associated
with this
DMAEX
device.
The
minor
interface takes one argument: the number (expressed as a
dev_t
data type) of the device for which the
minor device needs to be obtained.
In this call, the device number is stored in the
b_dev
member of the
buf
structure pointer associated with this
DMAEX
device.
[Return to example]
-
Sets the pointer to the
controller
structure to its associated
DMAEX
device.
Note that
unit,
which now contains this
DMAEX
device's minor device number,
is used as an index into the array of
controller
structures to obtain the
controller
structure associated with this
DMAEX
device.
[Return to example]
-
Initializes
sc
to the address of the
dmaex_softc
structure associated with this
DMAEX
device.
The minor device number (stored in the
unit
variable)
is used as an index into the array of
dmaex_softc
structures to determine which
dmaex_softc
structure is associated with this
DMAEX
device.
[Return to example]
-
Declares a variable called
csr
to store read, write, and enable interrupts status information.
[Return to example]
-
Declares a pointer to an
sg_entry
structure and calls it
dmaex_sg.
The example uses this structure pointer in calls to
dma_get_curr_sgentry
and
write_io_port.
[Return to example]
-
Checks the validity of the unit number and the
sc
pointer.
If they are not valid, returns the error code
EINVAL,
which indicates invalid argument.
This error code is defined in
/usr/sys/include/sys/errno.h.
[Return to example]
-
Saves the pointer to the
buf
structure that this
DMAEX
device uses.
The pointer to the
dmaex_softc
structure contains as a member a pointer to a
buf
structure.
[Return to example]
-
Initializes the
sc_dma_handle
member of the
sc
pointer associated with this
DMAEX
device to NULL.
You must do this to ensure the proper operation of
dma_map_load
without previously calling
dma_map_alloc.
At this point, you can also call the
vba_set_dma_addr
interface.
See the comments associated with the
dmaexstrategy
interface in
Appendix B
for a discussion of how to call
vba_set_dma_addr
in this driver.
[Return to example]
-
Calls the
dma_map_load
interface to load and set the allocated system resources for DMA data
transfers.
The
dma_map_load
interface takes seven arguments:
-
The first argument specifies
the maximum size (in bytes) of the data to be transferred during the
DMA transfer operation.
The kernel uses this size to determine the resources (mapping registers,
I/O channels, and other software resources) to allocate.
In this call, the maximum size is the value stored in the
b_bcount
member of the
buf
structure pointer.
The
b_bcount
member specifies
the size of the requested transfer (in bytes).
-
The second argument specifies
the virtual address where the DMA transfer occurs.
The interface
uses this address with the pointer to the
proc
structure to obtain the physical addresses of the system memory pages to
load into DMA mapping resources.
In this call, the example passes the value stored in the
b_un.b_addr
member of the
buf
structure pointer.
The
b_un.b_addr
member specifies
the address at which to pull or push the data.
-
The third argument specifies
a pointer to the
proc
structure associated with the valid context for the virtual address
specified in
virt_addr.
The
interface uses this pointer to retrieve the
pmap
that is needed to translate this virtual address
to a physical address.
If
proc_p
is equal to zero (0), the address is a kernel address.
In this call, the example passes a
proc
structure pointer stored in the
b_proc
member of the
buf
structure pointer.
The
b_proc
member specifies
a pointer to the
proc
structure that represents the process performing the I/O.
-
The fourth argument specifies
a pointer to the
controller
structure associated with this controller.
The interface
uses this pointer to obtain the bus-specific interfaces and data
structures that it needs to allocate the necessary mapping resources.
In this call, the example passes the
controller
structure pointer associated with this
DMAEX
device.
-
The fifth argument specifies
a pointer to a handle to DMA resources associated with the mapping of
an in-memory I/O buffer onto a controller's I/O bus.
This handle provides the information to access bus address/byte count pairs.
A
bus address/byte count pair
is represented by the
ba
and
bc
members of an
sg_entry
structure pointer.
Device driver writers can view the DMA handle as the tag to the
allocated system resources needed to perform a DMA operation.
In this call, the example passes the DMA handle stored in the
sc_dma_handle
member of the driver's
softc
structure associated with this device.
-
The sixth argument specifies
the maximum-size byte-count value that should be stored in the
bc
members of the
sg_entry
structures.
This call passes the value zero (0) to the
max_byte_count
argument.
-
The seventh argument specifies
special conditions that the device driver needs the system to
perform.
You can pass the bitwise inclusive OR of the following
special condition bits defined in
/usr/sys/include/io/common/devdriver.h:
Value
|
Meaning
|
DMA_GUARD_UPPER
|
Allocates additional resources so that contiguous data overruns are
captured by the system map error functions.
This bit is probably most useful during device driver development and
debugging.
|
DMA_GUARD_LOWER
|
Allocates additional resources so that contiguous data underruns are
captured by the system map error functions.
This bit is probably most useful during device driver development and
debugging.
|
DMA_SLEEP
|
Puts
the process to sleep if the system cannot allocate the necessary
resources to perform a data transfer of size
byte_count
at the time the driver calls the interface.
|
DMA_ALL
|
Returns a nonzero value, only if the system can satisfy a DMA transfer of size
byte_count.
|
Note
VME device drivers must not specifiy flags
DMA_IN
or
DMA_OUT
as flag parameters to the
flags
argument of the
dma_map_alloc
or
dma_map_load
interfaces.
These flags are reserved for use by VME software interfaces that
support a hardware DMA engine on the VME adapter.
See
Section D.3
for a description and examples of using the VME adapter's
DMA engine for performing master block transfers.
Also refer to the
vba_set_dma_addr
and
vba_dma
interfaces to set up and perform master block transfers with the
local DMA engine.
In addition, VMEbus device drivers must pass special condition bits
defined in
/usr/sys/include/io/dec/vme/vbareg.h.
The following table describes some of these bits:
Bit Category
|
Value
|
Meaning
|
Swap mode bits
|
VME_BS_NOSWAP
|
Specifies no byte swapping.
|
|
VME_BS_BYTE
|
Specifies byte swapping in bytes.
|
|
VME_BS_WORD
|
Specifies byte swapping in words.
|
|
VME_BS_LWORD
|
Specifies byte swapping in longwords.
|
|
VME_BS_QUAD
|
Specifies byte swapping in quadwords.
|
Address space bits
|
VME_A16
|
Specifies a request for the 16-bit address space.
|
|
VME_A24
|
Specifies a request for the 24-bit address space.
|
|
VME_A32
|
Specifies a request for the 32-bit address space.
|
|
VME_A64
|
Specifies a request for the 64-bit address space.
|
Transfer size bits
|
VME_D08
|
Specifies a request for the 8-bit data size.
|
|
VME_D16
|
Specifies a request for the 16-bit data size.
|
|
VME_D32
|
Specifies a request for the 32-bit data size.
|
Access mode bits
|
VME_UDATA
|
Specifies user data.
|
|
VME_UPROG
|
Specifies a user program.
|
|
VME_SDATA
|
Specifies supervisory data.
|
|
VME_SPROG
|
Specifies a supervisory program.
|
CPU allocation space bits
|
|
The default CPU allocation space is sparse space.
To specify dense space, you use the
VME_DENSE
bit.
|
[Return to example]
-
Sets the error bit to the constant
EIO.
[Return to example]
-
Copies
the error bit
EIO
to the
sc_error
member of the
sc
pointer.
[Return to example]
-
Sets
b_flags
to the bitwise inclusive OR of the read and error bits.
This indicates an error occurred on this buffer.
[Return to example]
-
Calls the
iodone
kernel interface to indicate that the I/O operation has completed.
This interface takes one argument: a pointer to a
buf
structure.
[Return to example]
-
Returns control to the calling interface, which is
physio.
The
physio
interface returns to its calling interface either
dmaexread
or
dmaexwrite.
Section 6.6.1
and
Section 6.6.2
show the calls to
physio.
[Return to example]
-
Calls the
dma_get_curr_sgentry
interface to obtain a pointer to the current
sg_entry
data structure.
The
dma_get_curr_sgentry
interface takes one argument:
a handle to DMA resources associated with the mapping of an in-memory I/O
buffer onto a controller's I/O bus.
This handle provides the information to access bus address/byte count pairs.
A
bus address/byte count pair
is represented by the
ba
and
bc
members of an
sg_entry
structure pointer.
Device driver writers can view the DMA handle as the tag to the
allocated system resources needed to perform a DMA operation.
In this call, the example passes the DMA handle stored in the
sc_dma_handle
member of the driver's
softc
structure associated with this device.
This DMA handle was passed in a previous call to
dma_map_load.
[Return to example]
-
Calls the
write_io_port
interface to write a byte to the VMEbus.
The
write_io_port
interface takes four arguments:
-
The first argument specifies
an I/O handle that you can use to reference a device register or memory
located in bus address space (either I/O space or memory space).
This I/O handle references a device register in the bus address space
where the write operation occurs.
In this call, the example passes the result of the I/O handle stored in the
reg
array added to the device register offset represented by
DMAEX_COUNT_OFF.
The
DMAEX_COUNT_OFF
device register offset represents a byte address.
Section 6.1
defines the device register offsets for the
DMAEX
device.
-
The second argument specifies
the width (in bytes) of the data to be written.
Valid values are 1, 2, 3, 4, and 8.
Not all CPU platforms or bus adapters support all of these values.
In this call, the example passes the constant
DMAEX_COUNT_SIZE,
which represents a call to the
sizeof
operator for size of type
short.
Section 6.1
shows the definition of this constant.
-
The third argument specifies
flags to indicate special processing requests.
Currently, no flags are used.
To indicate that no flags are used, the example passes the value zero
(0).
-
The fourth argument specifies
the data to be written to the specified device register in bus address
space.
In this call, the example passes the number of bytes to transfer.
[Return to example]
-
Calls the
mb
interface to perform a memory barrier.
You call
mb
in a device driver under the following circumstances:
-
To force a barrier between load/store operations
-
After the CPU has prepared a data buffer in memory and before the device
driver
tries to perform a DMA out of the buffer
-
Before attempting to read any device CSRs after taking a device
interrupt
-
Between writes
[Return to example]
-
Calls
write_io_port
a second time, passing the following values:
-
For the first argument, the example passes the result of the I/O handle
stored in the
reg
array added to the device register offset represented by
DMAEX_ADDR_OFF.
The
DMAEX_ADDR_OFF
device register offset represents the 32-bit VMEbus transfer address.
Section 6.1
defines the device register offsets for the
DMAEX
device.
-
For the second argument, the example passes the constant
DMAEX_ADDR_SIZE,
which represents a call to the
sizeof
operator for size of type
vme_addr_t.
Section 6.1
shows the definition of this constant.
-
For the third argument, the example passes the value zero (0) to
indicate that no flags are used.
-
For the fourth argument, the example passes the VMEbus address.
[Return to example]
-
Calls the
mb
interface to perform a memory barrier.
[Return to example]
-
If the request is for a read operation, sets the
READ
and
IE
flags in the
csr
variable.
[Return to example]
-
If the request is not for a read operation (that is, the operation is a
write operation), sets the
IE
flag in the
csr
variable.
[Return to example]
-
Calls
write_io_port
a third time, telling the
DMAEX
device to start the data transfer operation.
[Return to example]
-
Calls the
mb
interface to perform a memory barrier.
[Return to example]
-
Calls the
timeout
interface to initialize a callout queue element.
The
timeout
interface takes three arguments:
-
The first argument specifies
a pointer to the interface to be called.
If the timeout occurs, the example passes
dmaex_timeout,
the name of the interface to be called.
-
The second argument specifies
a single argument to be passed to the called interface.
In this call, the example passes the logical unit number for the
DMAEX
device that
is interrupting.
-
The third argument specifies
the amount of time to delay before calling the specified
interface.
You express time as time (in seconds) *
hz.
In this call, the example passes the result of the value stored in
dmaex_int_wait
and the
hz
global variable.
[Return to example]
-
Calls the
sleep
interface to put the calling process to sleep.
[Return to example]
-
Calls the
dma_map_unload
kernel interface to unload system DMA resources.
The
dma_map_unload
interface takes two arguments:
[Return to example]
-
Calls the
iodone
kernel interface to indicate that the I/O operation has completed.
The
iodone
interface takes one argument: a pointer to a
buf
structure.
[Return to example]
-
Returns control to the calling interface, which is
physio.
The
physio
interface returns to its calling interface either
dmaexread
or
dmaexwrite.
Section 6.6.1
and
Section 6.6.2
show the calls to
physio.
[Return to example]
6.8 Interrupt Section
The following code shows the Interrupt Section for the
/dev/dmaex
device driver:
dmaexintr1(unit)
int unit; [1]
{
register struct controller *ctlr = dmaexinfo[unit]; [2]
register struct dmaex_softc *sc = &dmaex_softc[unit]; [3]
char csr; [4]
struct buf *bp; [5]
untimeout (dmaex_timeout, (caddr_t) unit); [6]
bp = sc->bp; [7]
csr = read_io_port (reg[unit] + DMAEX_CSR_OFF,
DMAEX_CSR_SIZE, 0); [8]
if (csr & ERROR) { [9]
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
}
bp->b_resid = read_io_port(reg[unit] + DMAEX_COUNT_OFF,
DMAEX_COUNT_SIZE, 0); [10]
wakeup(&dmaex_softc[unit]); [11]
}
dmaexintr2(unit) [12]
int unit;
{
untimeout (dmaex_timeout, (caddr_t) unit);
wakeup(&dmaex_softc[unit]);
}
-
Declares a
unit
argument that specifies the logical unit number for the
DMAEX
device that
is interrupting.
This logical unit number was previously specified in the system
configuration file.
[Return to example]
-
Sets the pointer to the
controller
structure to its associated
DMAEX
device.
Note that
unit,
which now contains this
DMAEX
device's minor device number,
is used as an index into the array of
controller
structures to obtain the
controller
structure associated with this
DMAEX
device.
[Return to example]
-
Initializes
sc
to the address of the
dmaex_softc
structure associated with this
DMAEX
device.
The minor device number (stored in the
unit
variable)
is used as an index into the array of
dmaex_softc
structures to determine which
dmaex_softc
structure is associated with this
DMAEX
device.
[Return to example]
-
Declares a variable to store the return value from
read_io_port.
[Return to example]
-
Declares a pointer to a
buf
structure and calls it
bp.
The
dmaexintr
interface uses the following members of the
buf
structure:
b_error,
b_flags,
and
b_resid.
[Return to example]
-
Calls the
untimeout
interface to remove the scheduled interface from the callout queues.
The
untimeout
interface takes two arguments:
-
The first argument specifies
a pointer to the interface to be removed from the callout
queues.
In this call, the example passes
dmaex_timeout,
the name of the interface to be removed from the callout queues.
-
The second argument specifies
a single argument to be passed to the called interface.
In this call, the example passes the logical unit number for the
DMAEX
device that
is interrupting.
[Return to example]
-
Retrieves the pointer to the
buf
structure that was saved in
Section 6.7.
The line accomplishes this by setting the
bp
pointer to the pointer to the
buf
structure member in the
dmaex_softc
structure associated with this
DMAEX
device.
[Return to example]
-
Calls the
read_io_port
interface to read a byte from the VMEbus.
[Return to example]
-
If the error bit in the device control status register is set, then an error
occurred on the transfer.
The
dmaexintr1
interface:
-
Sets the
b_error
member to the constant
EIO,
which indicates an I/O error occurred.
-
Sets
b_flags
to the bitwise inclusive OR of the read and error bits.
This indicates an error occurred on this buffer.
[Return to example]
-
Sets the
b_resid
member of the
bp
pointer to the byte count register of the
DMAEX
device.
To return the requested data, the example calls
read_io_port.
[Return to example]
-
Calls the
wakeup
interface to wake up all processes sleeping on the specified address.
The
wakeup
interface takes one argument:
the address on which the wakeup is to be issued.
In this call, the example passes the address of the
softc
structure associated with this
DMAEX
device.
[Return to example]
-
The
dmaexintr2
interface handles other interrupts from the
DMAEX
controller.
This and the associated code in
dmaexprobe
are included only for the purpose of illustrating how multiple
interrupts and their interrupt handlers are registered.
This code has no functional meaning in this example.
[Return to example]
6.9 Timeout Section
The following code shows the Timeout Section for the
/dev/dmaex
device driver:
dmaex_timeout(unit)
int unit; [1]
{
register struct dmaex_softc *sc = &dmaex_softc[unit]; [2]
struct buf *bp; [3]
bp = sc->bp; [4]
bp->b_error = ETIMEDOUT; [5]
bp->b_flags |= B_ERROR; [6]
wakeup(&dmaex_softc[unit]); [7]
}
-
Declares an argument that stores the logical unit number for the
DMAEX
device that
is interrupting.
The
untimeout
interface discussed in
Section 6.8
passes this value to
dmaex_timeout.
[Return to example]
-
Initializes
sc
to the address of the
dmaex_softc
structure associated with this
DMAEX
device.
The minor device number (stored in the
unit
variable)
is used as an index into the array of
dmaex_softc
structures to determine which
dmaex_softc
structure is associated with this
DMAEX
device.
[Return to example]
-
Declares a pointer to a
buf
structure and calls it
bp.
The
dmaexintr1
interface uses the following members of the
buf
structure:
b_error
and
b_flags.
[Return to example]
-
Retrieves the saved
buf
structure pointer associated with this
DMAEX
device.
[Return to example]
-
Sets the
b_error
member to the constant
ETIMEDOUT,
which indicates a connection timed out.
[Return to example]
-
Sets
b_flags
to the bitwise inclusive OR of the error bits.
This indicates an error occurred on this buffer.
[Return to example]
-
Calls the
wakeup
interface to wake up all processes sleeping on the specified address.
The
wakeup
interface takes one argument:
the address on which the wakeup is to be issued.
In this call, the example passes the address of the
softc
structure associated with this
DMAEX
device.
[Return to example]