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


B    VMEbus Device Driver Source Listing

This appendix contains the source listing for the /dev/dmaex device driver.

/***************************************************
 *                                                 *
 *           Copyright (c) 1995 by                 *
 *   Digital Equipment Corporation, Maynard, MA    *
 *            All rights reserved.                 *
 *                                                 *
 * This software is furnished under the terms and  *
 * conditions of the TURBOchannel Technology       *
 * license and may be used and copied only in      *
 * accordance with the terms of such license and   *
 * with the inclusion of the above copyright       *
 * notice.  No title to and ownership of the       *
 * software is hereby transferred.                 *
 *                                                 *
 * The information in this software is subject to  *
 * change without notice and should not be         *
 * construed as a commitment by Digital Equipment  *
 * Corporation.                                    *
 *                                                 *
 * Digital assumes no responsibility for the use   *
 * or reliability of its software on equipment     *
 * which is not supplied by Digital.               *
 ***************************************************/

/*************************************************** * dmaexreg.h Device Register Header File for * * dmaex.c 13-Apr-1994 * * * * Device register offsets for DMAEX device * ***************************************************/

#define DMAEX_CSR_OFF 0 /* One byte control/status register */ #define DMAEX_COUNT_OFF 1 /* Short byte count */ #define DMAEX_ADDR_OFF 4 /* 32-bit VMEbus transfer address */ /***************************************************** * Convenience defines for calls to sizeof operator. * *****************************************************/ #define DMAEX_CSR_SIZE sizeof(char) #define DMAEX_COUNT_SIZE sizeof(short) #define DMAEX_ADDR_SIZE sizeof(vme_addr_t) /*************************************************** * Bits for csr device offset register * ***************************************************/

#define IE 0001 /* Interrupt Enable */ #define DMA_GO 0002 /* Start DMA */ #define RESET 0010 /* Ready for data transfer */ #define ERROR 0020 /* Indicate error */ #define READ 0040 /* Indicate data transfer is read */

/***************************************************
 * dmaex.c  Driver for dmaex device    13-Apr-1994 *
 *                                                 *
 * The /dev/dmaex device driver is an example      *
 * driver that supports a fictitious ``dmaex''     *
 * device.                                         *
 *                                                 *
 ***************************************************/
/****************************************************
 * Ron Bruckman wrote the original driver for       *
 * ULTRIX.  Ported to Digital UNIX by Joe Amato,    *
 * Doug Ferring, and Al Wojtas, Digital Device      *
 * Driver Project.  Updated to use the Digital UNIX *
 * generic interfaces by Rob Evers.                 *
 ****************************************************/
/***************************************************
 *            Include Files Section                *
 ***************************************************/

#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> /* VMEbus definitions */ #include <sys/sysconfig.h> #include <io/dec/tc/tc.h> #include <machine/cpu.h>

#include <io/dec/vme/dmaexreg.h> /* Device register header file */

#include <dmaex.h> /* Driver header file generated by config */

/***************************************************
 * Declarations Section                            *
 ***************************************************/

/*************************************************** * Forward declarations of driver interfaces * ***************************************************/

/* Forward declarations of driver interfaces */ int dmaexprobe(), dmaexminphys, dmaexopen(), dmaexclose(), dmaexread(), dmaexwrite(), dmaexstrategy(), dmaexintr1(), dmaexintr2(); /*************************************************** * Declare an array of buf structures, an * * array of pointers to controller structures, * * and initialize the driver structure. The * * system fills in the controller structures. * ***************************************************/

struct buf dmaexbuf[NDMAEX];

struct controller *dmaexinfo[NDMAEX]; io_handle_t reg[NDMAEX]; /* Array of I/O handles */

