Commonly, a single set of source files is used for multiple target machines. C preprocessor defines are used to define differences conditionally. Command line compile options expose the correct values. The following examples are interesting:
For an Indigo (R3000) system:
For an Indigo (R4000) system:
% cc -DIP12 -DR3000 -cckr -c gbd.c
For an Indigo2 (R4000) or Indy system:
% cc -DIP20 -DR4000 -cckr -c gbd.c
% cc -DIP22 -DR4000 -cckr -c gbd.c
Note: For R8000 systems, omit the -cckr argument. For more information on compile directives, see /var/sysgen/Makefile.kernio.
/* Source for a mythical GIO board device; it can be compiled * for devices that support DMA (with or without scatter/ * gather support), or for PIO mode only. This version is * designed for IRIX 5.1 or later. * Dave Olson, 5/93 */ /* defines for compilation; would normally be passed on compilation * line via Makefile */ #define _K32U32 1 #define _KERNEL 1 #define IP20 1 /* define cpu type */ #if IP20 || IP22 #define R4000 1 #elif IP12 #define R3000 1 #endif /* end of 'normal' compilation definitions */ /* The following definitions choose between PIO vs DMA * supporting boards, and if DMA is supported, whether * hardware scatter/gather is supported. */ #define GBD_NODMA 0 /* non-zero for PIO version of driver */ #define GBD_NUM_DMA_PGS 4 /* 0 for no hardware scatter/gather * support, else number of pages of * scatter/gather supported per * request */ #include <sys/param.h> #include <sys/sysmacros.h> #include <sys/systm.h> #include <sys/cpu.h> #include <sys/buf.h> #include <sys/cred.h> #include <sys/uio.h> #include <sys/ddi.h> #include <sys/errno.h> #include <sys/cmn_err.h> #include <sys/edt.h> /* NOTE: This sample driver ignores the possiblity that * the board might be busy handling some earlier request. * Any real device must deal with that possiblity, of * course, before changing the board registers. */ /* these defines and structures would normally be in * a separate header file */ #define GBD_BOARD_ID 0x75 #define GBD_MASK 0xff /* Use 0xff if using only first byte * of ID word; use 0xffff if using * whole ID word. */ #define GBD_MEMSIZE 0x8000 /* command definitions */ #define GBD_GO 1 /* state definitions */ #define GBD_SLEEPING 1 #define GBD_DONE 2 /* direction of DMA definitions */ #define GBD_READ 0 #define GBD_WRITE 1 /* status defines */ #define GBD_INTR_PEND 0x80 /* "gbd" is device prefix; also in master.d/xxx file */ /* devices interface to the board */ struct gbd_device { int command; int count; int direction; off_t offset; unsigned *sgregisters; /* if scatter/gather supported */ caddr_t startaddr; /* if no scatter/gather on board */ unsigned status; /* errors, interrupt pending, etc. */ }; /* These are used for no scatter/gather case only, and assume * (since they aren't protected!) that the driver is * completely single threaded. */ struct buf *gbd_curbp[2]; /* current buffer */ caddr_t gbd_curaddr[2]; /* current address to transfer */ int gbd_curcount[2]; int gbd_totcount[2]; /* pointer to on-board registers */ volatile struct gbd_device *gbd_device[2]; char *gbd_memory[2]; /* pointer to on-board memory */ static int gbd_state[2]; /* flag for transfer state * (PIO driver) */ void gbdintr(int); extern int splgio1(void); /* equipped device table initialization routine. The edt * structure is defined in edt.h. */ void gbdedtinit(struct edt *e) { int slot, val; /* Check to see if the device is present */ if(badaddr_val(e->e_base, sizeof(int), &val) || (val && GBD_MASK) != GBD_BOARD_ID) { if (showconfig) cmn_err (CE_CONT, "gbdedtinit: board not installed."); return; } /* figure out slot from base on VECTOR line in * system file */ if(e->e_base == (caddr_t)PHYS_TO_K1(0x1f400000)) slot = GIO_SLOT_0; else if(e->e_base == (caddr_t)0xBF600000) slot = GIO_SLOT_1; else { cmn_err (CE_NOTE, "ERROR from edtinit: Bad base address %x\n", e->e_base); return; } #if IP12 /* For Indigo R3000 system, set up board as a * realtime bus master. */ setgioconfig(slot,0); #endif #if IP20 /* For Indigo R4000 system, set up board as a * realtime bus master. */ setgioconfig(slot,GIO64_ARB_EXP0_RT | GIO64_ARB_EXP0_MST); #endif #if IP22 /* for Indigo2 system, set up board as a pipelined, * realtime bus master */ setgioconfig(slot,GIO64_ARB_EXP0_RT | GIO64_ARB_EXP0_PIPED); #endif /* Save the device addresses, because * they won't be available later. */ gbd_device[slot == GIO_SLOT_0 ? 0 : 1] = (struct gbd_device *)e->e_base; gbd_memory[slot == GIO_SLOT_0 ? 0 : 1] = (char *)e->e_base2; setgiovector(GIO_INTERRUPT_1,slot,gbdintr,unit_#); } /* minor number used to indicate which slot; open does * nothing but check that board is present. */ /* ARGSUSED */ gbdopen(dev_t *devp, int flag, int otyp, cred_t *crp) { if(!gbd_device[geteminor(*devp)&1]) return ENXIO; /* board not present */ return 0; /* OK */ } /* ARGSUSED */ gbdclose(dev_t dev, int flag, int otyp, cred_t *crp) { return 0; /* nothing to do */ } #ifdef GBD_NODMA /* device write routine entry point (for character devices) */ int gbdwrite(dev_t dev, uio_t *uio) { int unit = geteminor(dev)&1; int size, err=0, s; /* while there is data to transfer */ while((size=uio->uio_resid) > 0) { /* Transfer no more than GBD_MEMSIZE bytes * to the device */ size = size < GBD_MEMSIZE ? size : GBD_MEMSIZE; /* decrements count and updates uio fields, * and copies data */ if(err=uiomove(gbd_memory[unit], size, UIO_WRITE, uio)) break; /* prevent interrupts until we sleep */ s = splgio1(); /* Transfer is complete; start output */ gbd_device[unit]->count = size; gbd_device[unit]->command = GBD_GO; gbd_state[unit] = GBD_SLEEPING; while (gbd_state[unit] != GBD_DONE) { sleep(&gbd_state[unit], PRIBIO); } /* restore the process level after waking up */ splx(s); } return err; } /* interrupt routine for PIO only board, just wake up * upper half of driver */ /* ARGSUSED1 */ void gbdintr(int unit) { /* Read your board's registers to determine if there are * any errors or interrupts pending. If no interrupts * are pending, return without doing anything. */ if(!gbd_device[unit]->status & GBD_INTR_PEND) return; if (gbd_state[unit] == GBD_SLEEPING) { /* Output is complete; wake up top half * of driver, if it is waiting. */ gbd_state[unit] = GBD_DONE; wakeup(&gbd_state[unit]); } /* Do anything else to board to tell it we are done * with transfer and interrupt here. */ return; /* could just fall through */ } #else /* DMA version of driver */ void gbd_strategy(struct buf *); /* device write routine entry point (for character devices). * Does nothing but call uiophysio to setup passing a pointer * to the gbd_strategy routine, which does most of the work. */ int gbdwrite(dev_t dev, uio_t *uiop) { return uiophysio((int (*)())gbd_strategy, 0, dev, B_WRITE, uiop); } #if GBD_NUM_DMA_PGS > 0 /* Actual device setup for DMA, etc., if your board has * hardware scatter/gather DMA support. * Called from the gbdwrite() routine via physio(). */ void gbd_strategy(struct buf *bp) { int unit = geteminor(bp->b_dev)&1; int npages; volatile unsigned *sgregisters; int i, v_addr; /* Get address of the scatter/gather registers */ sgregisters = gbd_device[unit]->sgregisters; /* Get the kernel virtual address of the data; note * b_dmaaddr may be NULL if the BP_ISMAPPED(bp) macro * indicates false; in that case, the field bp->b_pages * is a pointer to a linked list of pfdat structure * pointers; that saves creating a virtual mapping and * then decoding that mapping back to physical addresses. * BP_ISMAPPED will never be false for character devices, * only block devices. */ if(!BP_ISMAPPED(bp)) { cmn_err(CE_WARN, "gbd driver can't handle unmapped buffers"); bioerror(bp, EIO); biodone(bp); return; } v_addr = bp->b_dmaaddr; /* Compute number of pages received. * The dma_len field provides the number of pages to * map. Note that this may be larger than the actual * number of bytes involved in the transfer. This is * because the transfer may cross page boundaries, * requiring an extra page to be mapped. Limit to * number of scatter/gather registers on board. * Note that this sample driver doesn't handle the * case of requests > than # of registers! */ npages = numpages (v_addr, bp->b_dmalen); /* * Provide the beginning byte offset and count to the * device. */ gbd_device[unit]->offset = (unsigned int)bp->b_dmaaddr & (NBPC-1); if(npages > GBD_NUM_DMA_PGS) { npages = GBD_NUM_DMA_PGS; cmn_err(CE_WARN, "request too large, only %d pages max", npages); if(gbd_device[unit]->offset) gbd_device[unit]->count = NBPC - gbd_device[unit]->offset + (npages-1)*NBPC; else gbd_device[unit]->count = npages*NBPC; bp->b_resid = bp->b_count - gbd_device[unit]->count; } else gbd_device[unit]->count = bp->b_count; /* Translate the virtual address of each page to a * physical page number and load it into the next * scatter/gather register. btop() * converts the byte value to a page value after * rounding down the byte value to a full page. */ for (i = 0; i < npages; i++) { *sgregisters++ = btop(kvtophys(v_addr)); /* /* Get the next virtual address to translate. * (NBPC is a symbolic constant for the page * size in bytes) */ v_addr += NBPC; } if ((bp->b_flags & B_READ) == 0) gbd_device[unit]->direction = GBD_WRITE; else gbd_device[unit]->direction = GBD_READ; gbd_device[unit]->command = GBD_GO; /* start DMA */ /* and return; upper layers of kernel wait for iodone(bp)*/ } /* not much to do in this interrupt routine, since we are * assuming for this driver that we can never have to do * multiple DMA's to handle the number of bytes requested... */ void gbdintr(int unit) { int error; /* Read your board's registers to determine if * there are any errors or interrupts pending. * If no interrupts are pending, return without * doing anything. */ if(!gbd_device[unit]->status & GBD_INTR_PEND) return; if(error) bioerror(bp, EIO); biodone(bp); /* we are done, tell upper layers */ /* do anything else to board to tell it we are done * with transfer and interrupt here */ } #else /* GBD_NUM_DMA_PGS == 0; no hardware * scatter/gather support */ /* Actual device setup for DMA, etc., if your board * does NOT have hardware scatter/gather DMA support. * Called from the gbdwrite() routine via physio(). */ void gbd_strategy(struct buf *bp) { int unit = geteminor(bp->b_dev)&1; /* any checking for initial state here. */ /* Get the kernel virtual address of the data; note * b_dmaaddr may be NULL if the BP_ISMAPPED(bp) macro * indicates false; in that case, the field bp->b_pages * is a pointer to a linked list of pfdat structure * pointers; that saves creating a virtual mapping and * then decoding that mapping back to physical addresses. * BP_ISMAPPED will never be false for character devices, * only block devices. */ if(!BP_ISMAPPED(bp)) { cmn_err(CE_WARN, "gbd driver can't handle unmapped buffers"); bioerror(bp, EIO); biodone(bp); return; } gbd_curbp[unit] = bp; /* * Initialize the current transfer address and count. * The first transfer should finish the rest of the * page, but do no more than the total byte count. */ gbd_curaddr[unit] = bp->b_dmaaddr; gbd_totcount[unit] = bp->b_count; gbd_curcount[unit] = NBPC - ((unsigned int)gbd_curaddr[unit] & (NBPC-1)); if (bp->b_count < gbd_curcount[unit]) gbd_curcount[unit] = bp->b_count; /* Tell the device starting physical address, count, * and direction */ gbd_device[unit]->startaddr = kvtophys(gbd_curaddr[unit]); gbd_device[unit]->count = gbd_curcount[unit]; if (bp->b_flags & B_READ) == 0) gbd_device[unit]->direction = GBD_WRITE; else gbd_device[unit]->direction = GBD_READ; gbd_device[unit]->command = GBD_GO; /* start DMA */ /* and return; upper layers of kernel wait for iodone(bp) */ } /* more complicated interrupt routine, not necessarily because * board has DMA, but more typical of boards that do have * DMA, since they are typically more complicated. * Also more typical of devices that support block i/o, as * opposed to character i/o. */ void gbdintr(int unit) { int error; register struct buf *bp = gbd_curbp[unit]; /* read your board's registers to determine if * there are any errors or interrupts pending. * If no interrupts are pending, return without * doing anything. */ if(!gbd_device[unit]->status & GBD_INTR_PEND) return; if(error) { bioerror(bp, EIO); biodone(bp); /* we are done, tell upper layers */ } else { /* On successful transfer of last chunk, continue * if necessary */ gbd_curaddr[unit] += gbd_curcount[unit]; gbd_totcount[unit] -= gbd_curcount[unit]; if(gbd_totcount[unit] <= 0) biodone(bp); /* we are done, tell upper layers */ else { /* else more to do, reprogram board and * start next dma */ gbd_curcount[unit] = (gbd_totcount[unit] < NBPC ? gbd_totcount[unit] : NBPC); gbd_device[unit]->startaddr = kvtophys(gbd_curaddr[unit]); gbd_device[unit]->count = gbd_curcount[unit]; if (bp->b_flags & B_READ) == 0) gbd_device[unit]->direction = GBD_WRITE; else gbd_device[unit]->direction = GBD_READ; gbd_device[unit]->command = GBD_GO; /* start next DMA */ } } /* Do anything else to board to tell it we are done * with transfer and interrupt here. */ } #endif /* GBD_NUM_DMA_PGS */ #endif /* GBD_NODMA */