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