/* #define HOWFAR */ /* #define WBBLK /* allows writing block 0 */ /* #define UNISOFT /* allows access to restricted blocks on boot disk */ #define KLUDGE /* kludge for format to work */ /* * (C) Copyright 1983 UniSoft Systems of Berkeley CA * * Sony driver * and eject 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/uioctl.h" #include "sys/al_ioctl.h" #include "sys/diskformat.h" #include "setjmp.h" #include "sys/pport.h" #include "sys/sony.h" #include "sys/cops.h" #define NRETRY 5 #define physical(d) ((minor(d)>>4)&0xF) /* physical unit number 0-15 */ #define logical(d) (minor(d)&0x7) /* logical unit number, 0-7 */ #define splsn spl1 #define GETBUF(bp) splsn(); \ while (bp->b_flags & B_BUSY) { \ bp->b_flags |= B_WANTED; \ (void)sleep((caddr_t)bp, PRIBIO+1); \ } \ bp->b_flags |= B_BUSY; \ spl0() #define FREEBUF(bp) splsn(); \ if (bp->b_flags & B_WANTED) \ wakeup((caddr_t)bp); \ bp->b_flags = 0; \ spl0() struct buf snhbuf; /* header buffer (for reading block 0) */ struct buf sncbuf; /* command buffer */ struct buf snrbuf; struct iostat snstat[NSN]; struct iobuf sntab = tabinit(SN1,snstat); /* active buffer header */ #ifdef WBBLK char sn_bblk[512]; #endif WBBLK char sn_bcount[NSN*2]; /* block opens */ char sn_ccount[NSN*2]; /* character opens */ #define count(d) (d<<1) /* first char is how many opens */ #define boot(d) ((d<<1)+1) /* 2nd char (sn_bcount only) is whether a boot disk */ char snbf; /* index into sn_fns array */ #ifdef KLUDGE char noerror; /* read before format is acceptable error */ #endif KLUDGE struct sn_sizes { daddr_t sn_offset, sn_size; } sn_sizes[] = { 0, 800, /* a = filesystem */ 201, 599, /* b = filesystem on a boot Sony */ 0, 201, /* c = 1-100 for lisa (serialization), 101-200 for boot program */ 0, 0, /* d = unused */ 0, 0, /* e = unused */ 0, 0, /* f = unused */ 0, 0, /* g = unused */ 0, 800 /* h = entire disk */ }; /* * called from oem7init at (at least) level 1, so won't be interrupted * by parallel port 0 or sony */ sninit() { char c = 0x00; /* to avoid clear byte intstructions */ if (SNIOB->type) { printf("Microdiskette with %d head%s\n", SNIOB->type, (SNIOB->type==1)?"":"s"); } else { printf("Unix sninit: not a sony drive\n"); return; } SNIOB->drive=0x80; /* always lower drive */ SNIOB->side = c; /* always first side */ SNIOB->mask= 0xff; /* clear ints and enable */ if (snwaitrdy()) { printf("Unix sninit: command to clear status failed\n"); return; } if (SNIOB->drv_connect != 0xff) { printf("Unix sninit: no drive connected\n"); return; } SNIOB->gobyte=SN_CLRST; SNIOB->mask =0x80; /* enable interrupts */ if (snwaitrdy()) { printf("Unix sninit: command to clear status failed\n"); } SNIOB->gobyte=SN_STMASK; } /* * check block 0 to see whether this is a boot disk */ sncz(dev) register dev_t dev; { static char hp[BSIZE]; register struct buf *bp = &snhbuf; register rc = 0; GETBUF(bp); sn_bcount[boot(physical(dev))] = 0; /* for now set to non-boot */ bp->b_bcount = BSIZE; bp->b_dev = dev | 7; /* set logical unit 7 */ bp->b_blkno = 0; bp->b_un.b_addr = hp; bp->b_flags |= B_READ; snbf = 1; snstrategy(bp); iowait(bp); if (bp->b_flags & B_ERROR) { rc++; snbf = 0; } FREEBUF(bp); return(rc); } /* * don't really check whether it's a boot disk */ /* ARGSUSED */ snconf(dev) dev_t dev; { snbf = 1; return(0); } struct sn_fns { int (*fnc)(); } sn_fns[] = { sncz, /* the real routine to check block 0 */ snconf /* fake routine to confuse */ }; snopen(dev) register dev_t dev; { dev = physical(dev); if (dev >= NSN) { u.u_error = ENXIO; return(-1); } return(0); } snbopen(dev) register dev_t dev; { if (snopen(dev) == 0) sn_bcount[count(physical(dev))]++; } sncopen(dev) register dev_t dev; { if (snopen(dev) == 0) sn_ccount[count(physical(dev))]++; } snclose(dev) register dev_t dev; { if (physical(dev) >= NSN) { u.u_error = ENXIO; return(-1); } snbf = 0; return(0); } snbclose(dev) register dev_t dev; { if (snclose(dev) == 0) sn_bcount[count(physical(dev))] = 0; } sncclose(dev) register dev_t dev; { if (snclose(dev) == 0) sn_ccount[count(physical(dev))] = 0; } snstrategy(bp) register struct buf *bp; { register punit; punit = physical(bp->b_dev); if (bp == &sncbuf) { /* if command */ snstat[punit].io_misc++; /* errlog: */ splsn(); if (sntab.b_actf == (struct buf *)NULL) /* set up links */ sntab.b_actf = bp; else sntab.b_actl->av_forw = bp; sntab.b_actl = bp; bp->av_forw = (struct buf *)NULL; } else { if ((*sn_fns[snbf].fnc)(bp->b_dev)) { bp->b_flags |= B_ERROR; iodone(bp); /* resets flags */ return; } snstat[punit].io_ops++; /* errlog: */ /* resid for disksort */ bp->b_resid = bp->b_blkno + sn_sizes[logical(bp->b_dev)].sn_offset; splsn(); disksort(&sntab, bp); } if (sntab.b_active == 0) snstart(); spl0(); } snstart() { register struct buf *bp; register punit, lunit; register daddr_t bn; caddr_t addr; loop: if ((bp = sntab.b_actf) == (struct buf *)NULL) return; if (sntab.b_active == 0) { sntab.b_active = 1; if (bp != &sncbuf) bp->b_resid = bp->b_bcount; } punit = physical(bp->b_dev); lunit = logical(bp->b_dev); blkacty |= (1<b_resid == UIOCWBBLK) snw0(punit); else #endif WBBLK sncmd(bp->b_resid); /* b_resid holds the command */ return; } bn = bp->b_blkno + ((bp->b_bcount - bp->b_resid) >> 9); if (bp->b_resid < 512 || bn >= sn_sizes[lunit].sn_size || ((bn += sn_sizes[lunit].sn_offset) >= SN_MAXBN) || (bn <= 200 && sn_bcount[boot(punit)])) { #ifdef HOWFAR if (bp->b_resid != 0) printf("Unix snstart: blkno=%d resid=%d bn=%d\n", bp->b_blkno, bp->b_resid, bn); #endif HOWFAR blkacty &= ~(1<av_forw; iodone(bp); goto loop; } addr = bp->b_un.b_addr + bp->b_bcount - bp->b_resid; snrw(punit, bn, bp->b_flags&B_READ, addr); } snread(dev) dev_t dev; { physio(snstrategy, &snrbuf, dev, B_READ); } snwrite(dev) dev_t dev; { physio(snstrategy, &snrbuf, dev, B_WRITE); } struct sn_blockmap { int maxblock, sectors; } sn_blockmap[] = { 192, 12, 176, 11, 160, 10, 144, 9, 128, 8 }; /* ARGSUSED */ snrw(unit, bn, rw, addr) /* always called at priority sn (see splsn above) */ int unit, rw; register daddr_t bn; register caddr_t addr; { register char *pm = (char *)SN_DATABUF; register struct sn_blockmap *p = sn_blockmap; register int i; int track, sector; char c = 0x00; track = 0; while( bn >= p->maxblock ) { bn -= p->maxblock; track += 16; p++; } sector = bn % p->sectors; track += bn / p->sectors; if (snwaitrdy()) return; SNIOB->side=c; SNIOB->drive=0x80; SNIOB->track = track; SNIOB->sector= sector; if (rw) { SNIOB->cmd=SN_READ; } else { SNIOB->cmd=SN_WRITE; i = 511; do { *pm++; /* cmos ram, every other byte */ *pm++ = *addr++; } while (--i != -1); } if (snwaitrdy()) return; SNIOB->gobyte = SN_CMD; } /* ARGSUSED */ snioctl(dev, cmd, adr, flag) dev_t dev; caddr_t adr; { int unit; register struct buf *bp; if ((unit = physical(dev)) >= NSN) { u.u_error = EFAULT; return; } bp = &sncbuf; switch (cmd) { case AL_EJECT: if ((sn_bcount[count(unit)] > 0) || (sn_ccount[count(unit)] > 1)) { u.u_error = EINVAL; return; } break; case UIOCFORMAT: if (!suser()) { u.u_error = EPERM; return; } #ifdef KLUDGE noerror=1; (void)(sncz(0)); noerror=0; #endif KLUDGE break; #ifdef WBBLK case UIOCWBBLK: if (!suser()) { u.u_error = EPERM; return; } if (copyin(adr, (caddr_t)sn_bblk, sizeof(sn_bblk))) { u.u_error = EFAULT; return; } break; #endif WBBLK default: u.u_error = ENOTTY; return; } GETBUF(bp); bp->b_dev = dev; bp->b_resid = cmd; /* stash the command in resid */ u.u_error = 0; /* any error on sncz is OK */ snstrategy(bp); iowait(bp); if (bp->b_flags & B_ERROR) u.u_error = EIO; FREEBUF(bp); } sncmd(cmd) /* always called at priority sn (see splsn above) */ unsigned int cmd; { if(snwaitrdy()) return; switch (cmd) { case UIOCFORMAT: SNIOB->confirm=0xff; SNIOB->cmd=SN_FORMAT; break; case AL_EJECT: SNIOB->cmd=SN_EJECT; break; default: return; } SNIOB->gobyte = SN_CMD; } #ifdef WBBLK snw0(punit) int punit; { register char *pm; struct sn_hdr sn_hdr; register char *addr; register i; char c = 0x00; if(snwaitrdy()) return; SNIOB->side=c; SNIOB->drive=0x80; SNIOB->track = 0; SNIOB->sector= 0; SNIOB->cmd=SN_WRITE; sn_hdr.version = 0; sn_hdr.volume = 0; sn_hdr.fileid = FILEID; sn_hdr.relpg = 0; sn_hdr.dum1 = 0; sn_hdr.dum2 = 0; i = sizeof(sn_hdr) - 1; pm = (char *)SN_HDRBUF; /* write special 12-byte hdr */ addr = (char *)&sn_hdr; do { *pm++; /* cmos ram, every other byte */ *pm++ = (unsigned)*addr++; } while (--i != -1); i = 511; pm = (char *)SN_DATABUF; /* write regular 512-byte buffer */ addr = (char *)sn_bblk; do { *pm++; /* cmos ram, every other byte */ *pm++ = *addr++; } while (--i != -1); if(snwaitrdy()) return; SNIOB->gobyte = SN_CMD; } #endif WBBLK snintr() /* called at pl 1 */ { struct buf *bp; register short i; register char *addr; register char *pm = (char *)SN_DATABUF; int cnt; struct deverreg snreg[1]; /* errlog: */ i = SNIOB->status; #ifdef HOWFAR if (i) printf("Unix snintr: SNIOB->status = 0x%x\n",i); #endif HOWFAR if (sntab.b_active == 0) { #ifdef HOWFAR printf("Unix snintr: b_active==0\n"); #endif HOWFAR snbf = 0; /* force real check for boot disk */ SNIOB->mask = SN_CLEARMSK; SNIOB->gobyte=SN_CLRST; (void) snwaitrdy(); return; } if ((bp = sntab.b_actf) == (struct buf *)NULL) { #ifdef HOWFAR printf("Unix snintr: bp==0\n"); #endif HOWFAR snbf = 0; /* force real check for boot disk */ SNIOB->mask = SN_CLEARMSK; SNIOB->gobyte=SN_CLRST; (void) snwaitrdy(); return; } cnt = 512; if (i) { cnt = 0; /* errlog: */ sntab.io_stp = &snstat[physical(bp->b_dev)]; snreg[0].drvalue = i; snreg[0].drname = "status"; snreg[0].drbits = "status register"; fmtberr(&sntab, (unsigned)physical(bp->b_dev), (unsigned)(SNIOB->track), /* trk */ (unsigned)(SNIOB->side), /* head */ (unsigned)(SNIOB->sector), /* sector */ (long)(sizeof(snreg)/sizeof(snreg[0])), /* regcnt */ &snreg[0]); if (++sntab.b_errcnt > NRETRY || bp == &sncbuf) { bp->b_flags |= B_ERROR; logberr(&sntab, 1); /* errlog: */ } } /* * because a single buffer can take several io operations, * we leave it to snstart() to figure out when it's done */ if (bp->b_flags&B_ERROR || bp == &sncbuf) { #ifdef KLUDGE if (!noerror) #endif KLUDGE if (bp->b_flags & B_ERROR) { printf("Unix: HARD I/O ERROR on /dev/s%d%c ", physical(bp->b_dev), logical(bp->b_dev)+'a'); if (bp != &sncbuf) printf("bn %d\n", bp->b_blkno + ((bp->b_bcount-bp->b_resid) >> 9) + sn_sizes[logical(bp->b_dev)].sn_offset); else printf("\n"); } blkacty &= ~(1<av_forw; iodone(bp); } else if (cnt && bp->b_flags & B_READ) { addr = bp->b_un.b_addr + bp->b_bcount - bp->b_resid; i = 511; do { *pm++; /* cmos ram, every other byte */ *addr++ = *pm++; } while (--i != -1); if (!(bp->b_blkno + ((bp->b_bcount-bp->b_resid) >> 9) + sn_sizes[logical(bp->b_dev)].sn_offset)) { /* bn=0 */ register caddr_t pm; register unsigned short fileid; pm = (char *)SN_HDRBUF; fileid = (*(pm+9))<<8; fileid |= *(pm+11); if (fileid == FILEID) { sn_bcount[boot(physical(bp->b_dev))] = 1; #ifdef UNISOFT sn_bcount[boot(physical(bp->b_dev))] = 0; #endif UNISOFT } } } bp->b_resid -= cnt; SNIOB->mask = SN_CLEARMSK; SNIOB->gobyte=SN_CLRST; snstart(); } snwaitrdy() { register int i, j, k; k = 1024; do { i = 1000; while (((PPADDR)->d_irb & DSKDIAG) == 0) { /* floppy is busy */ for(j=0;j<1024;j++); /* don't access flpy */ if (--i < 0) { printf("Unix snwaitrdy: DSKDIAG not ready\n"); return(1); } } if (SNIOB->gobyte == 0) return(0); } while (--k); printf("Unix snwaitrdy: drive not ready\n"); return (1); } snprint(dev, str) char *str; { printf("%s on sn drive %d, slice %d\n", str, (dev>>4)&0xF, dev&0x7); } /* This is the eject driver * which uses a different major device so * it can tell whether the sony is being used or not */ /* ARGSUSED */ ejioctl(dev, cmd, adr, flag) dev_t dev; caddr_t adr; { int unit; register struct buf *bp; if ((unit = physical(dev)) >= NSN) { u.u_error = EFAULT; return; } bp = &sncbuf; if ( cmd != AL_EJECT ) { u.u_error = ENOTTY; return; } if ((sn_bcount[count(unit)] != 0) || (sn_ccount[count(unit)] != 0)) { u.u_error = EINVAL; return; } GETBUF(bp); bp->b_dev = dev; bp->b_resid = cmd; /* stash the command in resid */ snstrategy(bp); iowait((struct buf *)bp); if (bp->b_flags & B_ERROR) u.u_error = EIO; FREEBUF(bp); return; }