/* * DSA disk class driver * drives RA-class disks * hooked up through an MSCP port */ #include "sys/param.h" #include "sys/buf.h" #include "sys/udaioc.h" #include "sys/diskio.h" #include "sys/ra.h" #include "sys/mscp.h" #include "sys/user.h" #include "sys/file.h" #include "sys/conf.h" extern struct msaddr raaddr[]; extern struct radisk radisk[]; extern int racnt; static long rarefno; /* ref seq num */ extern struct buf rabuf[]; static struct buf rctbuf; /* for reading replacement table */ static rareplace(), raonline(), rasonl(), racinit(); int raopen(), raread(), rawrite(), raioctl(), rastrategy(), raclose(); struct bdevsw rabdev = bdinit(raopen, raclose, rastrategy, 0); struct cdevsw racdev = cdinit(raopen, raclose, raread, rawrite, raioctl); /* * UNIT(d) == logical unit number; * the physical unit number is in raaddr[UNIT(d)].unit * * 0100 in the minor device is (temporarily?) * usurped to indicate bitmapped file systems * quietly ignore it for now * when things improve, change the UNIT mask to 037 */ #define UNIT(dev) (((dev)>>3) & 027) #define PART(dev) ((dev)&07) #define HUGE 0x7fffffff /* very large 32-bit number */ /* * default partition sizes */ static struct size { daddr_t nblocks; daddr_t blkoff; } ra_sizes[NRAPART] = { 11*960, 0*960, /* 0 = cyl 0 thru 10 */ 21*960, 11*960, /* 1 = cyl 11 thru 31 */ 242*960, 32*960, /* 2 = cyl 32 thru 273 */ 242*960, 274*960, /* 3 = cyl 274 thru 515 */ 242*960, 516*960, /* 4 = cyl 516 thru 757 */ 84*960, 758*960, /* 5 = cyl 758 thru 841 */ 842*960, 0*960, /* 6 = all cyl 0 thru 841 */ }; /* * reused bits of buf/iobuf struct */ #define b_next av_forw /* next buffer in queue */ #define b_pkt av_back /* pointer to mscp command */ #define b_crf b_resid /* saved refno for pending command */ /* * flags in radisk.flags */ #define ONLINE 01 /* drive is online */ #define WONLINE 02 /* waiting for online */ #define GOTDI 04 /* got unit info */ #define RPLOCK 010 /* replacement in progress */ #define RPWANT 020 /* want RPLOCK */ #define RPDONE 040 /* replacement done */ #define SPDOWN 0100 /* spin down on last close */ /* * random numbers */ #define PRIONL (PZERO-1) #define SECTOR 512 /* size of an MSCP sector */ #define IDRA 0 /* connection ID for MSCP */ /* * open a drive, if necessary */ int raseql(), radg(); raopen(dev, flag) dev_t dev; { register int unit; register struct radisk *ra; register struct msaddr *rp; register int part; unit = UNIT(dev); if (unit >= racnt) { u.u_error = ENXIO; return; } ra = &radisk[unit]; rp = &raaddr[unit]; if (ra->open == 0) { if (rp->ctype < 0 || rp->ctype >= nmsport || (ra->port = msportsw[rp->ctype]) == NULL) { u.u_error = ENXIO; return; } ra->di.radsize = HUGE; if ((*ra->port->mp_init)(rp->ctl, rp->ctype, 0, IDRA, raseql, radg) == 0) { u.u_error = ENXIO; return; } racinit(ra, rp); } part = PART(dev); if ((ra->pinit & (1<<part)) == 0) { ra->nblocks[part] = ra_sizes[part].nblocks; ra->blkoff[part] = ra_sizes[part].blkoff; ra->pinit |= (1<<part); } ra->open |= 1<<part; spl6(); if ((ra->flags & ONLINE) == 0) raonline(ra, rp); spl0(); if ((ra->flags & ONLINE) == 0) u.u_error = ENXIO; } raclose(dev) { register struct radisk *ra; register struct msaddr *rp; register struct mscmd *mp; ra = &radisk[UNIT(dev)]; rp = &raaddr[UNIT(dev)]; ra->open &=~ (1<<PART(dev)); if (ra->open || (ra->flags & ONLINE) == 0) return; mp = (*ra->port->mp_get)(rp->ctl); mp->m_crf = ++rarefno; mp->m_unit = rp->unit; mp->m_opcd = OPAVL; /* put it offline */ if ((ra->flags & SPDOWN) == 0) mp->m_mod = 0; else { mp->m_mod = MDSPD; ra->flags &=~ SPDOWN; } mp->m_unfl = 0; mp->m_dvpm = 0; /* ? */ ra->flags &=~ ONLINE; (*ra->port->mp_send)(rp->ctl, IDRA, mp); } int rastrategy(); raread(dev) { physio(rastrategy, &rabuf[UNIT(dev)], dev, B_READ, minphys); } rawrite(dev) { physio(rastrategy, &rabuf[UNIT(dev)], dev, B_WRITE, minphys); } /* * strategy routine; * used as strategy by all port drivers * send the packet right away */ rastrategy(bp) register struct buf *bp; { register struct radisk *ra; register struct mscmd *mp; register int unit; register int part; register struct msaddr *rp; int count; daddr_t limit; unit = UNIT(minor(bp->b_dev)); part = PART(minor(bp->b_dev)); ra = &radisk[unit]; rp = &raaddr[unit]; limit = ra->di.radsize - ra->blkoff[part]; if (limit > ra->nblocks[part]) limit = ra->nblocks[part]; if (bp->b_blkno >= limit && bp != &rctbuf) { if (bp->b_blkno == ra->nblocks[part]) bp->b_resid = bp->b_bcount; else { bp->b_error = ENOSPC; bp->b_flags |= B_ERROR; } iodone(bp); return; } count = bp->b_bcount; if (count/SECTOR + bp->b_blkno > limit && bp != &rctbuf) count = (limit - bp->b_blkno) * SECTOR; spl6(); if ((ra->flags & ONLINE) == 0 && raonline(ra, rp) == 0) { bp->b_flags |= B_ERROR; iodone(bp); spl0(); return; } mp = (*ra->port->mp_get)(rp->ctl); mp->m_crf = ++rarefno; mp->m_unit = rp->unit; mp->m_opcd = (bp->b_flags & B_READ) ? OPRD : OPWR; mp->m_mod = 0; mp->m_bcnt = count; mp->m_lbn = bp->b_blkno + ra->blkoff[part]; (*ra->port->mp_map)(rp->ctl, mp, bp); bp->b_pkt = (struct buf *)mp; bp->b_crf = mp->m_crf; bp->b_next = NULL; if (ra->actf) ra->actl->b_next = bp; else ra->actf = bp; ra->actl = bp; (*ra->port->mp_send)(rp->ctl, IDRA, mp); spl0(); } /* * ioctl * principally for bad block replacement */ raioctl(dev, cmd, addr, flag) dev_t dev; caddr_t addr; { register union arg { struct ud_rctbuf r; struct ud_repl b; } *uap; register struct radisk *ra; register int i; long parts[2]; ra = &radisk[UNIT(dev)]; if ((ra->flags & (ONLINE|GOTDI)) != (ONLINE|GOTDI)) { /* should bring it online here */ u.u_error = EIO; return; } uap = (union arg *)addr; switch(cmd) { default: u.u_error = ENOTTY; return; case DIOSSIZ: if ((flag & FWRITE) == 0) { u.u_error = EBADF; return; } if (copyin(addr, (caddr_t)parts, sizeof(parts)) < 0) { u.u_error = EFAULT; return; } ra->blkoff[PART(dev)] = parts[0]; ra->nblocks[PART(dev)] = parts[1]; return; case DIOGSIZ: parts[0] = ra->blkoff[PART(dev)]; parts[1] = ra->nblocks[PART(dev)]; if (copyout((caddr_t)parts, addr, sizeof(parts)) < 0) u.u_error = EFAULT; return; case UIOCHAR: if (copyout((caddr_t)&ra->di, addr, sizeof(struct ud_unit))) u.u_error = EFAULT; return; case UIORRCT: if(uap->r.lbn < 0 || uap->r.lbn > ra->di.rctsize) { u.u_error = EIO; return; } /* * try different copies until one works */ for (i = 0; i < ra->di.copies; i++) { u.u_count = SECTOR; /* block size on disk */ u.u_offset = ltoL((uap->r.lbn + ra->di.radsize) * SECTOR); u.u_offset = Lladd(u.u_offset,i * ra->di.rctsize * SECTOR); u.u_base = uap->r.buf; u.u_segflg = SEGUDATA; u.u_error = 0; physio(rastrategy, &rctbuf, dev, B_READ, minphys); if (u.u_error == 0) break; } return; case UIOWRCT: if ((flag & FWRITE) == 0) { u.u_error = EBADF; return; } if(uap->r.lbn < 0 || uap->r.lbn > ra->di.rctsize) { u.u_error = EIO; return; } /* * write every copy we can * should do read-after-write */ for (i = 0; i < ra->di.copies; i++) { u.u_count = SECTOR; /* block size on disk */ u.u_offset = ltoL((uap->r.lbn + ra->di.radsize) * SECTOR); u.u_offset = Lladd(u.u_offset, i * ra->di.rctsize * SECTOR); u.u_base = uap->r.buf; u.u_segflg = SEGUDATA; physio(rastrategy, &rctbuf, dev, B_WRITE, minphys); u.u_error = 0; } return; case UIOREPL: if ((flag & FWRITE) == 0) { u.u_error = EBADF; return; } rareplace(dev, uap->b.lbn, uap->b.replbn, uap->b.prim); return; case UIOSPDW: ra->flags |= SPDOWN; return; case UIORST: (*ra->port->mp_init)(raaddr[UNIT(dev)].ctl, raaddr[UNIT(dev)].ctype, 1, IDRA, raseql, radg); return; } } static rareplace(dev, badlbn, replbn, prim) int dev; daddr_t badlbn; daddr_t replbn; int prim; { register struct mscmd *mp; register struct radisk *ra; register struct msaddr *rp; register int unit; unit = UNIT(minor(dev)); ra = &radisk[unit]; rp = &raaddr[unit]; spl6(); while (ra->flags & RPLOCK) { ra->flags |= RPWANT; sleep((caddr_t)&ra->flags, PZERO + 1); } ra->flags |= RPLOCK; printf("ra%d replace %D with %D\n", unit, badlbn, replbn); mp = (*ra->port->mp_get)(rp->ctl); bzero((caddr_t)mp, sizeof(struct mscmd)); /* clear reserved fields */ mp->m_crf = ++rarefno; mp->m_unit = rp->unit; mp->m_opcd = OPRPL; mp->m_rbn = replbn; mp->m_lbn = badlbn; mp->m_mod = prim ? MDPRI : 0; ra->cmdcrf = mp->m_crf; ra->cmdopc = OPRPL; (*ra->port->mp_send)(rp->ctl, IDRA, mp); while ((ra->flags & RPDONE) == 0) sleep((caddr_t)&ra->rplret, PZERO); u.u_error = ra->rplret; if (ra->flags & RPWANT) wakeup((caddr_t)&ra->flags); ra->flags &=~ (RPWANT|RPLOCK|RPDONE); spl0(); } /* * here when the port gets a sequential message */ raseql(ctl, type, ep) int ctl, type; register struct msend *ep; { register struct buf *bp; register struct radisk *ra; register int unit; register struct buf *obp; int sts; if (ep->m_opcd == 0 && ep->m_sts == STRST) { rareset(ctl); return; } for (unit = 0; unit < racnt; unit++) if (raaddr[unit].ctl == ctl && raaddr[unit].ctype == type && raaddr[unit].unit == ep->m_unit) break; if (unit >= racnt) { printf("ra%d ctl%d typ%d: stray mscp packet sts x%x opcode %o\n", ep->m_unit, ctl, type, ep->m_sts, ep->m_opcd); return; } ra = &radisk[unit]; sts = ep->m_sts & STMSK; if (sts == STAVL || sts == STOFL) ra->flags &=~ ONLINE; /* help! */ illcmd: switch (ep->m_opcd & 0377) { case OPEND: /* eg invalid command */ if (ep->m_crf == ra->cmdcrf) { ep->m_opcd = ra->cmdopc | OPEND; goto illcmd; } /* else check for pending read or write */ case OPRD|OPEND: case OPWR|OPEND: for (bp = ra->actf, obp = NULL; bp; obp = bp, bp = bp->b_next) if (ep->m_crf == bp->b_crf) break; if (bp == NULL) { printf("ra%d stray end: crf %d sts x%x opcode 0%o\n", unit, ep->m_crf, ep->m_sts, ep->m_opcd & 0377); return; } if (((struct mscmd *)(bp->b_pkt))->m_crf != ep->m_crf) printf("ra%d sent %d got %d crf; flg %x dev %x\n", unit, ((struct mscmd *)(bp->b_pkt))->m_crf, ep->m_crf, bp->b_flags, bp->b_dev); if (obp) obp->b_next = bp->b_next; else ra->actf = bp->b_next; if (bp == ra->actl) ra->actl = obp; bp->b_resid = bp->b_bcount - ep->m_bcnt; if (sts != STSUC) { bp->b_flags |= B_ERROR; if (ep->m_sts == STBCK || ep->m_sts == STBK2) /* optical disk blank check */ bp->b_error = ENXIO; else printf("err on ra%d block %D: sts x%x\n", unit, bp->b_blkno, ep->m_sts); } (*ra->port->mp_unmap)(ctl, (struct mscmd *)bp->b_pkt); iodone(bp); return; case OPONL|OPEND: rasonl(ra, ep); return; case OPAVL|OPEND: ra->flags &=~ ONLINE; return; case OPGUS|OPEND: if (sts != STSUC) { printf("ra%d: can't get unit sts x%x\n", unit, ep->m_sts); return; } ra->di.medium = ep->m_medi; ra->di.tracksz = ep->m_trck; ra->di.groupsz = ep->m_grp; ra->di.cylsz = ep->m_cyl; ra->di.rctsize = ep->m_rcts; ra->di.rbns = ep->m_rbns; ra->di.copies = ep->m_rctc; ra->flags |= GOTDI; return; case OPSCC|OPEND: if (sts != STSUC) printf("ra ctl%d typ%d: bad init\n", ctl, type); return; case OPRPL|OPEND: ra->rplret = 0; if (sts != STSUC) { printf("ra%d: rpl sts x%x\n", unit, ep->m_sts); ra->rplret = EIO; } ra->flags |= RPDONE; wakeup((caddr_t)&ra->rplret); return; default: printf("ra%d ctl%d typ%d: stray mscp msg opcd 0%o sts x%x\n", ep->m_unit, ctl, type, ep->m_opcd&0377, ep->m_sts); return; } } /* * controller was reset * discard all pending io, * awake all sleepers, * mark everything offline */ rareset(ctl) int ctl; { register int unit; register struct radisk *ra; register struct buf *bp, *nbp; for (unit = 0; unit < racnt; unit++) { if (raaddr[unit].ctl != ctl) continue; ra = &radisk[unit]; for (bp = ra->actf; bp; bp = nbp) { nbp = bp->b_next; (*ra->port->mp_unmap)(ctl, (struct mscmd *)bp->b_pkt); bp->b_flags |= B_ERROR; iodone(bp); } ra->actf = ra->actl = NULL; ra->flags &=~ (ONLINE|WONLINE); wakeup((caddr_t)ra); } } /* * here with a datagram message * explanations really shouldn't be in the driver * * the hack for event 8 ignores error packets from * the US Design optical disk controller * that really mean `read a blank spot on the disk */ static char *raevents[] = { "ok", "inv cmd", "op aborted", "offline", "available", "med fmt", "write prot", "comp err", "data err", "host buf access err", "cntl err", "drive err", }; #define MAXEVT 0xb radg(ctl, type, ep) int ctl, type; register struct mserl *ep; { register u_short *sp; /* for sdi crap */ register int i; /* for useless design crap */ register unsigned char *cp; /* for useless design crap */ if (ep->l_fmt == FMDSK && ep->l_evnt == STBCK && ep->l_flgs == 0) return; printf("ra%d ctl%d typ%d seq %d: %s err; fmt x%x ev x%x fl x%x\n", ep->l_unit, ctl, type, ep->l_seq, /* phys unit, not log */ ep->l_flgs&(LFSUC|LFCON) ? "soft" : "hard", ep->l_fmt, ep->l_evnt, ep->l_flgs&0377); if ((ep->l_evnt & STMSK) <= MAXEVT) printf("%s; ", raevents[ep->l_evnt & STMSK]); switch (ep->l_fmt) { case FMCNT: /* now the thing should be marked disastrously bad */ printf("oops\n"); break; case FMBAD: printf("host mem access; addr x%x\n", ep->l_badr); break; case FMDSK: printf("%sbn %d; lev x%x, retry x%x\n", (ep->l_hdcd & 0xf0000000) == 0 ? "l" : "r", ep->l_hdcd & 0x0fffffff, ep->l_lvl, ep->l_rtry); break; case FMSDI: printf("%sbn %d;", (ep->l_hdcd & 0xf0000000) == 0 ? "l" : "r", ep->l_hdcd & 0x0fffffff); /* * print the bytes in the same order used * by the dec diagnostics */ sp = (u_short *)&ep->l_sdi[3]; while (sp > (u_short *)ep->l_sdi) printf(" %x", *--sp); printf(" xx\n"); break; case FMSMD: printf("cyl %d\n", ep->l_sdi[1]); break; case 0x40: /* hack for useless design */ printf("scsi:"); cp = (unsigned char *)ep->l_sdi; for(i = 0; i < 10; i++) printf(" %x", *cp++); printf(" [%x %x]\n", cp[0], cp[1]); break; default: printf("\n"); break; } } /* * unit is believed offline * try to bring it on */ static raonline(ra, rp) register struct radisk *ra; register struct msaddr *rp; { register struct mscmd *mp; int s; s = spl6(); if ((ra->flags & WONLINE) == 0) { ra->flags &=~ GOTDI; mp = (*ra->port->mp_get)(rp->ctl); bzero((caddr_t)mp, sizeof(struct mscmd)); /* clear reserved fields */ mp->m_crf = ++rarefno; mp->m_unit = rp->unit; mp->m_opcd = OPONL; ra->cmdcrf = mp->m_crf; ra->cmdopc = OPONL; (*ra->port->mp_send)(rp->ctl, IDRA, mp); ra->flags |= WONLINE; } while (ra->flags & WONLINE) tsleep((caddr_t)ra, PRIONL, 60); if ((ra->flags & ONLINE) == 0) return (0); if ((ra->flags & GOTDI) == 0) { mp = (*ra->port->mp_get)(rp->ctl); mp->m_crf = ++rarefno; mp->m_unit = rp->unit; mp->m_opcd = OPGUS; mp->m_mod = 0; mp->m_unfl = 0; mp->m_dvpm = 0; /* ? */ (*ra->port->mp_send)(rp->ctl, IDRA, mp); } splx(s); return (1); } static rasonl(ra, ep) register struct radisk *ra; register struct msend *ep; { if (ra->flags & WONLINE) { ra->flags &=~ WONLINE; wakeup((caddr_t)ra); } if ((ep->m_sts & STMSK) != STSUC) return; ra->flags |= ONLINE; if (ra->di.radsize != HUGE && ra->di.radsize != ep->m_unsz) printf("ra%d: changed size %d to %d\n", ra-radisk, ra->di.radsize, ep->m_unsz); ra->di.radsize = ep->m_unsz; } /* * controller init * set characteristics to turn off host timeouts */ static racinit(ra, rp) struct radisk *ra; struct msaddr *rp; { register struct mscmd *mp; register int s; mp = (*ra->port->mp_get)(rp->ctl); mp->m_crf = ++rarefno; mp->m_unit = rp->unit; mp->m_opcd = OPSCC; mp->m_mod = 0; mp->m_cntf = CFMSC | CFTHS; mp->m_vrsn = MSCPVER; mp->m_htmo = 0; /* no timeout */ mp->m_time[0] = mp->m_time[1] = 0L; s = spl6(); (*ra->port->mp_send)(rp->ctl, IDRA, mp); splx(s); }