| plan 9 kernel history: overview | file list | diff list |
1991/0919/pc/devfloppy.c (diff list | history)
| pc/devfloppy.c on 1991/0727 | ||
| 1991/0727 | #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" | |
| 1991/0802 | #include "errno.h" | |
| 1991/0727 | ||
| 1991/0802 | typedef struct Drive Drive; | |
| 1991/0727 | typedef struct Controller Controller; | |
| 1991/0802 | typedef struct Type Type; | |
| 1991/0727 | enum { Fmotor= 0x3f2, /* motor port */ Fintena= 0x4, /* enable floppy interrupt */ Fena= 0x8, /* 0 == reset controller */ Fstatus= 0x3f4, /* controller main status port */ Fready= 0x80, /* ready to be touched */ Ffrom= 0x40, /* data from controller */ Fbusy= 0x10, /* operation not over */ Fdata= 0x3f5, /* controller data port */ Frecal= 0x7, /* recalibrate cmd */ Fseek= 0xf, /* seek cmd */ Fsense= 0x8, /* sense cmd */ Fread= 0x66, /* read cmd */ | |
| 1991/0831 | Fwrite= 0x45, /* write cmd */ | |
| 1991/0727 | Fmulti= 0x80, /* or'd with Fread or Fwrite for multi-head */ | |
| 1991/0831 | Fchanged= 0x3F7, /* disk changed register */ Fchange= 0x80, /* disk has changed */ | |
| 1991/0728 | DMAchan= 2, /* floppy dma channel */ | |
| 1991/0727 | /* status 0 byte */ Drivemask= 3<<0, Seekend= 1<<5, Codemask= (3<<6)|(3<<3), | |
| 1991/0728 | /* file types */ Qdir= 0, | |
| 1991/0802 | Qdata= (1<<2), Qmask= (3<<2), | |
| 1991/0727 | }; /* * floppy types */ struct Type { char *name; int bytes; /* bytes/sector */ int sectors; /* sectors/track */ int heads; /* number of heads */ int steps; /* steps per cylinder */ int tracks; /* tracks/disk */ int gpl; /* intersector gap length for read/write */ | |
| 1991/0731 | int fgpl; /* intersector gap length for format */ /* * these depend on previous entries and are set filled in * by floppyinit */ int bcode; /* coded version of bytes for the controller */ | |
| 1991/0727 | long cap; /* drive capacity in bytes */ }; Type floppytype[] = { | |
| 1991/0731 | { "MF2HD", 512, 18, 2, 1, 80, 0x1B, 0x54, }, { "MF2DD", 512, 9, 2, 1, 80, 0x1B, 0x54, }, { "F2HD", 512, 15, 2, 1, 80, 0x2A, 0x50, }, { "F2DD", 512, 8, 2, 2, 40, 0x2A, 0x50, }, { "F1DD", 512, 8, 1, 2, 40, 0x2A, 0x50, }, | |
| 1991/0727 | }; | |
| 1991/0731 | #define NTYPES (sizeof(floppytype)/sizeof(Type)) /* | |
| 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 | }; /* * a floppy drive */ struct Drive { | |
| 1991/0731 | QLock; /* exclusive access to the drive */ | |
| 1991/0727 | Type *t; int dev; ulong lasttouched; /* time last touched */ int motoron; /* motor is on */ int cyl; /* current cylinder */ int confused; /* needs to be recalibrated (or worse) */ int tcyl; /* target cylinder */ int thead; /* target head */ int tsec; /* target sector */ | |
| 1991/0731 | long len; /* size of xfer */ | |
| 1991/0727 | ||
| 1991/0731 | Rendez r; /* waiting here for motor to spin up */ | |
| 1991/0809 | uchar *ccache; /* cylinder cache (always read a whole cyl) */ int ccyl; /* number of cached cylinder */ | |
| 1991/0727 | }; /* * NEC PD765A controller for 4 floppys */ struct Controller { | |
| 1991/0731 | QLock; /* exclusive access to the contoller */ | |
| 1991/0809 | Drive *d; /* the floppy drives */ | |
| 1991/0727 | uchar stat[8]; /* status of an operation */ int confused; | |
| 1991/0731 | int intr; /* true if interrupt occured */ Rendez r; /* wait here for command termination */ | |
| 1991/0802 | Rendez kr; /* for motor watcher */ | |
| 1991/0727 | }; | |
| 1991/0728 | Controller floppy; | |
| 1991/0727 | /* | |
| 1991/0731 | * predeclared */ static void motoron(Drive*); static void motoroff(Drive*); static void floppykproc(void*); static int floppysend(int); static int floppyrcv(void); static int floppyresult(int); | |
| 1991/0802 | static void floppypos(Drive*,long); | |
| 1991/0731 | static int floppysense(Drive*); static int interrupted(void*); static int floppyrecal(Drive*); static void floppyrevive(void); static long floppyseek(Drive*); | |
| 1991/0802 | static long floppyxfer(Drive*, int, void*, long, long); | |
| 1991/0731 | static void floppyintr(Ureg*); | |
| 1991/0802 | Dirtab floppydir[]={ | |
| 1991/0823 | "fd0disk", {Qdata + 0}, 0, 0600, "fd1disk", {Qdata + 1}, 0, 0600, "fd2disk", {Qdata + 2}, 0, 0600, "fd3disk", {Qdata + 3}, 0, 0600, | |
| 1991/0802 | }; | |
| 1991/0811 | #define NFDIR 2 /* directory entries/drive */ | |
| 1991/0731 | ||
| 1991/0809 | #define k64(x) (((ulong)(x))>>16) | |
| 1991/0731 | void floppyreset(void) { Drive *dp; | |
| 1991/0802 | Type *t; | |
| 1991/0810 | int n; | |
| 1991/0731 | ||
| 1991/0802 | /* * init dependent parameters */ for(t = floppytype; t < &floppytype[NTYPES]; t++){ t->cap = t->bytes * t->heads * t->sectors * t->tracks; t->bcode = b2c[t->bytes/128]; } /* | |
| 1991/0809 | * allocate the drive storage */ floppy.d = ialloc(conf.nfloppy*sizeof(Drive), 0); /* | |
| 1991/0802 | * stop the motors */ | |
| 1991/0809 | for(dp = floppy.d; dp < &floppy.d[conf.nfloppy]; dp++){ | |
| 1991/0731 | dp->dev = dp - floppy.d; dp->t = &floppytype[0]; /* default type */ | |
| 1991/0811 | floppydir[NFDIR*dp->dev].length = dp->t->cap; | |
| 1991/0731 | dp->motoron = 1; | |
| 1991/0802 | dp->cyl = -1; /* because we don't know */ | |
| 1991/0731 | motoroff(dp); } | |
| 1991/0809 | /* * allocate cylinder caches that don't cross 64k boundaries */ for(dp = floppy.d; dp < &floppy.d[conf.nfloppy]; dp++){ do { | |
| 1991/0810 | n = 512 * 18 * 2; /* MF2HD cylinder size */ dp->ccache = ialloc(n, 1); } while(k64(dp->ccache) != k64(dp->ccache+n)); | |
| 1991/0809 | dp->ccyl = -1; } | |
| 1991/0731 | setvec(Floppyvec, floppyintr); } void floppyinit(void) { /* * watchdog to turn off the motors */ | |
| 1991/0802 | kproc("floppy", floppykproc, 0); | |
| 1991/0731 | } | |
| 1991/0802 | Chan* floppyattach(char *spec) { return devattach('f', spec); } Chan* floppyclone(Chan *c, Chan *nc) { return devclone(c, nc); } int floppywalk(Chan *c, char *name) { | |
| 1991/0811 | return devwalk(c, name, floppydir, conf.nfloppy*NFDIR, devgen); | |
| 1991/0802 | } void floppystat(Chan *c, char *dp) { | |
| 1991/0811 | devstat(c, dp, floppydir, conf.nfloppy*NFDIR, devgen); | |
| 1991/0802 | } Chan* floppyopen(Chan *c, int omode) { | |
| 1991/0811 | return devopen(c, omode, floppydir, conf.nfloppy*NFDIR, devgen); | |
| 1991/0802 | } void floppycreate(Chan *c, char *name, int omode, ulong perm) { error(Eperm); } void floppyclose(Chan *c) { } void floppyremove(Chan *c) { error(Eperm); } void floppywstat(Chan *c, char *dp) { error(Eperm); } static void ul2user(uchar *a, ulong x) { a[0] = x >> 24; a[1] = x >> 16; a[2] = x >> 8; a[3] = x; } | |
| 1991/0809 | /* | |
| 1991/0831 | * look for a floppy change */ void | |
| 1991/0902 | floppychanged(Drive *dp) | |
| 1991/0831 | { if((inb(Fchanged) & Fchange) == 0) return; } /* | |
| 1991/0809 | * the floppy is so slow, we always read a cylinder * at a time and cache the extra bytes. */ | |
| 1991/0731 | long floppyread(Chan *c, void *a, long n) { Drive *dp; | |
| 1991/0809 | long rv, nn, len, cyl; int sec; | |
| 1991/0731 | uchar *aa = a; | |
| 1991/0802 | if(c->qid.path == CHDIR) | |
| 1991/0811 | return devdirread(c, a, n, floppydir, conf.nfloppy*NFDIR, devgen); | |
| 1991/0802 | rv = 0; dp = &floppy.d[c->qid.path & ~Qmask]; switch ((int)(c->qid.path & Qmask)) { case Qdata: | |
| 1991/0831 | floppychanged(dp); | |
| 1991/0809 | if(c->offset % dp->t->bytes) errors("bad offset"); if(n % dp->t->bytes) errors("bad len"); nn = dp->t->bytes * dp->t->sectors * dp->t->heads; for(rv = 0; rv < n; rv += len){ /* * truncate xfer at cylinder boundary */ dp->len = n - rv; floppypos(dp, c->offset+rv); cyl = dp->tcyl; len = dp->len; sec = dp->tsec + dp->thead * dp->t->sectors; /* * read the cylinder */ if(dp->ccyl != cyl){ dp->ccyl = -1; if(floppyxfer(dp, Fread, dp->ccache, cyl * nn, nn) != nn) errors("floppy read err"); dp->ccyl = cyl; } memmove(aa+rv, dp->ccache + (sec-1)*dp->t->bytes, len); | |
| 1991/0802 | } break; default: panic("floppyread: bad qid"); | |
| 1991/0731 | } return rv; } long floppywrite(Chan *c, void *a, long n) { Drive *dp; long rv, i; uchar *aa = a; | |
| 1991/0802 | rv = 0; dp = &floppy.d[c->qid.path & ~Qmask]; switch ((int)(c->qid.path & Qmask)) { case Qdata: | |
| 1991/0831 | floppychanged(dp); dp->ccyl = -1; | |
| 1991/0802 | for(rv = 0; rv < n; rv += i){ i = floppyxfer(dp, Fwrite, aa+rv, c->offset+rv, n-rv); if(i <= 0) break; } break; default: panic("floppywrite: bad qid"); | |
| 1991/0731 | } return rv; } /* | |
| 1991/0802 | * start a floppy drive's motor. set an alarm for .75 second later to | |
| 1991/0727 | * mark it as started (we get no interrupt to tell us). * * assume the caller qlocked the drive. */ | |
| 1991/0731 | static void motoron(Drive *dp) | |
| 1991/0727 | { int cmd; cmd = (1<<(dp->dev+4)) | Fintena | Fena | dp->dev; outb(Fmotor, cmd); | |
| 1991/0802 | tsleep(&dp->r, return0, 0, 750); | |
| 1991/0727 | dp->motoron = 1; dp->lasttouched = m->ticks; } /* * stop the floppy if it hasn't been used in 5 seconds */ | |
| 1991/0731 | static void motoroff(Drive *dp) | |
| 1991/0727 | { int cmd; cmd = Fintena | Fena | dp->dev; outb(Fmotor, cmd); dp->motoron = 0; } | |
| 1991/0731 | static void floppykproc(void *a) | |
| 1991/0727 | { Drive *dp; | |
| 1991/0803 | int disp = 0; | |
| 1991/0727 | ||
| 1991/0919 | while(waserror()) ; | |
| 1991/0802 | for(;;){ | |
| 1991/0809 | for(dp = floppy.d; dp < &floppy.d[conf.nfloppy]; dp++){ | |
| 1991/0802 | if(dp->motoron && TK2SEC(m->ticks - dp->lasttouched) > 5 && canqlock(dp)){ if(TK2SEC(m->ticks - dp->lasttouched) > 5) motoroff(dp); qunlock(dp); } | |
| 1991/0728 | } | |
| 1991/0803 | disp++; | |
| 1991/0802 | tsleep(&floppy.kr, return0, 0, 5*1000); | |
| 1991/0803 | ||
| 1991/0727 | } } | |
| 1991/0731 | /* * send a byte to the floppy */ static int | |
| 1991/0727 | floppysend(int data) { int tries; uchar c; for(tries = 0; tries < 100; tries++){ /* * see if its ready for data */ c = inb(Fstatus); if((c&(Ffrom|Fready)) != Fready) continue; /* * send the data */ outb(Fdata, data); return 0; } return -1; } | |
| 1991/0731 | /* * get a byte from the floppy */ static int | |
| 1991/0727 | floppyrcv(void) { int tries; uchar c; for(tries = 0; tries < 100; tries++){ /* * see if its ready for data */ c = inb(Fstatus); if((c&(Ffrom|Fready)) != (Ffrom|Fready)) continue; /* * get data */ return inb(Fdata)&0xff; } return -1; } | |
| 1991/0731 | /* * read a command result message from the floppy */ static int floppyresult(int n) | |
| 1991/0727 | { int i; int c; for(i = 0; i < n; i++){ c = floppyrcv(); if(c < 0) return -1; floppy.stat[i] = c; } return 0; } | |
| 1991/0731 | /* * calculate physical address of a logical byte offset into the disk * * truncate dp->length if it crosses a cylinder boundary */ static void | |
| 1991/0802 | floppypos(Drive *dp, long off) | |
| 1991/0727 | { int lsec; int end; int cyl; | |
| 1991/0802 | lsec = off/dp->t->bytes; | |
| 1991/0727 | dp->tcyl = lsec/(dp->t->sectors*dp->t->heads); dp->tsec = (lsec % dp->t->sectors) + 1; dp->thead = (lsec/dp->t->sectors) % dp->t->heads; /* * can't read across cylinder boundaries. * if so, decrement the bytes to be read. */ | |
| 1991/0802 | lsec = (off+dp->len)/dp->t->bytes; | |
| 1991/0727 | cyl = lsec/(dp->t->sectors*dp->t->heads); if(cyl != dp->tcyl){ | |
| 1991/0731 | dp->len -= (lsec % dp->t->sectors)*dp->t->bytes; dp->len -= ((lsec/dp->t->sectors) % dp->t->heads)*dp->t->bytes | |
| 1991/0727 | *dp->t->sectors; } } | |
| 1991/0731 | /* * get the interrupt cause from the floppy. we need to do this * after seeks and recalibrations since they don't return results. */ static int | |
| 1991/0727 | floppysense(Drive *dp) { /* * ask for floppy status */ if(floppysend(Fsense) < 0){ floppy.confused = 1; return -1; } | |
| 1991/0731 | if(floppyresult(2) < 0){ | |
| 1991/0727 | floppy.confused = 1; dp->confused = 1; return -1; } /* * make sure it's the right drive */ | |
| 1991/0802 | if((floppy.stat[0] & Drivemask) != dp->dev){ | |
| 1991/0727 | print("sense failed\n"); dp->confused = 1; return -1; } return 0; } | |
| 1991/0731 | /* * return true if interrupt occurred */ static int interrupted(void *a) { return floppy.intr; } /* * we've lost the floppy position, go to cylinder 0. */ static int | |
| 1991/0727 | floppyrecal(Drive *dp) { floppy.intr = 0; if(floppysend(Frecal) < 0 || floppysend(dp - floppy.d) < 0){ floppy.confused = 0; return -1; } | |
| 1991/0731 | sleep(&floppy.r, interrupted, 0); | |
| 1991/0727 | /* * get return values */ if(floppysense(dp) < 0) return -1; /* * see if it worked */ if((floppy.stat[0] & (Codemask|Seekend)) != Seekend){ print("recalibrate failed\n"); dp->confused = 1; return -1; } /* * see what cylinder we got to */ dp->tcyl = 0; dp->cyl = floppy.stat[1]/dp->t->steps; if(dp->cyl != dp->tcyl){ print("recalibrate went to wrong cylinder %d\n", dp->cyl); dp->confused = 1; return -1; } dp->confused = 0; return 0; } | |
| 1991/0731 | /* * if the controller or a specific drive is in a confused state, * reset it and get back to a kown state */ | |
| 1991/0727 | void | |
| 1991/0731 | floppyrevive(void) | |
| 1991/0727 | { Drive *dp; /* | |
| 1991/0731 | * reset the floppy if it's confused | |
| 1991/0727 | */ if(floppy.confused){ /* reset controller and turn all motors off */ floppy.intr = 0; splhi(); outb(Fmotor, 0); delay(1); outb(Fmotor, Fintena|Fena); spllo(); | |
| 1991/0809 | for(dp = floppy.d; dp < &floppy.d[conf.nfloppy]; dp++){ | |
| 1991/0727 | dp->motoron = 0; dp->confused = 1; } | |
| 1991/0731 | sleep(&floppy.r, interrupted, 0); | |
| 1991/0727 | floppy.confused = 0; } /* * recalibrate any confused drives */ | |
| 1991/0809 | for(dp = floppy.d; floppy.confused == 0 && dp < &floppy.d[conf.nfloppy]; dp++){ | |
| 1991/0727 | if(dp->confused == 0) floppyrecal(dp); } } | |
| 1991/0731 | static long floppyseek(Drive *dp) | |
| 1991/0727 | { | |
| 1991/0802 | if(dp->cyl == dp->tcyl) return dp->cyl; | |
| 1991/0727 | /* * tell floppy to seek */ | |
| 1991/0802 | floppy.intr = 0; dp->cyl = -1; /* once the seek starts it could end anywhere */ | |
| 1991/0727 | if(floppysend(Fseek) < 0 | |
| 1991/0731 | || floppysend((dp->thead<<2) | dp->dev) < 0 | |
| 1991/0727 | || floppysend(dp->tcyl * dp->t->steps) < 0){ print("seek cmd failed\n"); floppy.confused = 1; return -1; } | |
| 1991/0731 | sleep(&floppy.r, interrupted, 0); | |
| 1991/0727 | /* * get floppy status */ if(floppysense(dp) < 0) return -1; /* * see if it worked */ if((floppy.stat[0] & (Codemask|Seekend)) != Seekend){ print("seek failed\n"); dp->confused = 1; return -1; } /* * see what cylinder we got to */ dp->cyl = floppy.stat[1]/dp->t->steps; if(dp->cyl != dp->tcyl){ print("seek went to wrong cylinder %d instead of %d\n", dp->cyl, dp->tcyl); dp->confused = 1; return -1; } | |
| 1991/0802 | return dp->cyl; | |
| 1991/0727 | } | |
| 1991/0731 | static long | |
| 1991/0802 | floppyxfer(Drive *dp, int cmd, void *a, long off, long n) | |
| 1991/0727 | { long offset; | |
| 1991/0731 | qlock(&floppy); qlock(dp); | |
| 1991/0802 | if(waserror()){ | |
| 1991/0731 | qunlock(&floppy); qunlock(dp); } | |
| 1991/0727 | /* | |
| 1991/0731 | * get floppy reset and spinning | |
| 1991/0727 | */ | |
| 1991/0731 | if(floppy.confused || dp->confused) floppyrevive(); if(!dp->motoron) motoron(dp); | |
| 1991/0727 | ||
| 1991/0731 | /* * calculate new position and seek to it (dp->len may be trimmed) */ | |
| 1991/0727 | dp->len = n; | |
| 1991/0802 | floppypos(dp, off); | |
| 1991/0731 | if(floppyseek(dp) < 0) errors("seeking floppy"); | |
| 1991/0727 | ||
| 1991/0906 | ||
| 1991/0823 | /*print("tcyl %d, thead %d, tsec %d, addr %lux, n %d\n", | |
| 1991/0906 | dp->tcyl, dp->thead, dp->tsec, a, n);/**/ | |
| 1991/0727 | /* | |
| 1991/0731 | * set up the dma (dp->len may be trimmed) | |
| 1991/0727 | */ | |
| 1991/0731 | dp->len = dmasetup(2, a, dp->len, cmd==Fread); | |
| 1991/0727 | /* | |
| 1991/0731 | * start operation | |
| 1991/0727 | */ | |
| 1991/0802 | floppy.intr = 0; | |
| 1991/0727 | cmd = cmd | (dp->t->heads > 1 ? Fmulti : 0); if(floppysend(cmd) < 0 | |
| 1991/0802 | || floppysend((dp->thead<<2) | dp->dev) < 0 | |
| 1991/0727 | || floppysend(dp->tcyl * dp->t->steps) < 0 || floppysend(dp->thead) < 0 || floppysend(dp->tsec) < 0 | |
| 1991/0731 | || floppysend(dp->t->bcode) < 0 | |
| 1991/0727 | || floppysend(dp->t->sectors) < 0 || floppysend(dp->t->gpl) < 0 || floppysend(0xFF) < 0){ print("xfer cmd failed\n"); floppy.confused = 1; | |
| 1991/0731 | errors("floppy command failed"); | |
| 1991/0727 | } | |
| 1991/0731 | sleep(&floppy.r, interrupted, 0); | |
| 1991/0727 | /* * get status */ | |
| 1991/0731 | if(floppyresult(7) < 0){ print("xfer status failed\n"); | |
| 1991/0727 | floppy.confused = 1; | |
| 1991/0731 | errors("floppy result failed"); | |
| 1991/0727 | } if((floppy.stat[0] & Codemask)!=0 || floppy.stat[1] || floppy.stat[2]){ print("xfer failed %lux %lux %lux\n", floppy.stat[0], floppy.stat[1], floppy.stat[2]); dp->confused = 1; | |
| 1991/0731 | errors("floppy drive lost"); | |
| 1991/0727 | } offset = (floppy.stat[3]/dp->t->steps) * dp->t->heads + floppy.stat[4]; offset = offset*dp->t->sectors + floppy.stat[5] - 1; | |
| 1991/0731 | offset = offset * c2b[floppy.stat[6]]; | |
| 1991/0802 | if(offset != off+dp->len){ print("new offset %d instead of %d\n", offset, off+dp->len); | |
| 1991/0727 | dp->confused = 1; | |
| 1991/0731 | errors("floppy drive lost"); | |
| 1991/0727 | } | |
| 1991/0802 | dp->lasttouched = m->ticks; | |
| 1991/0731 | qunlock(&floppy); qunlock(dp); poperror(); | |
| 1991/0727 | return dp->len; } | |
| 1991/0731 | static void | |
| 1991/0727 | floppyintr(Ureg *ur) { | |
| 1991/0823 | /*print("floppy intr\n");/**/ | |
| 1991/0727 | floppy.intr = 1; | |
| 1991/0731 | wakeup(&floppy.r); | |
| 1991/0727 | } | |