[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


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

Part Section
The dmaexreg.h Header File Section 6.1
Include Files Section Section 6.2
Declarations Section Section 6.3
Autoconfiguration Support Section Section 6.4
Open and Close Device Section Section 6.5
Read and Write Device Section Section 6.6
Strategy Section Section 6.7
Interrupt Section Section 6.8
Timeout Section Section 6.9

The source code uses the following convention:

#define IE 0001 [1]

  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]


[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


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]

  1. Defines a device register byte offset that specifies a 1-byte control status register (CSR). [Return to example]

  2. Defines a device register byte offset that specifies a 16-bit byte count. [Return to example]

  3. Defines a device register byte offset that specifies the 32-bit VMEbus transfer address. [Return to example]

  4. 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]

  5. 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]

  6. 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]

  7. 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]

  8. 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]

  9. 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]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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]

  1. 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]

  2. 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]

  3. 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]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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]

  1. Declares the driver interfaces for the /dev/dmaex device driver. [Return to example]

  2. 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]

  3. 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]

  4. Declares an array of I/O handles and calls it reg. There is one I/O handle for each DMAEX device. [Return to example]

  5. 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:

    [Return to example]

  6. 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:

    [Return to example]

  7. Defines a constant used to size the multidimensional array. [Return to example]

  8. 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]

  9. 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]

  10. 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]

  11. 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]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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] }

  1. 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]

  2. 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]

  3. 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]

  4. 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]

  5. 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]

  6. Declares a variable called csr to store the return value from the read_io_port interface. [Return to example]

  7. Declares a variable called phys_addr to store the return value from the iohandle_to_phys interface. [Return to example]

  8. Declares a variable called sva to store the return value from the PHYS_TO_KSEG interface. [Return to example]

  9. Stores the controller number for this DMAEX device in the unit variable. [Return to example]

  10. 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]

  11. 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:

    [Return to example]

  12. 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]

  13. Calls the BADADDR interface to determine if the device is present.

    The BADADDR interface takes three arguments.

    If a device is not present, dmaexprobe returns the value zero (0). [Return to example]

  14. Calls the write_io_port interface to write a byte to the VMEbus.

    The write_io_port interface takes four arguments:

    [Return to example]

  15. Calls the mb interface to perform a memory barrier.

    You call mb in a device driver under the following circumstances:

    [Return to example]

  16. 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]

  17. Calls the read_io_port interface to read a byte from the VMEbus.

    The read_io_port interface takes three arguments:

    [Return to example]

  18. 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]

  19. 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]

  20. Calls the mb interface to perform a memory barrier. [Return to example]

  21. 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]

  22. Sets the intr member of the info data structure to dmaexintr1, the /dev/dmaex device driver's first interrupt service interface. [Return to example]

  23. 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]

  24. 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]

  25. Sets the vec member of the info data structure to the interrupt vector number for this /dev/dmaex device. [Return to example]

  26. Sets the irq member of the info data structure to the configured VMEbus priority level for this /dev/dmaex device. [Return to example]

  27. 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]

  28. 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]

  29. 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]

  30. 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]

  31. 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]

  32. Returns to the VMEbus configuration code a zero value to indicate that the device is not present. [Return to example]

  33. Sets the intr member of the info data structure to dmaexintr2, the /dev/dmaex device driver's second interrupt service interface. [Return to example]

  34. 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]

  35. 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]

  36. 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]

  37. 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]

  38. Returns to the VMEbus configuration code a nonzero value to indicate that the device is present. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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

