]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/devfloppy.c
kernel: cleanup the software mouse cursor mess
[plan9front.git] / sys / src / 9 / pc / devfloppy.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "io.h"
7 #include        "../port/error.h"
8
9 #include        "floppy.h"
10
11 /* Intel 82077A (8272A compatible) floppy controller */
12
13 /* This module expects the following functions to be defined
14  * elsewhere: 
15  * 
16  * inb()
17  * outb()
18  * floppyexec()
19  * floppyeject() 
20  * floppysetup0()
21  * floppysetup1()
22  * dmainit()
23  * dmasetup()
24  * dmaend()
25  * 
26  * On DMA systems, floppyexec() should be an empty function; 
27  * on non-DMA systems, dmaend() should be an empty function; 
28  * dmasetup() may enforce maximum transfer sizes. 
29  */
30
31 enum {
32         /* file types */
33         Qdir=           0, 
34         Qdata=          (1<<2),
35         Qctl=           (2<<2),
36         Qmask=          (3<<2),
37
38         DMAchan=        2,      /* floppy dma channel */
39 };
40
41 #define DPRINT if(floppydebug)print
42 int floppydebug = 0;
43
44 /*
45  *  types of drive (from PC equipment byte)
46  */
47 enum
48 {
49         Tnone=          0,
50         T360kb=         1,
51         T1200kb=        2,
52         T720kb=         3,
53         T1440kb=        4,
54 };
55
56 FType floppytype[] =
57 {
58  { "3½HD",     T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54, 0, },
59  { "3½DD",     T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
60  { "3½DD",     T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
61  { "5¼HD",     T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
62  { "5¼DD",     T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, },
63  { "ATT3B1",    T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, },
64  { "5¼DD",     T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, },
65 };
66
67 /*
68  *  bytes per sector encoding for the controller.
69  *  - index for b2c is is (bytes per sector/128).
70  *  - index for c2b is code from b2c
71  */
72 static int b2c[] =
73 {
74 [1]     0,
75 [2]     1,
76 [4]     2,
77 [8]     3,
78 };
79 static int c2b[] =
80 {
81         128,
82         256,
83         512,
84         1024,
85 };
86
87 FController     fl;
88
89 #define MOTORBIT(i)     (1<<((i)+4))
90
91 /*
92  *  predeclared
93  */
94 static int      cmddone(void*);
95 static void     floppyformat(FDrive*, Cmdbuf*);
96 static void     floppykproc(void*);
97 static void     floppypos(FDrive*,long);
98 static int      floppyrecal(FDrive*);
99 static int      floppyresult(void);
100 static void     floppyrevive(void);
101 static long     floppyseek(FDrive*, long);
102 static int      floppysense(void);
103 static void     floppywait(int);
104 static long     floppyxfer(FDrive*, int, void*, long, long);
105
106 Dirtab floppydir[]={
107         ".",            {Qdir, 0, QTDIR},       0,      0550,
108         "fd0disk",              {Qdata + 0},    0,      0660,
109         "fd0ctl",               {Qctl + 0},     0,      0660,
110         "fd1disk",              {Qdata + 1},    0,      0660,
111         "fd1ctl",               {Qctl + 1},     0,      0660,
112         "fd2disk",              {Qdata + 2},    0,      0660,
113         "fd2ctl",               {Qctl + 2},     0,      0660,
114         "fd3disk",              {Qdata + 3},    0,      0660,
115         "fd3ctl",               {Qctl + 3},     0,      0660,
116 };
117 #define NFDIR   2       /* directory entries/drive */
118
119 enum
120 {
121         CMdebug,
122         CMnodebug,
123         CMeject,
124         CMformat,
125         CMreset,
126 };
127
128 static Cmdtab floppyctlmsg[] =
129 {
130         CMdebug,        "debug",        1,
131         CMnodebug,      "nodebug", 1,
132         CMeject,        "eject",        1,
133         CMformat,       "format",       0,
134         CMreset,        "reset",        1,
135 };
136
137 static void
138 fldump(void)
139 {
140         DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
141                 inb(Pdor), inb(Pmsr), inb(Pdir));
142 }
143
144 /*
145  *  set floppy drive to its default type
146  */
147 static void
148 floppysetdef(FDrive *dp)
149 {
150         FType *t;
151
152         dp->t = floppytype;
153         for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
154                 if(dp->dt == t->dt){
155                         dp->t = t;
156                         break;
157                 }
158         floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
159         dp->dt = dp->t->dt;
160 }
161
162 static void
163 floppyreset(void)
164 {
165         FDrive *dp;
166         FType *t;
167         ulong maxtsize;
168         
169         floppysetup0(&fl);
170         if(fl.ndrive == 0)
171                 return;
172
173         /*
174          *  init dependent parameters
175          */
176         maxtsize = 0;
177         for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
178                 t->cap = t->bytes * t->heads * t->sectors * t->tracks;
179                 t->bcode = b2c[t->bytes/128];
180                 t->tsize = t->bytes * t->sectors;
181                 if(maxtsize < t->tsize)
182                         maxtsize = t->tsize;
183         }
184
185         /*
186          * Can fail if there is no space <= 16MB for the DMA
187          * bounce buffer.
188          */
189         if(dmainit(DMAchan, maxtsize)){
190                 print("floppy: dmainit failed\n");
191                 fl.ndrive = 0;
192                 return;
193         }
194
195         /*
196          *  allocate the drive storage
197          */
198         fl.d = xalloc(fl.ndrive*sizeof(FDrive));
199         fl.selected = fl.d;
200         if(fl.d == nil){
201                 print("floppy: can't allocate memory\n");
202                 fl.ndrive = 0;
203                 return;
204         }
205
206         /*
207          *  stop the motors
208          */
209         fl.motor = 0;
210         delay(10);
211         outb(Pdor, fl.motor | Fintena | Fena);
212         delay(10);
213
214         /*
215          *  init drives
216          */
217         for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
218                 dp->dev = dp - fl.d;
219                 dp->dt = T1440kb;
220                 floppysetdef(dp);
221                 dp->cyl = -1;                   /* because we don't know */
222                 dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
223                 dp->ccyl = -1;
224                 dp->vers = 0;
225         }
226
227         /*
228          *  first operation will recalibrate
229          */
230         fl.confused = 1;
231
232         floppysetup1(&fl);
233 }
234
235 static Chan*
236 floppyattach(char *spec)
237 {
238         static int kstarted;
239
240         if(fl.ndrive == 0)
241                 error(Enodev);
242
243         if(kstarted == 0){
244                 /*
245                  *  watchdog to turn off the motors
246                  */
247                 kstarted = 1;
248                 kproc("floppy", floppykproc, 0);
249         }
250         return devattach('f', spec);
251 }
252
253 static Walkqid*
254 floppywalk(Chan *c, Chan *nc, char **name, int nname)
255 {
256         return devwalk(c, nc, name, nname, floppydir, 1+fl.ndrive*NFDIR, devgen);
257 }
258
259 static int
260 floppystat(Chan *c, uchar *dp, int n)
261 {
262         return devstat(c, dp, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
263 }
264
265 static Chan*
266 floppyopen(Chan *c, int omode)
267 {
268         return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen);
269 }
270
271 static void
272 floppyclose(Chan *)
273 {
274 }
275
276 static void
277 islegal(ulong offset, long n, FDrive *dp)
278 {
279         if(offset % dp->t->bytes)
280                 error(Ebadarg);
281         if(n % dp->t->bytes)
282                 error(Ebadarg);
283 }
284
285 /*
286  *  check if the floppy has been replaced under foot.  cause
287  *  an error if it has.
288  *
289  *  a seek and a read clears the condition.  this was determined
290  *  experimentally, there has to be a better way.
291  *
292  *  if the read fails, cycle through the possible floppy
293  *  density till one works or we've cycled through all
294  *  possibilities for this drive.
295  */
296 static void
297 changed(Chan *c, FDrive *dp)
298 {
299         ulong old;
300         FType *start;
301
302         /*
303          *  if floppy has changed or first time through
304          */
305         if((inb(Pdir)&Fchange) || dp->vers == 0){
306                 DPRINT("changed\n");
307                 fldump();
308                 dp->vers++;
309                 start = dp->t;
310                 dp->maxtries = 3;       /* limit it when we're probing */
311
312                 /* floppyon will fail if there's a controller but no drive */
313                 dp->confused = 1;       /* make floppyon recal */
314                 if(floppyon(dp) < 0)
315                         error(Eio);
316
317                 /* seek to the first track */
318                 floppyseek(dp, dp->t->heads*dp->t->tsize);
319                 while(waserror()){
320                         /*
321                          *  if first attempt doesn't reset changed bit, there's
322                          *  no floppy there
323                          */
324                         if(inb(Pdir)&Fchange)
325                                 nexterror();
326
327                         while(++dp->t){
328                                 if(dp->t >= &floppytype[nelem(floppytype)])
329                                         dp->t = floppytype;
330                                 if(dp->t == start || dp->dt == dp->t->dt)
331                                         break;
332                         }
333                         floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
334
335                         /* floppyon will fail if there's a controller but no drive */
336                         if(floppyon(dp) < 0)
337                                 nexterror();
338                         if(dp->t == start)
339                                 nexterror();
340
341                         DPRINT("changed: trying %s\n", dp->t->name);
342                         fldump();
343                 }
344
345                 /* if the read succeeds, we've got the density right */
346                 floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize);
347                 poperror();
348                 dp->maxtries = 20;
349         }
350
351         old = c->qid.vers;
352         c->qid.vers = dp->vers;
353         if(old && old != dp->vers)
354                 error(Echange);
355 }
356
357 static int
358 readtrack(FDrive *dp, int cyl, int head)
359 {
360         int i, nn, sofar;
361         ulong pos;
362
363         nn = dp->t->tsize;
364         if(dp->ccyl==cyl && dp->chead==head)
365                 return nn;
366         pos = (cyl*dp->t->heads+head) * nn;
367         for(sofar = 0; sofar < nn; sofar += i){
368                 dp->ccyl = -1;
369                 i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
370                 if(i <= 0)
371                         return -1;
372         }
373         dp->ccyl = cyl;
374         dp->chead = head;
375         return nn;
376 }
377
378 static long
379 floppyread(Chan *c, void *a, long n, vlong off)
380 {
381         FDrive *dp;
382         long rv;
383         int sec, head, cyl;
384         long len;
385         uchar *aa;
386         ulong offset = off;
387
388         if(c->qid.type & QTDIR)
389                 return devdirread(c, a, n, floppydir, 1+fl.ndrive*NFDIR, devgen);
390
391         rv = 0;
392         dp = &fl.d[c->qid.path & ~Qmask];
393         switch ((int)(c->qid.path & Qmask)) {
394         case Qdata:
395                 islegal(offset, n, dp);
396                 aa = a;
397
398                 qlock(&fl);
399                 if(waserror()){
400                         qunlock(&fl);
401                         nexterror();
402                 }
403                 floppyon(dp);
404                 changed(c, dp);
405                 for(rv = 0; rv < n; rv += len){
406                         /*
407                          *  all xfers come out of the track cache
408                          */
409                         dp->len = n - rv;
410                         floppypos(dp, offset+rv);
411                         cyl = dp->tcyl;
412                         head = dp->thead;
413                         len = dp->len;
414                         sec = dp->tsec;
415                         if(readtrack(dp, cyl, head) < 0)
416                                 break;
417                         memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
418                 }
419                 qunlock(&fl);
420                 poperror();
421
422                 break;
423         case Qctl:
424                 return readstr(offset, a, n, dp->t->name);
425         default:
426                 panic("floppyread: bad qid");
427         }
428
429         return rv;
430 }
431
432 static long
433 floppywrite(Chan *c, void *a, long n, vlong off)
434 {
435         FDrive *dp;
436         long rv, i;
437         char *aa = a;
438         Cmdbuf *cb;
439         Cmdtab *ct;
440         ulong offset = off;
441
442         rv = 0;
443         dp = &fl.d[c->qid.path & ~Qmask];
444         switch ((int)(c->qid.path & Qmask)) {
445         case Qdata:
446                 islegal(offset, n, dp);
447                 qlock(&fl);
448                 if(waserror()){
449                         qunlock(&fl);
450                         nexterror();
451                 }
452                 floppyon(dp);
453                 changed(c, dp);
454                 for(rv = 0; rv < n; rv += i){
455                         floppypos(dp, offset+rv);
456                         if(dp->tcyl == dp->ccyl)
457                                 dp->ccyl = -1;
458                         i = floppyxfer(dp, Fwrite, aa+rv, offset+rv, n-rv);
459                         if(i < 0)
460                                 break;
461                         if(i == 0)
462                                 error(Eio);
463                 }
464                 qunlock(&fl);
465                 poperror();
466                 break;
467         case Qctl:
468                 rv = n;
469                 cb = parsecmd(a, n);
470                 if(waserror()){
471                         free(cb);
472                         nexterror();
473                 }
474                 qlock(&fl);
475                 if(waserror()){
476                         qunlock(&fl);
477                         nexterror();
478                 }
479                 ct = lookupcmd(cb, floppyctlmsg, nelem(floppyctlmsg));
480                 switch(ct->index){
481                 case CMeject:
482                         floppyeject(dp);
483                         break;
484                 case CMformat:
485                         floppyformat(dp, cb);
486                         break;
487                 case CMreset:
488                         fl.confused = 1;
489                         floppyon(dp);
490                         break;
491                 case CMdebug:
492                         floppydebug = 1;
493                         break;
494                 case CMnodebug:
495                         floppydebug = 0;
496                         break;
497                 }
498                 poperror();
499                 qunlock(&fl);
500                 poperror();
501                 free(cb);
502                 break;
503         default:
504                 panic("floppywrite: bad qid");
505         }
506
507         return rv;
508 }
509
510 static void
511 floppykproc(void *)
512 {
513         FDrive *dp;
514
515         while(waserror())
516                 ;
517         for(;;){
518                 for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
519                         if((fl.motor&MOTORBIT(dp->dev))
520                         && TK2SEC(m->ticks - dp->lasttouched) > 5
521                         && canqlock(&fl)){
522                                 if(TK2SEC(m->ticks - dp->lasttouched) > 5)
523                                         floppyoff(dp);
524                                 qunlock(&fl);
525                         }
526                 }
527                 tsleep(&up->sleep, return0, 0, 1000);
528         }
529 }
530
531 /*
532  *  start a floppy drive's motor.
533  */
534 static int
535 floppyon(FDrive *dp)
536 {
537         int alreadyon;
538         int tries;
539
540         if(fl.confused)
541                 floppyrevive();
542
543         /* start motor and select drive */
544         alreadyon = fl.motor & MOTORBIT(dp->dev);
545         fl.motor |= MOTORBIT(dp->dev);
546         outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
547         if(!alreadyon){
548                 /* wait for drive to spin up */
549                 tsleep(&up->sleep, return0, 0, 750);
550
551                 /* clear any pending interrupts */
552                 floppysense();
553         }
554
555         /* set transfer rate */
556         if(fl.rate != dp->t->rate){
557                 fl.rate = dp->t->rate;
558                 outb(Pdsr, fl.rate);
559         }
560
561         /* get drive to a known cylinder */
562         if(dp->confused)
563                 for(tries = 0; tries < 4; tries++)
564                         if(floppyrecal(dp) >= 0)
565                                 break;
566         dp->lasttouched = m->ticks;
567         fl.selected = dp;
568
569         /* return -1 if this didn't work */
570         if(dp->confused)
571                 return -1;
572         return 0;
573 }
574
575 /*
576  *  stop the floppy if it hasn't been used in 5 seconds
577  */
578 static void
579 floppyoff(FDrive *dp)
580 {
581         fl.motor &= ~MOTORBIT(dp->dev);
582         outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
583 }
584
585 /*
586  *  send a command to the floppy
587  */
588 static int
589 floppycmd(void)
590 {
591         int i;
592         int tries;
593
594         fl.nstat = 0;
595         for(i = 0; i < fl.ncmd; i++){
596                 for(tries = 0; ; tries++){
597                         if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
598                                 break;
599                         if(tries > 1000){
600                                 DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
601                                 fldump();
602
603                                 /* empty fifo, might have been a bad command */
604                                 floppyresult();
605                                 return -1;
606                         }
607                         microdelay(8);  /* for machine independence */
608                 }
609                 outb(Pfdata, fl.cmd[i]);
610         }
611         return 0;
612 }
613
614 /*
615  *  get a command result from the floppy
616  *
617  *  when the controller goes ready waiting for a command
618  *  (instead of sending results), we're done
619  * 
620  */
621 static int
622 floppyresult(void)
623 {
624         int i, s;
625         int tries;
626
627         /* get the result of the operation */
628         for(i = 0; i < sizeof(fl.stat); i++){
629                 /* wait for status byte */
630                 for(tries = 0; ; tries++){
631                         s = inb(Pmsr)&(Ffrom|Fready);
632                         if(s == Fready){
633                                 fl.nstat = i;
634                                 return fl.nstat;
635                         }
636                         if(s == (Ffrom|Fready))
637                                 break;
638                         if(tries > 1000){
639                                 DPRINT("floppyresult: %d stats\n", i);
640                                 fldump();
641                                 fl.confused = 1;
642                                 return -1;
643                         }
644                         microdelay(8);  /* for machine independence */
645                 }
646                 fl.stat[i] = inb(Pfdata);
647         }
648         fl.nstat = sizeof(fl.stat);
649         return fl.nstat;
650 }
651
652 /*
653  *  calculate physical address of a logical byte offset into the disk
654  *
655  *  truncate dp->length if it crosses a track boundary
656  */
657 static void
658 floppypos(FDrive *dp, long off)
659 {
660         int lsec;
661         int ltrack;
662         int end;
663
664         lsec = off/dp->t->bytes;
665         ltrack = lsec/dp->t->sectors;
666         dp->tcyl = ltrack/dp->t->heads;
667         dp->tsec = (lsec % dp->t->sectors) + 1;
668         dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
669
670         /*
671          *  can't read across track boundaries.
672          *  if so, decrement the bytes to be read.
673          */
674         end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
675         if(off+dp->len > end)
676                 dp->len = end - off;
677 }
678
679 /*
680  *  get the interrupt cause from the floppy.
681  */
682 static int
683 floppysense(void)
684 {
685         fl.ncmd = 0;
686         fl.cmd[fl.ncmd++] = Fsense;
687         if(floppycmd() < 0)
688                 return -1;
689         if(floppyresult() < 2){
690                 DPRINT("can't read sense response\n");
691                 fldump();
692                 fl.confused = 1;
693                 return -1;
694         }
695         return 0;
696 }
697
698 static int
699 cmddone(void *)
700 {
701         return fl.ncmd == 0;
702 }
703
704 /*
705  *  Wait for a floppy interrupt.  If none occurs in 5 seconds, we
706  *  may have missed one.  This only happens on some portables which
707  *  do power management behind our backs.  Call the interrupt
708  *  routine to try to clear any conditions.
709  */
710 static void
711 floppywait(int slow)
712 {
713         tsleep(&fl.r, cmddone, 0, slow ? 5000 : 1000);
714         if(!cmddone(0)){
715                 floppyintr(0);
716                 fl.confused = 1;
717         }
718 }
719
720 /*
721  *  we've lost the floppy position, go to cylinder 0.
722  */
723 static int
724 floppyrecal(FDrive *dp)
725 {
726         dp->ccyl = -1;
727         dp->cyl = -1;
728
729         fl.ncmd = 0;
730         fl.cmd[fl.ncmd++] = Frecal;
731         fl.cmd[fl.ncmd++] = dp->dev;
732         if(floppycmd() < 0)
733                 return -1;
734         floppywait(1);
735         if(fl.nstat < 2){
736                 DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
737                 fl.confused = 1;
738                 return -1;
739         }
740         if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
741                 DPRINT("recalibrate: failed\n");
742                 dp->confused = 1;
743                 return -1;
744         }
745         dp->cyl = fl.stat[1];
746         if(dp->cyl != 0){
747                 DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
748                 dp->cyl = -1;
749                 dp->confused = 1;
750                 return -1;
751         }
752
753         dp->confused = 0;
754         return 0;
755 }
756
757 /*
758  *  if the controller or a specific drive is in a confused state,
759  *  reset it and get back to a known state
760  */
761 static void
762 floppyrevive(void)
763 {
764         FDrive *dp;
765
766         /*
767          *  reset the controller if it's confused
768          */
769         if(fl.confused){
770                 DPRINT("floppyrevive in\n");
771                 fldump();
772
773                 /* reset controller and turn all motors off */
774                 splhi();
775                 fl.ncmd = 1;
776                 fl.cmd[0] = 0;
777                 outb(Pdor, 0);
778                 delay(10);
779                 outb(Pdor, Fintena|Fena);
780                 delay(10);
781                 spllo();
782                 fl.motor = 0;
783                 fl.confused = 0;
784                 floppywait(0);
785
786                 /* mark all drives in an unknown state */
787                 for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
788                         dp->confused = 1;
789
790                 /* set rate to a known value */
791                 outb(Pdsr, 0);
792                 fl.rate = 0;
793
794                 DPRINT("floppyrevive out\n");
795                 fldump();
796         }
797 }
798
799 /*
800  *  seek to the target cylinder
801  *
802  *      interrupt, no results
803  */
804 static long
805 floppyseek(FDrive *dp, long off)
806 {
807         floppypos(dp, off);
808         if(dp->cyl == dp->tcyl)
809                 return dp->tcyl;
810         dp->cyl = -1;
811
812         fl.ncmd = 0;
813         fl.cmd[fl.ncmd++] = Fseek;
814         fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
815         fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
816         if(floppycmd() < 0)
817                 return -1;
818         floppywait(1);
819         if(fl.nstat < 2){
820                 DPRINT("seek: confused\n");
821                 fl.confused = 1;
822                 return -1;
823         }
824         if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
825                 DPRINT("seek: failed\n");
826                 dp->confused = 1;
827                 return -1;
828         }
829
830         dp->cyl = dp->tcyl;
831         return dp->tcyl;
832 }
833
834 /*
835  *  read or write to floppy.  try up to three times.
836  */
837 static long
838 floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
839 {
840         long offset;
841         int tries;
842
843         if(off >= dp->t->cap)
844                 return 0;
845         if(off + n > dp->t->cap)
846                 n = dp->t->cap - off;
847
848         /* retry on error (until it gets ridiculous) */
849         tries = 0;
850         while(waserror()){
851                 if(tries++ >= dp->maxtries)
852                         nexterror();
853                 DPRINT("floppyxfer: retrying\n");
854         }
855
856         dp->len = n;
857         if(floppyseek(dp, off) < 0){
858                 DPRINT("xfer: seek failed\n");
859                 dp->confused = 1;
860                 error(Eio);
861         }
862
863         /*
864          *  set up the dma (dp->len may be trimmed)
865          */
866         if(waserror()){
867                 dmaend(DMAchan);
868                 nexterror();
869         }
870         dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread ? DMAREAD : DMAWRITE);
871         if(dp->len < 0)
872                 error(Eio);
873
874         /*
875          *  start operation
876          */
877         fl.ncmd = 0;
878         fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
879         fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
880         fl.cmd[fl.ncmd++] = dp->tcyl;
881         fl.cmd[fl.ncmd++] = dp->thead;
882         fl.cmd[fl.ncmd++] = dp->tsec;
883         fl.cmd[fl.ncmd++] = dp->t->bcode;
884         fl.cmd[fl.ncmd++] = dp->t->sectors;
885         fl.cmd[fl.ncmd++] = dp->t->gpl;
886         fl.cmd[fl.ncmd++] = 0xFF;
887         if(floppycmd() < 0)
888                 error(Eio);
889
890         /* Poll ready bits and transfer data */
891         floppyexec((char*)a, dp->len, cmd==Fread);
892
893         /*
894          *  give bus to DMA, floppyintr() will read result
895          */
896         floppywait(0);
897         dmaend(DMAchan);
898         poperror();
899
900         /*
901          *  check for errors
902          */
903         if(fl.nstat < 7){
904                 DPRINT("xfer: confused\n");
905                 fl.confused = 1;
906                 error(Eio);
907         }
908         if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
909                 DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
910                         fl.stat[1], fl.stat[2]);
911                 DPRINT("offset %lud len %ld\n", off, dp->len);
912                 if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
913                         DPRINT("DMA overrun: retry\n");
914                 } else
915                         dp->confused = 1;
916                 error(Eio);
917         }
918
919         /*
920          *  check for correct cylinder
921          */
922         offset = fl.stat[3] * dp->t->heads + fl.stat[4];
923         offset = offset*dp->t->sectors + fl.stat[5] - 1;
924         offset = offset * c2b[fl.stat[6]];
925         if(offset != off+dp->len){
926                 DPRINT("xfer: ends on wrong cyl\n");
927                 dp->confused = 1;
928                 error(Eio);
929         }
930         poperror();
931
932         dp->lasttouched = m->ticks;
933         return dp->len;
934 }
935
936 /*
937  *  format a track
938  */
939 static void
940 floppyformat(FDrive *dp, Cmdbuf *cb)
941 {
942         int cyl, h, sec;
943         ulong track;
944         uchar *buf, *bp;
945         FType *t;
946
947         /*
948          *  set the type
949          */
950         if(cb->nf == 2){
951                 for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
952                         if(strcmp(cb->f[1], t->name)==0 && t->dt==dp->dt){
953                                 dp->t = t;
954                                 floppydir[1+NFDIR*dp->dev].length = dp->t->cap;
955                                 break;
956                         }
957                 }
958                 if(t >= &floppytype[nelem(floppytype)])
959                         error(Ebadarg);
960         } else if(cb->nf == 1){
961                 floppysetdef(dp);
962                 t = dp->t;
963         } else {
964                 cmderror(cb, "invalid floppy format command");
965                 SET(t);
966         }
967
968         /*
969          *  buffer for per track info
970          */
971         buf = smalloc(t->sectors*4);
972         if(waserror()){
973                 free(buf);
974                 nexterror();
975         }
976
977         /* force a recalibrate to cylinder 0 */
978         dp->confused = 1;
979         if(!waserror()){
980                 floppyon(dp);
981                 poperror();
982         }
983
984         /*
985          *  format a track at time
986          */
987         for(track = 0; track < t->tracks*t->heads; track++){
988                 cyl = track/t->heads;
989                 h = track % t->heads;
990
991                 /*
992                  *  seek to track, ignore errors
993                  */
994                 floppyseek(dp, track*t->tsize);
995                 dp->cyl = cyl;
996                 dp->confused = 0;
997
998                 /*
999                  *  set up the dma (dp->len may be trimmed)
1000                  */
1001                 bp = buf;
1002                 for(sec = 1; sec <= t->sectors; sec++){
1003                         *bp++ = cyl;
1004                         *bp++ = h;
1005                         *bp++ = sec;
1006                         *bp++ = t->bcode;
1007                 }
1008                 if(waserror()){
1009                         dmaend(DMAchan);
1010                         nexterror();
1011                 }
1012                 if(dmasetup(DMAchan, buf, bp-buf, DMAWRITE) < 0)
1013                         error(Eio);
1014
1015                 /*
1016                  *  start operation
1017                  */
1018                 fl.ncmd = 0;
1019                 fl.cmd[fl.ncmd++] = Fformat;
1020                 fl.cmd[fl.ncmd++] = (h<<2) | dp->dev;
1021                 fl.cmd[fl.ncmd++] = t->bcode;
1022                 fl.cmd[fl.ncmd++] = t->sectors;
1023                 fl.cmd[fl.ncmd++] = t->fgpl;
1024                 fl.cmd[fl.ncmd++] = 0x5a;
1025                 if(floppycmd() < 0)
1026                         error(Eio);
1027
1028                 /* Poll ready bits and transfer data */
1029                 floppyexec((char *)buf, bp-buf, 0);
1030
1031                 /*
1032                  *  give bus to DMA, floppyintr() will read result
1033                  */
1034                 floppywait(1);
1035                 dmaend(DMAchan);
1036                 poperror();
1037
1038                 /*
1039                  *  check for errors
1040                  */
1041                 if(fl.nstat < 7){
1042                         DPRINT("format: confused\n");
1043                         fl.confused = 1;
1044                         error(Eio);
1045                 }
1046                 if((fl.stat[0]&Codemask)!=0 || fl.stat[1]|| fl.stat[2]){
1047                         DPRINT("format: failed %ux %ux %ux\n",
1048                                 fl.stat[0], fl.stat[1], fl.stat[2]);
1049                         dp->confused = 1;
1050                         error(Eio);
1051                 }
1052         }
1053         free(buf);
1054         dp->confused = 1;
1055         poperror();
1056 }
1057
1058 static void
1059 floppyintr(Ureg *)
1060 {
1061         switch(fl.cmd[0]&~Fmulti){
1062         case Fread:
1063         case Fwrite:
1064         case Fformat:
1065         case Fdumpreg: 
1066                 floppyresult();
1067                 break;
1068         case Fseek:
1069         case Frecal:
1070         default:
1071                 floppysense();  /* to clear interrupt */
1072                 break;
1073         }
1074         fl.ncmd = 0;
1075         wakeup(&fl.r);
1076 }
1077
1078 Dev floppydevtab = {
1079         'f',
1080         "floppy",
1081
1082         floppyreset,
1083         devinit,
1084         devshutdown,
1085         floppyattach,
1086         floppywalk,
1087         floppystat,
1088         floppyopen,
1089         devcreate,
1090         floppyclose,
1091         floppyread,
1092         devbread,
1093         floppywrite,
1094         devbwrite,
1095         devremove,
1096         devwstat,
1097 };