finally got ring ptrs right, hope error processing didn't break again!
[unix-history] / usr / src / sys / i386 / isa / fd.c
CommitLineData
32f56e31
BJ
1#include "fd.h"
2#if NFD > 0
7ff33365
DA
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 *
32f56e31 12 * @(#)fd.c 5.2 (Berkeley) %G%
7ff33365
DA
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"
32f56e31
BJ
31#include "machine/isa/device.h"
32#include "machine/isa/fdreg.h"
7ff33365
DA
33#include "icu.h"
34
7ff33365
DA
35#define FDUNIT(s) ((s)&1)
36#define FDTYPE(s) (((s)>>1)&7)
32f56e31 37
7ff33365 38#define b_cylin b_resid
32f56e31 39#define b_step b_resid
7ff33365
DA
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] = {
32f56e31 55 { 18,2,0xFF,0x1B,80,2880,1,0 }, /* 1.44 meg HD 3.5in floppy */
7ff33365
DA
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 */
7ff33365
DA
65 struct buf head; /* Head of buf chain */
66 struct buf rhead; /* Raw head of buf chain */
32f56e31 67 int reset;
7ff33365
DA
68} fd_unit[NFD];
69
32f56e31 70
7ff33365
DA
71extern int hz;
72
73/* state needed for current transfer */
32f56e31
BJ
74static fdc; /* floppy disk controller io base register */
75int fd_dmachan = 2;
7ff33365
DA
76static int fd_skip;
77static int fd_state;
78static int fd_retry;
79static int fd_drive;
32f56e31 80static int fd_track = -1;
7ff33365 81static int fd_status[7];
7ff33365 82
32f56e31
BJ
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[];
7ff33365
DA
88
89/****************************************************************************/
90/* autoconfiguration stuff */
91/****************************************************************************/
92int fdprobe(), fdattach(), fd_turnoff();
93
32f56e31 94struct isa_driver fddriver = {
7ff33365
DA
95 fdprobe, fdattach, "fd",
96};
97
98fdprobe(dev)
32f56e31 99struct isa_device *dev;
7ff33365
DA
100{
101 return 1;
102}
103
104fdattach(dev)
32f56e31
BJ
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;
7ff33365 117{
32f56e31 118 return(2400);
7ff33365
DA
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;
32f56e31 129 int unit, type, s;
7ff33365 130
32f56e31
BJ
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);
7ff33365
DA
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;
32f56e31 143 bp->b_flags |= B_ERROR;
7ff33365
DA
144 goto bad;
145 }
146 /*
147 * Set up block calculations.
148 */
149 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK;
32f56e31 150 nblocks = fd_types[type].size;
7ff33365 151 if (blknum + (bp->b_bcount / FDBLK) > nblocks) {
32f56e31
BJ
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 }
7ff33365
DA
158 goto bad;
159 }
32f56e31 160 bp->b_cylin = blknum / (fd_types[type].sectrac * 2);
7ff33365
DA
161 dp = &fd_unit[unit].head;
162 dp0 = &fd_unit[0].head;
163 dp1 = &fd_unit[1].head;
32f56e31 164 dp->b_step = (fd_types[fd_unit[unit].type].steptrac);
7ff33365
DA
165 s = splbio();
166 disksort(dp, bp);
167 if ((dp0->b_active == 0)&&(dp1->b_active == 0)) {
32f56e31
BJ
168#ifdef FDDEBUG
169printf("T|");
170#endif
7ff33365
DA
171 dp->b_active = 1;
172 fd_drive = unit;
32f56e31 173 fd_track = -1; /* force seek on first xfer */
7ff33365
DA
174 untimeout(fd_turnoff,unit);
175 fdstart(unit); /* start drive if idle */
176 }
177 splx(s);
178 return;
179
180bad:
7ff33365
DA
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;
32f56e31 193 outb(fdc+fdout,unit | (reset ? 0 : 0xC) | (m0 ? 16 : 0) | (m1 ? 32 : 0));
7ff33365
DA
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;
32f56e31
BJ
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);
7ff33365
DA
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 }
32f56e31
BJ
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] );
7ff33365
DA
233}
234
235out_fdc(x)
236int x;
237{
238 int r,errcnt;
239 static int maxcnt = 0;
240 errcnt = 0;
241 do {
32f56e31
BJ
242 r = (inb(fdc+fdsts) & (NE7_DIO|NE7_RQM));
243 if (r== NE7_RQM) break;
244 if (r==(NE7_DIO|NE7_RQM)) {
7ff33365
DA
245 dump_stat(); /* error: direction. eat up output */
246#ifdef FDOTHER
32f56e31 247printf("%X\n",x);
7ff33365
DA
248#endif
249 }
250 /* printf("Error r = %d:",r); */
251 errcnt++;
252 } while (1);
253 if (errcnt > maxcnt) {
254 maxcnt = errcnt;
255#ifdef FDOTHER
32f56e31 256printf("New MAX = %d\n",maxcnt);
7ff33365
DA
257#endif
258 }
32f56e31 259 outb(fdc+fddata,x);
7ff33365
DA
260}
261
262/* see if fdc responding */
263int
264check_fdc()
265{
266 int i;
267 for(i=0;i<100;i++) {
32f56e31 268 if (inb(fdc+fdsts)& NE7_RQM) return 0;
7ff33365
DA
269 }
270 return 1;
271}
272
273/****************************************************************************/
274/* fdopen/fdclose */
275/****************************************************************************/
276fdopen(dev, flags)
277 dev_t dev;
278 int flags;
279{
32f56e31
BJ
280 int unit = FDUNIT(minor(dev));
281 int type = FDTYPE(minor(dev));
7ff33365
DA
282 int s;
283
284 /* check bounds */
285 if (unit >= NFD) return(ENXIO);
286 if (type >= NUMTYPES) return(ENXIO);
32f56e31 287/*
7ff33365 288 if (check_fdc()) return(EBUSY);
32f56e31 289*/
7ff33365
DA
290
291 /* Set proper disk type, only allow one type */
7ff33365
DA
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{
32f56e31 310 int unit = FDUNIT(minor(dev)) ;
7ff33365
DA
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{
32f56e31 319 int unit = FDUNIT(minor(dev)) ;
7ff33365
DA
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
32f56e31
BJ
333#ifdef FDTEST
334printf("st%d|",unit);
335#endif
336 s = splbio();
7ff33365
DA
337 if (!fd_unit[unit].motor) {
338 fd_turnon(unit);
339 /* Wait for 1 sec */
340 timeout(fdstart,unit,hz);
32f56e31
BJ
341 /*DELAY(1000000);*/
342 }else
343 {
344 /* make sure drive is selected as well as on */
345 /*set_motor(unit,0);*/
346
7ff33365
DA
347 dp = &fd_unit[unit].head;
348 bp = dp->b_actf;
349 fd_retry = 0;
32f56e31
BJ
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 }
7ff33365 356 fd_skip = 0;
32f56e31
BJ
357#ifdef FDDEBUG
358printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
359#endif
360 if (bp->b_cylin != fd_track) {
7ff33365
DA
361 /* Seek necessary, never quite sure where head is at! */
362 out_fdc(15); /* Seek function */
363 out_fdc(unit); /* Drive number */
32f56e31
BJ
364 out_fdc(bp->b_cylin * dp->b_step);
365 } else fdintr(0);
7ff33365 366 }
32f56e31 367 splx(s);
7ff33365
DA
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;
32f56e31 400 int read,head,trac,sec,i,s,sectrac,cyl;
7ff33365
DA
401 unsigned long blknum;
402 struct fd_type *ft;
32f56e31
BJ
403
404#ifdef FDTEST
405 printf("state %d, vec %d, dr %d|",fd_state,vec,fd_drive);
406#endif
7ff33365
DA
407
408 dp = &fd_unit[fd_drive].head;
409 bp = dp->b_actf;
410 read = bp->b_flags & B_READ;
32f56e31 411 ft = &fd_types[FDTYPE(bp->b_dev)];
7ff33365
DA
412
413 switch (fd_state) {
414 case 1 : /* SEEK DONE, START DMA */
32f56e31
BJ
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
7ff33365 426 fd_track = bp->b_cylin;
32f56e31 427 at_dma(read,bp->b_un.b_addr+fd_skip,FDBLK, fd_dmachan);
7ff33365
DA
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
32f56e31 457printf("status0 err %d:",fd_status[0]);
7ff33365
DA
458#endif
459 goto retry;
460 }
32f56e31 461/*
7ff33365
DA
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 }
32f56e31 470*/
7ff33365
DA
471 /* All OK */
472 if (!kernel_space(bp->b_un.b_addr+fd_skip)) {
473 /* RAW transfer */
32f56e31
BJ
474 if (read) bcopy(dma_bounce[fd_dmachan]->b_un.b_addr,
475 bp->b_un.b_addr+fd_skip, FDBLK);
7ff33365
DA
476 }
477 fd_skip += FDBLK;
478 if (fd_skip >= bp->b_bcount) {
32f56e31
BJ
479#ifdef FDTEST
480printf("DONE %d|", bp->b_blkno);
481#endif
7ff33365
DA
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 {
32f56e31
BJ
490#ifdef FDDEBUG
491printf("next|");
492#endif
7ff33365
DA
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));
7ff33365 498 if (bp->b_cylin != fd_track) {
32f56e31
BJ
499#ifdef FDTEST
500printf("Seek|");
501#endif
7ff33365
DA
502 /* SEEK Necessary */
503 out_fdc(15); /* Seek function */
504 out_fdc(fd_drive);/* Drive number */
32f56e31 505 out_fdc(bp->b_cylin * dp->b_step);
7ff33365 506 break;
32f56e31 507 } else fdintr(0);
7ff33365
DA
508 }
509 break;
510 case 3:
32f56e31
BJ
511#ifdef FDOTHER
512printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
513#endif
7ff33365
DA
514 /* Seek necessary */
515 out_fdc(15); /* Seek function */
516 out_fdc(fd_drive);/* Drive number */
32f56e31 517 out_fdc(bp->b_cylin * dp->b_step);
7ff33365
DA
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;
32f56e31
BJ
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;
7ff33365 539 default:
7ff33365
DA
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] );
7ff33365
DA
553 break;
554 }
555 return;
556retry:
557 switch(fd_retry) {
558 case 0: case 1:
32f56e31 559 case 2: case 3:
7ff33365 560 break;
32f56e31 561 case 4:
7ff33365 562 fd_retry++;
32f56e31
BJ
563 fd_state = 5;
564 fdintr(0);
7ff33365 565 return;
32f56e31 566 case 5: case 6: case 7:
7ff33365
DA
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++;
32f56e31 577 fdintr(0);
7ff33365
DA
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) {
32f56e31
BJ
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;
7ff33365 616 dp->b_active = 0;
32f56e31
BJ
617 dpother->b_active = 1;
618 fdstart(fd_drive);
7ff33365 619 } else {
32f56e31
BJ
620#ifdef FDTEST
621printf("off|");
622#endif
7ff33365
DA
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}
32f56e31 629#endif