Part Section
Implementing the dmaexopen Interface Section 6.5.1
Implementing the dmaexclose Interface Section 6.5.2


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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] }

  1. 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]

  2. 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]

  3. 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]

  4. 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]

  5. Declares a pointer to the controller structure associated with this DMAEX device. [Return to example]

  6. Declares a pointer to the dmaex_softc structure associated with this DMAEX device. Section 6.3 defines this structure. [Return to example]

  7. 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]

  8. 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]

  9. 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]

  10. 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]

  11. 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]

  12. 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]

  13. 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]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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] }

  1. 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]

  2. 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]

  3. 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]

  4. 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]

  5. Declares a pointer to the controller structure associated with this DMAEX device. [Return to example]

  6. Declares a pointer to the dmaex_softc structure associated with this DMAEX device. [Return to example]

  7. 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]

  8. 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]

  9. Turns off the open bit by setting the sc_open member of the sc pointer to the close bit DMAEXCLOSE. [Return to example]

  10. 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]

  11. 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]

  12. 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]

  13. Calls the write_io_port interface to write a byte to the VMEbus.

    The write_io_port interface takes four arguments:

    [Return to example]

  14. Returns the value zero (0) to the close system call to indicate a successful close of the DMAEX device. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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

Part Section
Implementing the dmaexread Interface Section 6.6.1
Implementing the dmaexwrite Interface Section 6.6.2
Implementing the dmaexminphys Interface Section 6.6.3


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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] }

  1. 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]

  2. 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]

  3. 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]

  4. 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]

  5. 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]

  6. Calls the physio kernel interface to implement raw I/O.

    The physio interface takes six arguments:

    [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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] }

  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]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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]

  1. Declares a pointer to the structure associated with this DMAEX device. [Return to example]

  2. Defines the maximum size of the data for the DMA transfer operation. [Return to example]

  3. 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]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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] }

  1. 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]

  2. 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]

  3. 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]

  4. 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]

  5. Declares a variable called csr to store read, write, and enable interrupts status information. [Return to example]

  6. 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]

  7. 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]

  8. 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]

  9. 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]

  10. 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:

    [Return to example]

  11. Sets the error bit to the constant EIO. [Return to example]

  12. Copies the error bit EIO to the sc_error member of the sc pointer. [Return to example]

  13. 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]

  14. 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]

  15. 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]

  16. 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]

  17. Calls the write_io_port interface to write a byte to the VMEbus.

    The write_io_port interface takes four arguments:

    [Return to example]

  18. Calls the mb interface to perform a memory barrier.

    You call mb in a device driver under the following circumstances:

    [Return to example]

  19. Calls write_io_port a second time, passing the following values:

    [Return to example]

  20. Calls the mb interface to perform a memory barrier. [Return to example]

  21. If the request is for a read operation, sets the READ and IE flags in the csr variable. [Return to example]

  22. 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]

  23. Calls write_io_port a third time, telling the DMAEX device to start the data transfer operation. [Return to example]

  24. Calls the mb interface to perform a memory barrier. [Return to example]

  25. Calls the timeout interface to initialize a callout queue element.

    The timeout interface takes three arguments:

    [Return to example]

  26. Calls the sleep interface to put the calling process to sleep. [Return to example]

  27. Calls the dma_map_unload kernel interface to unload system DMA resources.

    The dma_map_unload interface takes two arguments:

    [Return to example]

  28. 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]

  29. 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]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


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]); }

  1. 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]

  2. 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]

  3. 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]

  4. Declares a variable to store the return value from read_io_port. [Return to example]

  5. 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]

  6. Calls the untimeout interface to remove the scheduled interface from the callout queues.

    The untimeout interface takes two arguments:

    [Return to example]

  7. 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]

  8. Calls the read_io_port interface to read a byte from the VMEbus. [Return to example]

  9. If the error bit in the device control status register is set, then an error occurred on the transfer. The dmaexintr1 interface:

    [Return to example]

  10. 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]

  11. 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]

  12. 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]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Chapter] [Index] [Help]


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] }

  1. 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]

  2. 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]

  3. 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]

  4. Retrieves the saved buf structure pointer associated with this DMAEX device. [Return to example]

  5. Sets the b_error member to the constant ETIMEDOUT, which indicates a connection timed out. [Return to example]

  6. Sets b_flags to the bitwise inclusive OR of the error bits. This indicates an error occurred on this buffer. [Return to example]

  7. 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]