📄 devfloppy.c
字号:
/* * send a command to the floppy */static intfloppycmd(void){ int i; int tries; fl.nstat = 0; for(i = 0; i < fl.ncmd; i++){ for(tries = 0; ; tries++){ if((inb(Pmsr)&(Ffrom|Fready)) == Fready) break; if(tries > 1000){ DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i); fldump(); /* empty fifo, might have been a bad command */ floppyresult(); return -1; } microdelay(8); /* for machine independence */ } outb(Pfdata, fl.cmd[i]); } return 0;}/* * get a command result from the floppy * * when the controller goes ready waiting for a command * (instead of sending results), we're done * */static intfloppyresult(void){ int i, s; int tries; /* get the result of the operation */ for(i = 0; i < sizeof(fl.stat); i++){ /* wait for status byte */ for(tries = 0; ; tries++){ s = inb(Pmsr)&(Ffrom|Fready); if(s == Fready){ fl.nstat = i; return fl.nstat; } if(s == (Ffrom|Fready)) break; if(tries > 1000){ DPRINT("floppyresult: %d stats\n", i); fldump(); fl.confused = 1; return -1; } microdelay(8); /* for machine independence */ } fl.stat[i] = inb(Pfdata); } fl.nstat = sizeof(fl.stat); return fl.nstat;}/* * calculate physical address of a logical byte offset into the disk * * truncate dp->length if it crosses a track boundary */static voidfloppypos(FDrive *dp, long off){ int lsec; int ltrack; int end; lsec = off/dp->t->bytes; ltrack = lsec/dp->t->sectors; dp->tcyl = ltrack/dp->t->heads; dp->tsec = (lsec % dp->t->sectors) + 1; dp->thead = (lsec/dp->t->sectors) % dp->t->heads; /* * can't read across track boundaries. * if so, decrement the bytes to be read. */ end = (ltrack+1)*dp->t->sectors*dp->t->bytes; if(off+dp->len > end) dp->len = end - off;}/* * get the interrupt cause from the floppy. */static intfloppysense(void){ fl.ncmd = 0; fl.cmd[fl.ncmd++] = Fsense; if(floppycmd() < 0) return -1; if(floppyresult() < 2){ DPRINT("can't read sense response\n"); fldump(); fl.confused = 1; return -1; } return 0;}static intcmddone(void *){ return fl.ncmd == 0;}/* * Wait for a floppy interrupt. If none occurs in 5 seconds, we * may have missed one. This only happens on some portables which * do power management behind our backs. Call the interrupt * routine to try to clear any conditions. */static voidfloppywait(void){ tsleep(&fl.r, cmddone, 0, 5000); if(!cmddone(0)){ floppyintr(0); fl.confused = 1; }}/* * we've lost the floppy position, go to cylinder 0. */static intfloppyrecal(FDrive *dp){ dp->ccyl = -1; dp->cyl = -1; fl.ncmd = 0; fl.cmd[fl.ncmd++] = Frecal; fl.cmd[fl.ncmd++] = dp->dev; if(floppycmd() < 0) return -1; floppywait(); if(fl.nstat < 2){ DPRINT("recalibrate: confused %ux\n", inb(Pmsr)); fl.confused = 1; return -1; } if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ DPRINT("recalibrate: failed\n"); dp->confused = 1; return -1; } dp->cyl = fl.stat[1]; if(dp->cyl != 0){ DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl); dp->cyl = -1; dp->confused = 1; return -1; } dp->confused = 0; return 0;}/* * if the controller or a specific drive is in a confused state, * reset it and get back to a known state */static voidfloppyrevive(void){ FDrive *dp; /* * reset the controller if it's confused */ if(fl.confused){ DPRINT("floppyrevive in\n"); fldump(); /* reset controller and turn all motors off */ splhi(); fl.ncmd = 1; fl.cmd[0] = 0; outb(Pdor, 0); delay(10); outb(Pdor, Fintena|Fena); delay(10); spllo(); fl.motor = 0; fl.confused = 0; floppywait(); /* mark all drives in an unknown state */ for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++) dp->confused = 1; /* set rate to a known value */ outb(Pdsr, 0); fl.rate = 0; DPRINT("floppyrevive out\n"); fldump(); }}/* * seek to the target cylinder * * interrupt, no results */static longfloppyseek(FDrive *dp, long off){ floppypos(dp, off); if(dp->cyl == dp->tcyl) return dp->tcyl; dp->cyl = -1; fl.ncmd = 0; fl.cmd[fl.ncmd++] = Fseek; fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps; if(floppycmd() < 0) return -1; floppywait(); if(fl.nstat < 2){ DPRINT("seek: confused\n"); fl.confused = 1; return -1; } if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ DPRINT("seek: failed\n"); dp->confused = 1; return -1; } dp->cyl = dp->tcyl; return dp->tcyl;}/* * read or write to floppy. try up to three times. */static longfloppyxfer(FDrive *dp, int cmd, void *a, long off, long n){ long offset; int tries; if(off >= dp->t->cap) return 0; if(off + n > dp->t->cap) n = dp->t->cap - off; /* retry on error (until it gets ridiculous) */ tries = 0; while(waserror()){ if(tries++ > 20) nexterror(); DPRINT("floppyxfer: retrying\n"); /*floppyon(dp);*/ } dp->len = n; if(floppyseek(dp, off) < 0){ DPRINT("xfer: seek failed\n"); dp->confused = 1; error(Eio); } /* * set up the dma (dp->len may be trimmed) */ if(waserror()){ dmaend(DMAchan); nexterror(); } dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread); if(dp->len < 0) error(Eio); /* * start operation */ fl.ncmd = 0; fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0); fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; fl.cmd[fl.ncmd++] = dp->tcyl; fl.cmd[fl.ncmd++] = dp->thead; fl.cmd[fl.ncmd++] = dp->tsec; fl.cmd[fl.ncmd++] = dp->t->bcode; fl.cmd[fl.ncmd++] = dp->t->sectors; fl.cmd[fl.ncmd++] = dp->t->gpl; fl.cmd[fl.ncmd++] = 0xFF; if(floppycmd() < 0) error(Eio); /* Poll ready bits and transfer data */ floppyexec((char*)a, dp->len, cmd==Fread); /* * give bus to DMA, floppyintr() will read result */ floppywait(); dmaend(DMAchan); poperror(); /* * check for errors */ if(fl.nstat < 7){ DPRINT("xfer: confused\n"); fl.confused = 1; error(Eio); } if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){ DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0], fl.stat[1], fl.stat[2]); DPRINT("offset %lud len %ld\n", off, dp->len); if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){ DPRINT("DMA overrun: retry\n"); } else dp->confused = 1; error(Eio); } /* * check for correct cylinder */ offset = fl.stat[3] * dp->t->heads + fl.stat[4]; offset = offset*dp->t->sectors + fl.stat[5] - 1; offset = offset * c2b[fl.stat[6]]; if(offset != off+dp->len){ DPRINT("xfer: ends on wrong cyl\n"); dp->confused = 1; error(Eio); } poperror(); dp->lasttouched = m->ticks; return dp->len;}/* * format a track */static voidfloppyformat(FDrive *dp, char *params){ int cyl, h, sec; ulong track; uchar *buf, *bp; FType *t; char *f[3]; /* * set the type */ if(getfields(params, f, 3, 1, " ") > 1){ for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ if(strcmp(f[1], t->name)==0 && t->dt==dp->dt){ dp->t = t; floppydir[NFDIR*dp->dev].length = dp->t->cap; break; } } if(t >= &floppytype[nelem(floppytype)]) error(Ebadarg); } else { floppysetdef(dp); t = dp->t; } /* * buffer for per track info */ buf = smalloc(t->sectors*4); if(waserror()){ free(buf); nexterror(); } /* force a recalibrate to cylinder 0 */ dp->confused = 1; if(!waserror()){ floppyon(dp); poperror(); } /* * format a track at time */ for(track = 0; track < t->tracks*t->heads; track++){ cyl = track/t->heads; h = track % t->heads; /* * seek to track, ignore errors */ floppyseek(dp, track*t->tsize); dp->cyl = cyl; dp->confused = 0; /* * set up the dma (dp->len may be trimmed) */ bp = buf; for(sec = 1; sec <= t->sectors; sec++){ *bp++ = cyl; *bp++ = h; *bp++ = sec; *bp++ = t->bcode; } if(waserror()){ dmaend(DMAchan); nexterror(); } if(dmasetup(DMAchan, buf, bp-buf, 0) < 0) error(Eio); /* * start operation */ fl.ncmd = 0; fl.cmd[fl.ncmd++] = Fformat; fl.cmd[fl.ncmd++] = (h<<2) | dp->dev; fl.cmd[fl.ncmd++] = t->bcode; fl.cmd[fl.ncmd++] = t->sectors; fl.cmd[fl.ncmd++] = t->fgpl; fl.cmd[fl.ncmd++] = 0x5a; if(floppycmd() < 0) error(Eio); /* Poll ready bits and transfer data */ floppyexec((char *)buf, bp-buf, 0); /* * give bus to DMA, floppyintr() will read result */ floppywait(); dmaend(DMAchan); poperror(); /* * check for errors */ if(fl.nstat < 7){ DPRINT("format: confused\n"); fl.confused = 1; error(Eio); } if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){ DPRINT("format: failed %ux %ux %ux\n", fl.stat[0], fl.stat[1], fl.stat[2]); dp->confused = 1; error(Eio); } } free(buf); dp->confused = 1; poperror();}static voidfloppyintr(Ureg *){ switch(fl.cmd[0]&~Fmulti){ case Fread: case Fwrite: case Fformat: case Fdumpreg: floppyresult(); break; case Fseek: case Frecal: default: floppysense(); /* to clear interrupt */ break; } fl.ncmd = 0; wakeup(&fl.r);}Dev floppydevtab = { 'f', "floppy", floppyreset, devinit, floppyattach, devclone, floppywalk, floppystat, floppyopen, devcreate, floppyclose, floppyread, devbread, floppywrite, devbwrite, devremove, devwstat,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -