finally got ring ptrs right, hope error processing didn't break again!
[unix-history] / usr / src / sys / i386 / isa / fd.c
... / ...
CommitLineData
1#include "fd.h"
2#if NFD > 0
3/*-
4 * Copyright (c) 1990 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Don Ahn.
9 *
10 * %sccs.include.386.c%
11 *
12 * @(#)fd.c 5.2 (Berkeley) %G%
13 */
14
15/****************************************************************************/
16/* fd driver */
17/****************************************************************************/
18#include "param.h"
19#include "dkbad.h"
20#include "systm.h"
21#include "conf.h"
22#include "file.h"
23#include "dir.h"
24#include "user.h"
25#include "ioctl.h"
26#include "disk.h"
27#include "buf.h"
28#include "vm.h"
29#include "uio.h"
30#include "machine/pte.h"
31#include "machine/isa/device.h"
32#include "machine/isa/fdreg.h"
33#include "icu.h"
34
35#define FDUNIT(s) ((s)&1)
36#define FDTYPE(s) (((s)>>1)&7)
37
38#define b_cylin b_resid
39#define b_step b_resid
40#define FDBLK 512
41#define NUMTYPES 4
42
43struct fd_type {
44 int sectrac; /* sectors per track */
45 int secsize; /* size code for sectors */
46 int datalen; /* data len when secsize = 0 */
47 int gap; /* gap len between sectors */
48 int tracks; /* total num of tracks */
49 int size; /* size of disk in sectors */
50 int steptrac; /* steps per cylinder */
51 int trans; /* transfer speed code */
52};
53
54struct fd_type fd_types[NUMTYPES] = {
55 { 18,2,0xFF,0x1B,80,2880,1,0 }, /* 1.44 meg HD 3.5in floppy */
56 { 15,2,0xFF,0x1B,80,2400,1,0 }, /* 1.2 meg HD floppy */
57 { 9,2,0xFF,0x23,40,720,2,1 }, /* 360k floppy in 1.2meg drive */
58 { 9,2,0xFF,0x2A,40,720,1,1 }, /* 360k floppy in DD drive */
59};
60
61struct fd_u {
62 int type; /* Drive type (HD, DD */
63 int active; /* Drive activity boolean */
64 int motor; /* Motor on flag */
65 struct buf head; /* Head of buf chain */
66 struct buf rhead; /* Raw head of buf chain */
67 int reset;
68} fd_unit[NFD];
69
70
71extern int hz;
72
73/* state needed for current transfer */
74static fdc; /* floppy disk controller io base register */
75int fd_dmachan = 2;
76static int fd_skip;
77static int fd_state;
78static int fd_retry;
79static int fd_drive;
80static int fd_track = -1;
81static int fd_status[7];
82
83/*
84 make sure bounce buffer for DMA is aligned since the DMA chip
85 doesn't roll over properly over a 64k boundary
86*/
87extern struct buf *dma_bounce[];
88
89/****************************************************************************/
90/* autoconfiguration stuff */
91/****************************************************************************/
92int fdprobe(), fdattach(), fd_turnoff();
93
94struct isa_driver fddriver = {
95 fdprobe, fdattach, "fd",
96};
97
98fdprobe(dev)
99struct isa_device *dev;
100{
101 return 1;
102}
103
104fdattach(dev)
105struct isa_device *dev;
106{ int s;
107
108 fdc = dev->id_iobase;
109 /* Set transfer to 500kbps */
110 outb(fdc+fdctl,0);
111 fd_turnoff(0);
112}
113
114int
115fdsize(dev)
116dev_t dev;
117{
118 return(2400);
119}
120
121/****************************************************************************/
122/* fdstrategy */
123/****************************************************************************/
124fdstrategy(bp)
125 register struct buf *bp; /* IO operation to perform */
126{
127 register struct buf *dp,*dp0,*dp1;
128 long nblocks,blknum;
129 int unit, type, s;
130
131 unit = FDUNIT(minor(bp->b_dev));
132 type = FDTYPE(minor(bp->b_dev));
133
134#ifdef FDTEST
135printf("fdstrat%d, blk = %d, bcount = %d, addr = %x|",
136 unit, bp->b_blkno, bp->b_bcount,bp->b_un.b_addr);
137#endif
138 if ((unit >= NFD) || (bp->b_blkno < 0)) {
139 printf("fdstrat: unit = %d, blkno = %d, bcount = %d\n",
140 unit, bp->b_blkno, bp->b_bcount);
141 pg("fd:error in fdstrategy");
142 bp->b_error = EINVAL;
143 bp->b_flags |= B_ERROR;
144 goto bad;
145 }
146 /*
147 * Set up block calculations.
148 */
149 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
150 nblocks = fd_types[type].size;
151 if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
152 if (blknum == nblocks) {
153 bp->b_resid = bp->b_bcount;
154 } else {
155 bp->b_error = ENOSPC;
156 bp->b_flags |= B_ERROR;
157 }
158 goto bad;
159 }
160 bp->b_cylin = blknum / (fd_types[type].sectrac * 2);
161 dp = &fd_unit[unit].head;
162 dp0 = &fd_unit[0].head;
163 dp1 = &fd_unit[1].head;
164 dp->b_step = (fd_types[fd_unit[unit].type].steptrac);
165 s = splbio();
166 disksort(dp, bp);
167 if ((dp0->b_active == 0)&&(dp1->b_active == 0)) {
168#ifdef FDDEBUG
169printf("T|");
170#endif
171 dp->b_active = 1;
172 fd_drive = unit;
173 fd_track = -1; /* force seek on first xfer */
174 untimeout(fd_turnoff,unit);
175 fdstart(unit); /* start drive if idle */
176 }
177 splx(s);
178 return;
179
180bad:
181 biodone(bp);
182}
183
184/****************************************************************************/
185/* motor control stuff */
186/****************************************************************************/
187set_motor(unit,reset)
188int unit,reset;
189{
190 int m0,m1;
191 m0 = fd_unit[0].motor;
192 m1 = fd_unit[1].motor;
193 outb(fdc+fdout,unit | (reset ? 0 : 0xC) | (m0 ? 16 : 0) | (m1 ? 32 : 0));
194}
195
196fd_turnoff(unit)
197int unit;
198{
199 fd_unit[unit].motor = 0;
200 if (unit) set_motor(0,0);
201 else set_motor(1,0);
202}
203
204fd_turnon(unit)
205int unit;
206{
207 fd_unit[unit].motor = 1;
208 set_motor(unit,0);
209}
210
211/****************************************************************************/
212/* fdc in/out */
213/****************************************************************************/
214int
215in_fdc()
216{
217 int i;
218 while ((i = inb(fdc+fdsts) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM))
219 if (i == NE7_RQM) return -1;
220 return inb(fdc+fddata);
221}
222
223dump_stat()
224{
225 int i;
226 for(i=0;i<7;i++) {
227 fd_status[i] = in_fdc();
228 if (fd_status[i] < 0) break;
229 }
230printf("FD bad status :%X %X %X %X %X %X %X\n",
231 fd_status[0], fd_status[1], fd_status[2], fd_status[3],
232 fd_status[4], fd_status[5], fd_status[6] );
233}
234
235out_fdc(x)
236int x;
237{
238 int r,errcnt;
239 static int maxcnt = 0;
240 errcnt = 0;
241 do {
242 r = (inb(fdc+fdsts) & (NE7_DIO|NE7_RQM));
243 if (r== NE7_RQM) break;
244 if (r==(NE7_DIO|NE7_RQM)) {
245 dump_stat(); /* error: direction. eat up output */
246#ifdef FDOTHER
247printf("%X\n",x);
248#endif
249 }
250 /* printf("Error r = %d:",r); */
251 errcnt++;
252 } while (1);
253 if (errcnt > maxcnt) {
254 maxcnt = errcnt;
255#ifdef FDOTHER
256printf("New MAX = %d\n",maxcnt);
257#endif
258 }
259 outb(fdc+fddata,x);
260}
261
262/* see if fdc responding */
263int
264check_fdc()
265{
266 int i;
267 for(i=0;i<100;i++) {
268 if (inb(fdc+fdsts)& NE7_RQM) return 0;
269 }
270 return 1;
271}
272
273/****************************************************************************/
274/* fdopen/fdclose */
275/****************************************************************************/
276fdopen(dev, flags)
277 dev_t dev;
278 int flags;
279{
280 int unit = FDUNIT(minor(dev));
281 int type = FDTYPE(minor(dev));
282 int s;
283
284 /* check bounds */
285 if (unit >= NFD) return(ENXIO);
286 if (type >= NUMTYPES) return(ENXIO);
287/*
288 if (check_fdc()) return(EBUSY);
289*/
290
291 /* Set proper disk type, only allow one type */
292 return 0;
293}
294
295fdclose(dev)
296 dev_t dev;
297{
298}
299
300/****************************************************************************/
301/* fdread/fdwrite */
302/****************************************************************************/
303/*
304 * Routines to do raw IO for a unit.
305 */
306fdread(dev, uio) /* character read routine */
307dev_t dev;
308struct uio *uio;
309{
310 int unit = FDUNIT(minor(dev)) ;
311 if (unit >= NFD) return(ENXIO);
312 return(physio(fdstrategy,&fd_unit[unit].rhead,dev,B_READ,minphys,uio));
313}
314
315fdwrite(dev, uio) /* character write routine */
316dev_t dev;
317struct uio *uio;
318{
319 int unit = FDUNIT(minor(dev)) ;
320 if (unit >= NFD) return(ENXIO);
321 return(physio(fdstrategy,&fd_unit[unit].rhead,dev,B_WRITE,minphys,uio));
322}
323
324/****************************************************************************/
325/* fdstart */
326/****************************************************************************/
327fdstart(unit)
328int unit;
329{
330 register struct buf *dp,*bp;
331 int s;
332
333#ifdef FDTEST
334printf("st%d|",unit);
335#endif
336 s = splbio();
337 if (!fd_unit[unit].motor) {
338 fd_turnon(unit);
339 /* Wait for 1 sec */
340 timeout(fdstart,unit,hz);
341 /*DELAY(1000000);*/
342 }else
343 {
344 /* make sure drive is selected as well as on */
345 /*set_motor(unit,0);*/
346
347 dp = &fd_unit[unit].head;
348 bp = dp->b_actf;
349 fd_retry = 0;
350 if (fd_unit[unit].reset) fd_state = 1;
351 else {
352 /* DO a RESET */
353 fd_unit[unit].reset = 1;
354 fd_state = 5;
355 }
356 fd_skip = 0;
357#ifdef FDDEBUG
358printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
359#endif
360 if (bp->b_cylin != fd_track) {
361 /* Seek necessary, never quite sure where head is at! */
362 out_fdc(15); /* Seek function */
363 out_fdc(unit); /* Drive number */
364 out_fdc(bp->b_cylin * dp->b_step);
365 } else fdintr(0);
366 }
367 splx(s);
368}
369
370fd_timeout(x)
371int x;
372{
373 int i,j;
374 struct buf *dp,*bp;
375
376 dp = &fd_unit[fd_drive].head;
377 bp = dp->b_actf;
378
379 out_fdc(0x4);
380 out_fdc(fd_drive);
381 i = in_fdc();
382 printf("Timeout drive status %X\n",i);
383
384 out_fdc(0x8);
385 i = in_fdc();
386 j = in_fdc();
387 printf("ST0 = %X, PCN = %X\n",i,j);
388
389 if (bp) badtrans(dp,bp);
390}
391
392/****************************************************************************/
393/* fdintr */
394/****************************************************************************/
395fdintr(vec)
396int vec;
397{
398 register struct buf *dp,*bp;
399 struct buf *dpother;
400 int read,head,trac,sec,i,s,sectrac,cyl;
401 unsigned long blknum;
402 struct fd_type *ft;
403
404#ifdef FDTEST
405 printf("state %d, vec %d, dr %d|",fd_state,vec,fd_drive);
406#endif
407
408 dp = &fd_unit[fd_drive].head;
409 bp = dp->b_actf;
410 read = bp->b_flags & B_READ;
411 ft = &fd_types[FDTYPE(bp->b_dev)];
412
413 switch (fd_state) {
414 case 1 : /* SEEK DONE, START DMA */
415 /* Make sure seek really happened*/
416 if (vec) {
417 out_fdc(0x8);
418 i = in_fdc();
419 cyl = in_fdc();
420 if (!(i&0x20) || (cyl != bp->b_cylin*dp->b_step)) {
421printf("Stray int ST0 = %X, PCN = %X:",i,cyl);
422 return;
423 }
424 }
425
426 fd_track = bp->b_cylin;
427 at_dma(read,bp->b_un.b_addr+fd_skip,FDBLK, fd_dmachan);
428 blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
429 + fd_skip/FDBLK;
430 sectrac = ft->sectrac;
431 sec = blknum % (sectrac * 2);
432 head = sec / sectrac;
433 sec = sec % sectrac + 1;
434
435 if (read) out_fdc(0xE6); /* READ */
436 else out_fdc(0xC5); /* WRITE */
437 out_fdc(head << 2 | fd_drive); /* head & unit */
438 out_fdc(fd_track); /* track */
439 out_fdc(head);
440 out_fdc(sec); /* sector XXX +1? */
441 out_fdc(ft->secsize); /* sector size */
442 out_fdc(sectrac); /* sectors/track */
443 out_fdc(ft->gap); /* gap size */
444 out_fdc(ft->datalen); /* data length */
445 fd_state = 2;
446 /* XXX PARANOIA */
447 untimeout(fd_timeout,2);
448 timeout(fd_timeout,2,hz);
449 break;
450 case 2 : /* IO DONE, post-analyze */
451 untimeout(fd_timeout,2);
452 for(i=0;i<7;i++) {
453 fd_status[i] = in_fdc();
454 }
455 if (fd_status[0]&0xF8) {
456#ifdef FDOTHER
457printf("status0 err %d:",fd_status[0]);
458#endif
459 goto retry;
460 }
461/*
462 if (fd_status[1]){
463 printf("status1 err %d:",fd_status[0]);
464 goto retry;
465 }
466 if (fd_status[2]){
467 printf("status2 err %d:",fd_status[0]);
468 goto retry;
469 }
470*/
471 /* All OK */
472 if (!kernel_space(bp->b_un.b_addr+fd_skip)) {
473 /* RAW transfer */
474 if (read) bcopy(dma_bounce[fd_dmachan]->b_un.b_addr,
475 bp->b_un.b_addr+fd_skip, FDBLK);
476 }
477 fd_skip += FDBLK;
478 if (fd_skip >= bp->b_bcount) {
479#ifdef FDTEST
480printf("DONE %d|", bp->b_blkno);
481#endif
482 /* ALL DONE */
483 fd_skip = 0;
484 bp->b_resid = 0;
485 dp->b_actf = bp->av_forw;
486 biodone(bp);
487 nextstate(dp);
488
489 } else {
490#ifdef FDDEBUG
491printf("next|");
492#endif
493 /* set up next transfer */
494 blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
495 + fd_skip/FDBLK;
496 fd_state = 1;
497 bp->b_cylin = (blknum / (ft->sectrac * 2));
498 if (bp->b_cylin != fd_track) {
499#ifdef FDTEST
500printf("Seek|");
501#endif
502 /* SEEK Necessary */
503 out_fdc(15); /* Seek function */
504 out_fdc(fd_drive);/* Drive number */
505 out_fdc(bp->b_cylin * dp->b_step);
506 break;
507 } else fdintr(0);
508 }
509 break;
510 case 3:
511#ifdef FDOTHER
512printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
513#endif
514 /* Seek necessary */
515 out_fdc(15); /* Seek function */
516 out_fdc(fd_drive);/* Drive number */
517 out_fdc(bp->b_cylin * dp->b_step);
518 fd_state = 1;
519 break;
520 case 4:
521 out_fdc(3); /* specify command */
522 out_fdc(0xDF);
523 out_fdc(2);
524 out_fdc(7); /* Recalibrate Function */
525 out_fdc(fd_drive);
526 fd_state = 3;
527 break;
528 case 5:
529#ifdef FDOTHER
530 printf("**RESET**\n");
531#endif
532 /* Try a reset, keep motor on */
533 set_motor(fd_drive,1);
534 set_motor(fd_drive,0);
535 outb(fdc+fdctl,ft->trans);
536 fd_retry++;
537 fd_state = 4;
538 break;
539 default:
540 printf("Unexpected FD int->");
541 out_fdc(0x8);
542 i = in_fdc();
543 sec = in_fdc();
544 printf("ST0 = %X, PCN = %X\n",i,sec);
545 out_fdc(0x4A);
546 out_fdc(fd_drive);
547 for(i=0;i<7;i++) {
548 fd_status[i] = in_fdc();
549 }
550 printf("intr status :%X %X %X %X %X %X %X ",
551 fd_status[0], fd_status[1], fd_status[2], fd_status[3],
552 fd_status[4], fd_status[5], fd_status[6] );
553 break;
554 }
555 return;
556retry:
557 switch(fd_retry) {
558 case 0: case 1:
559 case 2: case 3:
560 break;
561 case 4:
562 fd_retry++;
563 fd_state = 5;
564 fdintr(0);
565 return;
566 case 5: case 6: case 7:
567 break;
568 default:
569 printf("FD err %X %X %X %X %X %X %X\n",
570 fd_status[0], fd_status[1], fd_status[2], fd_status[3],
571 fd_status[4], fd_status[5], fd_status[6] );
572 badtrans(dp,bp);
573 return;
574 }
575 fd_state = 1;
576 fd_retry++;
577 fdintr(0);
578}
579
580badtrans(dp,bp)
581struct buf *dp,*bp;
582{
583
584 bp->b_flags |= B_ERROR;
585 bp->b_error = EIO;
586 bp->b_resid = bp->b_bcount - fd_skip;
587 dp->b_actf = bp->av_forw;
588 fd_skip = 0;
589 biodone(bp);
590 nextstate(dp);
591
592}
593
594/*
595 nextstate : After a transfer is done, continue processing
596 requests on the current drive queue. If empty, go to
597 the other drives queue. If that is empty too, timeout
598 to turn off the current drive in 5 seconds, and go
599 to state 0 (not expecting any interrupts).
600*/
601
602nextstate(dp)
603struct buf *dp;
604{
605 struct buf *dpother;
606
607 dpother = &fd_unit[fd_drive ? 0 : 1].head;
608 if (dp->b_actf) fdstart(fd_drive);
609 else if (dpother->b_actf) {
610#ifdef FDTEST
611printf("switch|");
612#endif
613 untimeout(fd_turnoff,fd_drive);
614 timeout(fd_turnoff,fd_drive,5*hz);
615 fd_drive = 1 - fd_drive;
616 dp->b_active = 0;
617 dpother->b_active = 1;
618 fdstart(fd_drive);
619 } else {
620#ifdef FDTEST
621printf("off|");
622#endif
623 untimeout(fd_turnoff,fd_drive);
624 timeout(fd_turnoff,fd_drive,5*hz);
625 fd_state = 0;
626 dp->b_active = 0;
627 }
628}
629#endif