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