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