struct driver dmaexdriver = { dmaexprobe, /* probe */ 0, /* slave */ 0, /* cattach */ 0, /* dattach */ 0, /* go */ 0, /* addr_list */ 0, /* dev_name */ 0, /* dev_list */ "dmaex", /* ctlr_name */ dmaexinfo, /* ctlr_list */ 0, /* xclu */ 0x8, /* addr1_size */ VME_A32_UDATA_D32, /* addr1_atype */ 0, /* addr2_size */ 0, /* addr2_atype */ 0, /* ctlr_unattach */ 0 /* dev_unattach */ };

/*************************************************** * Defines for softc structure members * ***************************************************/

#define DMAEXOPEN 1 #define DMAEXCLOSE 0 #define EACCFAULT 200 /* sc_error: Access violation */ #define ENOMAPREG 201 /* sc_error: No mapping registers */ #define EBUFTOOBIG 202 /* sc_error: Buffer too big */ /*************************************************** * Declare softc structure to allow the * * /dev/dmaex device driver's interfaces to * * share data. * ***************************************************/

struct dmaex_softc { int sc_open; /* DMAEXOPEN, DMAEXCLOSE */ int sc_error; /* Driver specific error code */ struct buf *bp; /* To save buffer pointer * * for use by dmaexintr */ dma_handle_t sc_dma_handle; /* handle used by * * dma-related interfaces */ /* handle used by * * dma-related interfaces */ } dmaex_softc[NDMAEX]; #define NINTS_PER_DMAEX 2 /* Number used in multi-dimensional array */ ihandler_id_t *dmaex_id_t[NDMAEX][NINTS_PER_DMAEX]; /* Tags for * * handler_del */ int dmaex_int_wait=1; /* Number of seconds to wait for interrupt * * timeout */ int dmaex_timeout(); /* Declares an internal timeout interface. */ extern void timeout(); extern void untimeout(); /* The value for hz is defined in param.c */ extern int hz;

/***************************************************
 *     Autoconfiguration Support Section           *
 *                                                 *
 *--------------- dmaexprobe ----------------------*
 *                                                 *
 * The dmaexprobe interface is called from the     *
 * VMEbus configuration code during the boot       *
 * phase.  The dmaexprobe interface calls the      *
 * BADADDR interface to determine if the device    *
 * is present.  If the device is present,          *
 * dmaexprobe registers the interrupt service      *
 * interface(s) and returns 1.  If the device is   *
 * not present, dmaexprobe returns a zero (0).     *
 ***************************************************/

dmaexprobe(addr, ctlr) io_handle_t addr; /* Base I/O handle for DMAEX device */ struct controller *ctlr; /* Pointer to controller structure */ {

/*************************************************** * Declarations: * * * * o An ihandler_t structure called handler to * * contain information associated with device * * driver interrupt handling. * * * * o A handler_intr_info structure called info to * * contain interrupt handler information. * * * * o A unit variable to contain the unit number * * associated with this DMAEX device. The * * unit number is obtained from ctlr->ctlr_num. * * * * o A csr variable to contain the return value * * from read_io_port. * ***************************************************/

ihandler_t handler; struct vme_handler_info info; int unit; char csr; u_long phys_addr; caddr_t sva; unit = ctlr->ctlr_num; reg[unit] = addr; /*************************************************** * Calls the iohandle_to_phys interface to convert * * an I/O handle to a valid system physical * * address. * ***************************************************/

phys_addr = iohandle_to_phys(reg[unit] + DMAEX_CSR_OFF, HANDLE_BYTE | HANDLE_SPARSE_SPACE); sva = (caddr_t)PHYS_TO_KSEG((vm_offset_t)phys_addr); /*************************************************** * Call BADADDR to determine if device is present. * * Return the value zero (0) if the device is * * present. Return a nonzero value if the device * * is not present. * ***************************************************/

if (BADADDR(sva, DMAEX_CSR_SIZE, ctlr)) return(0);

/*************************************************** * Reset the device. The write_io_port interface * * is called to write the data, and it will * * perform the wbflush to insure the write to I/O * * space completes. * ***************************************************/

write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, RESET); mb(); /* Perform a memory barrier */

