xref: /inferno-os/os/boot/pc/devfloppy.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 #include <u.h>
2 
3 #include	"lib.h"
4 #include	"mem.h"
5 #include	"dat.h"
6 #include	"fns.h"
7 #include	"io.h"
8 #include	"ureg.h"
9 
10 #include	"fs.h"
11 #include	"devfloppy.h"
12 
13 
14 /* Intel 82077A (8272A compatible) floppy controller */
15 
16 /* This module expects the following functions to be defined
17  * elsewhere:
18  *
19  * inb()
20  * outb()
21  * floppyexec()
22  * floppyeject()
23  * floppysetup0()
24  * floppysetup1()
25  * dmainit()
26  * dmasetup()
27  * dmaend()
28  *
29  * On DMA systems, floppyexec() should be an empty function;
30  * on non-DMA systems, dmaend() should be an empty function;
31  * dmasetup() may enforce maximum transfer sizes.
32  */
33 
34 enum {
35 	/* file types */
36 	Qdir=		0,
37 	Qdata=		(1<<2),
38 	Qctl=		(2<<2),
39 	Qmask=		(3<<2),
40 
41 	DMAchan=	2,	/* floppy dma channel */
42 };
43 
44 #define DPRINT if(0)print
45 
46 FType floppytype[] =
47 {
48  { "3½HD",	T1440kb, 512, 18, 2, 1, 80, 0x1B, 0x54,	0, },
49  { "3½DD",	T1440kb, 512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
50  { "3½DD",	T720kb,  512,  9, 2, 1, 80, 0x1B, 0x54, 2, },
51  { "5¼HD",	T1200kb, 512, 15, 2, 1, 80, 0x2A, 0x50, 0, },
52  { "5¼DD",	T1200kb, 512,  9, 2, 2, 40, 0x2A, 0x50, 1, },
53  { "ATT3B1",	T1200kb, 512,  8, 2, 2, 48, 0x2A, 0x50, 1, },
54  { "5¼DD",	T360kb,  512,  9, 2, 1, 40, 0x2A, 0x50, 2, },
55 };
56 
57 /*
58  *  bytes per sector encoding for the controller.
59  *  - index for b2c is is (bytes per sector/128).
60  *  - index for c2b is code from b2c
61  */
62 static int b2c[] =
63 {
64 [1]	0,
65 [2]	1,
66 [4]	2,
67 [8]	3,
68 };
69 static int c2b[] =
70 {
71 	128,
72 	256,
73 	512,
74 	1024,
75 };
76 
77 FController	fl;
78 
79 #define MOTORBIT(i)	(1<<((i)+4))
80 
81 /*
82  *  predeclared
83  */
84 static int	cmddone(void*);
85 static void	floppyformat(FDrive*, char*);
86 static void	floppykproc(void*);
87 static void	floppypos(FDrive*,long);
88 static int	floppyrecal(FDrive*);
89 static int	floppyresult(void);
90 static void	floppyrevive(void);
91 static vlong	pcfloppyseek(FDrive*, vlong);
92 static int	floppysense(void);
93 static void	floppywait(int);
94 static long	floppyxfer(FDrive*, int, void*, long, long);
95 
96 static void
fldump(void)97 fldump(void)
98 {
99 	DPRINT("sra %ux srb %ux dor %ux msr %ux dir %ux\n", inb(Psra), inb(Psrb),
100 		inb(Pdor), inb(Pmsr), inb(Pdir));
101 }
102 
103 static void
floppyalarm(Alarm * a)104 floppyalarm(Alarm* a)
105 {
106 	FDrive *dp;
107 
108 	for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
109 		if((fl.motor&MOTORBIT(dp->dev)) && TK2SEC(m->ticks - dp->lasttouched) > 5)
110 			floppyoff(dp);
111 	}
112 
113 	alarm(5*1000, floppyalarm, 0);
114 	cancel(a);
115 }
116 
117 /*
118  *  set floppy drive to its default type
119  */
120 static void
floppysetdef(FDrive * dp)121 floppysetdef(FDrive *dp)
122 {
123 	FType *t;
124 
125 	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++)
126 		if(dp->dt == t->dt){
127 			dp->t = t;
128 			break;
129 		}
130 }
131 
132 static void
_floppydetach(void)133 _floppydetach(void)
134 {
135 	/*
136 	 *  stop the motors
137 	 */
138 	fl.motor = 0;
139 	delay(10);
140 	outb(Pdor, fl.motor | Fintena | Fena);
141 	delay(10);
142 }
143 
144 int
floppyinit(void)145 floppyinit(void)
146 {
147 	FDrive *dp;
148 	FType *t;
149 	ulong maxtsize;
150 	int mask;
151 
152 	dmainit(DMAchan);
153 
154 	floppysetup0(&fl);
155 
156 	/*
157 	 *  init dependent parameters
158 	 */
159 	maxtsize = 0;
160 	for(t = floppytype; t < &floppytype[nelem(floppytype)]; t++){
161 		t->cap = t->bytes * t->heads * t->sectors * t->tracks;
162 		t->bcode = b2c[t->bytes/128];
163 		t->tsize = t->bytes * t->sectors;
164 		if(maxtsize < t->tsize)
165 			maxtsize = t->tsize;
166 	}
167 
168 	fl.selected = fl.d;
169 
170 	floppydetach = _floppydetach;
171 	floppydetach();
172 
173 	/*
174 	 *  init drives
175 	 */
176 	mask = 0;
177 	for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++){
178 		dp->dev = dp - fl.d;
179 		if(dp->dt == Tnone)
180 			continue;
181 		mask |= 1<<dp->dev;
182 		floppysetdef(dp);
183 		dp->cyl = -1;			/* because we don't know */
184 		dp->cache = (uchar*)xspanalloc(maxtsize, BY2PG, 64*1024);
185 		dp->ccyl = -1;
186 		dp->vers = 0;
187 		dp->maxtries = 5;
188 	}
189 
190 	/*
191 	 *  first operation will recalibrate
192 	 */
193 	fl.confused = 1;
194 
195 	floppysetup1(&fl);
196 
197 	/* to turn the motor off when inactive */
198 	alarm(5*1000, floppyalarm, 0);
199 
200 	return mask;
201 }
202 
203 void
floppyinitdev(int i,char * name)204 floppyinitdev(int i, char *name)
205 {
206 	if(i >= fl.ndrive)
207 		panic("floppyinitdev");
208 	sprint(name, "fd%d", i);
209 }
210 
211 void
floppyprintdevs(int i)212 floppyprintdevs(int i)
213 {
214 	if(i >= fl.ndrive)
215 		panic("floppyprintdevs");
216 	print(" fd%d", i);
217 }
218 
219 int
floppyboot(int dev,char * file,Boot * b)220 floppyboot(int dev, char *file, Boot *b)
221 {
222 	Fs *fs;
223 
224 	if(strncmp(file, "dos!", 4) == 0)
225 		file += 4;
226 	else if(strchr(file, '!') || strcmp(file, "")==0) {
227 		print("syntax is fd0!file\n");
228 		return -1;
229 	}
230 
231 	fs = floppygetfspart(dev, "dos", 1);
232 	if(fs == nil)
233 		return -1;
234 
235 	return fsboot(fs, file, b);
236 }
237 
238 void
floppyprintbootdevs(int dev)239 floppyprintbootdevs(int dev)
240 {
241 	print(" fd%d", dev);
242 }
243 
244 /*
245  *  check if the floppy has been replaced under foot.  cause
246  *  an error if it has.
247  *
248  *  a seek and a read clears the condition.  this was determined
249  *  experimentally, there has to be a better way.
250  *
251  *  if the read fails, cycle through the possible floppy
252  *  density till one works or we've cycled through all
253  *  possibilities for this drive.
254  */
255 static int
changed(FDrive * dp)256 changed(FDrive *dp)
257 {
258 	FType *start;
259 
260 	/*
261 	 *  if floppy has changed or first time through
262 	 */
263 	if((inb(Pdir)&Fchange) || dp->vers == 0){
264 		DPRINT("changed\n");
265 		fldump();
266 		dp->vers++;
267 		floppysetdef(dp);
268 		dp->maxtries = 3;
269 		start = dp->t;
270 
271 		/* flopppyon fails if there's no drive */
272 		dp->confused = 1;	/* make floppyon recal */
273 		if(floppyon(dp) < 0)
274 			return -1;
275 
276 		pcfloppyseek(dp, dp->t->heads*dp->t->tsize);
277 
278 		while(floppyxfer(dp, Fread, dp->cache, 0, dp->t->tsize) <= 0){
279 
280 			/*
281 			 *  if the xfer attempt doesn't clear the changed bit,
282 			 *  there's no floppy in the drive
283 			 */
284 			if(inb(Pdir)&Fchange)
285 				return -1;
286 
287 			while(++dp->t){
288 				if(dp->t == &floppytype[nelem(floppytype)])
289 					dp->t = floppytype;
290 				if(dp->dt == dp->t->dt)
291 					break;
292 			}
293 
294 			/* flopppyon fails if there's no drive */
295 			if(floppyon(dp) < 0)
296 				return -1;
297 
298 			DPRINT("changed: trying %s\n", dp->t->name);
299 			fldump();
300 			if(dp->t == start)
301 				return -1;
302 		}
303 	}
304 
305 	return 0;
306 }
307 
308 static int
readtrack(FDrive * dp,int cyl,int head)309 readtrack(FDrive *dp, int cyl, int head)
310 {
311 	int i, nn, sofar;
312 	ulong pos;
313 
314 	nn = dp->t->tsize;
315 	if(dp->ccyl==cyl && dp->chead==head)
316 		return nn;
317 	pos = (cyl*dp->t->heads+head) * nn;
318 	for(sofar = 0; sofar < nn; sofar += i){
319 		dp->ccyl = -1;
320 		i = floppyxfer(dp, Fread, dp->cache + sofar, pos + sofar, nn - sofar);
321 		if(i <= 0)
322 			return -1;
323 	}
324 	dp->ccyl = cyl;
325 	dp->chead = head;
326 	return nn;
327 }
328 
329 long
floppyread(Fs * fs,void * a,long n)330 floppyread(Fs *fs, void *a, long n)
331 {
332 	FDrive *dp;
333 	long rv, offset;
334 	int sec, head, cyl;
335 	long len;
336 	uchar *aa;
337 
338 	aa = a;
339 	dp = &fl.d[fs->dev];
340 
341 	offset = dp->offset;
342 	floppyon(dp);
343 	if(changed(dp))
344 		return -1;
345 
346 	for(rv = 0; rv < n; rv += len){
347 		/*
348 		 *  all xfers come out of the track cache
349 		 */
350 		dp->len = n - rv;
351 		floppypos(dp, offset+rv);
352 		cyl = dp->tcyl;
353 		head = dp->thead;
354 		len = dp->len;
355 		sec = dp->tsec;
356 		if(readtrack(dp, cyl, head) < 0)
357 			break;
358 		memmove(aa+rv, dp->cache + (sec-1)*dp->t->bytes, len);
359 	}
360 	dp->offset = offset+rv;
361 
362 	return rv;
363 }
364 
365 void*
floppygetfspart(int i,char * name,int chatty)366 floppygetfspart(int i, char *name, int chatty)
367 {
368 	static Fs fs;
369 
370 	if(strcmp(name, "dos") != 0){
371 		if(chatty)
372 			print("unknown partition fd%d!%s (use fd%d!dos)\n", i, name, i);
373 		return nil;
374 	}
375 
376 	fs.dev = i;
377 	fs.diskread = floppyread;
378 	fs.diskseek = floppyseek;
379 
380 	/* sometimes we get spurious errors and doing it again works */
381 	if(dosinit(&fs) < 0 && dosinit(&fs) < 0){
382 		if(chatty)
383 			print("fd%d!%s does not contain a FAT file system\n", i, name);
384 		return nil;
385 	}
386 	return &fs;
387 }
388 
389 static int
return0(void *)390 return0(void*)
391 {
392 	return 0;
393 }
394 
395 static void
timedsleep(int (* f)(void *),void * arg,int ms)396 timedsleep(int (*f)(void*), void* arg, int ms)
397 {
398 	int s;
399 	ulong end;
400 
401 	end = m->ticks + 1 + MS2TK(ms);
402 	while(m->ticks < end && !(*f)(arg)){
403 		s = spllo();
404 		delay(10);
405 		splx(s);
406 	}
407 }
408 
409 /*
410  *  start a floppy drive's motor.
411  */
412 static int
floppyon(FDrive * dp)413 floppyon(FDrive *dp)
414 {
415 	int alreadyon;
416 	int tries;
417 
418 	if(fl.confused)
419 		floppyrevive();
420 
421 	/* start motor and select drive */
422 	dp->lasttouched = m->ticks;
423 	alreadyon = fl.motor & MOTORBIT(dp->dev);
424 	if(!alreadyon){
425 		fl.motor |= MOTORBIT(dp->dev);
426 		outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
427 		/* wait for drive to spin up */
428 		timedsleep(return0, 0, 750);
429 
430 		/* clear any pending interrupts */
431 		floppysense();
432 	}
433 
434 	/* set transfer rate */
435 	if(fl.rate != dp->t->rate){
436 		fl.rate = dp->t->rate;
437 		outb(Pdsr, fl.rate);
438 	}
439 
440 	/* get drive to a known cylinder */
441 	if(dp->confused)
442 		for(tries = 0; tries < 4; tries++)
443 			if(floppyrecal(dp) >= 0)
444 				break;
445 	dp->lasttouched = m->ticks;
446 	fl.selected = dp;
447 	if(dp->confused)
448 		return -1;
449 	return 0;
450 }
451 
452 /*
453  *  stop the floppy if it hasn't been used in 5 seconds
454  */
455 static void
floppyoff(FDrive * dp)456 floppyoff(FDrive *dp)
457 {
458 	fl.motor &= ~MOTORBIT(dp->dev);
459 	outb(Pdor, fl.motor | Fintena | Fena | dp->dev);
460 }
461 
462 /*
463  *  send a command to the floppy
464  */
465 static int
floppycmd(void)466 floppycmd(void)
467 {
468 	int i;
469 	int tries;
470 
471 	fl.nstat = 0;
472 	for(i = 0; i < fl.ncmd; i++){
473 		for(tries = 0; ; tries++){
474 			if((inb(Pmsr)&(Ffrom|Fready)) == Fready)
475 				break;
476 			if(tries > 1000){
477 				DPRINT("cmd %ux can't be sent (%d)\n", fl.cmd[0], i);
478 				fldump();
479 
480 				/* empty fifo, might have been a bad command */
481 				floppyresult();
482 				return -1;
483 			}
484 			microdelay(1);
485 		}
486 		outb(Pfdata, fl.cmd[i]);
487 	}
488 	return 0;
489 }
490 
491 /*
492  *  get a command result from the floppy
493  *
494  *  when the controller goes ready waiting for a command
495  *  (instead of sending results), we're done
496  *
497  */
498 static int
floppyresult(void)499 floppyresult(void)
500 {
501 	int i, s;
502 	int tries;
503 
504 	/* get the result of the operation */
505 	for(i = 0; i < sizeof(fl.stat); i++){
506 		/* wait for status byte */
507 		for(tries = 0; ; tries++){
508 			s = inb(Pmsr)&(Ffrom|Fready);
509 			if(s == Fready){
510 				fl.nstat = i;
511 				return fl.nstat;
512 			}
513 			if(s == (Ffrom|Fready))
514 				break;
515 			if(tries > 1000){
516 				DPRINT("floppyresult: %d stats\n", i);
517 				fldump();
518 				fl.confused = 1;
519 				return -1;
520 			}
521 			microdelay(1);
522 		}
523 		fl.stat[i] = inb(Pfdata);
524 	}
525 	fl.nstat = sizeof(fl.stat);
526 	return fl.nstat;
527 }
528 
529 /*
530  *  calculate physical address of a logical byte offset into the disk
531  *
532  *  truncate dp->length if it crosses a track boundary
533  */
534 static void
floppypos(FDrive * dp,long off)535 floppypos(FDrive *dp, long off)
536 {
537 	int lsec;
538 	int ltrack;
539 	int end;
540 
541 	lsec = off/dp->t->bytes;
542 	ltrack = lsec/dp->t->sectors;
543 	dp->tcyl = ltrack/dp->t->heads;
544 	dp->tsec = (lsec % dp->t->sectors) + 1;
545 	dp->thead = (lsec/dp->t->sectors) % dp->t->heads;
546 
547 	/*
548 	 *  can't read across track boundaries.
549 	 *  if so, decrement the bytes to be read.
550 	 */
551 	end = (ltrack+1)*dp->t->sectors*dp->t->bytes;
552 	if(off+dp->len > end)
553 		dp->len = end - off;
554 }
555 
556 /*
557  *  get the interrupt cause from the floppy.
558  */
559 static int
floppysense(void)560 floppysense(void)
561 {
562 	fl.ncmd = 0;
563 	fl.cmd[fl.ncmd++] = Fsense;
564 	if(floppycmd() < 0)
565 		return -1;
566 	if(floppyresult() < 2){
567 		DPRINT("can't read sense response\n");
568 		fldump();
569 		fl.confused = 1;
570 		return -1;
571 	}
572 	return 0;
573 }
574 
575 static int
cmddone(void * a)576 cmddone(void *a)
577 {
578 	USED(a);
579 	return fl.ncmd == 0;
580 }
581 
582 /*
583  *  Wait for a floppy interrupt.  If none occurs in 5 seconds, we
584  *  may have missed one.  This only happens on some portables which
585  *  do power management behind our backs.  Call the interrupt
586  *  routine to try to clear any conditions.
587  */
588 static void
floppywait(int slow)589 floppywait(int slow)
590 {
591 	timedsleep(cmddone, 0, slow ? 5000 : 1000);
592 	if(!cmddone(0)){
593 		floppyintr(0);
594 		fl.confused = 1;
595 	}
596 }
597 
598 /*
599  *  we've lost the floppy position, go to cylinder 0.
600  */
601 static int
floppyrecal(FDrive * dp)602 floppyrecal(FDrive *dp)
603 {
604 	dp->ccyl = -1;
605 	dp->cyl = -1;
606 
607 	fl.ncmd = 0;
608 	fl.cmd[fl.ncmd++] = Frecal;
609 	fl.cmd[fl.ncmd++] = dp->dev;
610 	if(floppycmd() < 0)
611 		return -1;
612 	floppywait(1);
613 	if(fl.nstat < 2){
614 		DPRINT("recalibrate: confused %ux\n", inb(Pmsr));
615 		fl.confused = 1;
616 		return -1;
617 	}
618 	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
619 		DPRINT("recalibrate: failed\n");
620 		dp->confused = 1;
621 		return -1;
622 	}
623 	dp->cyl = fl.stat[1];
624 	if(dp->cyl != 0){
625 		DPRINT("recalibrate: wrong cylinder %d\n", dp->cyl);
626 		dp->cyl = -1;
627 		dp->confused = 1;
628 		return -1;
629 	}
630 
631 	dp->confused = 0;
632 	return 0;
633 }
634 
635 /*
636  *  if the controller or a specific drive is in a confused state,
637  *  reset it and get back to a kown state
638  */
639 static void
floppyrevive(void)640 floppyrevive(void)
641 {
642 	FDrive *dp;
643 
644 	/*
645 	 *  reset the controller if it's confused
646 	 */
647 	if(fl.confused){
648 		DPRINT("floppyrevive in\n");
649 		fldump();
650 
651 		/* reset controller and turn all motors off */
652 		splhi();
653 		fl.ncmd = 1;
654 		fl.cmd[0] = 0;
655 		outb(Pdor, 0);
656 		delay(10);
657 		outb(Pdor, Fintena|Fena);
658 		delay(10);
659 		spllo();
660 		fl.motor = 0;
661 		fl.confused = 0;
662 		floppywait(0);
663 
664 		/* mark all drives in an unknown state */
665 		for(dp = fl.d; dp < &fl.d[fl.ndrive]; dp++)
666 			dp->confused = 1;
667 
668 		/* set rate to a known value */
669 		outb(Pdsr, 0);
670 		fl.rate = 0;
671 
672 		DPRINT("floppyrevive out\n");
673 		fldump();
674 	}
675 }
676 
677 /*
678  *  seek to the target cylinder
679  *
680  *	interrupt, no results
681  */
682 static vlong
pcfloppyseek(FDrive * dp,vlong off)683 pcfloppyseek(FDrive *dp, vlong off)
684 {
685 	floppypos(dp, off);
686 	if(dp->cyl == dp->tcyl){
687 		dp->offset = off;
688 		return off;
689 	}
690 	dp->cyl = -1;
691 
692 	fl.ncmd = 0;
693 	fl.cmd[fl.ncmd++] = Fseek;
694 	fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
695 	fl.cmd[fl.ncmd++] = dp->tcyl * dp->t->steps;
696 	if(floppycmd() < 0)
697 		return -1;
698 	floppywait(1);
699 	if(fl.nstat < 2){
700 		DPRINT("seek: confused\n");
701 		fl.confused = 1;
702 		return -1;
703 	}
704 	if((fl.stat[0] & (Codemask|Seekend)) != Seekend){
705 		DPRINT("seek: failed\n");
706 		dp->confused = 1;
707 		return -1;
708 	}
709 
710 	dp->cyl = dp->tcyl;
711 	dp->offset = off;
712 	DPRINT("seek to %d succeeded\n", dp->offset);
713 
714 	return dp->offset;
715 }
716 
717 /*
718  *  read or write to floppy.  try up to three times.
719  */
720 static long
floppyxfer(FDrive * dp,int cmd,void * a,long off,long n)721 floppyxfer(FDrive *dp, int cmd, void *a, long off, long n)
722 {
723 	long offset;
724 	int tries;
725 
726 	if(off >= dp->t->cap)
727 		return 0;
728 	if(off + n > dp->t->cap)
729 		n = dp->t->cap - off;
730 
731 	/* retry on error (until it gets ridiculous) */
732 	for(tries = 0; tries < dp->maxtries; tries++){
733 
734 		dp->len = n;
735 		if(pcfloppyseek(dp, off) < 0){
736 			DPRINT("xfer: seek failed\n");
737 			dp->confused = 1;
738 			continue;
739 		}
740 
741 		/*
742 		 *  set up the dma (dp->len may be trimmed)
743 		 */
744 		dp->len = dmasetup(DMAchan, a, dp->len, cmd==Fread);
745 		if(dp->len < 0){
746 	buggery:
747 			dmaend(DMAchan);
748 			continue;
749 		}
750 
751 		/*
752 		 *  start operation
753 		 */
754 		fl.ncmd = 0;
755 		fl.cmd[fl.ncmd++] = cmd | (dp->t->heads > 1 ? Fmulti : 0);
756 		fl.cmd[fl.ncmd++] = (dp->thead<<2) | dp->dev;
757 		fl.cmd[fl.ncmd++] = dp->tcyl;
758 		fl.cmd[fl.ncmd++] = dp->thead;
759 		fl.cmd[fl.ncmd++] = dp->tsec;
760 		fl.cmd[fl.ncmd++] = dp->t->bcode;
761 		fl.cmd[fl.ncmd++] = dp->t->sectors;
762 		fl.cmd[fl.ncmd++] = dp->t->gpl;
763 		fl.cmd[fl.ncmd++] = 0xFF;
764 		if(floppycmd() < 0)
765 			goto buggery;
766 
767 		/*
768 		 *  give bus to DMA, floppyintr() will read result
769 		 */
770 		floppywait(0);
771 		dmaend(DMAchan);
772 
773 		/*
774 		 *  check for errors
775 		 */
776 		if(fl.nstat < 7){
777 			DPRINT("xfer: confused\n");
778 			fl.confused = 1;
779 			continue;
780 		}
781 		if((fl.stat[0] & Codemask)!=0 || fl.stat[1] || fl.stat[2]){
782 			DPRINT("xfer: failed %ux %ux %ux\n", fl.stat[0],
783 				fl.stat[1], fl.stat[2]);
784 			DPRINT("offset %lud len %ld\n", off, dp->len);
785 			if((fl.stat[0]&Codemask)==Cmdexec && fl.stat[1]==Overrun){
786 				DPRINT("DMA overrun: retry\n");
787 			} else
788 				dp->confused = 1;
789 			continue;
790 		}
791 
792 		/*
793 		 *  check for correct cylinder
794 		 */
795 		offset = fl.stat[3] * dp->t->heads + fl.stat[4];
796 		offset = offset*dp->t->sectors + fl.stat[5] - 1;
797 		offset = offset * c2b[fl.stat[6]];
798 		if(offset != off+dp->len){
799 			DPRINT("xfer: ends on wrong cyl\n");
800 			dp->confused = 1;
801 			continue;
802 		}
803 
804 		dp->lasttouched = m->ticks;
805 		dp->maxtries = 20;
806 		return dp->len;
807 	}
808 
809 	return -1;
810 }
811 
812 /*
813 void
814 floppymemwrite(void)
815 {
816 	int i;
817 	int n;
818 	uchar *a;
819 	FDrive *dp;
820 
821 	dp = &fl.d[0];
822 	a = (uchar*)0x80000000;
823 	n = 0;
824 	while(n < 1440*1024){
825 		i = floppyxfer(dp, Fwrite, a+n, n, 1440*1024-n);
826 		if(i <= 0)
827 			break;
828 		n += i;
829 	}
830 	print("floppymemwrite wrote %d bytes\n", n);
831 splhi(); for(;;);
832 }
833 */
834 
835 static void
floppyintr(Ureg * ur)836 floppyintr(Ureg *ur)
837 {
838 	USED(ur);
839 	switch(fl.cmd[0]&~Fmulti){
840 	case Fread:
841 	case Fwrite:
842 	case Fformat:
843 	case Fdumpreg:
844 		floppyresult();
845 		break;
846 	case Fseek:
847 	case Frecal:
848 	default:
849 		floppysense();	/* to clear interrupt */
850 		break;
851 	}
852 	fl.ncmd = 0;
853 }
854