/*#define NOERROR */ #define MOREASM /* * SY6522 disk driver */ #include "sys/param.h" #include "sys/config.h" #include "sys/mmu.h" #include "sys/types.h" #include "sys/sysmacros.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/errno.h" #include "sys/utsname.h" #include "sys/buf.h" #include "sys/elog.h" #include "sys/erec.h" #include "sys/iobuf.h" #include "sys/systm.h" #include "sys/var.h" #include "sys/ttold.h" #include "setjmp.h" #include "sys/profile.h" #include "sys/pport.h" #include "sys/d_profile.h" #include "sys/cops.h" #include "sys/swapsz.h" #ifdef notdef /* these are in d_profile.h */ #define logical(x) (minor(x) & 7) /* eight logicals per phys */ #define interleave(x) (minor(x) & 0x8) /* interleave bit for swaping */ #define physical(x) ((minor(x) & 0xF0) >> 4)/* 10 physical devs */ #endif char pro_secmap[NSEC] = { /*11*/ 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; /* Logical Units */ /* The first 100 blocks are reserved for the boot program and are inaccessible via unix. */ #define MAXBOOT 100 struct prlmap { daddr_t pm_beg; /* Base address in blocks */ daddr_t pm_len; /* Number of blocks in logical device */ } prlmap[] = { /* a */ {PRNSWAP+101, 16955}, /* root filesystem on 10 Meg. disk */ /* b */ {101, PRNSWAP}, /* swap area (2400 blocks) */ /* c */ {PRNSWAP+101, 7227}, /* root filesystem on 5 Meg. disk */ /* d */ {9728, 9728}, /* 2nd filesystem on 10 Meg. disk */ /* e */ {0, 0}, /* unused */ /* f */ {0, 7168}, /* old root filesystem (old a) */ /* g */ {7168, 2496}, /* old swap (old b) */ /* h */ {101, 19355}, /* f.s. using entire 10 Meg. disk */ }; /* THESE MAY REPLACE f AND g ABOVE */ /* f {4101, 15355}, /* alternate root f.s. on 10 Meg. disk */ /* g {101, 4000}, /* alternate swap */ struct iostat prostat[NPPDEVS]; struct iobuf protab = tabinit(PR0,prostat); /* active buffer header */ struct buf rprobuf; /* Raw input-output buffer */ struct proheader rphbuf; struct proheader phbuf; #ifndef NOERROR #define ERROR(x) printf("HARD DISK ERROR "); printf x char *pro_lefmt = "ASSERTION (%s) FAILED IN PROC %s "; #define ASSERT(e, p, m, x) if (!(e)) {\ printf(pro_lefmt,"e","p");\ printf m;\ printf ("\n");\ x;}; #else #define ERROR(x) #define ASSERT(e, p, m, x) #endif /* Contrast change ok flag. Maintained by the disk driver. When 0 the * parallel port is not in use and may be switched to allow console contrast * changes. If the contrast is waiting for the disk then 'l2_rcflag' is one. * When convenient and this is set the disk will call l2ramp. */ int ppinuse; extern char l2_rcflag; /* * proopen - check for existence of controller */ proopen(dev) { int i; register struct device_d *devp; int prointr(); extern char slot[]; i = physical(dev); if (i) { /* for expansion slot check slot number and type */ if (!PPOK(i) || (slot[PPSLOT(i)] != PR0)) { u.u_error = ENXIO; return 1; } } devp = pro_da[i]; u.u_error = 0; if (iocheck(&devp->d_ifr)) { { asm(" nop "); } if (prodata[i].pd_da != devp) { /* not already setup */ if (setppint((prodata[i].pd_da = devp),prointr)) goto fail; if (proinit(&prodata[i])) { freeppin(devp); goto fail; } } } else { fail: u.u_error = ENXIO; prodata[i].pd_da = (struct device_d *)0; return 1; } return 0; } /* * prostrategy - set up to start the transfer */ prostrategy(bp) register struct buf *bp; { int pun = physical(bp->b_dev); register struct prodata *p = &prodata[pun]; register struct buf *up; if (!p->pd_da) { printf("Attempt to read/write unopened profile device\n"); printf("bp=%x dev=%x (Unit %d)\n",bp,bp->b_dev,pun); p->pd_err = "device not open"; goto haderr; } if (bp->b_blkno >= 0 && (bp->b_blkno < prlmap[logical(bp->b_dev)].pm_len)) { bp->av_forw = (struct buf *)NULL; /* last of all bufs and */ bp->ul_forw = (struct buf *)NULL; /* last of units bufs */ SPL6(); /* must be highest of all ints for this code*/ if (protab.b_actf == NULL) protab.b_actf = bp; /* empty - put on front */ else protab.b_actl->av_forw = bp; /* else put at end */ protab.b_actl = bp; if (p->pd_actv == 0) { /* controller inactive */ p->pd_actv = bp; /* start of unit blk list */ /* Since we might fail before ever getting an interrupt * we must be prepared to do the buffer cleanup here also. */ while (prostart(p)) { /* start up the transfer */ bp->b_resid = bp->b_bcount; bp->b_flags |= B_ERROR; prorelse(p,bp); } } else { /* link onto unit list */ for (up=p->pd_actv; up->ul_forw; up=up->ul_forw) ; up->ul_forw = bp; } SPL0(); return; } p->pd_err = "invalid blkno"; haderr: bp->b_resid = bp->b_bcount; bp->b_flags |= B_ERROR; iodone(bp); return; } /* * Release finished buffer and unlink from list. Two lists are maintained. * The av_forw pointers are used to link all the buffers in use by the driver * onto the protab iobuf header. The av_back pointers (dubbed ul_forw) are * used to link together the buffers into unit lists (headed by the prodata * entry for that unit). */ prorelse (p, bp) register struct prodata *p; register struct buf *bp; { register struct buf *up; if (protab.b_actf == bp) { /* first buffer */ if ((protab.b_actf = bp->av_forw) == (struct buf *)0) protab.b_actl == (struct buf *)0; } else { /* middle or last buffer */ for (up=protab.b_actf; up->av_forw != bp; up=up->av_forw) if (!up->av_forw) panic("prorelse: buf list error"); up->av_forw = bp->av_forw; if (!up->av_forw && (protab.b_actl == bp)) protab.b_actl = up; } p->pd_actv = bp->ul_forw; /* next buf for this unit */ iodone(bp); } /* * proinit - initialize drive first time or after severe error */ proinit(p) struct prodata *p; { register char zero = 0; register struct device_d *devp = p->pd_da; int pl; pl = spl6(); devp->d_acr = zero; devp->d_pcr = 0x6B; /* set controller CA2 pulse mode strobe */ devp->d_ddra = zero; /* set port A bits to input **/ if (devp == PPADDR) devp->d_ddrb &= 0x5C; /* set BSY and OCD to input */ else devp->d_ddrb &= 0xFC; /* two or four port cards */ devp->d_ddrb |= 0x1C; /* set port B bits 2,3,4 to out */ devp->d_irb &= ~DEN; /* set enable = true */ devp->d_irb |= CMD|DRW; /* set command = false set direction = in */ devp->d_t2cl = zero; devp->d_t2ch = zero; devp->d_ier = FIRQ|FCA1; zero = devp->d_irb; if (zero & OCD) { p->pd_state = SERR; splx(pl); return 1; } p->pd_state = SCMD; splx(pl); return 0; } /* * proread - process read from disk */ proread(dev) dev_t dev; { physio(prostrategy, &rprobuf, dev, B_READ); } /* * prowrite - process write to disk */ prowrite(dev) dev_t dev; { physio(prostrategy, &rprobuf, dev, B_WRITE); } /* * prostart - initiate the next logical io operation */ prostart(p) register struct prodata *p; { register struct buf *bp = p->pd_actv; if (!bp) return 0; ASSERT(bp&&p,prostart,("bp=x%x p=x%x",bp,p),while(1)) p->pd_limit = prlmap[logical(bp->b_dev)].pm_beg;/* logical offset */ p->pd_blkno = bp->b_blkno + p->pd_limit; /* starting blk # */ #ifndef UNISOFT if (p->pd_blkno <= MAXBOOT) /* don't allow access to boot */ return(-1); #endif UNISOFT p->pd_limit += prlmap[logical(bp->b_dev)].pm_len; /* max blk # + 1*/ p->pd_bcount = bp->b_bcount; p->pd_addr = bp->b_un.b_addr; return procmd(bp->b_flags, bp->b_dev, p->pd_blkno, (unsigned)p->pd_bcount); } /* * Procmd - initiate next physical io operation */ procmd(func, dev, bn, ct) register daddr_t bn; unsigned ct; { register int pun = physical(dev); register struct prodata *p = &prodata[pun]; register struct cmd *pc; ASSERT(ct!=0,Procmd,("ct=%d",ct),while(1)) #ifndef UNISOFT if (bn <= MAXBOOT) /* check again to be sure */ return(-1); #endif UNISOFT if (p->pd_state == SERR) { if (proinit(p)) /* initialize drive */ return -1; } /* controller should not be busy now */ /* Build Command (ok to send extra bytes on write cmd) */ pc = &p->pd_cmdb; pc->p_cmd = (func & B_READ) ? PROREAD : PROWRITE; pc->p_high = bn >> 16; pc->p_mid = bn >> 8; if (interleave(dev) == 0) pc->p_low = bn; else pc->p_low = (bn & 0xF0) | pro_secmap[bn&0xF]; pc->p_retry = 10; pc->p_thold = 3; p->pd_state = SCMD; p->pd_nxtst = SCMD; if (prochk(p,SCMD)) { /* sync controller to cmd state */ if (!prochk(p,SCMD)) /* if it failed it should work now */ return 0; p->pd_err = "cant force disk into CMD state"; p->pd_state = SERR; return -1; } return 0; } prointr(pun) int pun; { register struct device_d *devp; /* a5 */ register char *cp; /* a4 */ register char csum; /* d7 */ /*NOTUSED*/ register char zero = 0; /* d6 */ register struct buf *bp; register struct prodata *p = &prodata[pun]; register short i; struct proheader *ph; devp = p->pd_da; (void) spl6(); /* added April 4/84 to prevent panic in prorelse */ /* changed from spl5 August 30/84 to fix multi-user bug on 2/10 */ /* ASSERT((stats&BSY)==BSY,prointr,("disk %d busy: state=%d, irb=x%x",pun,p->pd_state,stats),return(devp->d_ifr = devp->d_ifr)) */ #ifdef lint csum = 0; i = csum; #endif if ((bp = p->pd_actv) == 0) { /* spurious interrupt */ /* printf("Spurious INT on profile dev %d [at %x]\n",pun,devp); */ devp->d_ddrb |= 0x80; /* setup for a reset */ devp->d_irb |= 0x80; devp->d_ddrb &= 0x7F; devp->d_ifr = devp->d_ifr; /* reset interrupt trap */ /* proinit(p); */ return; } /* ASSERT(bp!=0,prointr,("dsk=%d ier=x%x ifr=x%x state=%d",pun,devp->d_ier&255,devp->d_ifr&255,p->pd_state),return devp->d_ifr=0x7F) */ ASSERT((p->pd_state!=SERR&&p->pd_state!=SSTOP),prointr,("b_flags=0x%x",bp->b_flags),while(1)) ASSERT(physical(bp->b_dev)==pun,prointr,("dev=x%x unit=%d",bp->b_dev,pun),while(1)) devp->d_ifr = devp->d_ifr; /* reset interrupt trap */ /* * Note that IO operations fail when OCD. * This may result in a `panic'. Allowing it to * block and be restarted has it problems also. */ if (devp->d_irb & OCD) { /* cable disconnected ? */ p->pd_err = "Open Cable Disconnect"; goto haderr; } if (p->pd_state == SCMD) { /* Send command */ devp->d_irb &= ~DRW; /* set dir=out */ devp->d_ddra = 0xFF; /* set port A bits to output */ /* Now send command */ cp = (char *)(&p->pd_cmdb); if (*cp == PROREAD) p->pd_nxtst = SRDBLK; else p->pd_nxtst = SWRTD; devp->d_ira = *cp++; devp->d_ira = *cp++; devp->d_ira = *cp++; devp->d_ira = *cp++; devp->d_ira = *cp++; devp->d_ira = *cp; devp->d_irb |= DRW; devp->d_ddra = zero; if (prochk(p,p->pd_nxtst)) { p->pd_err = "failed to issue cmd to disk"; goto haderr; } return; /* will send data or get status next */ } if (p->pd_state == SRDBLK || p->pd_state == SFINI) {/* Read status word */ devp->d_irb |= DRW; devp->d_ddra = zero; p->pd_sbuf = 0; cp = (char *)(&p->pd_sbuf); *cp++ = devp->d_ira; *cp++ = devp->d_ira; *cp++ = devp->d_ira; *cp = devp->d_ira; p->pd_sbuf &= ~STATMSK; /* mask off redund stat bits */ if (p->pd_sbuf) { ERROR(("dev %d: state=%d status=x%x\n",p-prodata,p->pd_state,p->pd_sbuf)); p->pd_err = "bad status"; goto haderr; } if (p->pd_state == SRDBLK) { /* Read successful so pickup data*/ ASSERT(p->pd_bcount>0,prointr,(""),goto haderr) i = sizeof(rphbuf) - 1; /* sizeof header */ cp = (char *)(&rphbuf); do *cp++ = devp->d_ira; while (--i != -1); cp = p->pd_addr; i = min(SECSIZE, (unsigned)p->pd_bcount); #ifdef MOREASM if ((i & 3) == 0) { i = (i >> 2) - 1; do { asm (" movb a5@(9),a4@+ "); asm (" movb a5@(9),a4@+ "); asm (" movb a5@(9),a4@+ "); asm (" movb a5@(9),a4@+ "); /* asm (" movb a5@(9),d6 "); */ /* asm (" movb d6,a4@+ "); */ /* asm (" eorb d6,d7 "); */ /* asm (" movb a5@(9),d6 "); */ /* asm (" movb d6,a4@+ "); */ /* asm (" eorb d6,d7 "); */ /* asm (" movb a5@(9),d6 "); */ /* asm (" movb d6,a4@+ "); */ /* asm (" eorb d6,d7 "); */ /* asm (" movb a5@(9),d6 "); */ /* asm (" movb d6,a4@+ "); */ /* asm (" eorb d6,d7 "); */ } while (--i != -1); /* optimizes to DBRA */ } else { #endif i--; do { asm (" movb a5@(9),a4@+ "); /* asm (" movb a5@(9),d6 "); */ /* asm (" movb d6,a4@+ "); */ /* asm (" eorb d6,d7 "); */ } while (--i != -1); #ifdef MOREASM } #endif } /* Determine if IO operation is completed or spans another block. */ p->pd_bcount -= min(SECSIZE, (unsigned)p->pd_bcount); if (p->pd_bcount <= 0 || ++p->pd_blkno >= p->pd_limit) { bp->b_resid = p->pd_bcount; prorelse(p,bp); if (p-prodata == 0) ppinuse = 0; /* contrast */ if (prostart(p)) { /* startup next buf in chain */ p->pd_err = "prostart failed on next blk"; goto haderr; } return; /* next op started or no next op */ } p->pd_addr += SECSIZE; /* setup for next block of io trans */ if(procmd(bp->b_flags,bp->b_dev,p->pd_blkno,(unsigned)p->pd_bcount)){ p->pd_err = "Procmd failed to continue operation"; goto haderr; } return; /* end of i/o or beg of next blk */ } if (p->pd_state == SWRTD) { /* send data */ p->pd_nxtst = SFINI; ASSERT(p->pd_bcount>0,prointr,(""),while(1)) devp->d_irb &= ~DRW; /* set dir=out */ devp->d_ddra = 0xFF; /* set port A bits to output */ ph = &phbuf; ph->ph_fileid = p->pd_blkno ?0 :0xAAAA; i = sizeof(phbuf) - 1; cp = (char *)(ph); do devp->d_ira = *cp++; while (--i != -1); cp = (char *)p->pd_addr; /* place to get data from */ i = min(SECSIZE, (unsigned)p->pd_bcount); #ifdef MOREASM if ((i & 3) == 0) { i = (i >> 2) - 1; do { asm (" movb a4@+,a5@(9) "); asm (" movb a4@+,a5@(9) "); asm (" movb a4@+,a5@(9) "); asm (" movb a4@+,a5@(9) "); /* asm (" movb a4@+,d6 "); */ /* asm (" eorb d6,d7 "); */ /* asm (" movb d6,a5@(9) "); */ /* asm (" movb a4@+,d6 "); */ /* asm (" eorb d6,d7 "); */ /* asm (" movb d6,a5@(9) "); */ /* asm (" movb a4@+,d6 "); */ /* asm (" eorb d6,d7 "); */ /* asm (" movb d6,a5@(9) "); */ /* asm (" movb a4@+,d6 "); */ /* asm (" eorb d6,d7 "); */ /* asm (" movb d6,a5@(9) "); */ } while (--i != -1); /* optimizes to DBRA */ } else { #endif i--; do { asm (" movb a4@+,a5@(9) "); /* asm (" movb a4@+,d6 "); */ /* asm (" eorb d6,d7 "); */ /* asm (" movb d6,a5@(9) "); */ } while (--i != -1); #ifdef MOREASM } #endif if (prochk(p,SPERFORM)) { p->pd_err = "didn't get to perfrom state"; goto haderr; } return; /* will pick up status next intr */ } p->pd_err = "invalid state"; haderr: do { bp->b_resid = p->pd_bcount; bp->b_flags |= B_ERROR; ERROR(("dev %d: %s\n",p-prodata,p->pd_err)); prorelse(p,bp); p->pd_state = SERR; } while (prostart(p)); } /* * Get in sync with disk. * (subroutine FINDD2 & CHKRSP from 'profrom.text' document) * Expects enable==true and Direction==in at start. * If disk response is 'state' then returns 0 (success) * otherwise fails (returns -1 if timeout and cur state if bad state). */ prochk(p, state) register struct prodata *p; { register struct device_d *devp = p->pd_da; register zero = 0; register i; int resp; /* while((devp->d_irb&BSY)==0); */ ASSERT((devp->d_irb&BSY)==BSY,prochk,("state=%d [waiting]",state),goto err) /* while ((devp->d_irb&BSY)==0); */ if (state == SCMD && (p-prodata == 0)) {/* PP inuse */ if (l2_rcflag) /* ramp in progress */ l2ramp(2); /* so help it along */ ppinuse = 1; } devp->d_irb |= DRW; /* set input mode */ devp->d_ddra = zero; /* set port A bits to input */ devp->d_irb &= ~CMD; /* set cmd and enable bufs */ i = RSPTIME; /* about 1ms */ while (devp->d_irb&BSY && i--); /* wait sig that resp byte is ready*/ resp = PIDL; /* reply to use if resp byte wrong */ if (i > 0) { /* didn't timeout */ i = devp->d_ira; /* get response from disk */ if (i == state) /* got correct state */ resp = PGO; /* reply to use if resp byte correct*/ devp->d_irb &= ~(DRW|CMD); /* set dir=out cmd=true */ devp->d_ddra = 0xFF; /* set port A bits to output */ devp->d_ira = resp; /* send reply (GO or RESET) */ devp->d_ier = FIRQ|FCA1; /* enable interrupts */ devp->d_irb |= CMD; /* sig disk to read resp */ p->pd_state = p->pd_nxtst; /* setup next state */ return (i == state) ?0 :i; } err: if (p-prodata == 0) ppinuse = 0; /* reset ppinuse flag */ p->pd_state = SERR; p->pd_err = "EXCESSIVE DISK DELAY -- (is the drive plugged in??)"; ERROR(("dev %d: %s\n",p-prodata,p->pd_err)); devp->d_ddra = zero; /* set port A bits to input */ devp->d_irb |= CMD|DRW; /* set dir=in, disable buffers */ devp->d_ier = ~FIRQ; /* disable all interrupts */ return (-1); } /* ARGSUSED */ proioctl(dev, cmd, addr, flag) dev_t dev; int cmd; caddr_t addr; int flag; /*NOTUSED*/ { struct prodata *p = &prodata[physical(dev)]; switch (cmd) { case TIOCGETP: if (copyout((caddr_t)&p->pd_flags, addr, sizeof(p->pd_flags))) u.u_error = EFAULT; break; case TIOCSETP: if (copyin(addr, (caddr_t)&p->pd_flags, sizeof(p->pd_flags))) u.u_error = EFAULT; break; default: u.u_error = ENOTTY; } } proprint(dev, str) char *str; { printf("%s on pro drive %d, slice %d\n", str, (dev>>4)&0xF, dev&7); }