xref: /plan9/sys/src/9/pc/devfloppy.c (revision 425afbab47e573297e4abb9165d4d95c66cf3758)
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
fldump(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
floppysetdef(FDrive * dp)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
floppyreset(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*
floppyattach(char * spec)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*
floppywalk(Chan * c,Chan * nc,char ** name,int nname)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
floppystat(Chan * c,uchar * dp,int n)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*
floppyopen(Chan * c,int omode)256 floppyopen(Chan *c, int omode)
257 {
258 	return devopen(c, omode, floppydir, 1+fl.ndrive*NFDIR, devgen);
259 }
260 
261 static void
floppyclose(Chan *)262 floppyclose(Chan *)
263 {
264 }
265 
266 static void
islegal(ulong offset,long n,FDrive * dp)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
changed(Chan * c,FDrive * dp)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
readtrack(FDrive * dp,int cyl,int head)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
floppyread(Chan * c,void * a,long n,vlong off)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
floppywrite(Chan * c,void * a,long n,vlong off)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
floppykproc(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
floppyon(FDrive * dp)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
floppyoff(FDrive * dp)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
floppycmd(void)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
floppyresult(void)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
floppypos(FDrive * dp,long off)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
floppysense(void)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
cmddone(void *)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
floppywait(int slow)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
floppyrecal(FDrive * dp)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
floppyrevive(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
floppyseek(FDrive * dp,long off)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
floppyxfer(FDrive * dp,int cmd,void * a,long off,long n)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
floppyformat(FDrive * dp,Cmdbuf * cb)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
floppyintr(Ureg *)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 };
1088