3 * Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
4 * Copyright (C) 2008 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
5 * Copyright (C) 2009 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 #include <partition.h>
33 #define MAX_DISKCHANGE 16
37 static volatile bool done
= false;
38 static bool dchange
= false;
39 static bool motor
= false;
41 static volatile int tmout
= 0;
42 static char status
[7] = { 0 };
43 static char statsz
= 0;
45 static char fdc_track
= 0xff;
46 static unsigned fdc_diskchange
= 0;
47 static DrvGeom geometry
= { DG144_HEADS
, DG144_TRACKS
, DG144_SPT
};
49 unsigned char *floppy_tbaddr
; /* physical address of track buffer */
52 void sendbyte (int byte
);
54 void floppy_handler (void *r
);
55 bool waitfdc (bool sensei
);
56 bool fdc_rw (int block
, char *blockbuff
, bool read
, unsigned long nosectors
);
58 static char *drive_type
[6] = { "no floppy drive",
59 "360kb 5.25in floppy drive",
60 "1.2mb 5.25in floppy drive",
61 "720kb3.5in", "1.44mb 3.5in",
64 /* helper functions */
66 unsigned int floppy_detect (void)
78 kprintf ("floppy drive /dev/fd0: %s\n", drive_type
[a
]);
80 if (b
) // if floppy drive not exist, dont write this
81 kprintf ("floppy drive /dev/fd1: %s\n", drive_type
[b
]);
87 /* This is the IRQ6 handler */
88 void floppy_handler (void *r
)
90 /* signal operation finished */
97 unsigned int floppy_install (void)
99 irq_install_handler (6, (void *) floppy_handler
);
104 unsigned int init_floppy (void)
108 /* allocate track buffer (must be located below 1M) */
109 /* see above for address assignment, floppy buffer is at floppy_tbaddr) */
110 /* install IRQ6 handler */
116 /* get floppy controller version */
117 sendbyte (CMD_VERSION
);
121 kprintf ("NEC765 controller found\n");
123 kprintf ("Enhanced controller found\n");
125 /* spin up the disk */
126 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|MOTORON");
129 floppy_tbaddr
= (unsigned char *) kmalloc (1024);
134 /* sendbyte() routine from intel manual */
135 void sendbyte (int byte
)
140 for (tmo
= 0; tmo
< 128; tmo
++) {
143 if ((msr
& 0xc0) == 0x80) {
144 outb (FDC_DATA
, byte
);
148 inb (0x80); /* delay */
152 /* getbyte() routine from intel manual */
158 for (tmo
= 0; tmo
< 128; tmo
++) {
161 if ((msr
& 0xd0) == 0xd0)
162 return inb (FDC_DATA
);
164 inb (0x80); /* delay */
166 return -1; /* read timeout */
169 /* this waits for FDC command to complete */
170 bool waitfdc (bool sensei
)
173 /* wait for IRQ6 handler to signal command finished */
174 /* while (!done && tmout) {
180 while (!done
&& tmout
) {
185 /* read in command result bytes */
188 while ((statsz
< 7) && (inb(FDC_MSR
) & 0x10)) {
189 status
[statsz
++] = getbyte ();
193 /* send a "sense interrupt status" command */
194 sendbyte (CMD_SENSEI
);
196 fdc_track
= getbyte ();
203 if (inb(FDC_DIR) & 0x80)
213 /* This is the timer (int 1ch) handler */
217 -- tmout
; /* bump timeout */
221 else if (!mtick
&& motor
) {
222 outb (FDC_DOR
, 0x0c); /* turn off floppy motor */
228 * converts linear block address to head/track/sector
230 * blocks are numbered 0..heads*tracks*spt-1
231 * blocks 0..spt-1 are serviced by head #0
232 * blocks spt..spt*2-1 are serviced by head 1
234 * WARNING: garbage in == garbage out
236 void block2hts (int block
,int *head
,int *track
,int *sector
)
238 *head
= (block
% (geometry
.spt
* geometry
.heads
)) / (geometry
.spt
);
239 *track
= block
/ (geometry
.spt
* geometry
.heads
);
240 *sector
= block
% geometry
.spt
+ 1;
243 /**** disk operations ****/
245 /* this gets the FDC to a known state */
248 /* stop the motor and disable IRQ */
254 /* program data rate (500K/s) */
257 /* re-enable interrupts */
258 outb (FDC_DOR
, 0x0c);
260 /* resetting triggered an interrupt - handle it */
270 /* specify drive timings (got these off the BIOS) */
271 sendbyte (CMD_SPECIFY
);
272 sendbyte (0xdf); /* SRT = 3ms, HUT = 240ms */
273 sendbyte (0x02); /* HLT = 16ms, ND = 0 */
275 /* clear "disk change" status */
276 //printf("RESET|SEEK\n");
284 /* this returns whether there was a disk change */
285 bool diskchange (void)
290 /* this turns the motor on */
294 mtick
= -1; /* stop motor kill countdown */
295 outb (FDC_DOR
, 0x1c);
297 delay (100); /* delay 500ms for motor to spin up */
302 /* this turns the motor off */
306 // mtick = 13500; /* start motor kill countdown: 36 ticks ~ 2s */
308 outb (FDC_DOR
, 0x0c);
315 /* recalibrate the drive */
316 void recalibrate (void)
318 /* turn the motor on */
321 /* send actual command bytes */
322 sendbyte (CMD_RECAL
);
324 /* wait until seek finished */
327 /* turn the motor off */
333 bool seek (int track
)
336 if (fdc_track
== track
) /* already there? */
340 /* send actual command bytes */
345 /* wait until seek finished */
346 if (!waitfdc (true)) {
347 DPRINT (DBG_DRIVER
| DBG_BLOCK
, "FLOPPY | SEEK | TIMEOUT");
348 return false; /* timeout! */
350 /* now let head settle for 15ms */
353 /* check that seek worked */
354 if ((sr0
!= 0x20) || (fdc_track
!= track
)) {
356 DPRINT (DBG_DRIVER
| DBG_BLOCK
, "FLOPPY | SEEK | SR0!=0x20");
358 if (fdc_track
!= track
)
359 DPRINT (DBG_DRIVER
| DBG_BLOCK
, "FLOPPY | SEEK | FDC_TRACK!=TRACK");
367 /* checks drive geometry - call this after any disk change */
368 bool log_disk (DrvGeom
*g
)
370 /* get drive in a known status before we do anything */
373 /* assume disk is 1.68M and try and read block #21 on first track */
374 geometry
.heads
= DG168_HEADS
;
375 geometry
.tracks
= DG168_TRACKS
;
376 geometry
.spt
= DG168_SPT
;
378 if (read_block (20, NULL
, 1)) {
379 /* disk is a 1.68M disk */
381 g
->heads
= geometry
.heads
;
382 g
->tracks
= geometry
.tracks
;
383 g
->spt
= geometry
.spt
;
389 /* OK, not 1.68M - try again for 1.44M reading block #18 on first track */
390 geometry
.heads
= DG144_HEADS
;
391 geometry
.tracks
= DG144_TRACKS
;
392 geometry
.spt
= DG144_SPT
;
394 if (read_block (17, NULL
, 1)) {
395 /* disk is a 1.44M disk */
397 g
->heads
= geometry
.heads
;
398 g
->tracks
= geometry
.tracks
;
399 g
->spt
= geometry
.spt
;
405 /* it's not 1.44M or 1.68M - we don't support it */
409 /* read block (blockbuff is 512 byte buffer) */
410 bool read_block (int block
, char *blockbuff
, unsigned long nosectors
)
412 int track
= 0, sector
= 0, head
= 0, track2
= 0, result
= 0, loop
= 0;
416 //The FDC can read multiple sides at once but not multiple tracks
417 //printf("READ 1\n");
418 block2hts (block
, &head
, &track
, §or
);
419 block2hts (block
+nosectors
, &head
, &track2
, §or
);
421 if(track
!= track2
) {
422 for(loop
= 0; (unsigned) loop
< nosectors
; loop
++)
423 result
= fdc_rw (block
+loop
, blockbuff
+ (loop
*512), true, 1);
427 // printf("FDC_RW\n");
428 return fdc_rw (block
, blockbuff
, true, nosectors
);
431 /* write block (blockbuff is 512 byte buffer) */
432 bool write_block (int block
, char *blockbuff
, unsigned long nosectors
)
434 return fdc_rw (block
, blockbuff
, false, nosectors
);
438 * since reads and writes differ only by a few lines, this handles both. This
439 * function is called by read_block() and write_block()
441 bool fdc_rw (int block
, char *blockbuff
, bool read
, unsigned long nosectors
)
443 int head
,track
,sector
,tries
, copycount
= 0;
444 unsigned char *p_tbaddr
= (unsigned char *) floppy_tbaddr
;
445 unsigned char *p_blockbuff
= blockbuff
;
447 /* convert logical address into physical address */
448 block2hts (block
, &head
, &track
, §or
);
450 /* spin up the disk */
451 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|MOTORON");
454 if (!read
&& blockbuff
) {
455 /* copy data from data buffer into track buffer */
456 for (copycount
= 0; (unsigned) copycount
< (nosectors
*512); copycount
++) {
457 *p_tbaddr
= *p_blockbuff
;
463 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|COPY1");
465 for (tries
= 0; tries
< 3; tries
++) {
467 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|RIGHT");
468 /* move head to right track */
471 DPRINT (DBG_DRIVER
| DBG_BLOCK
, "FLOPPY | CANNOT SEEK\n");
475 /* check for diskchange */
476 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|DISKCHANGE");
478 if (inb (FDC_DIR
) & 0x80) {
480 seek (1); /* clear "disk change" status */
484 DPRINT (DBG_DRIVER | DBG_BLOCK, "FLOPPY | DISK CHANGE STATUS => repeat");
485 if (fdc_diskchange < MAX_DISKCHANGE)*/
486 return fdc_rw (block
, blockbuff
, read
, nosectors
);
488 /*printf ("Floppy disk not response, resetting drive..\n");
493 //fdc_diskchange = 0;
495 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|RATE");
497 /* program data rate (500K/s) */
502 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|DMA");
503 dma_xfer (2, (unsigned long) floppy_tbaddr
, nosectors
* 512, false);
506 dma_xfer (2, (unsigned long) floppy_tbaddr
, nosectors
* 512, true);
507 sendbyte (CMD_WRITE
);
510 sendbyte (head
<< 2);
514 sendbyte (2); /* 512 bytes/sector */
515 sendbyte (geometry
.spt
);
517 if (geometry
.spt
== DG144_SPT
)
518 sendbyte (DG144_GAP3RW
); /* gap 3 size for 1.44M read/write */
520 sendbyte (DG168_GAP3RW
); /* gap 3 size for 1.68M read/write */
522 sendbyte (0xff); /* DTL = unused */
524 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|STEP1");
525 /* wait for command completion */
526 /* read/write don't need "sense interrupt status" */
528 kprintf ("Timed out, trying operation again after reset()\n");
530 return fdc_rw (block
, blockbuff
, read
, nosectors
);
533 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|%d",(status[0] & 0xc0));
535 if ((status
[0] & 0xc0) == 0)
536 break; /* worked! outta here! */
539 DPRINT (DBG_DRIVER
| DBG_BLOCK
, "Try %d error => read from floppy\n", tries
);
541 recalibrate (); /* oops, try again... */
546 //printf("FDC_RW|MOTOROFF\n");
549 if (read
&& blockbuff
) {
550 /* copy data from track buffer into data buffer */
551 //printf("FDC_RW|OK\n");
552 p_blockbuff
= blockbuff
;
553 p_tbaddr
= (unsigned char *) floppy_tbaddr
;
555 for (copycount
= 0; (unsigned) copycount
< (nosectors
* 512); copycount
++) {
556 *p_blockbuff
= *p_tbaddr
;
561 DPRINT (DBG_DRIVER
| DBG_BLOCK
, "FLOPPY | ERROR\n");
566 /* this formats a track, given a certain geometry */
567 bool format_track (char track
, DrvGeom
*g
)
569 int i
, h
, r
, r_id
, split
;
571 unsigned char *p_tbaddr
= (unsigned char *) floppy_tbaddr
;
572 unsigned int copycount
= 0;
575 if (g
->spt
!= DG144_SPT
&& g
->spt
!= DG168_SPT
)
578 /* spin up the disk */
581 /* program data rate (500K/s) */
584 seek (track
); /* seek to track */
586 /* precalc some constants for interleave calculation */
592 for (h
= 0; h
< g
->heads
; h
++) {
593 /* for each head... */
594 /* check for diskchange */
595 if (inb (FDC_DIR
) & 0x80) {
597 seek (1); /* clear "disk change" status */
604 i
= 0; /* reset buffer index */
605 for (r
= 0; r
< g
->spt
; r
++) {
606 /* for each sector... */
608 /* calculate 1:2 interleave (seems optimal in my system) */
614 /* add some head skew (2 sectors should be enough) */
621 /* add some track skew (1/2 a revolution) */
629 /**** interleave now calculated - sector ID is stored in r_id ****/
631 /* fill in sector ID's */
632 tmpbuff
[i
++] = track
;
634 tmpbuff
[i
++] = r_id
;
638 /* copy sector ID's to track buffer */
639 for (copycount
= 0; copycount
< (unsigned) i
; copycount
++) {
640 *p_tbaddr
= tmpbuff
[copycount
];
643 //movedata(_my_ds(),(long)tmpbuff,_dos_ds,tbaddr,i);
646 dma_xfer (2, (unsigned long) floppy_tbaddr
, i
, true);
648 /* prepare "format track" command */
649 sendbyte (CMD_FORMAT
);
654 if (g
->spt
== DG144_SPT
)
655 sendbyte (DG144_GAP3FMT
); /* gap3 size for 1.44M format */
657 sendbyte (DG168_GAP3FMT
); /* gap3 size for 1.68M format */
659 sendbyte (0); /* filler byte */
661 /* wait for command to finish */
662 if (!waitfdc (false))
665 if (status
[0] & 0xc0) {
678 bool floppy_acthandler (unsigned act
, partition_t
*p
, char *block
, char *more
, int n
)
683 if (!floppy_detect ())
696 read_block (n
, block
, 1);
707 if (!strcmp (more
, "."))
710 if (!strcmp (more
, ".."))
713 while (strlen (dir
[f
].name
)) {
714 if (!strncmp (dir
[f
].name
, more
, strlen (more
))) {
730 unsigned fl
= strlen (dir
[f
].name
);
733 unsigned attrib
= VFS_FILEATTR_MOUNTED
;
736 #define VFS_FILEATTR_FILE 0x1
737 #define VFS_FILEATTR_DIR 0x2
738 #define VFS_FILEATTR_HIDDEN 0x4
739 #define VFS_FILEATTR_SYSTEM 0x8
740 #define VFS_FILEATTR_BIN 0x10
741 #define VFS_FILEATTR_READ 0x20
742 #define VFS_FILEATTR_WRITE 0x40
744 case 0: dir[y].read=1; break;
745 case 1: dir[y].hidden=1; break;
746 case 2: dir[y].system=1; break;
747 case 3: dir[y].volume=1; break;
748 case 4: dir[y].dir=1; break;
749 case 5: dir[y].bin=1; break;
753 attrib
|= VFS_FILEATTR_READ
;
754 attrib
|= VFS_FILEATTR_WRITE
;
757 attrib
|= VFS_FILEATTR_HIDDEN
;
759 attrib
|= VFS_FILEATTR_SYSTEM
;
761 attrib
|= VFS_FILEATTR_DIR
;
763 attrib
|= VFS_FILEATTR_FILE
;
765 attrib
|= VFS_FILEATTR_BIN
;
767 dir
[f
].name
[10] = '\0';
768 vfs_list_add (dir
[f
].name
, attrib
, block
);
769 //printf ("add: %s to %s\n", dir[f].name, block);
772 fl
= strlen (dir
[f
].name
);