/* delay 10 millisecond */

DELAY(10000);

/*************************************************** * Read the byte from the VMEbus by calling the * * read_io_port interface. If the result of the * * bitwise AND is nonzero, dmaexprobe returns the * * value zero (0) to the VMEbus configuration code * * to indicate that the device is not responding. * ***************************************************/

csr = (char) read_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0); if (csr & ERROR) return(0);

/*************************************************** * Otherwise, the result of the bitwise AND is * * zero indicating that the error bit is not set. * * The dmaexprobe interface calls write_io_port * * to write a byte to the VMEbus. * ***************************************************/

write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, 0); mb(); /* Perform a memory barrier */ /*************************************************** * Register the driver's interrupt service * * interface by calling handler_add. Then enable * * it by calling handler_enable. * ***************************************************/

/* Initialize information for handler_add */ info.gen_intr_info.configuration_st = (caddr_t) ctlr; info.gen_intr_info.intr = dmaexintr1; info.gen_intr_info.param = (caddr_t) unit; info.gen_intr_info.config_type = CONTROLLER_CONFIG_TYPE; info.vec = ctlr->ivnum; /* VME vector */ info.irq = ctlr->bus_priority; /* VME IPL */ handler.ih_bus = ctlr->bus_hd; handler.ih_bus_info = (char *) &info; /* note: handler.ih_id is initialized in handler_add() */ dmaex_id_t[unit][0] = handler_add (&handler); if (dmaex_id_t[unit][0] == (ihandler_id_t *) NULL) { return (0); } if (handler_enable (dmaex_id_t[unit][0]) != 0) { handler_del (dmaex_id_t[unit][0]); return (0); } /*************************************************** * Adding another interrupt service interface. * * Generally, the next interrupt vector is * * sequentially incremented from what is entered * * in the config.file file fragment for the * * device. * ***************************************************/

info.gen_intr_info.intr = dmaexintr2; info.vec = ctlr->ivnum + 1; /* VME vector */ dmaex_id_t[unit][1] = handler_add (&handler); if (dmaex_id_t[unit][1] == (ihandler_id_t *) NULL) return (0); if (handler_enable (dmaex_id_t[unit][1]) != 0) { handler_del (dmaex_id_t[unit][1]); return (0); } /*************************************************** * The dmaexprobe interface returns a nonzero * * value to the VMEbus configuration code to * * indicate that the device is present. * ***************************************************/

return (1); }

/***************************************************
 *     Open and Close Device Section               *
 *                                                 *
 *--------------- dmaexopen -----------------------*
 *                                                 *
 * The dmaexopen interface checks that the device  *
 * is open uniquely.                               *
 ***************************************************/

dmaexopen(dev, flag, format) dev_t dev; /* Major/minor device number */ int flag; /* Flags from file.h */ int format; /* Format of special device */ {

/*************************************************** * The following code initializes a unit variable * * to the minor device number and declares: * * * * o A pointer to a controller structure * * associated with this DMAEX device * * * * o A pointer to a dmaex_softc structure * * associated with this DMAEX device * ***************************************************/

register int unit = minor(dev); register struct controller *ctlr; register struct dmaex_softc *sc;

/*************************************************** * The following code makes sure that: * * * * o The unit number is no more than the maximum * * number of devices configured on the system * * * * o The open is unique * * * * Note that this sequence of code also * * initializes sc to the address of the * * dmaex_softc structure associated with this * * DMAEX device. * ***************************************************/

if ((unit >= NDMAEX ) || (unit <= 0)) return (EIO); sc = &dmaex_softc[unit]; if (sc == (struct dmaex_softc *)NULL) return (ENODEV); if (sc->sc_open == DMAEXOPEN) return (EBUSY);

/*************************************************** * This sequence of code initializes the * * controller structure pointer to its associated * * DMAEX device. * * * * If the DMAEX device is initialized, sets the * * sc_open member to the open bit DMAEXOPEN and * * returns the value zero (0) to indicate success. * * * * Otherwise if the device does not exist, returns * * an error. * ***************************************************/

ctlr = dmaexinfo[unit]; if ((ctlr) && (ctlr->alive == ALV_ALIVE)) { sc->sc_open = DMAEXOPEN; return(0); } else return(ENXIO); }

