8 static volatile BOOL done = FALSE;
9 static BOOL dchange = FALSE;
10 static BOOL motor = FALSE;
12 static volatile int tmout = 0;
13 static BYTE status[7] = { 0 };
14 static BYTE statsz = 0;
16 static BYTE fdc_track = 0xff;
17 static DrvGeom geometry = { DG144_HEADS,DG144_TRACKS,DG144_SPT };
19 unsigned long tbaddr = 0x80000L; /* physical address of track buffer located below 1M */
22 extern void floppy_ISR();
25 void sendbyte(int byte);
27 void floppy_handler(struct regs *r);
29 BOOL waitfdc(BOOL sensei);
30 BOOL fdc_rw(int block,BYTE *blockbuff,BOOL read,unsigned long nosectors);
32 /* helper functions */
34 void floppy_detect(void)
38 char *drive_type[5] = { "no floppy drive", "360kb 5.25in floppy drive", "1.2mb 5.25in floppy drive", "720kb3.5in", "1.44mb 3.5in", "2.88mb 3.5in"};
43 kprintf("Floppy drive A: %s\n",drive_type[a]);
44 kprintf("Floppy drive B: %s\n",drive_type[b]);
48 /* This is the IRQ6 handler */
49 void floppy_handler(struct regs *r)
51 /* signal operation finished */
54 //outportb(0x20,0x20);
58 void floppy_install(void)
60 irq_install_handler(6, floppy_handler);
63 void init_floppy(void)
67 /* allocate track buffer (must be located below 1M) */
68 /* see above for address assignment, floppy buffer is at 0x80000) */
69 /* install IRQ6 handler */
75 /* get floppy controller version */
76 sendbyte(CMD_VERSION);
80 kprintf("NEC765 controller found\n");
82 kprintf("Enhanced controller found\n");
85 /* sendbyte() routine from intel manual */
86 void sendbyte(int byte)
90 for (tmo = 0;tmo < 128;tmo++)
92 msr = inportb(FDC_MSR);
93 if ((msr & 0xc0) == 0x80)
95 outportb(FDC_DATA,byte);
98 inportb(0x80); /* delay */
102 /* getbyte() routine from intel manual */
108 for (tmo = 0;tmo < 128;tmo++)
110 msr = inportb(FDC_MSR);
111 if ((msr & 0xd0) == 0xd0)
113 return inportb(FDC_DATA);
115 inportb(0x80); /* delay */
117 return -1; /* read timeout */
120 /* this waits for FDC command to complete */
121 BOOL waitfdc(BOOL sensei)
124 /* wait for IRQ6 handler to signal command finished */
125 while (!done && tmout)
128 /* read in command result bytes */
130 while ((statsz < 7) && (inportb(FDC_MSR) & 0x10))
132 status[statsz++] = getbyte();
137 /* send a "sense interrupt status" command */
138 sendbyte(CMD_SENSEI);
140 fdc_track = getbyte();
147 if (inportb(FDC_DIR) & 0x80)
157 /* This is the timer (int 1ch) handler */
160 if (tmout) --tmout; /* bump timeout */
163 else if (!mtick && motor)
165 outportb(FDC_DOR,0x0c); /* turn off floppy motor */
171 * converts linear block address to head/track/sector
173 * blocks are numbered 0..heads*tracks*spt-1
174 * blocks 0..spt-1 are serviced by head #0
175 * blocks spt..spt*2-1 are serviced by head 1
177 * WARNING: garbage in == garbage out
179 void block2hts(int block,int *head,int *track,int *sector)
181 *head = (block % (geometry.spt * geometry.heads)) / (geometry.spt);
182 *track = block / (geometry.spt * geometry.heads);
183 *sector = block % geometry.spt + 1;
186 /**** disk operations ****/
188 /* this gets the FDC to a known state */
191 /* stop the motor and disable IRQ*/
197 /* program data rate (500K/s) */
200 /* re-enable interrupts */
201 outportb(FDC_DOR,0x0c);
203 /* resetting triggered an interrupt - handle it */
213 /* specify drive timings (got these off the BIOS) */
215 sendbyte(CMD_SPECIFY);
216 sendbyte(0xdf); /* SRT = 3ms, HUT = 240ms */
217 sendbyte(0x02); /* HLT = 16ms, ND = 0 */
218 /* clear "disk change" status */
219 //kprintf("RESET|SEEK\n");
226 /* this returns whether there was a disk change */
227 BOOL diskchange(void)
232 /* this turns the motor on */
237 mtick = -1; /* stop motor kill countdown */
238 outportb(FDC_DOR,0x1c);
240 timer_wait(9); /* delay 500ms for motor to spin up */
245 /* this turns the motor off */
250 // mtick = 13500; /* start motor kill countdown: 36 ticks ~ 2s */
252 outportb(FDC_DOR,0x0c);
259 /* recalibrate the drive */
260 void recalibrate(void)
262 /* turn the motor on */
265 /* send actual command bytes */
268 /* wait until seek finished */
271 /* turn the motor off */
280 if (fdc_track == track) /* already there? */
284 /* send actual command bytes */
288 /* wait until seek finished */
291 kprintf("FLOPPY | SEEK | TIMEOUT\n");
292 return FALSE; /* timeout! */
294 /* now let head settle for 15ms */
297 /* check that seek worked */
298 if ((sr0 != 0x20) || (fdc_track != track))
300 if (sr0 != 0x20) kprintf("FLOPPY | SEEK | SR0!=0x20\n");
301 if (fdc_track != track) kprintf("FLOPPY | SEEK | FDC_TRACK!=TRACK\n");
308 /* checks drive geometry - call this after any disk change */
309 BOOL log_disk(DrvGeom *g)
311 /* get drive in a known status before we do anything */
314 /* assume disk is 1.68M and try and read block #21 on first track */
315 geometry.heads = DG168_HEADS;
316 geometry.tracks = DG168_TRACKS;
317 geometry.spt = DG168_SPT;
319 if (read_block(20,NULL,1))
321 /* disk is a 1.68M disk */
324 g->heads = geometry.heads;
325 g->tracks = geometry.tracks;
326 g->spt = geometry.spt;
331 /* OK, not 1.68M - try again for 1.44M reading block #18 on first track */
332 geometry.heads = DG144_HEADS;
333 geometry.tracks = DG144_TRACKS;
334 geometry.spt = DG144_SPT;
336 if (read_block(17,NULL,1))
338 /* disk is a 1.44M disk */
341 g->heads = geometry.heads;
342 g->tracks = geometry.tracks;
343 g->spt = geometry.spt;
348 /* it's not 1.44M or 1.68M - we don't support it */
352 /* read block (blockbuff is 512 byte buffer) */
353 BOOL read_block(int block,BYTE *blockbuff, unsigned long nosectors)
355 int track=0, sector=0, head=0, track2=0, result=0, loop=0;
357 //The FDC can read multiple sides at once but not multiple tracks
358 //kprintf("READ 1\n");
359 block2hts(block, &head, &track, §or);
360 block2hts(block+nosectors, &head, &track2, §or);
364 for(loop=0; (unsigned)loop<nosectors; loop++)
365 result = fdc_rw(block+loop, blockbuff+(loop*512), TRUE, 1);
368 // kprintf("FDC_RW\n");
369 return fdc_rw(block,blockbuff,TRUE,nosectors);
372 /* write block (blockbuff is 512 byte buffer) */
373 BOOL write_block(int block,BYTE *blockbuff, unsigned long nosectors)
375 return fdc_rw(block,blockbuff,FALSE, nosectors);
379 * since reads and writes differ only by a few lines, this handles both. This
380 * function is called by read_block() and write_block()
382 BOOL fdc_rw(int block,BYTE *blockbuff,BOOL read,unsigned long nosectors)
384 int head,track,sector,tries, copycount = 0;
385 unsigned char *p_tbaddr = (unsigned char *)0x80000;
386 unsigned char *p_blockbuff = blockbuff;
388 /* convert logical address into physical address */
389 block2hts(block,&head,&track,§or);
391 /* spin up the disk */
392 kprintf("FDC_RW|MOTORON\n");
395 if (!read && blockbuff)
397 /* copy data from data buffer into track buffer */
398 for(copycount=0; (unsigned)copycount<(nosectors*512); copycount++)
400 *p_tbaddr = *p_blockbuff;
405 kprintf("FDC_RW|COPY1\n");
406 for (tries = 0;tries < 3;tries++)
409 kprintf("FDC_RW|RIGHT\n");
410 /* move head to right track */
414 //kprintf("FLOPPY | CANNOT SEEK\n");
418 /* check for diskchange */
419 kprintf("FDC_RW|DISKCHANGE\n");
420 if (inportb(FDC_DIR) & 0x80)
423 seek(1); /* clear "disk change" status */
426 //kprintf("FLOPPY | DISK CHANGE STATUS => repeat\n");
427 return fdc_rw(block, blockbuff, read, nosectors);
431 kprintf("FDC_RW|RATE\n");
432 /* program data rate (500K/s) */
438 kprintf("FDC_RW|DMA\n");
439 dma_xfer(2,tbaddr,nosectors*512,FALSE);
444 dma_xfer(2,tbaddr,nosectors*512,TRUE);
451 sendbyte(2); /* 512 bytes/sector */
452 sendbyte(geometry.spt);
453 if (geometry.spt == DG144_SPT)
454 sendbyte(DG144_GAP3RW); /* gap 3 size for 1.44M read/write */
456 sendbyte(DG168_GAP3RW); /* gap 3 size for 1.68M read/write */
457 sendbyte(0xff); /* DTL = unused */
459 kprintf("FDC_RW|STEP1\n");
460 /* wait for command completion */
461 /* read/write don't need "sense interrupt status" */
464 kprintf("Timed out, trying operation again after reset()\n");
466 return fdc_rw(block, blockbuff, read, nosectors);
468 kprintf("FDC_RW|%d\n",(status[0] & 0xc0));
469 if ((status[0] & 0xc0) == 0){ break;} /* worked! outta here! */
471 kprintf("Try %d error => read from floppy\n",tries);
472 recalibrate(); /* oops, try again... */
477 //kprintf("FDC_RW|MOTOROFF\n");
480 if (read && blockbuff)
482 /* copy data from track buffer into data buffer */
483 //kprintf("FDC_RW|OK\n");
484 p_blockbuff = blockbuff;
485 p_tbaddr = (unsigned char *) 0x80000;
486 for(copycount=0; (unsigned)copycount<(nosectors*512); copycount++)
488 *p_blockbuff = *p_tbaddr;
493 //else{kprintf("FLOPPY | ERROR\n");}
497 /* this formats a track, given a certain geometry */
498 BOOL format_track(BYTE track,DrvGeom *g)
500 int i,h,r,r_id,split;
502 unsigned char *p_tbaddr = (unsigned char *)0x8000;
503 unsigned int copycount = 0;
506 if (g->spt != DG144_SPT && g->spt != DG168_SPT)
509 /* spin up the disk */
512 /* program data rate (500K/s) */
515 seek(track); /* seek to track */
517 /* precalc some constants for interleave calculation */
519 if (g->spt & 1) split++;
520 for (h = 0;h < g->heads;h++)
522 /* for each head... */
523 /* check for diskchange */
524 if (inportb(FDC_DIR) & 0x80)
527 seek(1); /* clear "disk change" status */
533 i = 0; /* reset buffer index */
534 for (r = 0;r < g->spt;r++)
536 /* for each sector... */
538 /* calculate 1:2 interleave (seems optimal in my system) */
540 if (r & 1) r_id += split;
542 /* add some head skew (2 sectors should be enough) */
546 if (r_id < 1) r_id += g->spt;
548 /* add some track skew (1/2 a revolution) */
552 if (r_id < 1) r_id += g->spt;
555 /**** interleave now calculated - sector ID is stored in r_id ****/
557 /* fill in sector ID's */
558 tmpbuff[i++] = track;
564 /* copy sector ID's to track buffer */
565 for(copycount = 0; copycount<(unsigned)i; copycount++)
567 *p_tbaddr = tmpbuff[copycount];
570 //movedata(_my_ds(),(long)tmpbuff,_dos_ds,tbaddr,i);
573 dma_xfer(2,tbaddr,i,TRUE);
575 /* prepare "format track" command */
576 sendbyte(CMD_FORMAT);
580 if (g->spt == DG144_SPT)
581 sendbyte(DG144_GAP3FMT); /* gap3 size for 1.44M format */
583 sendbyte(DG168_GAP3FMT); /* gap3 size for 1.68M format */
584 sendbyte(0); /* filler byte */
586 /* wait for command to finish */
589 if (status[0] & 0xc0)
599 //void floppy_install()
601 /* Installs 'timer_handler' to IRQ0 */
602 //irq_install_handler(6, floppy_handler);
608 " pusha \n" /* Save all registers */
609 " pushw %ds \n" /* Set up the data segment */
611 " pushw %ss \n" /* Note that ss is always valid */
619 " popw %ds \n" /* Restore registers */
621 " iret \n" /* Exit interrupt */
625 ".globl floppy_ISR \n"
627 " pusha \n" /* Save all registers */
628 " pushw %ds \n" /* Set up the data segment */
630 " pushw %ss \n" /* Note that ss is always valid */
638 " popw %ds \n" /* Restore registers */
640 " iret \n" /* Exit interrupt */