3 * Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@gmail.com)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include <partition.h>
27 #define MAX_DISKCHANGE 16
31 static volatile bool done
= false;
32 static bool dchange
= false;
33 static bool motor
= false;
35 static volatile int tmout
= 0;
36 static BYTE status
[7] = { 0 };
37 static BYTE statsz
= 0;
39 static BYTE fdc_track
= 0xff;
40 static unsigned fdc_diskchange
= 0;
41 static DrvGeom geometry
= { DG144_HEADS
,DG144_TRACKS
,DG144_SPT
};
43 unsigned long tbaddr
= 0x80000L
; /* physical address of track buffer located below 1M */
46 extern void floppy_ISR ();
47 extern void _int1c ();
49 void sendbyte (int byte
);
51 void floppy_handler (struct regs
*r
);
53 bool waitfdc (bool sensei
);
54 bool fdc_rw (int block
, BYTE
*blockbuff
, bool read
, unsigned long nosectors
);
56 char *drive_type
[6] = { "no floppy drive",
57 "360kb 5.25in floppy drive",
58 "1.2mb 5.25in floppy drive",
59 "720kb3.5in", "1.44mb 3.5in",
62 /* helper functions */
64 unsigned int floppy_detect (void)
76 printf("floppy drive /dev/fd0: %s\n", drive_type
[a
]);
78 if (b
) // if floppy drive dont exist, dont write this
79 printf("floppy drive /dev/fd1: %s\n", drive_type
[b
]);
85 /* This is the IRQ6 handler */
86 void floppy_handler (struct regs
*r
)
88 /* signal operation finished */
91 //outportb(0x20,0x20);
95 unsigned int floppy_install (void)
97 irq_install_handler (6, floppy_handler
);
102 unsigned int init_floppy (void)
106 /* allocate track buffer (must be located below 1M) */
107 /* see above for address assignment, floppy buffer is at 0x80000) */
108 /* install IRQ6 handler */
114 /* get floppy controller version */
115 sendbyte (CMD_VERSION
);
119 printf ("NEC765 controller found\n");
121 printf ("Enhanced controller found\n");
123 /* spin up the disk */
124 DPRINT ("FDC_RW|MOTORON");
130 /* sendbyte() routine from intel manual */
131 void sendbyte (int byte
)
135 for (tmo
= 0; tmo
< 128; tmo
++)
137 msr
= inportb (FDC_MSR
);
138 if ((msr
& 0xc0) == 0x80)
140 outportb (FDC_DATA
,byte
);
143 inportb (0x80); /* delay */
147 /* getbyte() routine from intel manual */
153 for (tmo
= 0; tmo
< 128; tmo
++)
155 msr
= inportb (FDC_MSR
);
156 if ((msr
& 0xd0) == 0xd0)
158 return inportb (FDC_DATA
);
160 inportb (0x80); /* delay */
162 return -1; /* read timeout */
165 /* this waits for FDC command to complete */
166 bool waitfdc (bool sensei
)
169 /* wait for IRQ6 handler to signal command finished */
170 while (!done
&& tmout
) {
174 /* read in command result bytes */
176 while ((statsz
< 7) && (inportb(FDC_MSR
) & 0x10))
178 status
[statsz
++] = getbyte ();
183 /* send a "sense interrupt status" command */
184 sendbyte (CMD_SENSEI
);
186 fdc_track
= getbyte ();
193 if (inportb(FDC_DIR) & 0x80)
203 /* This is the timer (int 1ch) handler */
206 if (tmout
) --tmout
; /* bump timeout */
209 else if (!mtick
&& motor
)
211 outportb (FDC_DOR
, 0x0c); /* turn off floppy motor */
217 * converts linear block address to head/track/sector
219 * blocks are numbered 0..heads*tracks*spt-1
220 * blocks 0..spt-1 are serviced by head #0
221 * blocks spt..spt*2-1 are serviced by head 1
223 * WARNING: garbage in == garbage out
225 void block2hts (int block
,int *head
,int *track
,int *sector
)
227 *head
= (block
% (geometry
.spt
* geometry
.heads
)) / (geometry
.spt
);
228 *track
= block
/ (geometry
.spt
* geometry
.heads
);
229 *sector
= block
% geometry
.spt
+ 1;
232 /**** disk operations ****/
234 /* this gets the FDC to a known state */
237 /* stop the motor and disable IRQ*/
239 outportb (FDC_DOR
,0);
243 /* program data rate (500K/s) */
244 outportb (FDC_DRS
,0);
246 /* re-enable interrupts */
247 outportb (FDC_DOR
,0x0c);
249 /* resetting triggered an interrupt - handle it */
259 /* specify drive timings (got these off the BIOS) */
261 sendbyte (CMD_SPECIFY
);
262 sendbyte (0xdf); /* SRT = 3ms, HUT = 240ms */
263 sendbyte (0x02); /* HLT = 16ms, ND = 0 */
264 /* clear "disk change" status */
265 //printf("RESET|SEEK\n");
272 /* this returns whether there was a disk change */
273 bool diskchange (void)
278 /* this turns the motor on */
283 mtick
= -1; /* stop motor kill countdown */
284 outportb (FDC_DOR
, 0x1c);
286 timer_wait (9); /* delay 500ms for motor to spin up */
291 /* this turns the motor off */
298 // mtick = 13500; /* start motor kill countdown: 36 ticks ~ 2s */
300 outportb (FDC_DOR
,0x0c);
307 /* recalibrate the drive */
308 void recalibrate (void)
310 /* turn the motor on */
313 /* send actual command bytes */
314 sendbyte (CMD_RECAL
);
316 /* wait until seek finished */
319 /* turn the motor off */
325 bool seek (int track
)
328 if (fdc_track
== track
) /* already there? */
332 /* send actual command bytes */
336 /* wait until seek finished */
339 DPRINT ("FLOPPY | SEEK | TIMEOUT");
340 return false; /* timeout! */
342 /* now let head settle for 15ms */
345 /* check that seek worked */
346 if ((sr0
!= 0x20) || (fdc_track
!= track
))
348 if (sr0
!= 0x20) DPRINT ("FLOPPY | SEEK | SR0!=0x20");
349 if (fdc_track
!= track
) DPRINT ("FLOPPY | SEEK | FDC_TRACK!=TRACK");
356 /* checks drive geometry - call this after any disk change */
357 bool log_disk (DrvGeom
*g
)
359 /* get drive in a known status before we do anything */
362 /* assume disk is 1.68M and try and read block #21 on first track */
363 geometry
.heads
= DG168_HEADS
;
364 geometry
.tracks
= DG168_TRACKS
;
365 geometry
.spt
= DG168_SPT
;
367 if (read_block (20, NULL
,1))
369 /* disk is a 1.68M disk */
372 g
->heads
= geometry
.heads
;
373 g
->tracks
= geometry
.tracks
;
374 g
->spt
= geometry
.spt
;
379 /* OK, not 1.68M - try again for 1.44M reading block #18 on first track */
380 geometry
.heads
= DG144_HEADS
;
381 geometry
.tracks
= DG144_TRACKS
;
382 geometry
.spt
= DG144_SPT
;
384 if (read_block (17, NULL
, 1))
386 /* disk is a 1.44M disk */
389 g
->heads
= geometry
.heads
;
390 g
->tracks
= geometry
.tracks
;
391 g
->spt
= geometry
.spt
;
396 /* it's not 1.44M or 1.68M - we don't support it */
400 /* read block (blockbuff is 512 byte buffer) */
401 bool read_block (int block
, BYTE
*blockbuff
, unsigned long nosectors
)
403 int track
=0, sector
=0, head
=0, track2
=0, result
=0, loop
=0;
407 //The FDC can read multiple sides at once but not multiple tracks
408 //printf("READ 1\n");
409 block2hts (block
, &head
, &track
, §or
);
410 block2hts (block
+nosectors
, &head
, &track2
, §or
);
414 for(loop
=0; (unsigned) loop
< nosectors
; loop
++)
415 result
= fdc_rw (block
+loop
, blockbuff
+ (loop
*512), true, 1);
419 // printf("FDC_RW\n");
420 return fdc_rw (block
, blockbuff
, true, nosectors
);
423 /* write block (blockbuff is 512 byte buffer) */
424 bool write_block (int block
, BYTE
*blockbuff
, unsigned long nosectors
)
426 return fdc_rw (block
,blockbuff
, false, nosectors
);
430 * since reads and writes differ only by a few lines, this handles both. This
431 * function is called by read_block() and write_block()
433 bool fdc_rw (int block
, BYTE
*blockbuff
, bool read
, unsigned long nosectors
)
435 int head
,track
,sector
,tries
, copycount
= 0;
436 unsigned char *p_tbaddr
= (unsigned char *)0x80000;
437 unsigned char *p_blockbuff
= blockbuff
;
439 /* convert logical address into physical address */
440 block2hts (block
, &head
, &track
, §or
);
442 if (!read
&& blockbuff
)
444 /* copy data from data buffer into track buffer */
445 for (copycount
= 0; (unsigned) copycount
< (nosectors
*512); copycount
++)
447 *p_tbaddr
= *p_blockbuff
;
452 DPRINT ("FDC_RW|COPY1");
453 for (tries
= 0;tries
< 3;tries
++)
456 DPRINT ("FDC_RW|RIGHT");
457 /* move head to right track */
461 //printf("FLOPPY | CANNOT SEEK\n");
465 /* check for diskchange */
466 DPRINT ("FDC_RW|DISKCHANGE");
467 if (inportb(FDC_DIR
) & 0x80)
470 seek(1); /* clear "disk change" status */
474 //printf("FLOPPY | DISK CHANGE STATUS => repeat\n");
475 if (fdc_diskchange
< MAX_DISKCHANGE
)
476 return fdc_rw(block
, blockbuff
, read
, nosectors
);
478 printf ("Floppy disk not response, resetting drive..\n");
485 DPRINT ("FDC_RW|RATE");
486 /* program data rate (500K/s) */
492 DPRINT ("FDC_RW|DMA");
493 dma_xfer(2,tbaddr
,nosectors
*512,false);
498 dma_xfer(2,tbaddr
,nosectors
*512,true);
505 sendbyte(2); /* 512 bytes/sector */
506 sendbyte(geometry
.spt
);
507 if (geometry
.spt
== DG144_SPT
)
508 sendbyte(DG144_GAP3RW
); /* gap 3 size for 1.44M read/write */
510 sendbyte(DG168_GAP3RW
); /* gap 3 size for 1.68M read/write */
511 sendbyte(0xff); /* DTL = unused */
513 DPRINT ("FDC_RW|STEP1");
514 /* wait for command completion */
515 /* read/write don't need "sense interrupt status" */
518 puts ("Timed out, trying operation again after reset()\n");
520 return fdc_rw (block
, blockbuff
, read
, nosectors
);
523 DPRINT ("FDC_RW|%d",(status
[0] & 0xc0));
525 if ((status
[0] & 0xc0) == 0)
526 break; /* worked! outta here! */
528 printf ("Try %d error => read from floppy\n", tries
);
529 recalibrate(); /* oops, try again... */
534 //printf("FDC_RW|MOTOROFF\n");
537 if (read
&& blockbuff
)
539 /* copy data from track buffer into data buffer */
540 //printf("FDC_RW|OK\n");
541 p_blockbuff
= blockbuff
;
542 p_tbaddr
= (unsigned char *) 0x80000;
543 for(copycount
= 0; (unsigned) copycount
< (nosectors
*512); copycount
++)
545 *p_blockbuff
= *p_tbaddr
;
550 //else{printf("FLOPPY | ERROR\n");}
554 /* this formats a track, given a certain geometry */
555 bool format_track (BYTE track
, DrvGeom
*g
)
557 int i
,h
,r
,r_id
,split
;
559 unsigned char *p_tbaddr
= (unsigned char *)0x8000;
560 unsigned int copycount
= 0;
563 if (g
->spt
!= DG144_SPT
&& g
->spt
!= DG168_SPT
)
566 /* spin up the disk */
569 /* program data rate (500K/s) */
570 outportb (FDC_CCR
, 0);
572 seek (track
); /* seek to track */
574 /* precalc some constants for interleave calculation */
576 if (g
->spt
& 1) split
++;
577 for (h
= 0;h
< g
->heads
;h
++)
579 /* for each head... */
580 /* check for diskchange */
581 if (inportb(FDC_DIR
) & 0x80)
584 seek (1); /* clear "disk change" status */
590 i
= 0; /* reset buffer index */
591 for (r
= 0;r
< g
->spt
;r
++)
593 /* for each sector... */
595 /* calculate 1:2 interleave (seems optimal in my system) */
597 if (r
& 1) r_id
+= split
;
599 /* add some head skew (2 sectors should be enough) */
603 if (r_id
< 1) r_id
+= g
->spt
;
605 /* add some track skew (1/2 a revolution) */
609 if (r_id
< 1) r_id
+= g
->spt
;
612 /**** interleave now calculated - sector ID is stored in r_id ****/
614 /* fill in sector ID's */
615 tmpbuff
[i
++] = track
;
621 /* copy sector ID's to track buffer */
622 for(copycount
= 0; copycount
< (unsigned) i
; copycount
++)
624 *p_tbaddr
= tmpbuff
[copycount
];
627 //movedata(_my_ds(),(long)tmpbuff,_dos_ds,tbaddr,i);
630 dma_xfer (2, tbaddr
, i
, true);
632 /* prepare "format track" command */
633 sendbyte (CMD_FORMAT
);
637 if (g
->spt
== DG144_SPT
)
638 sendbyte (DG144_GAP3FMT
); /* gap3 size for 1.44M format */
640 sendbyte (DG168_GAP3FMT
); /* gap3 size for 1.68M format */
641 sendbyte (0); /* filler byte */
643 /* wait for command to finish */
646 if (status
[0] & 0xc0)
658 bool floppy_acthandler (unsigned act
, partition_t
*p
, char *block
, char *more
, int n
)
663 if (!floppy_detect ())
667 __asm__
__volatile__ ("sti");
675 read_block (n
, block
, 1);
685 if (!strcmp (more
, ".")) {
690 if (!strcmp (more
, "..")) {
695 while (strlen (dir
[f
].name
)) {
696 if (!strncmp (dir
[f
].name
, more
, strlen (more
))) {
710 unsigned fl
= strlen (dir
[f
].name
);
713 unsigned attrib
= VFS_FILEATTR_MOUNTED
;
717 if (dir[f].name[x] != ' ')
722 #define VFS_FILEATTR_FILE 0x1
723 #define VFS_FILEATTR_DIR 0x2
724 #define VFS_FILEATTR_HIDDEN 0x4
725 #define VFS_FILEATTR_SYSTEM 0x8
726 #define VFS_FILEATTR_BIN 0x10
727 #define VFS_FILEATTR_READ 0x20
728 #define VFS_FILEATTR_WRITE 0x40
730 case 0: dir[y].read=1; break;
731 case 1: dir[y].hidden=1; break;
732 case 2: dir[y].system=1; break;
733 case 3: dir[y].volume=1; break;
734 case 4: dir[y].dir=1; break;
735 case 5: dir[y].bin=1; break;
739 attrib
|= VFS_FILEATTR_READ
;
740 attrib
|= VFS_FILEATTR_WRITE
;
743 attrib
|= VFS_FILEATTR_HIDDEN
;
745 attrib
|= VFS_FILEATTR_SYSTEM
;
747 attrib
|= VFS_FILEATTR_DIR
;
749 attrib
|= VFS_FILEATTR_FILE
;
751 attrib
|= VFS_FILEATTR_BIN
;
753 dir
[f
].name
[10] = '\0';
754 vfs_list_add (dir
[f
].name
, attrib
, block
);
755 //printf ("add: %s to %s\n", dir[f].name, block);
758 fl
= strlen (dir
[f
].name
);
773 " pusha \n" /* Save all registers */
774 " pushw %ds \n" /* Set up the data segment */
776 " pushw %ss \n" /* Note that ss is always valid */
784 " popw %ds \n" /* Restore registers */
786 " iret \n" /* Exit interrupt */
790 ".globl floppy_ISR \n"
792 " pusha \n" /* Save all registers */
793 " pushw %ds \n" /* Set up the data segment */
795 " pushw %ss \n" /* Note that ss is always valid */
803 " popw %ds \n" /* Restore registers */
805 " iret \n" /* Exit interrupt */