/***************************************************
 *                                                 *
 *--------------- dmaexclose ----------------------*
 *                                                 *
 * The dmaexclose interface clears the DMAEXOPEN   * 
 * flag to allow other processes to use the        *
 * device.                                         *
 ***************************************************/

dmaexclose(dev, flag, format) dev_t dev; /* Major/minor device number */ int flag; /* Flags from file.h */ int format; /* Format of special device */ {

/*************************************************** * The following code initializes a unit variable * * to the minor device number and declares: * * * * o A pointer to a controller structure * * associated with this DMAEX device * * * * o A pointer to a dmaex_softc structure * * associated with this DMAEX device * ***************************************************/

register int unit = minor(dev); register struct controller *ctlr; register struct dmaex_softc *sc;

/*************************************************** * The following code: * * * * o Initializes the sc pointer to the * * dmaex_softc structure associated with this * * DMAEX device * * * * o Initializes the ctlr pointer to the * * controller structure associated with this * * DMAEX device * * * * o Initializes the reg array to the * * value zero (0) * * * * o Calls write_io_port to write a byte to * * the VMEbus * * * * o Returns the value zero (0) to indicate a * * successful close of the DMAEX device * ***************************************************/

sc = &dmaex_softc[unit]; if (sc == (struct dmaex_softc *)NULL) return (ENODEV); sc->sc_open = DMAEXCLOSE; ctlr = dmaexinfo[unit]; if (!(ctlr)) return (ENODEV); if (reg[unit] == (io_handle_t)0) return (ENODEV); write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, 0); return(0); }

/***************************************************
 *     Read and Write Device Section               *
 *                                                 *
 *--------------- dmaexread -----------------------*
 *                                                 *
 * The dmaexread interface calls the physio kernel * 
 * interface to perform the buffer lock, check the *
 * buffer, and set up the I/O packet.  The physio  *
 * interface calls the dmaexstrategy interface to  *
 * access the device.                              *
 ***************************************************/

dmaexread(dev, uio, flag) dev_t dev; /* Major/minor device number */ struct uio *uio; /* Pointer to uio structure */ int flag; /* Access mode of device */ {

/*************************************************** * The following code: * * * * o Initializes a unit variable to the minor * * device number. * * * * o Determines if the minor device number is * * greater than the maximum. If so, returns * * EIO to indicate an I/O error. * * * * o Otherwise, calls physio to perform the * * buffer lock, check the buffer, and set up * * the I/O packet. * ***************************************************/

register int unit = minor(dev);

if ((unit >= NDMAEX ) || (unit < 0)) return (EIO);

return (physio(dmaexstrategy, &dmaexbuf[unit], dev, B_READ, dmaexminphys, uio)); }

/***************************************************
 *                                                 *
 *--------------- dmaexwrite ----------------------*
 *                                                 *
 * The dmaexwrite interface calls the physio       * 
 * kernel interface to perform the buffer lock,    *
 * check the buffer, and set up the I/O packet.    *
 * The physio interface calls the dmaexstrategy    *
 * interface to access the device.                 *
 ***************************************************/

