|
|
1.1 root 1: /*
2: * This is a driver for the IBM AT (286 & up) floppy, using interrupts and DMA
3: * on the NEC 756 floppy chip.
4: * Handles single/double/quad density drives, 8/9/15/18 sectors per track.
5: *
6: * Minor device assignments: xxuuhkkk
7: * uu - unit = 0/1/2/3
8: * kkk - kind, struct fdata infra.
9: * h - alternating head rather than side by side
10: *
11: */
12:
13: #include <sys/coherent.h>
14: #include <sys/buf.h>
15: #include <sys/con.h>
16: #include <sys/stat.h>
17: #include <errno.h>
18: #include <sys/timeout.h>
19: #include <sys/fdioctl.h>
20: #include <sys/sched.h>
21: #include <sys/dmac.h>
22: #include <sys/devices.h>
23:
24: #ifdef _I386
25: #include <sys/reg.h>
26: #else
27: #include <sys/i8086.h>
28: #endif
29:
30: #define BIT(n) (1 << (n))
31:
32: #include <sys/abios.h>
33: static short intimeout;
34: static request_block_fl fl_rb;
35:
36: void floppy_function();
37: void floppy_functionb();
38: static void fl_drive_off();
39:
40: /*
41: * Patchable parameters (default to IBM PC/XT values).
42: */
43:
44: int fl_srt = 0xC; /* Floppy seek step rate, in unit 2 millisec */
45: /* NOT DIRECTLY ENCODED */
46: /* COMPAQ wants 0xD */
47: int fl_hlt = 1; /* Floppy head load time, in unit 4 millisec */
48: int fl_hut = 0xF; /* Floppy head unload time, in unit 32 millisec */
49:
50: int flload();
51: int flunload();
52: void flreset();
53: int flopen();
54: int flblock();
55: int flread();
56: int flwrite();
57: int flioctl();
58: static void flstart();
59: static void flintr();
60: static void fldone();
61: static void fltimer();
62: static void flstatus();
63: int nulldev();
64: int nonedev();
65:
66: CON flcon = {
67: DFBLK|DFCHR, /* Flags */
68: FL_MAJOR, /* Major index */
69: flopen, /* Open */
70: nulldev, /* Close */
71: flblock, /* Block */
72: flread, /* Read */
73: flwrite, /* Write */
74: flioctl, /* Ioctl */
75: nulldev, /* Powerfail */
76: fltimer, /* Timeout */
77: flload, /* Load */
78: flunload /* Unload */
79: };
80:
81: /*
82: * Driver States.
83: */
84: #ifdef OLD
85: #define SIDLE 0 /* Idle */
86: #define SSEEK 1 /* Need seek */
87: #define SRDWR 2 /* Need read/write command */
88: #define SENDIO 3 /* Need end I/O processing */
89: #define SDELAY 4 /* Delay before next disk operation */
90: #define SHDLY 5 /* Head settling delay before r/w */
91: #define SRESET 6 /* Doing a reset */
92: #else
93: #define SIDLE 0 /* controller idle */
94: #define SRETRY 1 /* seeking */
95: #define SREAD 2 /* reading */
96: #define SWRITE 3 /* writing */
97: #define SRESET 4 /* reseting */
98:
99: extern char *smsg[];
100: #endif
101:
102: #define funit(x) (minor(x)>>4) /* Unit/drive number */
103: #define fkind(x) (0x7) /* Kind of format */
104: /* #define fkind(x) (minor(x)&0x7) Kind of format */
105: #define fhbyh(x) (minor(x)&0x8) /* 0=Side by side, 1=Head by head */
106:
107: static
108: struct fdata {
109: int fd_size; /* Blocks per diskette */
110: int fd_nhds; /* Heads per drive */
111: int fd_trks; /* Tracks per side */
112: int fd_offs; /* Sector base */
113: int fd_nspt; /* Sectors per track */
114: char fd_GPL[4]; /* Controller gap param (indexed by rate) */
115: char fd_N; /* Controller size param */
116: char fd_FGPL; /* Format gap length */
117: } fdata[] = {
118: /* 8 sectors per track, surface by surface seek. */
119: { 320,1,40,0, 8, { 0x00,0x23,0x2A }, 2,0x50 }, /* Single sided */
120: { 640,2,40,0, 8, { 0x00,0x23,0x2A }, 2,0x50 }, /* Double sided */
121: { 1280,2,80,0, 8, { 0x00,0x23,0x2A }, 2,0x50 }, /* Quad density */
122: /* 9 sectors per track, surface by surface seek. */
123: { 360,1,40,0, 9, { 0x00,0x23,0x2A }, 2,0x50 }, /* Single sided */
124: { 720,2,40,0, 9, { 0x00,0x23,0x2A }, 2,0x50 }, /* Double sided */
125: { 1440,2,80,0, 9, { 0x00,0x23,0x2A }, 2,0x50 }, /* Quad density */
126: /* 15 sectors per track, surface by surface seek. */
127: { 2400,2,80,0,15, { 0x1B,0x00,0x00 }, 2,0x54 }, /* High capacity */
128: /* 18 sectors per track, surface by surface seek. */
129: { 2880,2,80,0,18, { 0x1B,0x00,0x00 }, 2,0x54 } /* 1.44 3.5" */
130: };
131:
132:
133: static
134: struct fl {
135: BUF *fl_actf; /* Queue, forward */
136: BUF *fl_actl; /* Queue, backward */
137: paddr_t fl_addr; /* Address */
138: int fl_nsec; /* # of sectors */
139: int fl_secn; /* Current sector */
140: struct fdata fl_fd; /* Disk kind data */
141: int fl_fcyl; /* Floppy cylinder # */
142: char fl_incal[4]; /* Disk in cal flags */
143: char fl_ndsk; /* # of 5 1/4" drives */
144: char fl_unit; /* Unit # */
145: char fl_mask; /* Handy unit mask */
146: char fl_hbyh; /* 0/1 = Side by side/Head by head */
147: char fl_nerr; /* Error count */
148: int fl_ncmdstat; /* Number of cmd status bytes recvd */
149: char fl_cmdstat[8]; /* Command Status buffer */
150: int fl_nintstat; /* Number of intr status bytes recvd */
151: char fl_intstat[4]; /* Interrupt Status buffer */
152: int fl_fsec; /* Floppy sector # */
153: int fl_head; /* Floppy head */
154: char fl_init; /* FDC init done flag */
155: char fl_state; /* Processing state */
156: char fl_mstatus; /* Motor status */
157: char fl_time[4]; /* Motor timeout */
158: char fl_rate; /* Data rate: 500,300,250,?? kbps */
159: char fl_type[4]; /* Type of drive: 2 = HiCap */
160: int fl_wflag; /* Write operation */
161: int fl_recov; /* Recovery initiated */
162: } fl;
163:
164:
165: static BUF flbuf;
166: static TIM fltim;
167: static TIM flrstlck;
168:
169: /*
170: * The load routine asks the
171: * switches how many drives are present
172: * in the machine, and sets up the field
173: * in the floppy database. It also grabs
174: * the level 6 interrupt vector.
175: */
176: static
177: flload()
178: {
179: register int eflag;
180: register int s;
181:
182: init_abios();
183:
184: /*
185: * Read floppy equipment byte from CMOS ram
186: * drive 0 is in high nibble, drive 1 is in low nibble.
187: */
188: outb( 0x70, 0x10 );
189: /* delay */
190: eflag = inb( 0x71 );
191:
192: /*
193: * Flag hardware as an IBM AT if neither equipment byte nibble is
194: * greater than 4 (since 5 through 15 are reserved nibble values - see
195: * IBM AT Technical Reference manual, page 1-50). Note that this
196: * relies on the fact that in the XT, this byte will "float" high.
197: * NOTE: 1.44 Mbyte 3.5 inch drives are type 4
198: */
199: if ( (eflag & 0x88) == 0 ) {
200:
201: /*
202: * Reinitialize patchable parameters for IBM AT.
203: */
204: fl_srt = 0xD; /* Floppy seek step rate, in unit 2 ms */
205: /* NOT DIRECTLY ENCODED */
206: fl_hlt = 25; /* Floppy head load time, in unit 4 ms */
207:
208: /*
209: * Define AT drive information.
210: */
211: fl.fl_type[0] = eflag >> 4;
212: fl.fl_type[1] = eflag & 15;
213: fl.fl_rate = 1; /* Must not be 2 */
214:
215: /*
216: * Determine number of AT floppy drives.
217: */
218: if ( eflag & 0xF0 ) {
219: fl.fl_ndsk++;
220: if ( eflag & 0x0F )
221: fl.fl_ndsk++;
222: }
223: } else {
224: /*
225: * Define XT drive information.
226: */
227: eflag = int11();
228: fl.fl_rate = 2;
229: if ( eflag & 1 )
230: fl.fl_ndsk = ((eflag >> 6) & 0x03) + 1;
231: }
232:
233: fl.fl_actf = NULL; /* Start up with this Null to avoid phony calls */
234:
235: if ( fl.fl_ndsk ) {
236:
237: s = sphi();
238: setivec(6, &flintr);
239: spl( s );
240:
241: fl_rb.length = 0x51;
242: fl_rb.logical_id = 3;
243: fl_rb.unit = 0;
244: fl_rb.function = 3;
245: fl_rb.reserved = 0L;
246: fl_rb.ret_code = 0xffff;
247: d1_func(&fl_rb, START_P);
248: if (fl_rb.ret_code)
249: printf("\nfloppy function 3 returned = %d\n",
250: fl_rb.ret_code);
251: flreset();
252: }
253: }
254:
255: /*
256: * Release resources.
257: */
258: flunload()
259: {
260: /*
261: * Clear interrupt vector.
262: */
263: if ( fl.fl_ndsk )
264: clrivec(6);
265:
266: /*
267: * Cancel timed function.
268: */
269: timeout( &fltim, 0, NULL, NULL );
270:
271: /*
272: * Cancel periodic [1 second] invocation.
273: */
274: drvl[FL_MAJOR].d_time = 0;
275:
276: /*
277: * Turn motors off.
278: */
279: /* outb(FDCDOR, DORNMR | DORIEN );*/
280: }
281:
282:
283: /**
284: * void
285: * flreset() -- reset floppy disk controller.
286: */
287: static void
288: flreset()
289: {
290: register int s;
291:
292: fl.fl_state = SRESET;
293: fl_rb.length = 0x51;
294: fl_rb.logical_id = 3;
295: fl_rb.unit = 0;
296: fl_rb.function = 5;
297: fl_rb.reserved = 0L;
298: fl_rb.ret_code = 0xffff;
299: fl_rb.vars.f5.reserved = 0;
300:
301: floppy_function(START_P);
302:
303: while ((fl_rb.ret_code == 1) || (fl_rb.ret_code == 2))
304: {
305: long i;
306: for (i=0;i<50000L;i++)
307: ;
308: }
309:
310:
311: fl.fl_state = SIDLE;
312: if (fl_rb.ret_code != 0)
313: printf("FL: reset failed - %u\n", fl_rb.ret_code);
314: }
315:
316:
317: /*
318: * The open routine screens out
319: * opens of illegal minor devices and
320: * performs the NEC specify command if
321: * this is the very first floppy disk
322: * open call.
323: */
324:
325: static
326: flopen( dev, mode )
327: dev_t dev;
328: int mode;
329:
330: {
331: /*
332: * Validate existence and data rate [Gap length != 0].
333: */
334: if ( funit(dev) >= fl.fl_ndsk )
335: {
336: u.u_error = ENXIO;
337: return;
338: }
339: }
340:
341: /*
342: * The read routine just calls
343: * off to the common raw I/O processing
344: * code, using a static buffer header in
345: * the driver.
346: */
347:
348: static
349: flread( dev, iop )
350: dev_t dev;
351: IO *iop;
352: {
353: ioreq(&flbuf, iop, dev, BREAD);
354: }
355:
356: /*
357: * The write routine is just like the
358: * read routine, except that the function code
359: * is write instead of read.
360: */
361:
362: static
363: flwrite( dev, iop )
364:
365: dev_t dev;
366: IO *iop;
367:
368: {
369: ioreq(&flbuf, iop, dev, BWRITE);
370: }
371:
372: /*
373: * The only valid command is to format a track.
374: * The parameter block contains the header records supplied to the controller.
375: */
376:
377: static
378: flioctl( dev, com, par )
379:
380: dev_t dev;
381: int com;
382: char *par;
383: {
384: register unsigned s;
385: register struct fdata *fdp;
386: unsigned hd, cyl;
387:
388: if (com != FDFORMAT) {
389: u.u_error = EINVAL;
390: return;
391: }
392:
393: fdp = &fdata[ fkind(dev) ];
394: cyl = getubd(par);
395: hd = getubd(par+1);
396:
397: if (hd > 1 || cyl >= fdp->fd_trks) {
398: u.u_error = EINVAL;
399: return;
400: }
401:
402: fl_rb.length = 0x51;
403: fl_rb.logical_id = 3;
404: fl_rb.unit = 0;
405: fl_rb.reserved = 0L;
406: fl_rb.ret_code = 0xffff;
407:
408: /* Note that the items below are set up the same way for a
409: * read or a write. */
410: fl_rb.vars.f8.reserved = 0;
411: fl_rb.vars.f8.reserved1 = 0L;
412: fl_rb.vars.f8.dptr = fl.fl_addr;
413: fl_rb.vars.f8.reserved2 = 0;
414: fl_rb.vars.f8.cylinder = fl.fl_fcyl;
415: fl_rb.vars.f8.head = fl.fl_head;
416: fl_rb.vars.f8.sector = fl.fl_secn;
417: fl_rb.vars.f8.sectors_read = fl.fl_nsec;
418:
419: if (fl.fl_actf->b_req == BWRITE) {
420: fl_rb.function = 9;
421: fl.fl_state = SWRITE;
422: }
423:
424: /*
425: * The following may need some explanation.
426: * dmareq will:
427: * claim the buffer,
428: * bounds check the parameter buffer,
429: * lock the parameter buffer in memory,
430: * convert io_seek to b_bno,
431: * dispatch the request,
432: * wait for completion,
433: * and unlock the parameter buffer.
434: * The b_bno is reconverted to hd, cyl in flfsm.
435: */
436:
437: s = fhbyh(dev) ? (cyl * fdp->fd_nhds + hd) : (hd * fdp->fd_trks + cyl);
438: s *= fdp->fd_nspt;
439: u.u_io.io_seek = ((long)s) * BSIZE;
440: #ifdef _I386
441: u.u_io.io.vbase = par;
442: #else
443: u.u_io.io_base = par;
444: #endif
445: u.u_io.io_ioc = fdp->fd_nspt * 4;
446: dmareq(&flbuf, &u.u_io, dev, FDFORMAT);
447: }
448:
449: /*
450: * Start up block I/O on a
451: * buffer. Check that the block number
452: * is not out of range, given the style of
453: * the disk. Put the buffer header into the
454: * device queue. Start up the disk if the
455: * device is idle.
456: */
457:
458: static
459: flblock( bp )
460:
461: register BUF *bp;
462: {
463: register unsigned bno;
464:
465: intimeout = 0;
466:
467: bno = bp->b_bno + (bp->b_count >> 9) - 1;
468: if ((unsigned)bp->b_bno > fdata[ fkind(bp->b_dev) ].fd_size) {
469: bp->b_flag |= BFERR;
470: bdone(bp);
471: return;
472: }
473:
474: if (bp->b_req != FDFORMAT && bno>=fdata[ fkind(bp->b_dev) ].fd_size) {
475: bp->b_resid = bp->b_count;
476: if (bp->b_flag & BFRAW)
477: bp->b_flag |= BFERR;
478: bdone(bp); /* return w/ b_resid != 0 */
479: return;
480: }
481:
482: if ((bp->b_count&0x1FF) != 0) {
483: if (bp->b_req != FDFORMAT) {
484: bp->b_flag |= BFERR;
485: bdone(bp);
486: return;
487: }
488: }
489:
490: bp->b_actf = NULL;
491:
492: if (fl.fl_actf == NULL)
493: fl.fl_actf = bp;
494: else
495: fl.fl_actl->b_actf = bp;
496:
497: fl.fl_actl = bp;
498:
499: if (fl.fl_state == SIDLE)
500: {
501: drvl[FL_MAJOR].d_time = 0;
502: if (fldequeue())
503: flstart();
504: }
505: }
506:
507: /**
508: *
509: * int
510: * fldequeue() - obtain next disk read/write operation
511: *
512: * Action: Pull some work from the disk queue.
513: *
514: * Return: 0 = no work.
515: * * = work to do.
516: */
517: static int
518: fldequeue()
519: {
520: register BUF * bp = fl.fl_actf;
521: register struct fdata *dp;
522:
523: if (bp == NULL)
524: return (0);
525:
526: dp = &fdata[fkind(bp->b_dev)];
527: fl.fl_secn = (bp->b_bno % dp->fd_nspt) + 1;
528: fl.fl_head = bp->b_bno / dp->fd_nspt;
529: fl.fl_fcyl = fl.fl_head / dp->fd_nhds;
530: fl.fl_head = fl.fl_head % dp->fd_nhds;
531:
532: fl.fl_nsec = bp->b_count / BSIZE;
533:
534: if (bp->b_faddr == 0L)
535: {
536: printf("FL: called with a null address for b_faddr, bno=%d\n"
537: ,bp->b_bno);
538: fl.fl_actf = bp->b_actf;
539: return 0;
540: }
541: else
542: fl.fl_addr = vtop(bp->b_faddr);
543:
544: return (1);
545: }
546:
547:
548: /**
549: *
550: * void
551: * flstart() - start or restart next disk read/write operation.
552: *
553: * Action: Initiate disk read/write operation.
554: **/
555: static void
556: flstart()
557: {
558: fl_rb.length = 0x51;
559: fl_rb.logical_id = 3;
560: fl_rb.unit = 0;
561: fl_rb.reserved = 0L;
562: fl_rb.ret_code = 0xffff;
563:
564: /* Note that the items below are set up the same way for a
565: * read or a write. */
566: fl_rb.vars.f8.reserved = 0;
567: fl_rb.vars.f8.reserved1 = 0L;
568: fl_rb.vars.f8.dptr = fl.fl_addr;
569: fl_rb.vars.f8.reserved2 = 0;
570: fl_rb.vars.f8.cylinder = fl.fl_fcyl;
571: fl_rb.vars.f8.head = fl.fl_head;
572: fl_rb.vars.f8.sector = fl.fl_secn;
573: fl_rb.vars.f8.sectors_read = fl.fl_nsec;
574:
575: if (fl.fl_actf->b_req == BWRITE) {
576: fl_rb.function = 9;
577: fl.fl_state = SWRITE;
578: }
579: else {
580: fl_rb.function = 8;
581: fl.fl_state = SREAD;
582: }
583:
584: floppy_function(START_P);
585: }
586:
587: /**
588: *
589: * void
590: * flintr() - Interrupt routine.
591: *
592: */
593: static void
594: flintr()
595: {
596: d1_func(&fl_rb, INTERRUPT_P);
597: defer(floppy_functionb, INTERRUPT_P);
598: }
599:
600: /**
601: *
602: * int
603: * flerror()
604: *
605: * Action: Check for drive error.
606: * If found, increment error count and report it.
607: *
608: * Return: 0 = No error found.
609: * 1 = Error occurred.
610: */
611: static int
612: flerror()
613: {
614: register BUF * bp = fl.fl_actf;
615:
616: if (fl_rb.ret_code <= 2)
617: return 0; /* For now, do nothing */
618: else
619: {
620: #if 0
621: printf("fl%d%c: bno=%U head=%u cyl=%u error=%x",
622: fl.fl_drv,
623: (bp->b_dev & SDEV) ? 'x' : fl.fl_partn % NPARTN + 'a',
624: (bp->b_count/BSIZE) + bp->b_bno
625: + fl.fl_caching - fl.fl_nsec,
626: fl.fl_head, fl.fl_fcyl, fl_rb.ret_code);
627: #endif
628: return fl_rb.ret_code;
629: }
630: }
631:
632: /**
633: *
634: * void
635: * flrecov()
636: *
637: * Action: Attempt recovery.
638: */
639: static void
640: flrecov()
641: {
642: register BUF *bp = fl.fl_actf;
643:
644: switch (fl_rb.ret_code) {
645:
646: case 0x8006: /* Media changed - retry */
647: switch(fl.fl_state)
648: {
649: case SREAD:
650: case SWRITE:
651: flreset();
652: fldequeue();
653: flstart();
654: break;
655: default:
656: break;
657: }
658: break;
659: default: /* Anything else - Give up on block */
660: flstatus();
661: bp->b_flag |= BFERR;
662: fldone();
663: }
664: }
665:
666:
667: /*
668: * Dissassemble the floppy error status for user reference.
669: */
670:
671: static void
672: flstatus()
673: {
674: printf("fd0: head=%u cyl=%u <",
675: fl.fl_head, fl.fl_fcyl );
676:
677: switch (fl_rb.ret_code) {
678: case 0x800d:
679: printf("Not Ready>");
680: break;
681: case 0x9120:
682: printf("Equipment Check>");
683: break;
684: case 0x9102:
685: printf("Missing Address Mark>");
686: break;
687: case 0x8003:
688: printf("Write Protected>");
689: break;
690: case 0x9108:
691: printf("Overrun>");
692: break;
693: case 0x9110:
694: printf("Bad Data CRC>");
695: break;
696: defualt:
697: printf("Error 0x%x>", fl_rb.ret_code);
698: }
699:
700: printf("\n");
701: }
702:
703: /**
704: *
705: * void
706: * fldone()
707: *
708: * Action: Release current i/o buffer to the O/S.
709: */
710: static void
711: fldone()
712: {
713: register BUF * bp = fl.fl_actf;
714:
715: drvl[FL_MAJOR].d_time = 0;
716: fl.fl_state = SIDLE;
717: bdone(bp);
718:
719: if (bp != NULL)
720: fl.fl_actf = bp->b_actf;
721: else
722: fl.fl_actf = NULL;
723:
724: if (fldequeue())
725: flstart();
726: else if (intimeout == 0) {
727: intimeout = 1; /* Set up to turn off the drive */
728: drvl[FL_MAJOR].d_time = 5;
729: }
730: }
731:
732:
733:
734: void floppy_function(type)
735: int type;
736: {
737: d1_func(&fl_rb, type);
738: floppy_functionb();
739: }
740:
741: static TIM fltim1;
742:
743: void floppy_functionb()
744: {
745: while (fl_rb.ret_code == 2) /* Wait for time */
746: {
747: /*
748: timeout(&fltim1, 1, wakeup, (int)&fltim1);
749: sleep((char *)&fltim1, CVTTOUT, IVTTOUT, SVTTOUT);
750: */
751: long i;
752: for(i=0L; i < fl_rb.vars.f8.wait_time; i+=16)
753: ;
754:
755: d1_func(&fl_rb, INTERRUPT_P);
756: }
757:
758: if (fl_rb.ret_code == 0) /* Finished */
759: {
760: if (fl.fl_state != SRESET)
761: {
762: fl.fl_actf->b_resid = 0;
763: fldone();
764: }
765: }
766: else if (fl_rb.ret_code == 1) /* Wait for int */
767: {
768: #if 0
769: if (fl.fl_state == SRESET) /* Wait for int */
770: {long i;for (i=0;i<500000L;i++);}
771: /*fl.fl_state = SINT; */
772: #endif
773: }
774: else
775: {
776: flrecov();
777:
778: /*
779: printf("FL: error %x in %s.\n", fl_rb.ret_code, smsg[fl.fl_state]);
780: printf("fl: bp->b_bno=%lx, fl.fl_secn=%d, fl.fl_fcyl=%d, fl.fl_h"
781: "ead=%d, fl.fl_nsec=%d\n", fl.fl_actf->b_bno,
782: fl.fl_secn, fl.fl_fcyl, fl.fl_head, fl.fl_nsec);
783: */
784: }
785: }
786:
787: /**
788: *
789: * void
790: * fltimer() - wait for timeout
791: *
792: * Action: If drvl[FL_MAJOR] is greater than zero, decrement it.
793: * If it decrements to zero, call the abios again
794: * or turn off the disktimer it timeout = 1;
795: */
796: static void
797: fltimer()
798: {
799: register int s;
800:
801: s = sphi();
802: if (--drvl[FL_MAJOR].d_time > 0) {
803: spl(s);
804: return;
805: }
806: if (intimeout) {
807: intimeout = 2;
808: defer(fl_drive_off,0);
809: }
810: else
811: floppy_function(INTERRUPT_P);
812: spl(s);
813: }
814:
815:
816: static void
817: fl_drive_off()
818: {
819: fl_rb.logical_id = 3;
820: fl_rb.unit = 0;
821: fl_rb.function = 0xf;
822: fl_rb.reserved = 0L;
823: fl_rb.ret_code = 0xffff;
824: fl_rb.vars.ff.reserved = 0;
825: d1_func(&fl_rb, START_P);
826: if (fl_rb.ret_code)
827: printf("\nfloppy function 0xf returned = %d\n",
828: fl_rb.ret_code);
829: intimeout = 0; /* Drive is now turned off */
830: }
831:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.