#include "im.h" #include "../h/param.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/buf.h" #include "../h/systm.h" #include "../h/pte.h" #include "../h/map.h" #include "../h/ubareg.h" #include "../h/ubavar.h" struct imdevice { short wcr; /* Unibus word count reg */ u_short bar; /* Unibus address register */ u_short csr; /* Unibus status/command reg */ u_short idr; /* Input Data Register */ }; #define eir csr /* Error and Information Register */ #define odr idr /* Output Data Register */ /* * Unibus status/command register bits */ #define GO 0000001 /* starts dma (W) */ #define H_STROBE 0000002 /* host strobe (R/W) */ #define H_INTER 0000004 /* host interrupt (R/W) */ #define H_RESET 0000010 /* host reset (R/W) */ #define X16 0000020 /* extended buss address bit 16 (R/W) */ #define X17 0000040 /* extended buss address bit 17 (R/W) */ #define IENABLE 0000100 /* interrupt enable (R/W) */ #define READY 0000200 /* dev req's int. at end of dma (R) */ #define NOT_USED 0000400 #define S_STROBE 0001000 /* scanner strobe (R) */ #define S_INTER 0002000 /* scanner interrupt (R) */ #define S_RESET 0004000 /* scanner power off (R) */ #define MAINT 0010000 /* maintenance mode (R/W) */ #define ATTNT 0020000 /* attention, interface interrupt (R) */ #define NEX 0040000 /* non existent memory error */ #define ERROR 0100000 /* dr11w interface error */ /* * Function codes */ #define WR_IMAGE (1<<1) #define WR_CMD (4<<1) #define WR_RLE (5<<1) #define RD_CMD (7<<1) #define RD_IMAGE (3<<1) #define FUNCMASK (7<<1) #define TOGGLE (8<<1) /* toggle between X and WR_CMD, WR_CMD first */ /* * IOCTL */ #define IMRESET (('i'<<8)|1) #define BUSY 01 #define DMAPRI (PZERO-1) #define WAITPRI (PZERO+1) int improbe(), imattach(), imintr(); struct uba_device *imdinfo[NIM]; u_short imstd[] = { 0772470, 0000000, 0 }; struct uba_driver imdriver = { improbe, 0, imattach, 0, imstd, "im", imdinfo, 0, 0 }; struct imsoftc { char open; short uid; short state; int ubinfo; int count; struct buf *bp; int bufp; int icnt; } imsoftc[NIM]; int imstrategy(); u_int imminphys(); struct buf rimbuf[NIM]; #define UNIT(dev) (minor(dev)) improbe(reg) caddr_t reg; { register int br, imec; /* value-result */ register struct imdevice *imaddr = (struct imdevice *) reg; #ifdef lint br = 0; imec = br; br = imec; imintr(0); #endif /* imaddr->csr = H_STROBE|H_INTER|H_RESET|IENABLE; DELAY(10000); imaddr->csr = 0; /* KLUDGE */ br=0x15; imec=0174; return (sizeof (struct imdevice)); } /*ARGSUSED*/ imattach(ui) struct uba_device *ui; { } imopen(dev) dev_t dev; { register struct imsoftc *imp; register struct uba_device *ui; if (UNIT(dev) >= NIM || (imp = &imsoftc[minor(dev)])->open || (ui = imdinfo[UNIT(dev)]) == 0 || ui->ui_alive == 0) u.u_error = EBUSY; else { imp->open = 1; imp->icnt = 0; imp->state = 0; imp->uid = u.u_uid; } } imclose(dev) dev_t dev; { imsoftc[minor(dev)].open = 0; imsoftc[minor(dev)].state = 0; } imread(dev, uio) dev_t dev; struct uio *uio; { register int unit = UNIT(dev); if (unit >= NIM) return (ENXIO); return (physio(imstrategy, &rimbuf[unit], dev, B_READ, imminphys, uio)); } imwrite(dev, uio) dev_t dev; struct uio *uio; { register int unit = UNIT(dev); if (unit >= NIM) return (ENXIO); return (physio(imstrategy, &rimbuf[unit], dev, B_WRITE, imminphys, uio)); } u_int imminphys(bp) register struct buf *bp; { if (bp->b_bcount > 65536) /* may be able to do twice as much */ bp->b_bcount = 65536; } imstrategy(bp) register struct buf *bp; { register struct imsoftc *imp = &imsoftc[UNIT(bp->b_dev)]; register struct uba_device *ui; if (UNIT(bp->b_dev) >= NIM || (ui = imdinfo[UNIT(bp->b_dev)]) == 0 || ui->ui_alive == 0) goto bad; (void) spl5(); while (imp->state & BUSY) sleep((caddr_t)imp, DMAPRI+1); imp->state |= BUSY; imp->bp = bp; if(bp->b_bcount<=0 || (bp->b_bcount&1) || ((int)bp->b_un.b_addr&1)){ u.u_error = EINVAL; goto bad; } imp->ubinfo = ubasetup(ui->ui_ubanum, bp, UBA_NEEDBDP); imp->bufp = imp->ubinfo & 0x3ffff; imp->count = -(bp->b_bcount>>1); /* it's a word count */ imstart(ui); if(tsleep((caddr_t)imp, DMAPRI+1, 15) != TS_OK){ register struct imdevice *imaddr = (struct imdevice *) ui->ui_addr; bp->b_flags |= B_ERROR; /* stop the dma */ imslam(imaddr); imaddr->wcr=0xFFFF; /* gently; -1 first... */ imaddr->wcr=0x0000; /* then zero */ /* reset device */ imslam(imaddr); /* fake an interrupt to clear software status */ imintr(UNIT(bp->b_dev)); } imp->count = 0; imp->bufp = 0; (void) spl0(); ubarelse(ui->ui_ubanum, &imp->ubinfo); imp->bp = 0; iodone(bp); wakeup((caddr_t)imp); return; bad: imp->state &= ~BUSY; bp->b_flags |= B_ERROR; iodone(bp); return; } imslam(imaddr) register struct imdevice *imaddr; { register i; imaddr->csr=MAINT; for(i=0; i<10; i++) ; imaddr->csr=0; for(i=0; i<10; i++) ; } imstart(ui) register struct uba_device *ui; { register int csr; register struct imdevice *imaddr = (struct imdevice *) ui->ui_addr; register struct imsoftc *imp = &imsoftc[UNIT(ui->ui_unit)]; csr = IENABLE|((imp->bufp>>12)&060) | H_INTER; imaddr->wcr = imp->count; imaddr->bar = imp->bufp; imaddr->csr = csr; /* No GO bit; let function codes settle */ imaddr->csr = csr|GO; } /*ARGSUSED*/ imioctl(dev, cmd, data) dev_t dev; int cmd; register caddr_t data; { register struct uba_device *ui = imdinfo[UNIT(dev)]; register struct imsoftc *imp = &imsoftc[UNIT(dev)]; int i; switch (cmd) { case IMRESET: if(i = imreset(dev)) return(i); break; default: return (ENOTTY); } return u.u_error; } /*ARGSUSED*/ imintr(dev) dev_t dev; { register struct imdevice *imaddr = (struct imdevice *) imdinfo[UNIT(dev)]->ui_addr; register struct imsoftc *imp = &imsoftc[UNIT(dev)]; register csr; imp->icnt++; if (imp->state&BUSY) { csr = imaddr->csr; if(csr & ERROR){ imaddr->eir |= ERROR; printf("im: CSR %x EIR %x\n", csr, imaddr->eir); } imaddr->csr = csr&~FUNCMASK; imp->state &= ~BUSY; wakeup((caddr_t)imp); } } imreset(dev) dev_t dev; { register int i; register struct uba_device *ui; register struct imsoftc *imp = imsoftc; register struct imdevice *imaddr = (struct imdevice *) imdinfo[UNIT(dev)]->ui_addr; int csr; printf("reset im%d", dev); if(!(imaddr->csr&S_RESET)) /* no power ? */ return(ENODEV); if(imp->state&BUSY) return(EBUSY); csr = H_INTER|H_STROBE|H_RESET; imaddr->csr = csr; return(0); }