dmaexwrite(dev, uio, flag) dev_t dev; /* Major/minor device number */ struct uio *uio; /* Pointer to uio structure */ int flag; /* Access mode of device */ {

/*************************************************** * The following code: * * * * o Initializes a unit variable to the minor * * device number. * * * * o Determines if the minor device number is * * greater than the maximum. If so, returns * * EIO to indicate an I/O error. * * * * o Otherwise, calls physio to perform the * * buffer lock, check the buffer, and set up * * the I/O packet. * ***************************************************/

register int unit = minor(dev);

if ((unit >= NDMAEX) || (unit <= 0)) return (EIO);

return (physio(dmaexstrategy, &dmaexbuf[unit], dev, B_WRITE, dmaexminphys, uio)); }

dmaexminphys (bp)
register struct buf *bp;
{

/*************************************************** * The following code defines the maximum size of * * the DMA for this device. If the maximum size * * for the DMA exceeds dmaexMAXPHYS, returns * * to physio, which calls strategy. This can * * occur several times until the transfer is * * complete. * ***************************************************/

#define dmaexMAXPHYS (64*1024) if (bp->b_bcount > dmaexMAXPHYS) bp->b_bcount = dmaexMAXPHYS; return; }

/***************************************************
 *     Strategy Section                            *
 *                                                 *
 *--------------- dmaexstrategy -------------------*
 *                                                 *
 * The dmaexstrategy interface is called by the    *
 * phsyio kernel interface.  (The physio interface *
 * is called by the dmaexread and dmaexwrite       *
 * driver interfaces.)  The dmaexstrategy          * 
 * interface first makes sure that the user buffer *
 * is both readable and writeable.  It determines  *
 * if the buffer size is larger than dmaexMAXPHYS  *
 * and then initiates the I/O.                     *
 ***************************************************/

dmaexstrategy(bp) struct buf *bp; /* Pointer to buf structure */ {

/*************************************************** * The following code initializes: * * * * o A unit variable to the minor device number * * * * o A controller structure to its associated * * DMAEX device * * * * o A dmaex_softc structure to the address of * * the dmaex_softc associated with this * * DMAEX device * * * * o A variable to store read, write, and enable * * interrupts status information * * * * o An sg_entry structure pointer that the code * * uses in calls to dma_get_curr_sgentry and * * write_io_port. * ***************************************************/

register int unit = minor(bp->b_dev); register struct controller *ctlr = dmaexinfo[unit]; register struct dmaex_softc *sc = &dmaex_softc[unit]; short csr; sg_entry_t dmaex_sg;

/*************************************************** * Check the validity of the unit number and the * * pointers. * ***************************************************/

if ( (!ctlr) || (!sc) ) return (EINVAL); /*************************************************** * The following code saves the buffer pointer * * (bp) for use in dmaexintr1. * * * * It sets up the DMA mapping registers by calling * * the dma_map_alloc and dma_map_load interfaces. * * * * If the return from dma_map_alloc or * * dma_map_load is zero (0), it could not perform * * the requested operation. The dmaexstrategy * * interface: * * * * o Sets the error bit to ENOMAPREG * * * * o Copies the error bit to sc_error * * * * o Sets b_flags to the bitwise inclusive OR of * * the read and error bits * * * * o Completes the I/O operation by calling * * iodone * * * * Return to calling routine (either dmaexread or * * dmaexwrite. * ***************************************************/

sc->bp = bp; sc->sc_dma_handle = (dma_handle_t) NULL;

/****************************************************************** * * * To use the vba_set_dma_addr() interface: * * * * Declare a variable int flags and vme_addr_t vmeaddr = * * YOUR_VME_ADDRESS. * * * * then: * * * * flags = vba_set_dma_addr(ctlr, VME_A32_UDATA_D32 | * * VME_BS_NOSWAP | * * DMA_GUARD_UPPER | * * DMA_ALL,vme_addr); * * 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, flags) == 0) { * * bp->b_error = EIO; * * sc->sc_error = bp->b_error; * * bp->b_flags |= B_ERROR; * * iodone(bp); * * return; * * } * * * ******************************************************************/

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) { bp->b_error = EIO; sc->sc_error = bp->b_error; bp->b_flags |= B_ERROR; iodone(bp); return; }

/*************************************************** * * * If the requested mapping was performed, set up * * the device for transfer. * * * ***************************************************/

dmaex_sg = dma_get_curr_sgentry (sc->sc_dma_handle); write_io_port (reg[unit] + DMAEX_COUNT_OFF, DMAEX_COUNT_SIZE, 0, dmaex_sg->bc); mb(); write_io_port (reg[unit] + DMAEX_ADDR_OFF, DMAEX_ADDR_SIZE, 0, (long)dmaex_sg->ba); mb(); if (bp->b_flags & B_READ) csr = READ | IE; else csr = IE; write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, (csr | DMA_GO)); mb();

