/* * Copyright (c) 1982, 1986, 1988 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)rl.c 7.6 (Berkeley) 7/9/88 */ /* * Standalone RL02 disk driver */ #include "param.h" #include "inode.h" #include "fs.h" #include "../vax/pte.h" #include "../vaxuba/rlreg.h" #include "../vaxuba/ubareg.h" #include "saio.h" #include "savax.h" #define MAXPART 8 #define MAXCTLR 1 /* all addresses must be specified */ u_short rlstd[MAXCTLR] = { 0774400 }; short rl_off[] = { 0, 361, 0, -1, -1, -1, -1, -1 }; /* struct to keep state info about the controller */ struct rl_stat { short rl_dn; /* drive number */ short rl_cylnhd; /* cylinder and head */ u_short rl_bleft; /* bytes left to transfer */ u_short rl_bpart; /* bytes transferred */ } rl_stat[MAXCTLR] = { -1, 0, 0, 0 }; rlopen(io) register struct iob *io; { register struct rldevice *rladdr; register struct rl_stat *st; register int ctr = 0; if ((u_int)io->i_adapt >= nuba) return (EADAPT); if ((u_int)io->i_ctlr >= MAXCTLR) return (ECTLR); rladdr = (struct rldevice *)ubamem(io->i_adapt, rlstd[io->i_ctlr]); if (badaddr((char *)rladdr, sizeof(short))) return (ENXIO); if ((u_int)io->i_part >= MAXPART || rl_off[io->i_part] == -1) return (EPART); /* * DEC reports that: * For some unknown reason the RL02 (seems to be only drive 1) * does not return a valid drive status the first time that a * GET STATUS request is issued for the drive, in fact it can * take up to three or more GET STATUS requests to obtain the * correct status. * In order to overcome this, the driver has been modified to * issue a GET STATUS request and validate the drive status * returned. If a valid status is not returned after eight * attempts, then an error message is printed. */ do { rladdr->rlda.getstat = RL_RESET; rladdr->rlcs = (io->i_unit <<8) | RL_GETSTAT; /* Get status*/ rlwait(rladdr); } while ((rladdr->rlmp.getstat&RLMP_STATUS) != RLMP_STATOK && ++ctr<8); if ((rladdr->rlcs & RL_DE) || (ctr >= 8)) { printf("rl: unit does not respond\n"); return (EUNIT); } if ((rladdr->rlmp.getstat & RLMP_DT) == 0) { /* NO RL01'S */ printf("rl01 unit not supported\n"); return (ENXIO); } /* Determine disk posistion */ rladdr->rlcs = (io->i_unit << 8) | RL_RHDR; rlwait(rladdr); /* save disk drive posistion */ st = &rl_stat[io->i_ctlr]; st->rl_cylnhd = (rladdr->rlmp.readhdr & 0177700) >> 6; st->rl_dn = io->i_unit; /* byte offset for cylinder desired */ io->i_boff = rl_off[io->i_part] * NRLBPSC * NRLTRKS * NRLSECT; return (0); } rlstrategy(io, func) register struct iob *io; { register struct rldevice *rladdr; register struct rl_stat *st; int com; daddr_t bn; short cn, sn, head; int diff, ubinfo, ubaaddr, errcnt = 0; rladdr = (struct rldevice *)ubamem(io->i_adapt, rlstd[io->i_ctlr]); st = &rl_stat[io->i_ctlr]; retry: ubinfo = ubasetup(io, 1); bn = io->i_bn; /* block number */ cn = bn / 40; /* 40 512 byte blocks per cylinder */ sn = (bn % 20) << 1; head = (bn / 20) & 1; st->rl_bleft = io->i_cc; /* total number of bytes to trans */ ubaaddr = ubinfo; stupid_rl: /* find out how many cylinders to seek */ diff = (st->rl_cylnhd >> 1) - cn; if (diff == 0 && (st->rl_cylnhd & 1) == head) goto noseek; /* first time or we switched drives */ st->rl_dn = io->i_unit; /* drive number */ if (diff < 0) rladdr->rlda.seek = -diff<<7 | RLDA_HGH | head << 4; else rladdr->rlda.seek = diff<<7 | RLDA_LOW | head << 4; rladdr->rlcs = (st->rl_dn << 8) | RL_SEEK; /* reset position of drive */ st->rl_cylnhd = (cn << 1) | head; noseek: /* wait for controller and drive */ while( (rladdr->rlcs & RL_DCRDY) != RL_DCRDY) continue; /* calculate the max number of bytes we can trans */ st->rl_bpart = NRLSECT * NRLBPSC - (sn * NRLBPSC); if (st->rl_bleft < st->rl_bpart) st->rl_bpart = st->rl_bleft; rladdr->rlda.rw = (st->rl_cylnhd << 6) | sn; rladdr->rlmp.rw = -(st->rl_bpart >> 1); rladdr->rlba = ubaaddr; com = (st->rl_dn << 8) | ((ubaaddr>>12)&RL_BAE); if (func == READ) com |= RL_READ; else com |= RL_WRITE; rladdr->rlcs = com; /* wait for controller and drive */ while( (rladdr->rlcs & RL_DCRDY) != RL_DCRDY) continue; if (rladdr->rlcs & RL_ERR) { int status; if (rladdr->rlcs & RL_DE) { rladdr->rlda.getstat = RL_GSTAT; rladdr->rlcs = (st->rl_dn << 8) | RL_GETSTAT; rlwait(rladdr); status = rladdr->rlmp.getstat; rladdr->rlda.getstat = RL_RESET; rladdr->rlcs = (st->rl_dn <<8) | RL_GETSTAT; rlwait(rladdr); } printf("rl error: (cyl,head,sec)=(%d,%d,%d) cs=%b mp=%b\n", cn, head, sn, rladdr->rlcs & 0xffff, RLCS_BITS, status, RLER_BITS); /* Determine disk posistion */ rladdr->rlcs = (st->rl_dn << 8) | RL_RHDR; rlwait(rladdr); /* save disk drive posistion */ st->rl_cylnhd = (rladdr->rlmp.readhdr & 0177700) >> 6; if (errcnt++ == 10) { printf("rl: unrecovered error\n"); return (-1); } goto retry; } /* do we have to finish off the rest of the transfer? */ if ((st->rl_bleft -= st->rl_bpart) > 0) { /* increment head and/or cylinder */ if (++head > 1) { cn++; /* want next cyl, head 0 sector 0 */ head = 0; } /* we always want sector to be zero */ sn = 0; /* * standalone code for ubafree does what regular * ubapurge does and we want to purge last transfer */ ubafree(io, ubinfo); ubaaddr = ubinfo + io->i_cc - st->rl_bleft; goto stupid_rl; } ubafree(io, ubinfo); if (errcnt) printf("rl: recovered by retry\n"); return (io->i_cc); } static rlwait(rladdr) register struct rldevice *rladdr; { while ((rladdr->rlcs & RL_CRDY) == 0); }