54394gt@hocda.UUCP (G.TOMASEVICH) (01/04/84)
The following code fragments exercise the DR11-W in maintenance mode on a PDP-11/45 from a user process. The device registers are accessed from /dev/mem, and the physical address of the DMA buffer is calculated from the user segmentation registers. I have not tried such a scheme on our VAX. There are several important notes. 1. The usage is a call to blkdrw(), which returns an indication of success or failure. 2. Anything can be read or written from /dev/mem if the permissions are relaxed. Thus one could easily crash the system. Accessing a non- existent DR11-W causes the lseek to hang, according to an abort forced with the kill(1) command. 3. The PDP-11/70 would require use of the Unibus Map, which rules out such a simple method of getting the physical address for the DR11-W, and hence the whole idea of user-space DMA. I assume the same is true for the VAX. 4. If the user swaps out during the DMA, someone else's program gets over- written for DMA into memory (CRASH). Thus this is no method for a busy machine, only for debugging. 5. Interrupts can be taken into the user space, too, but there is a trap on the 'rti' as the PS restore is attempted from user mode. Otherwise, the interrupt routine works, but is useless because of the trap, unless one wants to do a nonlocal jump on every trap (ugh). #define UDSA 0777660L /* 1st user data seg adr reg */ unsigned areg[8]; /* buffer for reading seg regs */ int fm; /* file desc for reading /dev/mem */ extern long lseek(), physadr(); /* device register structures */ struct drwreg { /* DR11-W registers */ int drwc; /* word count */ int drba; /* buffer address */ int drst; /* status reg */ int drio; /* input data reg, output data reg */ }; struct drwreg drtemp[1]; /* mem loc to read/write registers */ #define DRWREG 0772410L /* adr of 1st DR11-W */ /* Simulation of UNIX kernel driver for DR11-W */ #define loword(X) (((unsigned*)&X)[1]) #define hiword(X) (((unsigned*)&X)[0]) #define GO 1 /* start DR11-W */ #define READY 0200 /* set when opn is done */ #define CYCLE 0400 /* do one NPR cycle */ #define MAINT 010000 /* do maintenance mode */ struct buf { /* physical i/o driver buffer */ int b_flags; /* i/o control */ int b_count; /* transfer byte count */ long b_paddr; /* phys adr of buffer */ } drwbuf[3]; /* 3 DR11-W's */ /* Combined open read/write, physio functions done in UNIX physical i/o. * DR11-W read or write is determined by hardware. */ blkdrw(d,b,n) /* blocking i/o */ int d; /* device number instead of f.d. from open() */ char *b; /* buffer adr */ int n; /* byte count */ { register struct buf *bp; bp = &drwbuf[d]; /* ought to check if legal d */ bp->b_count = n; bp->b_flags = MAINT|CYCLE|GO; /* maintenance mode */ bp->b_paddr = physadr(b); /* physio() does this */ drwstrat(bp); return(drwait(bp)); /* wait for done */ } drwstrat(bp) /* simulated strategy routine */ register struct buf *bp; { register com; register struct drwreg *dradr = drtemp; drrd(bp); /* read DR11 regs */ dradr->drwc = -((bp->b_count)>>1); /* -(word count) */ dradr->drba = loword(bp->b_paddr); com = (hiword(bp->b_paddr) & 03) << 4; com |= bp->b_flags; /* get specific cmd */ dradr->drst = com; drwr(bp); /* write DR11 regs */ return; } /* KI simulates a keyboard interrupt from nonblocking raw reads. * It can set flag. */ drwait(bp) /* wait for DMA to finish */ struct buf *bp; { register struct drwreg *dradr = drtemp; do { KI(); if(flag<0) return(0); /* bail out */ drrd(bp); } while((dradr->drst & READY) == 0); return(1); } int drwid; /* selected DR11-W (global for adb(1) ) */ drrd(bp) /* read DR11-W registers */ struct buf *bp; { long padr; drwid = (int)(bp-drwbuf); padr = DRWREG + (drwid<<4); (void)lseek(fm,padr,0); (void)read(fm,(char*)drtemp,sizeof(drtemp[0])); return; } drwr(bp) /* write DR11-W registers */ /* same as drrd() but has write() instead of read() */ long physadr(a) /* calc physical adr from virtual adr */ char *a; { register unsigned d, i; long padr; d = (unsigned)a; (void)lseek(fm,UDSA,0); /* get to segmentation regs */ (void)read(fm,(char*)areg,16); i = (d>>13)&7; /* segment */ padr = (long)areg[i]; /* block */ padr <<= 6; return(padr + (long)(d&017777)); } George Tomasevich