/*************************************************** * Set a timeout value so the interface can return * * if the board doesn't respond. It's also * * programmable from the dbx debugger. * ***************************************************/

timeout(dmaex_timeout, (caddr_t) unit, dmaex_int_wait*hz);

/*************************************************** * Wait for either the timeout or dma to complete * * the interrupt. * ***************************************************/

sleep(&dmaex_softc[unit], PRIBIO);

/*************************************************** * resources freed here versus in isr for * * performance reasons * ***************************************************/

dma_map_unload (DMA_DEALLOC, sc->sc_dma_handle);

/* Return to physio indicating io is complete */

iodone(bp); return; }

/***************************************************
 *     Interrupt Section                           *
 *                                                 *
 *--------------- dmaexintr1 ----------------------*
 *                                                 *
 * The dmaexintr1 interface releases VMEbus        *
 * mapping registers.  It then calls iodone to     *
 * finish the  I/O.                                *
 *                                                 *
 ***************************************************/

dmaexintr1(unit) int unit; /* Logical unit number for device */ { /*************************************************** * The following code initializes: * * * * o A controller structure to its associated * * DMAEX device * * * * o A dmaex_softc structure to the address of * * the dmaex_softc structure associated with * * this DMAEX device * * * * The code also declares a variable to store the * * return from read_io_port and a pointer to a * * buf structure. * * * ***************************************************/

register struct controller *ctlr = dmaexinfo[unit]; register struct dmaex_softc *sc = &dmaex_softc[unit]; char csr; struct buf *bp; untimeout (dmaex_timeout, (caddr_t) unit); bp = sc->bp; /* Retrieve saved buf pointer */

/*************************************************** * * * Call read_io_port to read a byte from the * * VMEbus. If the error bit is set, an error * * occurred. The code does the following: * * * * o Sets the error bit in b_error * * * * o Sets b_flags to the bitwise inclusive OR * * of the read and error bits to indicate * * an error occurred on this buffer * * * ***************************************************/

csr = read_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0); if (csr & ERROR) { bp->b_error = EIO; bp->b_flags |= B_ERROR; }

/*************************************************** * * * The following code: * * * * o Records the number of bytes remaining in * * b_resid * * * * o Calls wakeup returning to strategy * * * ***************************************************/

bp->b_resid = read_io_port(reg[unit] + DMAEX_COUNT_OFF, DMAEX_COUNT_SIZE, 0); wakeup(&dmaex_softc[unit]); } /*************************************************** * Handle other interrupts from the dmaex * * controller. This and associated code in * * dmaexprobe are included only for the purpose * * of illustrating how multiple interrupts and * * their handlers are registered. This has no * * functional meaning in this example. * ***************************************************/ dmaexintr2(unit) int unit; /* Logical unit number for device */ { untimeout (dmaex_timeout, (caddr_t) unit); wakeup(&dmaex_softc[unit]); }


dmaex_timeout(unit) int unit; { register struct dmaex_softc *sc = &dmaex_softc[unit]; struct buf *bp; bp = sc->bp; /* Retrieve saved buf pointer */ bp->b_error = ETIMEDOUT; bp->b_flags |= B_ERROR; wakeup(&dmaex_softc[unit]); }