17dd7cddfSDavid du Colombier #include "u.h"
27dd7cddfSDavid du Colombier #include "../port/lib.h"
37dd7cddfSDavid du Colombier #include "mem.h"
47dd7cddfSDavid du Colombier #include "dat.h"
57dd7cddfSDavid du Colombier #include "fns.h"
67dd7cddfSDavid du Colombier #include "io.h"
77dd7cddfSDavid du Colombier #include "../port/error.h"
87dd7cddfSDavid du Colombier
97dd7cddfSDavid du Colombier #include "floppy.h"
107dd7cddfSDavid du Colombier
117dd7cddfSDavid du Colombier /* Intel 82077A (8272A compatible) floppy controller */
127dd7cddfSDavid du Colombier
137dd7cddfSDavid du Colombier /* This module expects the following functions to be defined
147dd7cddfSDavid du Colombier * elsewhere:
157dd7cddfSDavid du Colombier *
167dd7cddfSDavid du Colombier * inb()
177dd7cddfSDavid du Colombier * outb()
187dd7cddfSDavid du Colombier * floppyexec()
197dd7cddfSDavid du Colombier * floppyeject()
207dd7cddfSDavid du Colombier * floppysetup0()
217dd7cddfSDavid du Colombier * floppysetup1()
227dd7cddfSDavid du Colombier * dmainit()
237dd7cddfSDavid du Colombier * dmasetup()
247dd7cddfSDavid du Colombier * dmaend()
257dd7cddfSDavid du Colombier *
267dd7cddfSDavid du Colombier * On DMA systems, floppyexec() should be an empty function;
277dd7cddfSDavid du Colombier * on non-DMA systems, dmaend() should be an empty function;
287dd7cddfSDavid du Colombier * dmasetup() may enforce maximum transfer sizes.
297dd7cddfSDavid du Colombier */
307dd7cddfSDavid du Colombier
317dd7cddfSDavid du Colombier enum {
327dd7cddfSDavid du Colombier /* file types */
337dd7cddfSDavid du Colombier Qdir= 0,
347dd7cddfSDavid du Colombier Qdata= (1<<2),
357dd7cddfSDavid du Colombier Qctl= (2<<2),
367dd7cddfSDavid du Colombier Qmask= (3<<2),
377dd7cddfSDavid du Colombier
387dd7cddfSDavid du Colombier DMAchan= 2, /* floppy dma channel */
397dd7cddfSDavid du Colombier };
407dd7cddfSDavid du Colombier
417dd7cddfSDavid du Colombier #define DPRINT if(floppydebug)print
427dd7cddfSDavid du Colombier int floppydebug = 0;
437dd7cddfSDavid du Colombier
447dd7cddfSDavid du Colombier /*
457dd7cddfSDavid du Colombier * types of drive (from PC equipment byte)
467dd7cddfSDavid du Colombier */
477dd7cddfSDavid du Colombier enum
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier Tnone= 0,
507dd7cddfSDavid du Colombier T360kb= 1,
517dd7cddfSDavid du Colombier T1200kb= 2,
527dd7cddfSDavid du Colombier T720kb= 3,
537dd7cddfSDavid du Colombier T1440kb= 4,
547dd7cddfSDavid du Colombier };
557dd7cddfSDavid du Colombier
567dd7cddfSDavid du Colombier FType floppytype[] =
577dd7cddfSDavid du Colombier {
587dd7cddfSDavid du Colombier { "3½HD", T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, },
597dd7cddfSDavid du Colombier { "3½DD", T1440kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, },
607dd7cddfSDavid du Colombier { "3½DD", T720kb, 512, 9, 2, 1, 80, 0x1B, 0x54, 2, },
617dd7cddfSDavid du Colombier { "5¼HD", T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
627dd7cddfSDavid du Colombier { "5¼DD", T1200kb, 512, 9, 2, 2, 40, 0x2A, 0x50, 1, },
637dd7cddfSDavid du Colombier { "ATT3B1", T1200kb, 512, 8, 2, 2, 48, 0x2A, 0x50, 1, },
647dd7cddfSDavid du Colombier { "5¼DD", T360kb, 512, 9, 2, 1, 40, 0x2A, 0x50, 2, },
657dd7cddfSDavid du Colombier };
667dd7cddfSDavid du Colombier
677dd7cddfSDavid du Colombier /*
687dd7cddfSDavid du Colombier * bytes per sector encoding for the controller.
697dd7cddfSDavid du Colombier * - index for b2c is is (bytes per sector/128).
707dd7cddfSDavid du Colombier * - index for c2b is code from b2c
717dd7cddfSDavid du Colombier */
727dd7cddfSDavid du Colombier static int b2c[] =
737dd7cddfSDavid du Colombier {
747dd7cddfSDavid du Colombier [1] 0,
757dd7cddfSDavid du Colombier [2] 1,
767dd7cddfSDavid du Colombier [4] 2,
777dd7cddfSDavid du Colombier [8] 3,
787dd7cddfSDavid du Colombier };
797dd7cddfSDavid du Colombier static int c2b[] =
807dd7cddfSDavid du Colombier {
817dd7cddfSDavid du Colombier 128,
827dd7cddfSDavid du Colombier 256,
837dd7cddfSDavid du Colombier 512,
847dd7cddfSDavid du Colombier 1024,
857dd7cddfSDavid du Colombier };
867dd7cddfSDavid du Colombier
877dd7cddfSDavid du Colombier FController fl;
887dd7cddfSDavid du Colombier
897dd7cddfSDavid du Colombier #define MOTORBIT(i) (1<<((i)+4))
907dd7cddfSDavid du Colombier
917dd7cddfSDavid du Colombier /*
927dd7cddfSDavid du Colombier * predeclared
937dd7cddfSDavid du Colombier */
947dd7cddfSDavid du Colombier static int cmddone(void*);
959a747e4fSDavid du Colombier static void floppyformat(FDrive*, Cmdbuf*);
967dd7cddfSDavid du Colombier static void floppykproc(void*);
977dd7cddfSDavid du Colombier static void floppypos(FDrive*,long);
987dd7cddfSDavid du Colombier static int floppyrecal(FDrive*);
997dd7cddfSDavid du Colombier static int floppyresult(void);
1007dd7cddfSDavid du Colombier static void floppyrevive(void);
1017dd7cddfSDavid du Colombier static long floppyseek(FDrive*, long);
1027dd7cddfSDavid du Colombier static int floppysense(void);
10359cc4ca5SDavid du Colombier static void floppywait(int);
1047dd7cddfSDavid du Colombier static long floppyxfer(FDrive*, int, void*, long, long);
1057dd7cddfSDavid du Colombier
1067dd7cddfSDavid du Colombier Dirtab floppydir[]={
1079a747e4fSDavid du Colombier ".", {Qdir, 0, QTDIR}, 0, 0550,
1087dd7cddfSDavid du Colombier "fd0disk", {Qdata + 0}, 0, 0660,
1097dd7cddfSDavid du Colombier "fd0ctl", {Qctl + 0}, 0, 0660,
1107dd7cddfSDavid du Colombier "fd1disk", {Qdata + 1}, 0, 0660,
1117dd7cddfSDavid du Colombier "fd1ctl", {Qctl + 1}, 0, 0660,
1127dd7cddfSDavid du Colombier "fd2disk", {Qdata + 2}, 0, 0660,
1137dd7cddfSDavid du Colombier "fd2ctl", {Qctl + 2}, 0, 0660,
1147dd7cddfSDavid du Colombier "fd3disk", {Qdata + 3}, 0, 0660,
1157dd7cddfSDavid du Colombier "fd3ctl", {Qctl + 3}, 0, 0660,
1167dd7cddfSDavid du Colombier };
1177dd7cddfSDavid du Colombier #define NFDIR 2 /* directory entries/drive */
1187dd7cddfSDavid du Colombier
1199a747e4fSDavid du Colombier enum
1209a747e4fSDavid du Colombier {
1219a747e4fSDavid du Colombier CMdebug,
122fb7f0c93SDavid du Colombier CMnodebug,
1239a747e4fSDavid du Colombier CMeject,
1249a747e4fSDavid du Colombier CMformat,
1259a747e4fSDavid du Colombier CMreset,
1269a747e4fSDavid du Colombier };
1279a747e4fSDavid du Colombier
1289a747e4fSDavid du Colombier static Cmdtab floppyctlmsg[] =
1299a747e4fSDavid du Colombier {
1309a747e4fSDavid du Colombier CMdebug, "debug", 1,
131fb7f0c93SDavid du Colombier CMnodebug, "nodebug", 1,
1329a747e4fSDavid du Colombier CMeject, "eject", 1,
1339a747e4fSDavid du Colombier CMformat, "format", 0,
1349a747e4fSDavid du Colombier CMreset, "reset", 1,
1359a747e4fSDavid du Colombier };
1369a747e4fSDavid du Colombier
1377dd7cddfSDavid du Colombier static void
fldump(void)1387dd7cddfSDavid du Colombier fldump(void)
1397dd7cddfSDavid du Colombier {
1407dd7cddfSDavid du Colombier DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
1417dd7cddfSDavid du Colombier inb(Pdor), inb(Pmsr), inb(Pdir));
1427dd7cddfSDavid du Colombier }
1437dd7cddfSDavid du Colombier
1447dd7cddfSDavid du Colombier /*
1457dd7cddfSDavid du Colombier * set floppy drive to its default type
1467dd7cddfSDavid du Colombier */
1477dd7cddfSDavid du Colombier static void
floppysetdef(FDrive * dp)1487dd7cddfSDavid du Colombier floppysetdef(FDrive *dp)
1497dd7cddfSDavid du Colombier {
1507dd7cddfSDavid du Colombier FType *t;
1517dd7cddfSDavid du Colombier
1527dd7cddfSDavid du Colombier for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
1537dd7cddfSDavid du Colombier if(dp->dt == t->dt){
1547dd7cddfSDavid du Colombier dp->t = t;
1559a747e4fSDavid du Colombier floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
1567dd7cddfSDavid du Colombier break;
1577dd7cddfSDavid du Colombier }
1587dd7cddfSDavid du Colombier }
1597dd7cddfSDavid du Colombier
1607dd7cddfSDavid du Colombier static void
floppyreset(void)1617dd7cddfSDavid du Colombier floppyreset(void)
1627dd7cddfSDavid du Colombier {
1637dd7cddfSDavid du Colombier FDrive *dp;
1647dd7cddfSDavid du Colombier FType *t;
1657dd7cddfSDavid du Colombier ulong maxtsize;
1667dd7cddfSDavid du Colombier
1677dd7cddfSDavid du Colombier floppysetup0(&fl);
1687dd7cddfSDavid du Colombier if(fl.ndrive == 0)
1697dd7cddfSDavid du Colombier return;
1707dd7cddfSDavid du Colombier
1717dd7cddfSDavid du Colombier /*
1727dd7cddfSDavid du Colombier * init dependent parameters
1737dd7cddfSDavid du Colombier */
1747dd7cddfSDavid du Colombier maxtsize = 0;
1757dd7cddfSDavid du Colombier for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
1767dd7cddfSDavid du Colombier t->cap = t->bytes * t->heads * t->sectors * t->tracks;
1777dd7cddfSDavid du Colombier t->bcode = b2c[t->bytes/128];
1787dd7cddfSDavid du Colombier t->tsize = t->bytes * t->sectors;
1797dd7cddfSDavid du Colombier if(maxtsize < t->tsize)
1807dd7cddfSDavid du Colombier maxtsize = t->tsize;
1817dd7cddfSDavid du Colombier }
1827dd7cddfSDavid du Colombier
183*425afbabSDavid du Colombier /*
184*425afbabSDavid du Colombier * Should check if this fails. Can do so
185*425afbabSDavid du Colombier * if there is no space <= 16MB for the DMA
186*425afbabSDavid du Colombier * bounce buffer.
187*425afbabSDavid du Colombier */
1887dd7cddfSDavid du Colombier dmainit(DMAchan, maxtsize);
1897dd7cddfSDavid du Colombier
1907dd7cddfSDavid du Colombier /*
1917dd7cddfSDavid du Colombier * allocate the drive storage
1927dd7cddfSDavid du Colombier */
1937dd7cddfSDavid du Colombier fl.d = xalloc(fl.ndrive*sizeof(FDrive));
1947dd7cddfSDavid du Colombier fl.selected = fl.d;
1957dd7cddfSDavid du Colombier
1967dd7cddfSDavid du Colombier /*
1977dd7cddfSDavid du Colombier * stop the motors
1987dd7cddfSDavid du Colombier */
1997dd7cddfSDavid du Colombier fl.motor = 0;
2007dd7cddfSDavid du Colombier delay(10);
2017dd7cddfSDavid du Colombier outb(Pdor, fl.motor | Fintena | Fena);
2027dd7cddfSDavid du Colombier delay(10);
2037dd7cddfSDavid du Colombier
2047dd7cddfSDavid du Colombier /*
2057dd7cddfSDavid du Colombier * init drives
2067dd7cddfSDavid du Colombier */
2077dd7cddfSDavid du Colombier for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
2087dd7cddfSDavid du Colombier dp->dev = dp - fl.d;
2097dd7cddfSDavid du Colombier dp->dt = T1440kb;
2107dd7cddfSDavid du Colombier floppysetdef(dp);
2117dd7cddfSDavid du Colombier dp->cyl = -1; /* because we don't know */
2127dd7cddfSDavid du Colombier dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
2137dd7cddfSDavid du Colombier dp->ccyl = -1;
2147dd7cddfSDavid du Colombier dp->vers = 0;
2157dd7cddfSDavid du Colombier }
2167dd7cddfSDavid du Colombier
2177dd7cddfSDavid du Colombier /*
2187dd7cddfSDavid du Colombier * first operation will recalibrate
2197dd7cddfSDavid du Colombier */
2207dd7cddfSDavid du Colombier fl.confused = 1;
2217dd7cddfSDavid du Colombier
2227dd7cddfSDavid du Colombier floppysetup1(&fl);
2237dd7cddfSDavid du Colombier }
2247dd7cddfSDavid du Colombier
2257dd7cddfSDavid du Colombier static Chan*
floppyattach(char * spec)2267dd7cddfSDavid du Colombier floppyattach(char *spec)
2277dd7cddfSDavid du Colombier {
2287dd7cddfSDavid du Colombier static int kstarted;
2297dd7cddfSDavid du Colombier
2307dd7cddfSDavid du Colombier if(fl.ndrive == 0)
2317dd7cddfSDavid du Colombier error(Enodev);
2327dd7cddfSDavid du Colombier
2337dd7cddfSDavid du Colombier if(kstarted == 0){
2347dd7cddfSDavid du Colombier /*
2357dd7cddfSDavid du Colombier * watchdog to turn off the motors
2367dd7cddfSDavid du Colombier */
2377dd7cddfSDavid du Colombier kstarted = 1;
2387dd7cddfSDavid du Colombier kproc("floppy", floppykproc, 0);
2397dd7cddfSDavid du Colombier }
2407dd7cddfSDavid du Colombier return devattach('f', spec);
2417dd7cddfSDavid du Colombier }
2427dd7cddfSDavid du Colombier
2439a747e4fSDavid du Colombier static Walkqid*
floppywalk(Chan * c,Chan * nc,char ** name,int nname)2449a747e4fSDavid du Colombier floppywalk(Chan *c, Chan *nc, char **name, int nname)
2457dd7cddfSDavid du Colombier {
2469a747e4fSDavid du Colombier return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen);
2477dd7cddfSDavid du Colombier }
2487dd7cddfSDavid du Colombier
2499a747e4fSDavid du Colombier static int
floppystat(Chan * c,uchar * dp,int n)2509a747e4fSDavid du Colombier floppystat(Chan *c, uchar *dp, int n)
2517dd7cddfSDavid du Colombier {
2529a747e4fSDavid du Colombier return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
2537dd7cddfSDavid du Colombier }
2547dd7cddfSDavid du Colombier
2557dd7cddfSDavid du Colombier static Chan*
floppyopen(Chan * c,int omode)2567dd7cddfSDavid du Colombier floppyopen(Chan *c, int omode)
2577dd7cddfSDavid du Colombier {
2589a747e4fSDavid du Colombier return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen);
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier
2617dd7cddfSDavid du Colombier static void
floppyclose(Chan *)2627dd7cddfSDavid du Colombier floppyclose(Chan *)
2637dd7cddfSDavid du Colombier {
2647dd7cddfSDavid du Colombier }
2657dd7cddfSDavid du Colombier
2667dd7cddfSDavid du Colombier static void
islegal(ulong offset,long n,FDrive * dp)2677dd7cddfSDavid du Colombier islegal(ulong offset, long n, FDrive *dp)
2687dd7cddfSDavid du Colombier {
2697dd7cddfSDavid du Colombier if(offset % dp->t->bytes)
2707dd7cddfSDavid du Colombier error(Ebadarg);
2717dd7cddfSDavid du Colombier if(n % dp->t->bytes)
2727dd7cddfSDavid du Colombier error(Ebadarg);
2737dd7cddfSDavid du Colombier }
2747dd7cddfSDavid du Colombier
2757dd7cddfSDavid du Colombier /*
2767dd7cddfSDavid du Colombier * check if the floppy has been replaced under foot. cause
2777dd7cddfSDavid du Colombier * an error if it has.
2787dd7cddfSDavid du Colombier *
2797dd7cddfSDavid du Colombier * a seek and a read clears the condition. this was determined
2807dd7cddfSDavid du Colombier * experimentally, there has to be a better way.
2817dd7cddfSDavid du Colombier *
2827dd7cddfSDavid du Colombier * if the read fails, cycle through the possible floppy
2837dd7cddfSDavid du Colombier * density till one works or we've cycled through all
2847dd7cddfSDavid du Colombier * possibilities for this drive.
2857dd7cddfSDavid du Colombier */
2867dd7cddfSDavid du Colombier static void
changed(Chan * c,FDrive * dp)2877dd7cddfSDavid du Colombier changed(Chan *c, FDrive *dp)
2887dd7cddfSDavid du Colombier {
2897dd7cddfSDavid du Colombier ulong old;
2907dd7cddfSDavid du Colombier FType *start;
2917dd7cddfSDavid du Colombier
2927dd7cddfSDavid du Colombier /*
2937dd7cddfSDavid du Colombier * if floppy has changed or first time through
2947dd7cddfSDavid du Colombier */
2957dd7cddfSDavid du Colombier if((inb(Pdir)&Fchange) || dp->vers == 0){
2967dd7cddfSDavid du Colombier DPRINT("changed\n");
2977dd7cddfSDavid du Colombier fldump();
2987dd7cddfSDavid du Colombier dp->vers++;
2997dd7cddfSDavid du Colombier start = dp->t;
30059cc4ca5SDavid du Colombier dp->maxtries = 3; /* limit it when we're probing */
30159cc4ca5SDavid du Colombier
30259cc4ca5SDavid du Colombier /* floppyon will fail if there's a controller but no drive */
3037dd7cddfSDavid du Colombier dp->confused = 1; /* make floppyon recal */
30459cc4ca5SDavid du Colombier if(floppyon(dp) < 0)
30559cc4ca5SDavid du Colombier error(Eio);
30659cc4ca5SDavid du Colombier
30759cc4ca5SDavid du Colombier /* seek to the first track */
3087dd7cddfSDavid du Colombier floppyseek(dp, dp->t->heads*dp->t->tsize);
3097dd7cddfSDavid du Colombier while(waserror()){
31059cc4ca5SDavid du Colombier /*
31159cc4ca5SDavid du Colombier * if first attempt doesn't reset changed bit, there's
31259cc4ca5SDavid du Colombier * no floppy there
31359cc4ca5SDavid du Colombier */
31459cc4ca5SDavid du Colombier if(inb(Pdir)&Fchange)
31559cc4ca5SDavid du Colombier nexterror();
31659cc4ca5SDavid du Colombier
3177dd7cddfSDavid du Colombier while(++dp->t){
3187dd7cddfSDavid du Colombier if(dp->t == &floppytype[nelem(floppytype)])
3197dd7cddfSDavid du Colombier dp->t = floppytype;
3207dd7cddfSDavid du Colombier if(dp->dt == dp->t->dt)
3217dd7cddfSDavid du Colombier break;
3227dd7cddfSDavid du Colombier }
3239a747e4fSDavid du Colombier floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
32459cc4ca5SDavid du Colombier
32559cc4ca5SDavid du Colombier /* floppyon will fail if there's a controller but no drive */
32659cc4ca5SDavid du Colombier if(floppyon(dp) < 0)
32759cc4ca5SDavid du Colombier error(Eio);
32859cc4ca5SDavid du Colombier
3297dd7cddfSDavid du Colombier DPRINT("changed: trying %s\n", dp->t->name);
3307dd7cddfSDavid du Colombier fldump();
3317dd7cddfSDavid du Colombier if(dp->t == start)
3327dd7cddfSDavid du Colombier nexterror();
3337dd7cddfSDavid du Colombier }
33459cc4ca5SDavid du Colombier
33559cc4ca5SDavid du Colombier /* if the read succeeds, we've got the density right */
3367dd7cddfSDavid du Colombier floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize);
3377dd7cddfSDavid du Colombier poperror();
33859cc4ca5SDavid du Colombier dp->maxtries = 20;
3397dd7cddfSDavid du Colombier }
3407dd7cddfSDavid du Colombier
3417dd7cddfSDavid du Colombier old = c->qid.vers;
3427dd7cddfSDavid du Colombier c->qid.vers = dp->vers;
3437dd7cddfSDavid du Colombier if(old && old != dp->vers)
3447dd7cddfSDavid du Colombier error(Eio);
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier
3477dd7cddfSDavid du Colombier static int
readtrack(FDrive * dp,int cyl,int head)3487dd7cddfSDavid du Colombier readtrack(FDrive *dp, int cyl, int head)
3497dd7cddfSDavid du Colombier {
3507dd7cddfSDavid du Colombier int i, nn, sofar;
3517dd7cddfSDavid du Colombier ulong pos;
3527dd7cddfSDavid du Colombier
3537dd7cddfSDavid du Colombier nn = dp->t->tsize;
3547dd7cddfSDavid du Colombier if(dp->ccyl==cyl && dp->chead==head)
3557dd7cddfSDavid du Colombier return nn;
3567dd7cddfSDavid du Colombier pos = (cyl*dp->t->heads+head) * nn;
3577dd7cddfSDavid du Colombier for(sofar = 0; sofar < nn; sofar += i){
3587dd7cddfSDavid du Colombier dp->ccyl = -1;
3597dd7cddfSDavid du Colombier i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
3607dd7cddfSDavid du Colombier if(i <= 0)
3617dd7cddfSDavid du Colombier return -1;
3627dd7cddfSDavid du Colombier }
3637dd7cddfSDavid du Colombier dp->ccyl = cyl;
3647dd7cddfSDavid du Colombier dp->chead = head;
3657dd7cddfSDavid du Colombier return nn;
3667dd7cddfSDavid du Colombier }
3677dd7cddfSDavid du Colombier
3687dd7cddfSDavid du Colombier static long
floppyread(Chan * c,void * a,long n,vlong off)3697dd7cddfSDavid du Colombier floppyread(Chan *c, void *a, long n, vlong off)
3707dd7cddfSDavid du Colombier {
3717dd7cddfSDavid du Colombier FDrive *dp;
3727dd7cddfSDavid du Colombier long rv;
3737dd7cddfSDavid du Colombier int sec, head, cyl;
3747dd7cddfSDavid du Colombier long len;
3757dd7cddfSDavid du Colombier uchar *aa;
3767dd7cddfSDavid du Colombier ulong offset = off;
3777dd7cddfSDavid du Colombier
3789a747e4fSDavid du Colombier if(c->qid.type & QTDIR)
3799a747e4fSDavid du Colombier return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
3807dd7cddfSDavid du Colombier
3817dd7cddfSDavid du Colombier rv = 0;
3827dd7cddfSDavid du Colombier dp = &fl.d[c->qid.path & ~Qmask];
3837dd7cddfSDavid du Colombier switch ((int)(c->qid.path & Qmask)) {
3847dd7cddfSDavid du Colombier case Qdata:
3857dd7cddfSDavid du Colombier islegal(offset, n, dp);
3867dd7cddfSDavid du Colombier aa = a;
3877dd7cddfSDavid du Colombier
3887dd7cddfSDavid du Colombier qlock(&fl);
3897dd7cddfSDavid du Colombier if(waserror()){
3907dd7cddfSDavid du Colombier qunlock(&fl);
3917dd7cddfSDavid du Colombier nexterror();
3927dd7cddfSDavid du Colombier }
3937dd7cddfSDavid du Colombier floppyon(dp);
3947dd7cddfSDavid du Colombier changed(c, dp);
3957dd7cddfSDavid du Colombier for(rv = 0; rv < n; rv += len){
3967dd7cddfSDavid du Colombier /*
3977dd7cddfSDavid du Colombier * all xfers come out of the track cache
3987dd7cddfSDavid du Colombier */
3997dd7cddfSDavid du Colombier dp->len = n - rv;
4007dd7cddfSDavid du Colombier floppypos(dp, offset+rv);
4017dd7cddfSDavid du Colombier cyl = dp->tcyl;
4027dd7cddfSDavid du Colombier head = dp->thead;
4037dd7cddfSDavid du Colombier len = dp->len;
4047dd7cddfSDavid du Colombier sec = dp->tsec;
4057dd7cddfSDavid du Colombier if(readtrack(dp, cyl, head) < 0)
4067dd7cddfSDavid du Colombier break;
4077dd7cddfSDavid du Colombier memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
4087dd7cddfSDavid du Colombier }
4097dd7cddfSDavid du Colombier qunlock(&fl);
4107dd7cddfSDavid du Colombier poperror();
4117dd7cddfSDavid du Colombier
4127dd7cddfSDavid du Colombier break;
4137dd7cddfSDavid du Colombier case Qctl:
4147dd7cddfSDavid du Colombier return readstr(offset, a, n, dp->t->name);
4157dd7cddfSDavid du Colombier default:
4167dd7cddfSDavid du Colombier panic("floppyread: bad qid");
4177dd7cddfSDavid du Colombier }
4187dd7cddfSDavid du Colombier
4197dd7cddfSDavid du Colombier return rv;
4207dd7cddfSDavid du Colombier }
4217dd7cddfSDavid du Colombier
4227dd7cddfSDavid du Colombier static long
floppywrite(Chan * c,void * a,long n,vlong off)4237dd7cddfSDavid du Colombier floppywrite(Chan *c, void *a, long n, vlong off)
4247dd7cddfSDavid du Colombier {
4257dd7cddfSDavid du Colombier FDrive *dp;
4267dd7cddfSDavid du Colombier long rv, i;
4277dd7cddfSDavid du Colombier char *aa = a;
4289a747e4fSDavid du Colombier Cmdbuf *cb;
4299a747e4fSDavid du Colombier Cmdtab *ct;
4307dd7cddfSDavid du Colombier ulong offset = off;
4317dd7cddfSDavid du Colombier
4327dd7cddfSDavid du Colombier rv = 0;
4337dd7cddfSDavid du Colombier dp = &fl.d[c->qid.path & ~Qmask];
4347dd7cddfSDavid du Colombier switch ((int)(c->qid.path & Qmask)) {
4357dd7cddfSDavid du Colombier case Qdata:
4367dd7cddfSDavid du Colombier islegal(offset, n, dp);
4377dd7cddfSDavid du Colombier qlock(&fl);
4387dd7cddfSDavid du Colombier if(waserror()){
4397dd7cddfSDavid du Colombier qunlock(&fl);
4407dd7cddfSDavid du Colombier nexterror();
4417dd7cddfSDavid du Colombier }
4427dd7cddfSDavid du Colombier floppyon(dp);
4437dd7cddfSDavid du Colombier changed(c, dp);
4447dd7cddfSDavid du Colombier for(rv = 0; rv < n; rv += i){
4457dd7cddfSDavid du Colombier floppypos(dp, offset+rv);
4467dd7cddfSDavid du Colombier if(dp->tcyl == dp->ccyl)
4477dd7cddfSDavid du Colombier dp->ccyl = -1;
4487dd7cddfSDavid du Colombier i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv);
4497dd7cddfSDavid du Colombier if(i < 0)
4507dd7cddfSDavid du Colombier break;
4517dd7cddfSDavid du Colombier if(i == 0)
4527dd7cddfSDavid du Colombier error(Eio);
4537dd7cddfSDavid du Colombier }
4547dd7cddfSDavid du Colombier qunlock(&fl);
4557dd7cddfSDavid du Colombier poperror();
4567dd7cddfSDavid du Colombier break;
4577dd7cddfSDavid du Colombier case Qctl:
4587dd7cddfSDavid du Colombier rv = n;
4599a747e4fSDavid du Colombier cb = parsecmd(a, n);
4609a747e4fSDavid du Colombier if(waserror()){
4619a747e4fSDavid du Colombier free(cb);
4629a747e4fSDavid du Colombier nexterror();
4639a747e4fSDavid du Colombier }
4647dd7cddfSDavid du Colombier qlock(&fl);
4657dd7cddfSDavid du Colombier if(waserror()){
4667dd7cddfSDavid du Colombier qunlock(&fl);
4677dd7cddfSDavid du Colombier nexterror();
4687dd7cddfSDavid du Colombier }
4699a747e4fSDavid du Colombier ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg));
4709a747e4fSDavid du Colombier switch(ct->index){
4719a747e4fSDavid du Colombier case CMeject:
4727dd7cddfSDavid du Colombier floppyeject(dp);
4739a747e4fSDavid du Colombier break;
4749a747e4fSDavid du Colombier case CMformat:
4759a747e4fSDavid du Colombier floppyformat(dp, cb);
4769a747e4fSDavid du Colombier break;
4779a747e4fSDavid du Colombier case CMreset:
4787dd7cddfSDavid du Colombier fl.confused = 1;
4797dd7cddfSDavid du Colombier floppyon(dp);
4809a747e4fSDavid du Colombier break;
4819a747e4fSDavid du Colombier case CMdebug:
4827dd7cddfSDavid du Colombier floppydebug = 1;
4839a747e4fSDavid du Colombier break;
484fb7f0c93SDavid du Colombier case CMnodebug:
485fb7f0c93SDavid du Colombier floppydebug = 0;
486fb7f0c93SDavid du Colombier break;
4879a747e4fSDavid du Colombier }
4887dd7cddfSDavid du Colombier poperror();
4897dd7cddfSDavid du Colombier qunlock(&fl);
4909a747e4fSDavid du Colombier poperror();
4919a747e4fSDavid du Colombier free(cb);
4927dd7cddfSDavid du Colombier break;
4937dd7cddfSDavid du Colombier default:
4947dd7cddfSDavid du Colombier panic("floppywrite: bad qid");
4957dd7cddfSDavid du Colombier }
4967dd7cddfSDavid du Colombier
4977dd7cddfSDavid du Colombier return rv;
4987dd7cddfSDavid du Colombier }
4997dd7cddfSDavid du Colombier
5007dd7cddfSDavid du Colombier static void
floppykproc(void *)5017dd7cddfSDavid du Colombier floppykproc(void *)
5027dd7cddfSDavid du Colombier {
5037dd7cddfSDavid du Colombier FDrive *dp;
5047dd7cddfSDavid du Colombier
5057dd7cddfSDavid du Colombier while(waserror())
5067dd7cddfSDavid du Colombier ;
5077dd7cddfSDavid du Colombier for(;;){
5087dd7cddfSDavid du Colombier for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
5097dd7cddfSDavid du Colombier if((fl.motor&MOTORBIT(dp->dev))
5107dd7cddfSDavid du Colombier && TK2SEC(m->ticks - dp->lasttouched) > 5
5117dd7cddfSDavid du Colombier && canqlock(&fl)){
5127dd7cddfSDavid du Colombier if(TK2SEC(m->ticks - dp->lasttouched) > 5)
5137dd7cddfSDavid du Colombier floppyoff(dp);
5147dd7cddfSDavid du Colombier qunlock(&fl);
5157dd7cddfSDavid du Colombier }
5167dd7cddfSDavid du Colombier }
517dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, 1000);
5187dd7cddfSDavid du Colombier }
5197dd7cddfSDavid du Colombier }
5207dd7cddfSDavid du Colombier
5217dd7cddfSDavid du Colombier /*
5227dd7cddfSDavid du Colombier * start a floppy drive's motor.
5237dd7cddfSDavid du Colombier */
52459cc4ca5SDavid du Colombier static int
floppyon(FDrive * dp)5257dd7cddfSDavid du Colombier floppyon(FDrive *dp)
5267dd7cddfSDavid du Colombier {
5277dd7cddfSDavid du Colombier int alreadyon;
5287dd7cddfSDavid du Colombier int tries;
5297dd7cddfSDavid du Colombier
5307dd7cddfSDavid du Colombier if(fl.confused)
5317dd7cddfSDavid du Colombier floppyrevive();
5327dd7cddfSDavid du Colombier
5337dd7cddfSDavid du Colombier /* start motor and select drive */
5347dd7cddfSDavid du Colombier alreadyon = fl.motor & MOTORBIT(dp->dev);
5357dd7cddfSDavid du Colombier fl.motor |= MOTORBIT(dp->dev);
5367dd7cddfSDavid du Colombier outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
5377dd7cddfSDavid du Colombier if(!alreadyon){
5387dd7cddfSDavid du Colombier /* wait for drive to spin up */
539dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, 750);
5407dd7cddfSDavid du Colombier
5417dd7cddfSDavid du Colombier /* clear any pending interrupts */
5427dd7cddfSDavid du Colombier floppysense();
5437dd7cddfSDavid du Colombier }
5447dd7cddfSDavid du Colombier
5457dd7cddfSDavid du Colombier /* set transfer rate */
5467dd7cddfSDavid du Colombier if(fl.rate != dp->t->rate){
5477dd7cddfSDavid du Colombier fl.rate = dp->t->rate;
5487dd7cddfSDavid du Colombier outb(Pdsr, fl.rate);
5497dd7cddfSDavid du Colombier }
5507dd7cddfSDavid du Colombier
5517dd7cddfSDavid du Colombier /* get drive to a known cylinder */
5527dd7cddfSDavid du Colombier if(dp->confused)
5537dd7cddfSDavid du Colombier for(tries = 0; tries < 4; tries++)
5547dd7cddfSDavid du Colombier if(floppyrecal(dp) >= 0)
5557dd7cddfSDavid du Colombier break;
5567dd7cddfSDavid du Colombier dp->lasttouched = m->ticks;
5577dd7cddfSDavid du Colombier fl.selected = dp;
55859cc4ca5SDavid du Colombier
55959cc4ca5SDavid du Colombier /* return -1 if this didn't work */
56059cc4ca5SDavid du Colombier if(dp->confused)
56159cc4ca5SDavid du Colombier return -1;
56259cc4ca5SDavid du Colombier return 0;
5637dd7cddfSDavid du Colombier }
5647dd7cddfSDavid du Colombier
5657dd7cddfSDavid du Colombier /*
5667dd7cddfSDavid du Colombier * stop the floppy if it hasn't been used in 5 seconds
5677dd7cddfSDavid du Colombier */
5687dd7cddfSDavid du Colombier static void
floppyoff(FDrive * dp)5697dd7cddfSDavid du Colombier floppyoff(FDrive *dp)
5707dd7cddfSDavid du Colombier {
5717dd7cddfSDavid du Colombier fl.motor &= ~MOTORBIT(dp->dev);
5727dd7cddfSDavid du Colombier outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
5737dd7cddfSDavid du Colombier }
5747dd7cddfSDavid du Colombier
5757dd7cddfSDavid du Colombier /*
5767dd7cddfSDavid du Colombier * send a command to the floppy
5777dd7cddfSDavid du Colombier */
5787dd7cddfSDavid du Colombier static int
floppycmd(void)5797dd7cddfSDavid du Colombier floppycmd(void)
5807dd7cddfSDavid du Colombier {
5817dd7cddfSDavid du Colombier int i;
5827dd7cddfSDavid du Colombier int tries;
5837dd7cddfSDavid du Colombier
5847dd7cddfSDavid du Colombier fl.nstat = 0;
5857dd7cddfSDavid du Colombier for(i = 0; i < fl.ncmd; i++){
5867dd7cddfSDavid du Colombier for(tries = 0; ; tries++){
5877dd7cddfSDavid du Colombier if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
5887dd7cddfSDavid du Colombier break;
5897dd7cddfSDavid du Colombier if(tries > 1000){
5907dd7cddfSDavid du Colombier DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
5917dd7cddfSDavid du Colombier fldump();
5927dd7cddfSDavid du Colombier
5937dd7cddfSDavid du Colombier /* empty fifo, might have been a bad command */
5947dd7cddfSDavid du Colombier floppyresult();
5957dd7cddfSDavid du Colombier return -1;
5967dd7cddfSDavid du Colombier }
5977dd7cddfSDavid du Colombier microdelay(8); /* for machine independence */
5987dd7cddfSDavid du Colombier }
5997dd7cddfSDavid du Colombier outb(Pfdata, fl.cmd[i]);
6007dd7cddfSDavid du Colombier }
6017dd7cddfSDavid du Colombier return 0;
6027dd7cddfSDavid du Colombier }
6037dd7cddfSDavid du Colombier
6047dd7cddfSDavid du Colombier /*
6057dd7cddfSDavid du Colombier * get a command result from the floppy
6067dd7cddfSDavid du Colombier *
6077dd7cddfSDavid du Colombier * when the controller goes ready waiting for a command
6087dd7cddfSDavid du Colombier * (instead of sending results), we're done
6097dd7cddfSDavid du Colombier *
6107dd7cddfSDavid du Colombier */
6117dd7cddfSDavid du Colombier static int
floppyresult(void)6127dd7cddfSDavid du Colombier floppyresult(void)
6137dd7cddfSDavid du Colombier {
6147dd7cddfSDavid du Colombier int i, s;
6157dd7cddfSDavid du Colombier int tries;
6167dd7cddfSDavid du Colombier
6177dd7cddfSDavid du Colombier /* get the result of the operation */
6187dd7cddfSDavid du Colombier for(i = 0; i < sizeof(fl.stat); i++){
6197dd7cddfSDavid du Colombier /* wait for status byte */
6207dd7cddfSDavid du Colombier for(tries = 0; ; tries++){
6217dd7cddfSDavid du Colombier s = inb(Pmsr)&(Ffrom|Fready);
6227dd7cddfSDavid du Colombier if(s == Fready){
6237dd7cddfSDavid du Colombier fl.nstat = i;
6247dd7cddfSDavid du Colombier return fl.nstat;
6257dd7cddfSDavid du Colombier }
6267dd7cddfSDavid du Colombier if(s == (Ffrom|Fready))
6277dd7cddfSDavid du Colombier break;
6287dd7cddfSDavid du Colombier if(tries > 1000){
6297dd7cddfSDavid du Colombier DPRINT("floppyresult: %d stats\n", i);
6307dd7cddfSDavid du Colombier fldump();
6317dd7cddfSDavid du Colombier fl.confused = 1;
6327dd7cddfSDavid du Colombier return -1;
6337dd7cddfSDavid du Colombier }
6347dd7cddfSDavid du Colombier microdelay(8); /* for machine independence */
6357dd7cddfSDavid du Colombier }
6367dd7cddfSDavid du Colombier fl.stat[i] = inb(Pfdata);
6377dd7cddfSDavid du Colombier }
6387dd7cddfSDavid du Colombier fl.nstat = sizeof(fl.stat);
6397dd7cddfSDavid du Colombier return fl.nstat;
6407dd7cddfSDavid du Colombier }
6417dd7cddfSDavid du Colombier
6427dd7cddfSDavid du Colombier /*
6437dd7cddfSDavid du Colombier * calculate physical address of a logical byte offset into the disk
6447dd7cddfSDavid du Colombier *
6457dd7cddfSDavid du Colombier * truncate dp->length if it crosses a track boundary
6467dd7cddfSDavid du Colombier */
6477dd7cddfSDavid du Colombier static void
floppypos(FDrive * dp,long off)6487dd7cddfSDavid du Colombier floppypos(FDrive *dp, long off)
6497dd7cddfSDavid du Colombier {
6507dd7cddfSDavid du Colombier int lsec;
6517dd7cddfSDavid du Colombier int ltrack;
6527dd7cddfSDavid du Colombier int end;
6537dd7cddfSDavid du Colombier
6547dd7cddfSDavid du Colombier lsec = off/dp->t->bytes;
6557dd7cddfSDavid du Colombier ltrack = lsec/dp->t->sectors;
6567dd7cddfSDavid du Colombier dp->tcyl = ltrack/dp->t->heads;
6577dd7cddfSDavid du Colombier dp->tsec = (lsec % dp->t->sectors) + 1;
6587dd7cddfSDavid du Colombier dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
6597dd7cddfSDavid du Colombier
6607dd7cddfSDavid du Colombier /*
6617dd7cddfSDavid du Colombier * can't read across track boundaries.
6627dd7cddfSDavid du Colombier * if so, decrement the bytes to be read.
6637dd7cddfSDavid du Colombier */
6647dd7cddfSDavid du Colombier end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
6657dd7cddfSDavid du Colombier if(off+dp->len > end)
6667dd7cddfSDavid du Colombier dp->len = end - off;
6677dd7cddfSDavid du Colombier }
6687dd7cddfSDavid du Colombier
6697dd7cddfSDavid du Colombier /*
6707dd7cddfSDavid du Colombier * get the interrupt cause from the floppy.
6717dd7cddfSDavid du Colombier */
6727dd7cddfSDavid du Colombier static int
floppysense(void)6737dd7cddfSDavid du Colombier floppysense(void)
6747dd7cddfSDavid du Colombier {
6757dd7cddfSDavid du Colombier fl.ncmd = 0;
6767dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = Fsense;
6777dd7cddfSDavid du Colombier if(floppycmd() < 0)
6787dd7cddfSDavid du Colombier return -1;
6797dd7cddfSDavid du Colombier if(floppyresult() < 2){
6807dd7cddfSDavid du Colombier DPRINT("can't read sense response\n");
6817dd7cddfSDavid du Colombier fldump();
6827dd7cddfSDavid du Colombier fl.confused = 1;
6837dd7cddfSDavid du Colombier return -1;
6847dd7cddfSDavid du Colombier }
6857dd7cddfSDavid du Colombier return 0;
6867dd7cddfSDavid du Colombier }
6877dd7cddfSDavid du Colombier
6887dd7cddfSDavid du Colombier static int
cmddone(void *)6897dd7cddfSDavid du Colombier cmddone(void *)
6907dd7cddfSDavid du Colombier {
6917dd7cddfSDavid du Colombier return fl.ncmd == 0;
6927dd7cddfSDavid du Colombier }
6937dd7cddfSDavid du Colombier
6947dd7cddfSDavid du Colombier /*
6957dd7cddfSDavid du Colombier * Wait for a floppy interrupt. If none occurs in 5 seconds, we
6967dd7cddfSDavid du Colombier * may have missed one. This only happens on some portables which
6977dd7cddfSDavid du Colombier * do power management behind our backs. Call the interrupt
6987dd7cddfSDavid du Colombier * routine to try to clear any conditions.
6997dd7cddfSDavid du Colombier */
7007dd7cddfSDavid du Colombier static void
floppywait(int slow)70159cc4ca5SDavid du Colombier floppywait(int slow)
7027dd7cddfSDavid du Colombier {
70359cc4ca5SDavid du Colombier tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000);
7047dd7cddfSDavid du Colombier if(!cmddone(0)){
7057dd7cddfSDavid du Colombier floppyintr(0);
7067dd7cddfSDavid du Colombier fl.confused = 1;
7077dd7cddfSDavid du Colombier }
7087dd7cddfSDavid du Colombier }
7097dd7cddfSDavid du Colombier
7107dd7cddfSDavid du Colombier /*
7117dd7cddfSDavid du Colombier * we've lost the floppy position, go to cylinder 0.
7127dd7cddfSDavid du Colombier */
7137dd7cddfSDavid du Colombier static int
floppyrecal(FDrive * dp)7147dd7cddfSDavid du Colombier floppyrecal(FDrive *dp)
7157dd7cddfSDavid du Colombier {
7167dd7cddfSDavid du Colombier dp->ccyl = -1;
7177dd7cddfSDavid du Colombier dp->cyl = -1;
7187dd7cddfSDavid du Colombier
7197dd7cddfSDavid du Colombier fl.ncmd = 0;
7207dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = Frecal;
7217dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = dp->dev;
7227dd7cddfSDavid du Colombier if(floppycmd() < 0)
7237dd7cddfSDavid du Colombier return -1;
72459cc4ca5SDavid du Colombier floppywait(1);
7257dd7cddfSDavid du Colombier if(fl.nstat < 2){
7267dd7cddfSDavid du Colombier DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
7277dd7cddfSDavid du Colombier fl.confused = 1;
7287dd7cddfSDavid du Colombier return -1;
7297dd7cddfSDavid du Colombier }
7307dd7cddfSDavid du Colombier if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
7317dd7cddfSDavid du Colombier DPRINT("recalibrate: failed\n");
7327dd7cddfSDavid du Colombier dp->confused = 1;
7337dd7cddfSDavid du Colombier return -1;
7347dd7cddfSDavid du Colombier }
7357dd7cddfSDavid du Colombier dp->cyl = fl.stat[1];
7367dd7cddfSDavid du Colombier if(dp->cyl != 0){
7377dd7cddfSDavid du Colombier DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
7387dd7cddfSDavid du Colombier dp->cyl = -1;
7397dd7cddfSDavid du Colombier dp->confused = 1;
7407dd7cddfSDavid du Colombier return -1;
7417dd7cddfSDavid du Colombier }
7427dd7cddfSDavid du Colombier
7437dd7cddfSDavid du Colombier dp->confused = 0;
7447dd7cddfSDavid du Colombier return 0;
7457dd7cddfSDavid du Colombier }
7467dd7cddfSDavid du Colombier
7477dd7cddfSDavid du Colombier /*
7487dd7cddfSDavid du Colombier * if the controller or a specific drive is in a confused state,
7497dd7cddfSDavid du Colombier * reset it and get back to a known state
7507dd7cddfSDavid du Colombier */
7517dd7cddfSDavid du Colombier static void
floppyrevive(void)7527dd7cddfSDavid du Colombier floppyrevive(void)
7537dd7cddfSDavid du Colombier {
7547dd7cddfSDavid du Colombier FDrive *dp;
7557dd7cddfSDavid du Colombier
7567dd7cddfSDavid du Colombier /*
7577dd7cddfSDavid du Colombier * reset the controller if it's confused
7587dd7cddfSDavid du Colombier */
7597dd7cddfSDavid du Colombier if(fl.confused){
7607dd7cddfSDavid du Colombier DPRINT("floppyrevive in\n");
7617dd7cddfSDavid du Colombier fldump();
7627dd7cddfSDavid du Colombier
7637dd7cddfSDavid du Colombier /* reset controller and turn all motors off */
7647dd7cddfSDavid du Colombier splhi();
7657dd7cddfSDavid du Colombier fl.ncmd = 1;
7667dd7cddfSDavid du Colombier fl.cmd[0] = 0;
7677dd7cddfSDavid du Colombier outb(Pdor, 0);
7687dd7cddfSDavid du Colombier delay(10);
7697dd7cddfSDavid du Colombier outb(Pdor, Fintena|Fena);
7707dd7cddfSDavid du Colombier delay(10);
7717dd7cddfSDavid du Colombier spllo();
7727dd7cddfSDavid du Colombier fl.motor = 0;
7737dd7cddfSDavid du Colombier fl.confused = 0;
77459cc4ca5SDavid du Colombier floppywait(0);
7757dd7cddfSDavid du Colombier
7767dd7cddfSDavid du Colombier /* mark all drives in an unknown state */
7777dd7cddfSDavid du Colombier for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
7787dd7cddfSDavid du Colombier dp->confused = 1;
7797dd7cddfSDavid du Colombier
7807dd7cddfSDavid du Colombier /* set rate to a known value */
7817dd7cddfSDavid du Colombier outb(Pdsr, 0);
7827dd7cddfSDavid du Colombier fl.rate = 0;
7837dd7cddfSDavid du Colombier
7847dd7cddfSDavid du Colombier DPRINT("floppyrevive out\n");
7857dd7cddfSDavid du Colombier fldump();
7867dd7cddfSDavid du Colombier }
7877dd7cddfSDavid du Colombier }
7887dd7cddfSDavid du Colombier
7897dd7cddfSDavid du Colombier /*
7907dd7cddfSDavid du Colombier * seek to the target cylinder
7917dd7cddfSDavid du Colombier *
7927dd7cddfSDavid du Colombier * interrupt, no results
7937dd7cddfSDavid du Colombier */
7947dd7cddfSDavid du Colombier static long
floppyseek(FDrive * dp,long off)7957dd7cddfSDavid du Colombier floppyseek(FDrive *dp, long off)
7967dd7cddfSDavid du Colombier {
7977dd7cddfSDavid du Colombier floppypos(dp, off);
7987dd7cddfSDavid du Colombier if(dp->cyl == dp->tcyl)
7997dd7cddfSDavid du Colombier return dp->tcyl;
8007dd7cddfSDavid du Colombier dp->cyl = -1;
8017dd7cddfSDavid du Colombier
8027dd7cddfSDavid du Colombier fl.ncmd = 0;
8037dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = Fseek;
8047dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
8057dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
8067dd7cddfSDavid du Colombier if(floppycmd() < 0)
8077dd7cddfSDavid du Colombier return -1;
80859cc4ca5SDavid du Colombier floppywait(1);
8097dd7cddfSDavid du Colombier if(fl.nstat < 2){
8107dd7cddfSDavid du Colombier DPRINT("seek: confused\n");
8117dd7cddfSDavid du Colombier fl.confused = 1;
8127dd7cddfSDavid du Colombier return -1;
8137dd7cddfSDavid du Colombier }
8147dd7cddfSDavid du Colombier if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
8157dd7cddfSDavid du Colombier DPRINT("seek: failed\n");
8167dd7cddfSDavid du Colombier dp->confused = 1;
8177dd7cddfSDavid du Colombier return -1;
8187dd7cddfSDavid du Colombier }
8197dd7cddfSDavid du Colombier
8207dd7cddfSDavid du Colombier dp->cyl = dp->tcyl;
8217dd7cddfSDavid du Colombier return dp->tcyl;
8227dd7cddfSDavid du Colombier }
8237dd7cddfSDavid du Colombier
8247dd7cddfSDavid du Colombier /*
8257dd7cddfSDavid du Colombier * read or write to floppy. try up to three times.
8267dd7cddfSDavid du Colombier */
8277dd7cddfSDavid du Colombier static long
floppyxfer(FDrive * dp,int cmd,void * a,long off,long n)8287dd7cddfSDavid du Colombier floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
8297dd7cddfSDavid du Colombier {
8307dd7cddfSDavid du Colombier long offset;
8317dd7cddfSDavid du Colombier int tries;
8327dd7cddfSDavid du Colombier
8337dd7cddfSDavid du Colombier if(off >= dp->t->cap)
8347dd7cddfSDavid du Colombier return 0;
8357dd7cddfSDavid du Colombier if(off + n > dp->t->cap)
8367dd7cddfSDavid du Colombier n = dp->t->cap - off;
8377dd7cddfSDavid du Colombier
8387dd7cddfSDavid du Colombier /* retry on error (until it gets ridiculous) */
8397dd7cddfSDavid du Colombier tries = 0;
8407dd7cddfSDavid du Colombier while(waserror()){
84159cc4ca5SDavid du Colombier if(tries++ >= dp->maxtries)
8427dd7cddfSDavid du Colombier nexterror();
8437dd7cddfSDavid du Colombier DPRINT("floppyxfer: retrying\n");
8447dd7cddfSDavid du Colombier }
8457dd7cddfSDavid du Colombier
8467dd7cddfSDavid du Colombier dp->len = n;
8477dd7cddfSDavid du Colombier if(floppyseek(dp, off) < 0){
8487dd7cddfSDavid du Colombier DPRINT("xfer: seek failed\n");
8497dd7cddfSDavid du Colombier dp->confused = 1;
8507dd7cddfSDavid du Colombier error(Eio);
8517dd7cddfSDavid du Colombier }
8527dd7cddfSDavid du Colombier
8537dd7cddfSDavid du Colombier /*
8547dd7cddfSDavid du Colombier * set up the dma (dp->len may be trimmed)
8557dd7cddfSDavid du Colombier */
8567dd7cddfSDavid du Colombier if(waserror()){
8577dd7cddfSDavid du Colombier dmaend(DMAchan);
8587dd7cddfSDavid du Colombier nexterror();
8597dd7cddfSDavid du Colombier }
8607dd7cddfSDavid du Colombier dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
8617dd7cddfSDavid du Colombier if(dp->len < 0)
8627dd7cddfSDavid du Colombier error(Eio);
8637dd7cddfSDavid du Colombier
8647dd7cddfSDavid du Colombier /*
8657dd7cddfSDavid du Colombier * start operation
8667dd7cddfSDavid du Colombier */
8677dd7cddfSDavid du Colombier fl.ncmd = 0;
8687dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
8697dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
8707dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = dp->tcyl;
8717dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = dp->thead;
8727dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = dp->tsec;
8737dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = dp->t->bcode;
8747dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = dp->t->sectors;
8757dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = dp->t->gpl;
8767dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = 0xFF;
8777dd7cddfSDavid du Colombier if(floppycmd() < 0)
8787dd7cddfSDavid du Colombier error(Eio);
8797dd7cddfSDavid du Colombier
8807dd7cddfSDavid du Colombier /* Poll ready bits and transfer data */
8817dd7cddfSDavid du Colombier floppyexec((char*)a, dp->len, cmd==Fread);
8827dd7cddfSDavid du Colombier
8837dd7cddfSDavid du Colombier /*
8847dd7cddfSDavid du Colombier * give bus to DMA, floppyintr() will read result
8857dd7cddfSDavid du Colombier */
88659cc4ca5SDavid du Colombier floppywait(0);
8877dd7cddfSDavid du Colombier dmaend(DMAchan);
8887dd7cddfSDavid du Colombier poperror();
8897dd7cddfSDavid du Colombier
8907dd7cddfSDavid du Colombier /*
8917dd7cddfSDavid du Colombier * check for errors
8927dd7cddfSDavid du Colombier */
8937dd7cddfSDavid du Colombier if(fl.nstat < 7){
8947dd7cddfSDavid du Colombier DPRINT("xfer: confused\n");
8957dd7cddfSDavid du Colombier fl.confused = 1;
8967dd7cddfSDavid du Colombier error(Eio);
8977dd7cddfSDavid du Colombier }
8987dd7cddfSDavid du Colombier if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
8997dd7cddfSDavid du Colombier DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
9007dd7cddfSDavid du Colombier fl.stat[1], fl.stat[2]);
9017dd7cddfSDavid du Colombier DPRINT("offset %lud len %ld\n", off, dp->len);
9027dd7cddfSDavid du Colombier if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
9037dd7cddfSDavid du Colombier DPRINT("DMA overrun: retry\n");
9047dd7cddfSDavid du Colombier } else
9057dd7cddfSDavid du Colombier dp->confused = 1;
9067dd7cddfSDavid du Colombier error(Eio);
9077dd7cddfSDavid du Colombier }
9087dd7cddfSDavid du Colombier
9097dd7cddfSDavid du Colombier /*
9107dd7cddfSDavid du Colombier * check for correct cylinder
9117dd7cddfSDavid du Colombier */
9127dd7cddfSDavid du Colombier offset = fl.stat[3] * dp->t->heads + fl.stat[4];
9137dd7cddfSDavid du Colombier offset = offset*dp->t->sectors + fl.stat[5] - 1;
9147dd7cddfSDavid du Colombier offset = offset * c2b[fl.stat[6]];
9157dd7cddfSDavid du Colombier if(offset != off+dp->len){
9167dd7cddfSDavid du Colombier DPRINT("xfer: ends on wrong cyl\n");
9177dd7cddfSDavid du Colombier dp->confused = 1;
9187dd7cddfSDavid du Colombier error(Eio);
9197dd7cddfSDavid du Colombier }
9207dd7cddfSDavid du Colombier poperror();
9217dd7cddfSDavid du Colombier
9227dd7cddfSDavid du Colombier dp->lasttouched = m->ticks;
9237dd7cddfSDavid du Colombier return dp->len;
9247dd7cddfSDavid du Colombier }
9257dd7cddfSDavid du Colombier
9267dd7cddfSDavid du Colombier /*
9277dd7cddfSDavid du Colombier * format a track
9287dd7cddfSDavid du Colombier */
9297dd7cddfSDavid du Colombier static void
floppyformat(FDrive * dp,Cmdbuf * cb)9309a747e4fSDavid du Colombier floppyformat(FDrive *dp, Cmdbuf *cb)
9317dd7cddfSDavid du Colombier {
9327dd7cddfSDavid du Colombier int cyl, h, sec;
9337dd7cddfSDavid du Colombier ulong track;
9347dd7cddfSDavid du Colombier uchar *buf, *bp;
9357dd7cddfSDavid du Colombier FType *t;
9367dd7cddfSDavid du Colombier
9377dd7cddfSDavid du Colombier /*
9387dd7cddfSDavid du Colombier * set the type
9397dd7cddfSDavid du Colombier */
9409a747e4fSDavid du Colombier if(cb->nf == 2){
9417dd7cddfSDavid du Colombier for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
9429a747e4fSDavid du Colombier if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){
9437dd7cddfSDavid du Colombier dp->t = t;
9449a747e4fSDavid du Colombier floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
9457dd7cddfSDavid du Colombier break;
9467dd7cddfSDavid du Colombier }
9477dd7cddfSDavid du Colombier }
9487dd7cddfSDavid du Colombier if(t >= &floppytype[nelem(floppytype)])
9497dd7cddfSDavid du Colombier error(Ebadarg);
9509a747e4fSDavid du Colombier } else if(cb->nf == 1){
9517dd7cddfSDavid du Colombier floppysetdef(dp);
9527dd7cddfSDavid du Colombier t = dp->t;
9539a747e4fSDavid du Colombier } else {
9549a747e4fSDavid du Colombier cmderror(cb, "invalid floppy format command");
9559a747e4fSDavid du Colombier SET(t);
9567dd7cddfSDavid du Colombier }
9577dd7cddfSDavid du Colombier
9587dd7cddfSDavid du Colombier /*
9597dd7cddfSDavid du Colombier * buffer for per track info
9607dd7cddfSDavid du Colombier */
9617dd7cddfSDavid du Colombier buf = smalloc(t->sectors*4);
9627dd7cddfSDavid du Colombier if(waserror()){
9637dd7cddfSDavid du Colombier free(buf);
9647dd7cddfSDavid du Colombier nexterror();
9657dd7cddfSDavid du Colombier }
9667dd7cddfSDavid du Colombier
9677dd7cddfSDavid du Colombier /* force a recalibrate to cylinder 0 */
9687dd7cddfSDavid du Colombier dp->confused = 1;
9697dd7cddfSDavid du Colombier if(!waserror()){
9707dd7cddfSDavid du Colombier floppyon(dp);
9717dd7cddfSDavid du Colombier poperror();
9727dd7cddfSDavid du Colombier }
9737dd7cddfSDavid du Colombier
9747dd7cddfSDavid du Colombier /*
9757dd7cddfSDavid du Colombier * format a track at time
9767dd7cddfSDavid du Colombier */
9777dd7cddfSDavid du Colombier for(track = 0; track < t->tracks*t->heads; track++){
9787dd7cddfSDavid du Colombier cyl = track/t->heads;
9797dd7cddfSDavid du Colombier h = track % t->heads;
9807dd7cddfSDavid du Colombier
9817dd7cddfSDavid du Colombier /*
9827dd7cddfSDavid du Colombier * seek to track, ignore errors
9837dd7cddfSDavid du Colombier */
9847dd7cddfSDavid du Colombier floppyseek(dp, track*t->tsize);
9857dd7cddfSDavid du Colombier dp->cyl = cyl;
9867dd7cddfSDavid du Colombier dp->confused = 0;
9877dd7cddfSDavid du Colombier
9887dd7cddfSDavid du Colombier /*
9897dd7cddfSDavid du Colombier * set up the dma (dp->len may be trimmed)
9907dd7cddfSDavid du Colombier */
9917dd7cddfSDavid du Colombier bp = buf;
9927dd7cddfSDavid du Colombier for(sec = 1; sec <= t->sectors; sec++){
9937dd7cddfSDavid du Colombier *bp++ = cyl;
9947dd7cddfSDavid du Colombier *bp++ = h;
9957dd7cddfSDavid du Colombier *bp++ = sec;
9967dd7cddfSDavid du Colombier *bp++ = t->bcode;
9977dd7cddfSDavid du Colombier }
9987dd7cddfSDavid du Colombier if(waserror()){
9997dd7cddfSDavid du Colombier dmaend(DMAchan);
10007dd7cddfSDavid du Colombier nexterror();
10017dd7cddfSDavid du Colombier }
10027dd7cddfSDavid du Colombier if(dmasetup(DMAchan, buf, bp-buf, 0) < 0)
10037dd7cddfSDavid du Colombier error(Eio);
10047dd7cddfSDavid du Colombier
10057dd7cddfSDavid du Colombier /*
10067dd7cddfSDavid du Colombier * start operation
10077dd7cddfSDavid du Colombier */
10087dd7cddfSDavid du Colombier fl.ncmd = 0;
10097dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = Fformat;
10107dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = (h<<2) | dp->dev;
10117dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = t->bcode;
10127dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = t->sectors;
10137dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = t->fgpl;
10147dd7cddfSDavid du Colombier fl.cmd[fl.ncmd++] = 0x5a;
10157dd7cddfSDavid du Colombier if(floppycmd() < 0)
10167dd7cddfSDavid du Colombier error(Eio);
10177dd7cddfSDavid du Colombier
10187dd7cddfSDavid du Colombier /* Poll ready bits and transfer data */
10197dd7cddfSDavid du Colombier floppyexec((char *)buf, bp-buf, 0);
10207dd7cddfSDavid du Colombier
10217dd7cddfSDavid du Colombier /*
10227dd7cddfSDavid du Colombier * give bus to DMA, floppyintr() will read result
10237dd7cddfSDavid du Colombier */
102459cc4ca5SDavid du Colombier floppywait(1);
10257dd7cddfSDavid du Colombier dmaend(DMAchan);
10267dd7cddfSDavid du Colombier poperror();
10277dd7cddfSDavid du Colombier
10287dd7cddfSDavid du Colombier /*
10297dd7cddfSDavid du Colombier * check for errors
10307dd7cddfSDavid du Colombier */
10317dd7cddfSDavid du Colombier if(fl.nstat < 7){
10327dd7cddfSDavid du Colombier DPRINT("format: confused\n");
10337dd7cddfSDavid du Colombier fl.confused = 1;
10347dd7cddfSDavid du Colombier error(Eio);
10357dd7cddfSDavid du Colombier }
10367dd7cddfSDavid du Colombier if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){
10377dd7cddfSDavid du Colombier DPRINT("format: failed %ux %ux %ux\n",
10387dd7cddfSDavid du Colombier fl.stat[0], fl.stat[1], fl.stat[2]);
10397dd7cddfSDavid du Colombier dp->confused = 1;
10407dd7cddfSDavid du Colombier error(Eio);
10417dd7cddfSDavid du Colombier }
10427dd7cddfSDavid du Colombier }
10437dd7cddfSDavid du Colombier free(buf);
10447dd7cddfSDavid du Colombier dp->confused = 1;
10457dd7cddfSDavid du Colombier poperror();
10467dd7cddfSDavid du Colombier }
10477dd7cddfSDavid du Colombier
10487dd7cddfSDavid du Colombier static void
floppyintr(Ureg *)10497dd7cddfSDavid du Colombier floppyintr(Ureg *)
10507dd7cddfSDavid du Colombier {
10517dd7cddfSDavid du Colombier switch(fl.cmd[0]&~Fmulti){
10527dd7cddfSDavid du Colombier case Fread:
10537dd7cddfSDavid du Colombier case Fwrite:
10547dd7cddfSDavid du Colombier case Fformat:
10557dd7cddfSDavid du Colombier case Fdumpreg:
10567dd7cddfSDavid du Colombier floppyresult();
10577dd7cddfSDavid du Colombier break;
10587dd7cddfSDavid du Colombier case Fseek:
10597dd7cddfSDavid du Colombier case Frecal:
10607dd7cddfSDavid du Colombier default:
10617dd7cddfSDavid du Colombier floppysense(); /* to clear interrupt */
10627dd7cddfSDavid du Colombier break;
10637dd7cddfSDavid du Colombier }
10647dd7cddfSDavid du Colombier fl.ncmd = 0;
10657dd7cddfSDavid du Colombier wakeup(&fl.r);
10667dd7cddfSDavid du Colombier }
10677dd7cddfSDavid du Colombier
10687dd7cddfSDavid du Colombier Dev floppydevtab = {
10697dd7cddfSDavid du Colombier 'f',
10707dd7cddfSDavid du Colombier "floppy",
10717dd7cddfSDavid du Colombier
10727dd7cddfSDavid du Colombier floppyreset,
10737dd7cddfSDavid du Colombier devinit,
10749a747e4fSDavid du Colombier devshutdown,
10757dd7cddfSDavid du Colombier floppyattach,
10767dd7cddfSDavid du Colombier floppywalk,
10777dd7cddfSDavid du Colombier floppystat,
10787dd7cddfSDavid du Colombier floppyopen,
10797dd7cddfSDavid du Colombier devcreate,
10807dd7cddfSDavid du Colombier floppyclose,
10817dd7cddfSDavid du Colombier floppyread,
10827dd7cddfSDavid du Colombier devbread,
10837dd7cddfSDavid du Colombier floppywrite,
10847dd7cddfSDavid du Colombier devbwrite,
10857dd7cddfSDavid du Colombier devremove,
10867dd7cddfSDavid du Colombier devwstat,
10877dd7cddfSDavid du Colombier };
1088