Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
};