| plan 9 kernel history: overview | file list | diff list |
1999/0507/pc/devfloppy.c (diff list | history)
| pc/devfloppy.c on 1991/0727 | ||
| 1991/0727 | #include "u.h" | |
| 1992/0321 | #include "../port/lib.h" | |
| 1991/0727 | #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" | |
| 1992/0111 | #include "../port/error.h" | |
| 1991/0727 | ||
| 1997/0327 | #include "floppy.h" | |
| 1991/0924 | /* Intel 82077A (8272A compatible) floppy controller */ | |
| 1991/0920 | ||
| 1997/0327 | /* This module expects the following functions to be defined * elsewhere: * * inb() * outb() * floppyexec() * floppyeject() * floppysetup0() * floppysetup1() | |
| 1997/0404 | * dmainit() | |
| 1997/0327 | * dmasetup() * dmaend() * * On DMA systems, floppyexec() should be an empty function; * on non-DMA systems, dmaend() should be an empty function; * dmasetup() may enforce maximum transfer sizes. */ | |
| 1991/0727 | ||
| 1997/0327 | enum { | |
| 1991/0728 | /* file types */ | |
| 1997/0327 | Qdir= 0, | |
| 1991/0802 | Qdata= (1<<2), | |
| 1991/0920 | Qctl= (2<<2), | |
| 1991/0802 | Qmask= (3<<2), | |
| 1991/0924 | DMAchan= 2, /* floppy dma channel */ | |
| 1991/0727 | }; | |
| 1998/0120 | #define DPRINT if(floppydebug)print | |
| 1998/0213 | int floppydebug = 0; | |
| 1997/0327 | ||
| 1991/0727 | /* | |
| 1992/1006 | * types of drive (from PC equipment byte) */ enum { Tnone= 0, T360kb= 1, T1200kb= 2, T720kb= 3, T1440kb= 4, }; | |
| 1997/0327 | FType floppytype[] = | |
| 1991/0727 | { | |
| 1992/1006 | { "3½HD", T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, }, { "3½DD", T1440kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, { "3½DD", T720kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, }, { "5¼HD", T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, }, { "5¼DD", T1200kb, 512, 9, 2, 2, 40, 0x2A, 0x50, 1, }, | |
| 1993/0430 | { "ATT3B1", T1200kb, 512, 8, 2, 2, 48, 0x2A, 0x50, 1, }, | |
| 1992/1006 | { "5¼DD", T360kb, 512, 9, 2, 1, 40, 0x2A, 0x50, 2, }, | |
| 1991/0727 | }; | |
| 1991/0731 | /* | |
| 1991/0802 | * bytes per sector encoding for the controller. * - index for b2c is is (bytes per sector/128). * - index for c2b is code from b2c | |
| 1991/0731 | */ static int b2c[] = | |
| 1991/0727 | { | |
| 1991/0731 | [1] 0, [2] 1, [4] 2, [8] 3, }; static int c2b[] = { | |
| 1991/0727 | 128, 256, 512, | |
| 1991/0731 | 1024, | |
| 1991/0727 | }; | |
| 1997/0327 | FController fl; | |
| 1991/0727 | ||
| 1991/0921 | #define MOTORBIT(i) (1<<((i)+4)) | |
| 1991/0727 | /* | |
| 1991/0731 | * predeclared */ | |
| 1997/0327 | static int cmddone(void*); static void floppyformat(FDrive*, char*); | |
| 1991/0731 | static void floppykproc(void*); | |
| 1997/0327 | static void floppypos(FDrive*,long); static int floppyrecal(FDrive*); | |
| 1991/0924 | static int floppyresult(void); | |
| 1991/0731 | static void floppyrevive(void); | |
| 1997/0327 | static long floppyseek(FDrive*, long); | |
| 1991/0924 | static int floppysense(void); static void floppywait(void); | |
| 1997/0327 | static long floppyxfer(FDrive*, int, void*, long, long); | |
| 1991/0731 | ||
| 1991/0802 | Dirtab floppydir[]={ | |
| 1997/0910 | "fd0disk", {Qdata + 0}, 0, 0660, "fd0ctl", {Qctl + 0}, 0, 0660, "fd1disk", {Qdata + 1}, 0, 0660, "fd1ctl", {Qctl + 1}, 0, 0660, "fd2disk", {Qdata + 2}, 0, 0660, "fd2ctl", {Qctl + 2}, 0, 0660, "fd3disk", {Qdata + 3}, 0, 0660, "fd3ctl", {Qctl + 3}, 0, 0660, | |
| 1991/0802 | }; | |
| 1991/0811 | #define NFDIR 2 /* directory entries/drive */ | |
| 1991/0731 | ||
| 1992/1217 | static void fldump(void) { DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb), inb(Pdor), inb(Pmsr), inb(Pdir)); } | |
| 1992/1006 | /* * set floppy drive to its default type */ static void | |
| 1997/0327 | floppysetdef(FDrive *dp) | |
| 1992/1006 | { | |
| 1997/0327 | FType *t; | |
| 1992/1006 | ||
| 1997/0327 | for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++) | |
| 1992/1006 | if(dp->dt == t->dt){ dp->t = t; floppydir[NFDIR*dp->dev].length = dp->t->cap; | |
| 1997/0327 | break; | |
| 1992/1006 | } } | |
| 1997/0327 | static void | |
| 1991/0731 | floppyreset(void) { | |
| 1997/0327 | FDrive *dp; FType *t; | |
| 1992/1218 | ulong maxtsize; | |
| 1997/0327 | floppysetup0(&fl); | |
| 1991/0731 | ||
| 1991/0802 | /* * init dependent parameters */ | |
| 1992/1218 | maxtsize = 0; | |
| 1997/0327 | for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ | |
| 1991/0802 | t->cap = t->bytes * t->heads * t->sectors * t->tracks; t->bcode = b2c[t->bytes/128]; | |
| 1991/0924 | t->tsize = t->bytes * t->sectors; | |
| 1992/1218 | if(maxtsize < t->tsize) maxtsize = t->tsize; | |
| 1991/0802 | } | |
| 1999/0403 | dmainit(DMAchan, maxtsize); | |
| 1991/0802 | /* | |
| 1991/0809 | * allocate the drive storage */ | |
| 1997/0327 | fl.d = xalloc(fl.ndrive*sizeof(FDrive)); | |
| 1991/0925 | fl.selected = fl.d; | |
| 1991/0809 | /* | |
| 1991/0802 | * stop the motors */ | |
| 1991/0924 | fl.motor = 0; delay(10); outb(Pdor, fl.motor | Fintena | Fena); delay(10); /* * init drives */ | |
| 1997/0327 | for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ | |
| 1991/0924 | dp->dev = dp - fl.d; | |
| 1992/1006 | dp->dt = T1440kb; | |
| 1997/0327 | floppysetdef(dp); | |
| 1992/1006 | dp->cyl = -1; /* because we don't know */ | |
| 1992/1218 | dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024); | |
| 1991/0924 | dp->ccyl = -1; | |
| 1998/0120 | dp->vers = 0; | |
| 1991/0731 | } | |
| 1991/0809 | /* | |
| 1991/0924 | * first operation will recalibrate | |
| 1991/0809 | */ | |
| 1991/0924 | fl.confused = 1; | |
| 1991/0731 | ||
| 1997/0327 | floppysetup1(&fl); | |
| 1991/0731 | } | |
| 1991/0802 | ||
| 1997/0327 | static Chan* | |
| 1991/0802 | floppyattach(char *spec) { | |
| 1991/0920 | static int kstarted; if(kstarted == 0){ /* * watchdog to turn off the motors */ kstarted = 1; kproc("floppy", floppykproc, 0); } | |
| 1991/0802 | return devattach('f', spec); } | |
| 1997/0327 | static int | |
| 1991/0802 | floppywalk(Chan *c, char *name) { | |
| 1997/0327 | return devwalk(c, name, floppydir, fl.ndrive*NFDIR, devgen); | |
| 1991/0802 | } | |
| 1997/0327 | static void | |
| 1991/0802 | floppystat(Chan *c, char *dp) { | |
| 1997/0327 | devstat(c, dp, floppydir, fl.ndrive*NFDIR, devgen); | |
| 1991/0802 | } | |
| 1997/0327 | static Chan* | |
| 1991/0802 | floppyopen(Chan *c, int omode) { | |
| 1997/0327 | return devopen(c, omode, floppydir, fl.ndrive*NFDIR, devgen); | |
| 1991/0802 | } | |
| 1997/0327 | static void | |
| 1998/0319 | floppyclose(Chan *) | |
| 1991/0802 | { } | |
| 1991/0925 | static void | |
| 1997/0327 | islegal(ulong offset, long n, FDrive *dp) | |
| 1991/0925 | { | |
| 1992/1009 | if(offset % dp->t->bytes) | |
| 1992/0114 | error(Ebadarg); | |
| 1991/0925 | if(n % dp->t->bytes) | |
| 1992/0114 | error(Ebadarg); | |
| 1991/0925 | } /* | |
| 1992/1013 | * check if the floppy has been replaced under foot. cause * an error if it has. | |
| 1991/0925 | * | |
| 1992/1013 | * a seek and a read clears the condition. this was determined * experimentally, there has to be a better way. * * if the read fails, cycle through the possible floppy * density till one works or we've cycled through all | |
| 1992/1217 | * possibilities for this drive. | |
| 1991/0925 | */ static void | |
| 1997/0327 | changed(Chan *c, FDrive *dp) | |
| 1991/0925 | { ulong old; | |
| 1997/0327 | FType *start; | |
| 1991/0925 | ||
| 1992/1013 | /* * if floppy has changed or first time through */ if((inb(Pdir)&Fchange) || dp->vers == 0){ | |
| 1992/1217 | DPRINT("changed\n"); fldump(); | |
| 1991/0925 | dp->vers++; | |
| 1997/0327 | floppysetdef(dp); | |
| 1992/1013 | start = dp->t; dp->confused = 1; /* make floppyon recal */ | |
| 1992/1006 | floppyon(dp); | |
| 1992/1013 | floppyseek(dp, dp->t->heads*dp->t->tsize); while(waserror()){ while(++dp->t){ | |
| 1997/0327 | if(dp->t == &floppytype[nelem(floppytype)]) | |
| 1992/1013 | dp->t = floppytype; if(dp->dt == dp->t->dt) break; } floppydir[NFDIR*dp->dev].length = dp->t->cap; | |
| 1992/1216 | floppyon(dp); | |
| 1992/1217 | DPRINT("changed: trying %s\n", dp->t->name); fldump(); | |
| 1992/1013 | if(dp->t == start) nexterror(); } floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize); poperror(); | |
| 1991/0925 | } | |
| 1992/1013 | ||
| 1991/1003 | old = c->qid.vers; c->qid.vers = dp->vers; | |
| 1992/1013 | if(old && old != dp->vers) | |
| 1992/0114 | error(Eio); | |
| 1991/0925 | } | |
| 1993/0915 | static int | |
| 1997/0327 | readtrack(FDrive *dp, int cyl, int head) | |
| 1993/0915 | { int i, nn, sofar; ulong pos; nn = dp->t->tsize; if(dp->ccyl==cyl && dp->chead==head) return nn; pos = (cyl*dp->t->heads+head) * nn; for(sofar = 0; sofar < nn; sofar += i){ dp->ccyl = -1; i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar); if(i <= 0) return -1; } dp->ccyl = cyl; dp->chead = head; return nn; } | |
| 1997/0327 | static long | |
| 1998/0319 | floppyread(Chan *c, void *a, long n, vlong off) | |
| 1991/0731 | { | |
| 1997/0327 | FDrive *dp; | |
| 1993/0915 | long rv; int sec, head, cyl; | |
| 1991/0924 | long len; uchar *aa; | |
| 1998/0319 | ulong offset = off; | |
| 1991/0731 | ||
| 1991/0802 | if(c->qid.path == CHDIR) | |
| 1997/0327 | return devdirread(c, a, n, floppydir, fl.ndrive*NFDIR, devgen); | |
| 1991/0802 | rv = 0; | |
| 1991/0924 | dp = &fl.d[c->qid.path & ~Qmask]; | |
| 1991/0802 | switch ((int)(c->qid.path & Qmask)) { case Qdata: | |
| 1992/1009 | islegal(offset, n, dp); | |
| 1991/0924 | aa = a; | |
| 1991/0925 | qlock(&fl); if(waserror()){ qunlock(&fl); nexterror(); } floppyon(dp); changed(c, dp); | |
| 1991/0809 | for(rv = 0; rv < n; rv += len){ /* | |
| 1993/0915 | * all xfers come out of the track cache | |
| 1991/0809 | */ dp->len = n - rv; | |
| 1992/1009 | floppypos(dp, offset+rv); | |
| 1991/0809 | cyl = dp->tcyl; | |
| 1991/0924 | head = dp->thead; | |
| 1991/0809 | len = dp->len; | |
| 1991/0924 | sec = dp->tsec; | |
| 1993/0915 | if(readtrack(dp, cyl, head) < 0) break; | |
| 1991/0924 | memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len); | |
| 1991/0802 | } | |
| 1991/0925 | qunlock(&fl); poperror(); | |
| 1991/0802 | break; | |
| 1991/0924 | case Qctl: | |
| 1992/1007 | return readstr(offset, a, n, dp->t->name); | |
| 1991/0802 | default: panic("floppyread: bad qid"); | |
| 1991/0731 | } | |
| 1991/0924 | ||
| 1991/0731 | return rv; } | |
| 1991/0924 | #define SNCMP(a, b) strncmp(a, b, sizeof(b)-1) | |
| 1997/0327 | static long | |
| 1998/0319 | floppywrite(Chan *c, void *a, long n, vlong off) | |
| 1991/0731 | { | |
| 1997/0327 | FDrive *dp; | |
| 1991/0731 | long rv, i; | |
| 1991/0924 | char *aa = a; | |
| 1992/1006 | char ctlmsg[64]; | |
| 1998/0319 | ulong offset = off; | |
| 1991/0731 | ||
| 1991/0802 | rv = 0; | |
| 1991/0924 | dp = &fl.d[c->qid.path & ~Qmask]; | |
| 1991/0802 | switch ((int)(c->qid.path & Qmask)) { case Qdata: | |
| 1992/1009 | islegal(offset, n, dp); | |
| 1991/0925 | qlock(&fl); if(waserror()){ qunlock(&fl); nexterror(); } floppyon(dp); changed(c, dp); | |
| 1991/0802 | for(rv = 0; rv < n; rv += i){ | |
| 1992/1009 | floppypos(dp, offset+rv); | |
| 1991/0924 | if(dp->tcyl == dp->ccyl) dp->ccyl = -1; | |
| 1992/1013 | i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv); | |
| 1991/1006 | if(i < 0) | |
| 1991/0802 | break; | |
| 1991/1006 | if(i == 0) error(Eio); | |
| 1991/0802 | } | |
| 1991/0925 | qunlock(&fl); poperror(); | |
| 1991/0802 | break; | |
| 1991/0924 | case Qctl: | |
| 1992/1009 | rv = n; | |
| 1991/0925 | qlock(&fl); | |
| 1992/1007 | if(waserror()){ qunlock(&fl); nexterror(); } | |
| 1992/1006 | if(n >= sizeof(ctlmsg)) n = sizeof(ctlmsg) - 1; memmove(ctlmsg, aa, n); ctlmsg[n] = 0; if(SNCMP(ctlmsg, "eject") == 0){ | |
| 1991/0924 | floppyeject(dp); | |
| 1992/1006 | } else if(SNCMP(ctlmsg, "reset") == 0){ | |
| 1991/0925 | fl.confused = 1; | |
| 1991/0924 | floppyon(dp); | |
| 1992/1006 | } else if(SNCMP(ctlmsg, "format") == 0){ | |
| 1992/1009 | floppyformat(dp, ctlmsg); | |
| 1992/1016 | } else if(SNCMP(ctlmsg, "debug") == 0){ floppydebug = 1; | |
| 1997/0327 | } else error(Ebadctl); | |
| 1992/1007 | poperror(); | |
| 1991/0925 | qunlock(&fl); | |
| 1991/0924 | break; | |
| 1991/0802 | default: panic("floppywrite: bad qid"); | |
| 1991/0731 | } | |
| 1991/0924 | ||
| 1991/0731 | return rv; | |
| 1995/0108 | } | |
| 1991/0731 | static void | |
| 1998/0319 | floppykproc(void *) | |
| 1991/0727 | { | |
| 1997/0327 | FDrive *dp; | |
| 1991/0727 | ||
| 1991/0919 | while(waserror()) ; | |
| 1991/0802 | for(;;){ | |
| 1997/0327 | for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){ | |
| 1991/0924 | if((fl.motor&MOTORBIT(dp->dev)) | |
| 1991/0921 | && TK2SEC(m->ticks - dp->lasttouched) > 5 | |
| 1991/0925 | && canqlock(&fl)){ | |
| 1991/0802 | if(TK2SEC(m->ticks - dp->lasttouched) > 5) | |
| 1991/0924 | floppyoff(dp); | |
| 1991/0925 | qunlock(&fl); | |
| 1991/0802 | } | |
| 1991/0728 | } | |
| 1991/0925 | tsleep(&fl.kr, return0, 0, 1000); | |
| 1991/0727 | } } | |
| 1991/0731 | /* | |
| 1991/0924 | * start a floppy drive's motor. | |
| 1991/0921 | */ static void | |
| 1997/0327 | floppyon(FDrive *dp) | |
| 1991/0921 | { int alreadyon; | |
| 1991/0924 | int tries; | |
| 1991/0921 | ||
| 1991/0924 | if(fl.confused) floppyrevive(); /* start motor and select drive */ alreadyon = fl.motor & MOTORBIT(dp->dev); fl.motor |= MOTORBIT(dp->dev); outb(Pdor, fl.motor | Fintena | Fena | dp->dev); | |
| 1992/1217 | if(!alreadyon){ /* wait for drive to spin up */ | |
| 1991/0921 | tsleep(&dp->r, return0, 0, 750); | |
| 1991/0924 | ||
| 1992/1217 | /* clear any pending interrupts */ floppysense(); } | |
| 1992/1003 | /* set transfer rate */ if(fl.rate != dp->t->rate){ fl.rate = dp->t->rate; outb(Pdsr, fl.rate); } | |
| 1991/0924 | /* get drive to a known cylinder */ if(dp->confused) for(tries = 0; tries < 4; tries++) if(floppyrecal(dp) >= 0) break; | |
| 1991/0921 | dp->lasttouched = m->ticks; | |
| 1991/0925 | fl.selected = dp; | |
| 1991/0921 | } /* * stop the floppy if it hasn't been used in 5 seconds */ static void | |
| 1997/0327 | floppyoff(FDrive *dp) | |
| 1991/0921 | { | |
| 1991/0924 | fl.motor &= ~MOTORBIT(dp->dev); outb(Pdor, fl.motor | Fintena | Fena | dp->dev); | |
| 1991/0921 | } /* | |
| 1991/0924 | * send a command to the floppy | |
| 1991/0731 | */ static int | |
| 1991/0924 | floppycmd(void) | |
| 1991/0727 | { | |
| 1991/0924 | int i; | |
| 1991/0727 | int tries; | |
| 1992/1016 | fl.nstat = 0; | |
| 1991/0924 | for(i = 0; i < fl.ncmd; i++){ for(tries = 0; ; tries++){ | |
| 1997/0327 | if((inb(Pmsr)&(Ffrom|Fready)) == Fready) break; | |
| 1992/1217 | 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(); | |
| 1991/0924 | return -1; } | |
| 1997/0327 | microdelay(8); /* for machine independence */ | |
| 1991/0924 | } | |
| 1997/0327 | outb(Pfdata, fl.cmd[i]); | |
| 1991/0727 | } | |
| 1991/0924 | return 0; | |
| 1991/0727 | } | |
| 1991/0731 | /* | |
| 1991/0924 | * get a command result from the floppy * * when the controller goes ready waiting for a command * (instead of sending results), we're done * | |
| 1991/0731 | */ static int | |
| 1991/0924 | floppyresult(void) | |
| 1991/0727 | { | |
| 1991/0924 | int i, s; int tries; | |
| 1991/0727 | ||
| 1992/1217 | /* get the result of the operation */ | |
| 1991/0924 | for(i = 0; i < sizeof(fl.stat); i++){ | |
| 1992/1217 | /* wait for status byte */ | |
| 1991/0924 | for(tries = 0; ; tries++){ s = inb(Pmsr)&(Ffrom|Fready); if(s == Fready){ fl.nstat = i; | |
| 1992/1217 | return fl.nstat; | |
| 1991/0924 | } if(s == (Ffrom|Fready)) break; | |
| 1997/0327 | if(tries > 1000){ DPRINT("floppyresult: %d stats\n", i); fldump(); fl.confused = 1; return -1; } microdelay(8); /* for machine independence */ | |
| 1991/0924 | } | |
| 1997/0327 | fl.stat[i] = inb(Pfdata); | |
| 1991/0727 | } | |
| 1992/1217 | fl.nstat = sizeof(fl.stat); return fl.nstat; | |
| 1991/0727 | } | |
| 1991/0731 | /* * calculate physical address of a logical byte offset into the disk * | |
| 1991/0924 | * truncate dp->length if it crosses a track boundary | |
| 1991/0731 | */ static void | |
| 1997/0327 | floppypos(FDrive *dp, long off) | |
| 1991/0727 | { int lsec; | |
| 1991/0924 | int ltrack; | |
| 1991/0727 | int end; | |
| 1991/0802 | lsec = off/dp->t->bytes; | |
| 1991/0924 | ltrack = lsec/dp->t->sectors; dp->tcyl = ltrack/dp->t->heads; | |
| 1991/0727 | dp->tsec = (lsec % dp->t->sectors) + 1; dp->thead = (lsec/dp->t->sectors) % dp->t->heads; /* | |
| 1991/0924 | * can't read across track boundaries. | |
| 1991/0727 | * if so, decrement the bytes to be read. */ | |
| 1991/0924 | end = (ltrack+1)*dp->t->sectors*dp->t->bytes; if(off+dp->len > end) dp->len = end - off; | |
| 1991/0727 | } | |
| 1991/0731 | /* | |
| 1991/0924 | * get the interrupt cause from the floppy. | |
| 1991/0731 | */ static int | |
| 1991/0924 | floppysense(void) | |
| 1991/0727 | { | |
| 1991/0924 | fl.ncmd = 0; fl.cmd[fl.ncmd++] = Fsense; if(floppycmd() < 0) | |
| 1991/0727 | return -1; | |
| 1991/0924 | if(floppyresult() < 2){ | |
| 1992/0901 | DPRINT("can't read sense response\n"); | |
| 1992/1217 | fldump(); | |
| 1991/0924 | fl.confused = 1; | |
| 1991/0727 | return -1; } return 0; } | |
| 1991/0924 | static int | |
| 1998/0319 | cmddone(void *) | |
| 1991/0924 | { return fl.ncmd == 0; } | |
| 1991/0731 | /* | |
| 1992/1217 | * 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. | |
| 1991/0731 | */ | |
| 1991/0924 | static void floppywait(void) | |
| 1991/0731 | { | |
| 1992/1217 | tsleep(&fl.r, cmddone, 0, 5000); | |
| 1992/1218 | if(!cmddone(0)){ | |
| 1997/0327 | floppyintr(0); | |
| 1992/1218 | fl.confused = 1; } | |
| 1991/0731 | } /* * we've lost the floppy position, go to cylinder 0. */ static int | |
| 1997/0327 | floppyrecal(FDrive *dp) | |
| 1991/0727 | { | |
| 1991/0924 | dp->ccyl = -1; | |
| 1992/1016 | dp->cyl = -1; | |
| 1991/0727 | ||
| 1991/0924 | fl.ncmd = 0; fl.cmd[fl.ncmd++] = Frecal; fl.cmd[fl.ncmd++] = dp->dev; if(floppycmd() < 0) return -1; floppywait(); if(fl.nstat < 2){ | |
| 1992/1120 | DPRINT("recalibrate: confused %ux\n", inb(Pmsr)); | |
| 1991/0924 | fl.confused = 1; return -1; } if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ | |
| 1992/1016 | DPRINT("recalibrate: failed\n"); | |
| 1991/0727 | dp->confused = 1; return -1; } | |
| 1992/1006 | dp->cyl = fl.stat[1]; if(dp->cyl != 0){ | |
| 1992/1016 | DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl); | |
| 1992/1013 | dp->cyl = -1; | |
| 1991/0727 | dp->confused = 1; return -1; } dp->confused = 0; return 0; } | |
| 1991/0731 | /* * if the controller or a specific drive is in a confused state, | |
| 1999/0507 | * reset it and get back to a known state | |
| 1991/0731 | */ | |
| 1995/1121 | static void | |
| 1991/0731 | floppyrevive(void) | |
| 1991/0727 | { | |
| 1997/0327 | FDrive *dp; | |
| 1991/0727 | /* | |
| 1991/0924 | * reset the controller if it's confused | |
| 1991/0727 | */ | |
| 1991/0924 | if(fl.confused){ | |
| 1992/1217 | DPRINT("floppyrevive in\n"); fldump(); | |
| 1991/0727 | /* reset controller and turn all motors off */ splhi(); | |
| 1992/1217 | fl.ncmd = 1; | |
| 1991/0924 | fl.cmd[0] = 0; outb(Pdor, 0); | |
| 1992/1218 | delay(10); | |
| 1991/0924 | outb(Pdor, Fintena|Fena); | |
| 1992/1218 | delay(10); | |
| 1991/0727 | spllo(); | |
| 1991/0924 | fl.motor = 0; fl.confused = 0; | |
| 1992/1217 | floppywait(); /* mark all drives in an unknown state */ | |
| 1997/0327 | for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++) | |
| 1992/1217 | dp->confused = 1; /* set rate to a known value */ | |
| 1991/0925 | outb(Pdsr, 0); | |
| 1992/1216 | fl.rate = 0; | |
| 1992/1217 | DPRINT("floppyrevive out\n"); fldump(); | |
| 1991/0727 | } } | |
| 1991/0924 | /* * seek to the target cylinder * * interrupt, no results */ | |
| 1991/0731 | static long | |
| 1997/0327 | floppyseek(FDrive *dp, long off) | |
| 1991/0727 | { | |
| 1991/0924 | floppypos(dp, off); | |
| 1992/1006 | if(dp->cyl == dp->tcyl) | |
| 1992/1003 | return dp->tcyl; | |
| 1992/1016 | dp->cyl = -1; | |
| 1991/0727 | ||
| 1991/0924 | 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; | |
| 1992/1016 | if(floppycmd() < 0) | |
| 1991/0727 | return -1; | |
| 1991/0924 | floppywait(); if(fl.nstat < 2){ | |
| 1992/1016 | DPRINT("seek: confused\n"); | |
| 1991/0924 | fl.confused = 1; | |
| 1991/0727 | return -1; | |
| 1991/0924 | } if((fl.stat[0] & (Codemask|Seekend)) != Seekend){ | |
| 1992/1016 | DPRINT("seek: failed\n"); | |
| 1991/0727 | dp->confused = 1; return -1; } | |
| 1992/1006 | dp->cyl = dp->tcyl; | |
| 1992/1003 | return dp->tcyl; | |
| 1991/0727 | } | |
| 1991/1006 | /* | |
| 1992/1013 | * read or write to floppy. try up to three times. | |
| 1991/1006 | */ | |
| 1991/0731 | static long | |
| 1997/0327 | floppyxfer(FDrive *dp, int cmd, void *a, long off, long n) | |
| 1991/0727 | { long offset; | |
| 1992/1016 | int tries; | |
| 1991/0727 | ||
| 1991/0924 | if(off >= dp->t->cap) return 0; if(off + n > dp->t->cap) n = dp->t->cap - off; | |
| 1997/0327 | /* retry on error (until it gets ridiculous) */ | |
| 1992/1016 | tries = 0; while(waserror()){ | |
| 1995/0728 | if(tries++ > 20) | |
| 1992/1013 | nexterror(); | |
| 1997/0327 | DPRINT("floppyxfer: retrying\n"); /*floppyon(dp);*/ | |
| 1992/1016 | } | |
| 1992/1013 | ||
| 1991/0727 | dp->len = n; | |
| 1992/1016 | if(floppyseek(dp, off) < 0){ DPRINT("xfer: seek failed\n"); dp->confused = 1; | |
| 1992/0114 | error(Eio); | |
| 1992/1016 | } | |
| 1991/0727 | /* | |
| 1991/0731 | * set up the dma (dp->len may be trimmed) | |
| 1991/0727 | */ | |
| 1992/1013 | if(waserror()){ dmaend(DMAchan); nexterror(); } | |
| 1991/0924 | dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread); | |
| 1997/0404 | if(dp->len < 0) error(Eio); | |
| 1991/0727 | /* | |
| 1991/0731 | * start operation | |
| 1991/0727 | */ | |
| 1991/0924 | fl.ncmd = 0; | |
| 1995/0728 | fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0); | |
| 1991/0924 | fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev; | |
| 1992/1006 | fl.cmd[fl.ncmd++] = dp->tcyl; | |
| 1991/0924 | fl.cmd[fl.ncmd++] = dp->thead; fl.cmd[fl.ncmd++] = dp->tsec; fl.cmd[fl.ncmd++] = dp->t->bcode; | |
| 1991/1001 | fl.cmd[fl.ncmd++] = dp->t->sectors; | |
| 1991/0924 | fl.cmd[fl.ncmd++] = dp->t->gpl; fl.cmd[fl.ncmd++] = 0xFF; | |
| 1992/1016 | if(floppycmd() < 0) | |
| 1992/0114 | error(Eio); | |
| 1991/0727 | ||
| 1997/0327 | /* Poll ready bits and transfer data */ floppyexec((char*)a, dp->len, cmd==Fread); | |
| 1991/0727 | /* | |
| 1991/0924 | * give bus to DMA, floppyintr() will read result */ floppywait(); dmaend(DMAchan); | |
| 1992/1013 | poperror(); | |
| 1991/0924 | /* * check for errors */ if(fl.nstat < 7){ | |
| 1992/1016 | DPRINT("xfer: confused\n"); | |
| 1991/0924 | fl.confused = 1; | |
| 1992/0114 | error(Eio); | |
| 1991/0727 | } | |
| 1991/0924 | if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){ | |
| 1998/0825 | DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0], | |
| 1991/0925 | fl.stat[1], fl.stat[2]); | |
| 1998/0825 | DPRINT("offset %lud len %ld\n", off, dp->len); | |
| 1997/0327 | if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){ | |
| 1995/0818 | DPRINT("DMA overrun: retry\n"); | |
| 1997/0327 | } else | |
| 1995/0818 | dp->confused = 1; | |
| 1992/0114 | error(Eio); | |
| 1991/0727 | } | |
| 1991/0924 | /* * check for correct cylinder */ | |
| 1992/1006 | offset = fl.stat[3] * dp->t->heads + fl.stat[4]; | |
| 1991/0924 | offset = offset*dp->t->sectors + fl.stat[5] - 1; offset = offset * c2b[fl.stat[6]]; | |
| 1991/0802 | if(offset != off+dp->len){ | |
| 1992/1016 | DPRINT("xfer: ends on wrong cyl\n"); | |
| 1991/0727 | dp->confused = 1; | |
| 1992/0114 | error(Eio); | |
| 1991/0727 | } | |
| 1992/1013 | poperror(); | |
| 1991/0727 | ||
| 1991/0802 | dp->lasttouched = m->ticks; | |
| 1991/0727 | return dp->len; } | |
| 1992/1006 | /* * format a track */ | |
| 1992/1009 | static void | |
| 1997/0327 | floppyformat(FDrive *dp, char *params) | |
| 1992/1006 | { int cyl, h, sec; | |
| 1992/1009 | ulong track; | |
| 1992/1006 | uchar *buf, *bp; | |
| 1997/0327 | FType *t; | |
| 1992/1009 | char *f[3]; | |
| 1992/1006 | /* | |
| 1992/1009 | * set the type | |
| 1992/1006 | */ | |
| 1996/0315 | if(parsefields(params, f, 3, " ") > 1){ | |
| 1997/0327 | for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){ | |
| 1992/1009 | if(strcmp(f[1], t->name)==0 && t->dt==dp->dt){ dp->t = t; floppydir[NFDIR*dp->dev].length = dp->t->cap; break; } | |
| 1995/0218 | } | |
| 1997/0327 | if(t >= &floppytype[nelem(floppytype)]) | |
| 1995/0218 | error(Ebadarg); | |
| 1992/1009 | } else { | |
| 1997/0327 | floppysetdef(dp); | |
| 1992/1009 | t = dp->t; | |
| 1992/1006 | } /* | |
| 1992/1009 | * buffer for per track info | |
| 1992/1006 | */ | |
| 1992/1009 | buf = smalloc(t->sectors*4); if(waserror()){ free(buf); nexterror(); | |
| 1992/1006 | } | |
| 1992/1016 | /* force a recalibrate to cylinder 0 */ dp->confused = 1; if(!waserror()){ floppyon(dp); poperror(); } | |
| 1992/1006 | /* | |
| 1992/1009 | * format a track at time | |
| 1992/1006 | */ | |
| 1992/1009 | for(track = 0; track < t->tracks*t->heads; track++){ cyl = track/t->heads; h = track % t->heads; | |
| 1992/1006 | ||
| 1992/1009 | /* * 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; } | |
| 1992/1013 | if(waserror()){ dmaend(DMAchan); nexterror(); } | |
| 1997/0404 | if(dmasetup(DMAchan, buf, bp-buf, 0) < 0) error(Eio); | |
| 1992/1009 | /* * 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; | |
| 1992/1016 | if(floppycmd() < 0) | |
| 1992/1009 | error(Eio); | |
| 1997/0327 | /* Poll ready bits and transfer data */ floppyexec((char *)buf, bp-buf, 0); | |
| 1992/1009 | /* * give bus to DMA, floppyintr() will read result */ floppywait(); dmaend(DMAchan); | |
| 1992/1013 | poperror(); | |
| 1992/1009 | /* * check for errors */ if(fl.nstat < 7){ | |
| 1992/1016 | DPRINT("format: confused\n"); | |
| 1992/1009 | fl.confused = 1; error(Eio); } if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){ | |
| 1998/0825 | DPRINT("format: failed %ux %ux %ux\n", | |
| 1992/1009 | fl.stat[0], fl.stat[1], fl.stat[2]); dp->confused = 1; error(Eio); } | |
| 1992/1006 | } free(buf); | |
| 1992/1013 | dp->confused = 1; | |
| 1992/1007 | poperror(); | |
| 1992/1006 | } | |
| 1991/0925 | static void | |
| 1998/0319 | floppyintr(Ureg *) | |
| 1991/0727 | { | |
| 1991/0924 | switch(fl.cmd[0]&~Fmulti){ case Fread: case Fwrite: | |
| 1992/1006 | case Fformat: | |
| 1997/0327 | case Fdumpreg: | |
| 1991/0924 | floppyresult(); break; case Fseek: case Frecal: default: floppysense(); /* to clear interrupt */ break; } fl.ncmd = 0; wakeup(&fl.r); | |
| 1991/0727 | } | |
| 1997/0327 | Dev floppydevtab = { | |
| 1997/0408 | 'f', "floppy", | |
| 1997/0327 | floppyreset, devinit, floppyattach, devclone, floppywalk, floppystat, floppyopen, devcreate, floppyclose, floppyread, devbread, floppywrite, devbwrite, devremove, devwstat, }; | |