/* * Copyright (c) 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)rx.c 1.4 (2.11BSD GTE) 1995/11/27 */ /* * RX02 floppy disk device driver * * sms - November 26, 1995. * Actually got it working with a 18 bit controller on a 22 bit Qbus. * * sms - November 21, 1995. * Moved from OTHERS/rx02/#2 into the supported directory: sys/pdpuba. * Added conditionalized support for a "software unibus/qbus map" so that * 18 bit controllers could be supported in 22 bit Qbus systems. * * Date: Sun, 8 May 88 18:42:38 CDT * uunet!nuchat!steve@rutgers.edu (Steve Nuchia) * The rx02 driver as distributed didn't even come close to working * on a Q22 machine - probe was wrong and it got worse from there. * * This driver was written by Bill Shannon and distributed on the * DEC v7m UNIX tape. It has been modified for 2BSD and has been * included with the permission of the DEC UNIX Engineering Group. * * Modified to actually work with 2.9BSD by Gregory Travis, Indiana Univ. * * Layout of logical devices: * * name min dev unit density * ---- ------- ---- ------- * rx0a 0 0 single * rx1a 1 1 single * rx0b 2 0 double * rx1b 3 1 double * * ioctl function call may be used to format a disk. */ #include "rx.h" #if NRX > 0 #include "param.h" #include "buf.h" #include "conf.h" #include "ioctl.h" #include "tty.h" #include "rxreg.h" #include "errno.h" #include "map.h" #include "uba.h" struct rxdevice *RXADDR; /* * the following defines use some fundamental * constants of the RX02. */ #define NSPB ((minor(bp->b_dev)&2) ? 2 : 4) /* sectors per block */ #define NRXBLKS ((minor(bp->b_dev)&2) ? 1001 : 500) /* blocks on device */ #define NBPS ((minor(bp->b_dev)&2) ? 256 : 128) /* bytes per sector */ #define DENSITY (minor(bp->b_dev)&2) /* Density: 0 = single, 2 = double */ #define UNIT (minor(bp->b_dev)&1) /* Unit Number: 0 = left, 1 = right */ #define RXGID (RX_GO | RX_IE | (DENSITY << 7)) #define rxwait() while (((RXADDR->rxcs) & RX_XREQ) == 0) #define seccnt(bp) ((int)((bp)->b_seccnt)) struct buf rxtab; struct buf crxbuf; /* buffer header for control functions */ /* * DEC controllers do not do 22 bit DMA but 3rd party (Sigma MXV-22) can. * 'rxsoftmap' can be patched (via 'adb') as indicated below to inhibit * the probing (checking bit 10 in the CSR) for 22 bit controllers. */ static char mxv22; /* MXV22 can do native 22 bit DMA */ static char rxsoftmap = -1; /* -1 = OK to check for soft map * 0 = Never use soft map * 1 = Always use soft map */ /* * states of driver, kept in b_state */ #define SREAD 1 /* read started */ #define SEMPTY 2 /* empty started */ #define SFILL 3 /* fill started */ #define SWRITE 4 /* write started */ #define SINIT 5 /* init started */ #define SFORMAT 6 /* format started */ rxattach(addr, unit) struct rxdevice *addr; u_int unit; { if (unit != 0) return(0); RXADDR = addr; if (addr->rxcs & RX_Q22) /* 22 bit capable? */ mxv22 = 1; /* * If it is not a 22 bit controller and there is no Unibus map and * it is permitted to switch to a soft map then set the "use soft map" flag. */ if (!mxv22 && !ubmap && rxsoftmap == -1) rxsoftmap = 1; return(1); } /*ARGSUSED*/ rxopen(dev, flag) dev_t dev; { if (minor(dev) >= 4 || !RXADDR) return (ENXIO); return (0); } rxstrategy(bp) register struct buf *bp; { register int s; if (minor(bp->b_dev) >= 4 || !RXADDR) goto bad; if (bp->b_blkno >= NRXBLKS) { if (bp->b_flags&B_READ) bp->b_resid = bp->b_bcount; else { bad: bp->b_flags |= B_ERROR; bp->b_error = ENXIO; } iodone(bp); return; } #ifdef SOFUB_MAP if (rxsoftmap == 1) { if (sofub_alloc(bp) == 0) return; } else #endif mapalloc(bp); bp->av_forw = (struct buf *) NULL; /* * seccnt is actually the number of floppy sectors transferred, * incremented by one after each successful transfer of a sector. */ seccnt(bp) = 0; /* * We'll modify b_resid as each piece of the transfer * successfully completes. It will also tell us when * the transfer is complete. */ bp->b_resid = bp->b_bcount; s = splbio(); if (rxtab.b_actf == NULL) rxtab.b_actf = bp; else rxtab.b_actl->av_forw = bp; rxtab.b_actl = bp; if (rxtab.b_state == NULL) rxstart(); splx(s); } rxstart() { register struct buf *bp; int addr, xmem, cmd; int n, sector, track; if ((bp = rxtab.b_actf) == NULL) { rxtab.b_state = NULL; return; } if (bp == &crxbuf) { /* is it a control request ? */ rxtab.b_state = SFORMAT; RXADDR->rxcs = RX_SMD | RXGID | (UNIT << 4); rxwait(); RXADDR->rxdb = 0111; } else if (bp->b_flags & B_READ) { rxtab.b_state = SREAD; rxfactr((int)bp->b_blkno * NSPB + seccnt(bp), §or, &track); RXADDR->rxcs = RX_RSECT | RXGID | (UNIT << 4); rxwait(); RXADDR->rxsa = sector; rxwait(); RXADDR->rxta = track; } else { rxtab.b_state = SFILL; n = bp->b_resid >= NBPS ? NBPS : bp->b_resid; rxaddr ( bp, &addr, &xmem ); if (rxsoftmap <= 0) cmd = RX_Q22; else cmd = 0; RXADDR->rxcs = RX_FILL | RXGID | ((xmem & 3) << 12) | cmd; rxwait(); RXADDR->rxwc = n >> 1; rxwait(); RXADDR->rxba = addr; if (rxsoftmap <= 0) { rxwait(); RXADDR->rxba = xmem; } } } rxintr() { register struct buf *bp; int n, sector, track, cmd; static rxerr[4]; char *decode; int addr, xmem; if (rxtab.b_state == SINIT) { rxstart(); return; } if ((bp = rxtab.b_actf) == NULL) return; if (RXADDR->rxcs & RX_ERR) { if (rxtab.b_errcnt++ > 10 || rxtab.b_state == SFORMAT) { bp->b_flags |= B_ERROR; harderr(bp, "rx"); printf("cs=%b er=%b\n", RXADDR->rxcs, RX_BITS, RXADDR->rxes, RXES_BITS); RXADDR->rxcs = RX_RDEC | RX_GO; rxwait(); RXADDR->rxba = (short) rxerr; while ( ! (RXADDR->rxcs & RX_DONE) ); switch ( rxerr[0] ) { case 0040: decode = "bad track"; break; case 0050: decode = "found home"; break; case 0070: decode = "no sch sctr"; break; case 0120: decode = "no preamble"; break; case 0150: decode = "ozone headers"; break; case 0160: decode = "too many IDAM"; break; case 0170: decode = "data AM missing"; break; case 0200: decode = "CRC error"; break; case 0240: decode = "density error"; break; case 0250: decode = "bad fmt key"; break; case 0260: decode = "bad data AM"; break; case 0270: decode = "POK while write"; break; case 0300: decode = "drv not ready"; break; case 0310: decode = "write protected"; break; default: decode = "unknown error"; break; } printf("rx: err %o=%s\n", rxerr[0], decode ); rxtab.b_errcnt = 0; rxtab.b_actf = bp->av_forw; #ifdef SOFUB_MAP if (rxsoftmap == 1) sofub_relse(bp, bp->b_bcount); #endif iodone(bp); } RXADDR->rxcs = RX_INIT; RXADDR->rxcs = RX_IE; rxtab.b_state = SINIT; return; } switch (rxtab.b_state) { case SREAD: /* read done, start empty */ rxtab.b_state = SEMPTY; n = bp->b_resid >= NBPS? NBPS : bp->b_resid; rxaddr ( bp, &addr, &xmem ); if (rxsoftmap <= 0) cmd = RX_Q22; else cmd = 0; RXADDR->rxcs = RX_EMPTY | RXGID | ((xmem & 3) << 12) | cmd; rxwait(); RXADDR->rxwc = n >> 1; rxwait(); RXADDR->rxba = addr; if (rxsoftmap <= 0) { rxwait(); RXADDR->rxba = xmem; } return; case SFILL: /* fill done, start write */ rxtab.b_state = SWRITE; rxfactr((int)bp->b_blkno * NSPB + seccnt(bp), §or, &track); RXADDR->rxcs = RX_WSECT | RXGID | (UNIT << 4); rxwait(); RXADDR->rxsa = sector; rxwait(); RXADDR->rxta = track; return; case SEMPTY: /* empty done, start next read */ case SWRITE: /* write done, start next fill */ /* * increment amount remaining to be transferred. * if it becomes positive, last transfer was a * partial sector and we're done, so set remaining * to zero. */ if (bp->b_resid <= NBPS) { done: bp->b_resid = 0; rxtab.b_errcnt = 0; rxtab.b_actf = bp->av_forw; #ifdef SOFUB_MAP if (rxsoftmap == 1) sofub_relse(bp, bp->b_bcount); #endif iodone(bp); break; } bp->b_resid -= NBPS; seccnt(bp)++; break; case SFORMAT: /* format done (whew!!!) */ goto done; /* driver's getting too big... */ } /* end up here from states SWRITE and SEMPTY */ rxstart(); } /* * rxfactr -- calculates the physical sector and physical * track on the disk for a given logical sector. * call: * rxfactr(logical_sector,&p_sector,&p_track); * the logical sector number (0 - 2001) is converted * to a physical sector number (1 - 26) and a physical * track number (0 - 76). * the logical sectors specify physical sectors that * are interleaved with a factor of 2. thus the sectors * are read in the following order for increasing * logical sector numbers (1,3, ... 23,25,2,4, ... 24,26) * There is also a 6 sector slew between tracks. * Logical sectors start at track 1, sector 1; go to * track 76 and then to track 0. Thus, for example, unix block number * 498 starts at track 0, sector 25 and runs thru track 0, sector 2 * (or 6 depending on density). */ static rxfactr(sectr, psectr, ptrck) register int sectr; int *psectr, *ptrck; { register int p1, p2; p1 = sectr / 26; p2 = sectr % 26; /* 2 to 1 interleave */ p2 = (2 * p2 + (p2 >= 13 ? 1 : 0)) % 26; /* 6 sector per track slew */ *psectr = 1 + (p2 + 6 * p1) % 26; if (++p1 >= 77) p1 = 0; *ptrck = p1; } /* * rxaddr -- compute core address where next sector * goes to / comes from based on bp->b_un.b_addr, bp->b_xmem, * and seccnt(bp). */ static rxaddr(bp, addr, xmem) register struct buf *bp; register u_int *addr, *xmem; { *addr = (u_int)bp->b_un.b_addr + (seccnt(bp) * NBPS); *xmem = bp->b_xmem; if (*addr < (u_int)bp->b_un.b_addr) /* overflow, bump xmem */ (*xmem)++; } /* * rxioctl -- format RX02 disk, single or double density. * density determined by device opened. */ /*ARGSUSED*/ rxioctl(dev, cmd, addr, flag) dev_t dev; u_int cmd; { register int s; register struct buf *bp; if (cmd != RXIOC_FORMAT) return (ENXIO); bp = &crxbuf; while (bp->b_flags & B_BUSY) { s = splbio(); bp->b_flags |= B_WANTED; sleep(bp, PRIBIO); } splx(s); bp->b_flags = B_BUSY; bp->b_dev = dev; bp->b_error = 0; rxstrategy(bp); iowait(bp); bp->b_flags = 0; return (0); } #endif