Kernel 0.5.0 ! Lot of fixes, fast, no few new features, etc ..
[ZeXOS.git] / kernel / drivers / block / floppy.c
blob7cffc44603e8527aad7bbf59d0dee24f4d86b751
1 /*
2 * ZeX/OS
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/>.
20 #include <system.h>
21 #include <x86.h>
22 #include <string.h>
23 #include <dev.h>
24 #include <vfs.h>
25 #include <partition.h>
27 #define MAX_DISKCHANGE 16
29 /* FDC_H */
30 /* globals */
31 static volatile bool done = false;
32 static bool dchange = false;
33 static bool motor = false;
34 static int mtick = 0;
35 static volatile int tmout = 0;
36 static BYTE status[7] = { 0 };
37 static BYTE statsz = 0;
38 static BYTE sr0 = 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 */
45 /* prototypes */
46 extern void floppy_ISR ();
47 extern void _int1c ();
49 void sendbyte (int byte);
50 int getbyte ();
51 void floppy_handler (struct regs *r);
52 void int1c (void);
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",
60 "2.88mb 3.5in" };
62 /* helper functions */
64 unsigned int floppy_detect (void)
66 unsigned char c;
67 int a,b;
68 outportb(0x70, 0x10);
69 c = inportb(0x71);
70 a = c >> 4;
71 b = c & 0xF;
73 if (!a)
74 return 0;
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]);
81 return 1;
85 /* This is the IRQ6 handler */
86 void floppy_handler (struct regs *r)
88 /* signal operation finished */
89 done = true;
90 /* EOI the PIC */
91 //outportb(0x20,0x20);
94 /* init driver */
95 unsigned int floppy_install (void)
97 irq_install_handler (6, floppy_handler);
99 return 1;
102 unsigned int init_floppy (void)
104 int i;
106 /* allocate track buffer (must be located below 1M) */
107 /* see above for address assignment, floppy buffer is at 0x80000) */
108 /* install IRQ6 handler */
110 done = false;
112 reset ();
114 /* get floppy controller version */
115 sendbyte (CMD_VERSION);
116 i = getbyte ();
118 if (i == 0x80)
119 printf ("NEC765 controller found\n");
120 else
121 printf ("Enhanced controller found\n");
123 /* spin up the disk */
124 DPRINT ("FDC_RW|MOTORON");
125 motoron ();
127 return 1;
130 /* sendbyte() routine from intel manual */
131 void sendbyte (int byte)
133 volatile int msr;
134 int tmo;
135 for (tmo = 0; tmo < 128; tmo++)
137 msr = inportb (FDC_MSR);
138 if ((msr & 0xc0) == 0x80)
140 outportb (FDC_DATA,byte);
141 return;
143 inportb (0x80); /* delay */
147 /* getbyte() routine from intel manual */
148 int getbyte ()
150 volatile int msr;
151 int tmo;
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)
168 tmout=80000;
169 /* wait for IRQ6 handler to signal command finished */
170 while (!done && tmout) {
171 printf ("");;
174 /* read in command result bytes */
175 statsz = 0;
176 while ((statsz < 7) && (inportb(FDC_MSR) & 0x10))
178 status[statsz++] = getbyte ();
181 if (sensei)
183 /* send a "sense interrupt status" command */
184 sendbyte (CMD_SENSEI);
185 sr0 = getbyte ();
186 fdc_track = getbyte ();
189 done = false;
191 if (!tmout)
193 if (inportb(FDC_DIR) & 0x80)
194 dchange = true;
195 return false;
197 else*/
198 return true;
203 /* This is the timer (int 1ch) handler */
204 void int1c (void)
206 if (tmout) --tmout; /* bump timeout */
207 if (mtick > 0)
208 --mtick;
209 else if (!mtick && motor)
211 outportb (FDC_DOR, 0x0c); /* turn off floppy motor */
212 motor = false;
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 */
235 void reset (void)
237 /* stop the motor and disable IRQ*/
238 motoroff ();
239 outportb (FDC_DOR,0);
240 mtick = 0;
241 motor = false;
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 */
250 done = true;
251 waitfdc (true);
252 done = true;
253 waitfdc (true);
254 done = true;
255 waitfdc (true);
256 done = true;
257 waitfdc (true);
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");
266 seek (5);
268 recalibrate ();
269 dchange = false;
272 /* this returns whether there was a disk change */
273 bool diskchange (void)
275 return dchange;
278 /* this turns the motor on */
279 void motoron (void)
281 if (!motor)
283 mtick = -1; /* stop motor kill countdown */
284 outportb (FDC_DOR, 0x1c);
286 timer_wait (9); /* delay 500ms for motor to spin up */
287 motor = true;
291 /* this turns the motor off */
292 void motoroff (void)
294 return;
296 if (motor)
298 // mtick = 13500; /* start motor kill countdown: 36 ticks ~ 2s */
300 outportb (FDC_DOR,0x0c);
301 outportb (0x3f2,0);
302 motor = false;
303 timer_wait (18);
307 /* recalibrate the drive */
308 void recalibrate (void)
310 /* turn the motor on */
311 //motoron();
313 /* send actual command bytes */
314 sendbyte (CMD_RECAL);
315 sendbyte (0);
316 /* wait until seek finished */
317 waitfdc (true);
319 /* turn the motor off */
320 //motoroff();
324 /* seek to track */
325 bool seek (int track)
328 if (fdc_track == track) /* already there? */
329 return true;
331 //motoron();
332 /* send actual command bytes */
333 sendbyte (CMD_SEEK);
334 sendbyte (0);
335 sendbyte (track);
336 /* wait until seek finished */
337 if (!waitfdc (true))
339 DPRINT ("FLOPPY | SEEK | TIMEOUT");
340 return false; /* timeout! */
342 /* now let head settle for 15ms */
343 timer_wait (1);
344 //motoroff();
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");
350 return false;
352 else
353 return true;
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 */
360 reset();
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 */
370 if (g)
372 g->heads = geometry.heads;
373 g->tracks = geometry.tracks;
374 g->spt = geometry.spt;
376 return true;
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 */
387 if (g)
389 g->heads = geometry.heads;
390 g->tracks = geometry.tracks;
391 g->spt = geometry.spt;
393 return true;
396 /* it's not 1.44M or 1.68M - we don't support it */
397 return false;
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;
405 reset();
407 //The FDC can read multiple sides at once but not multiple tracks
408 //printf("READ 1\n");
409 block2hts (block, &head, &track, &sector);
410 block2hts (block+nosectors, &head, &track2, &sector);
412 if(track != track2)
414 for(loop=0; (unsigned) loop < nosectors; loop ++)
415 result = fdc_rw (block+loop, blockbuff + (loop*512), true, 1);
417 return result;
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, &sector);
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;
448 p_blockbuff++;
449 p_tbaddr++;
452 DPRINT ("FDC_RW|COPY1");
453 for (tries = 0;tries < 3;tries++)
456 DPRINT ("FDC_RW|RIGHT");
457 /* move head to right track */
458 if (!seek(track))
460 motoroff();
461 //printf("FLOPPY | CANNOT SEEK\n");
462 return false;
465 /* check for diskchange */
466 DPRINT ("FDC_RW|DISKCHANGE");
467 if (inportb(FDC_DIR) & 0x80)
469 dchange = true;
470 seek(1); /* clear "disk change" status */
471 recalibrate();
472 motoroff();
473 fdc_diskchange ++;
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");
479 reset ();
480 motoroff();
481 return false;
483 fdc_diskchange = 0;
485 DPRINT ("FDC_RW|RATE");
486 /* program data rate (500K/s) */
487 outportb(FDC_CCR,0);
488 /* send command */
490 if (read)
492 DPRINT ("FDC_RW|DMA");
493 dma_xfer(2,tbaddr,nosectors*512,false);
494 sendbyte(CMD_READ);
496 else
498 dma_xfer(2,tbaddr,nosectors*512,true);
499 sendbyte(CMD_WRITE);
501 sendbyte(head << 2);
502 sendbyte(track);
503 sendbyte(head);
504 sendbyte(sector);
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 */
509 else
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" */
516 if (!waitfdc(1))
518 puts ("Timed out, trying operation again after reset()\n");
519 reset();
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... */
533 /* stop the motor */
534 //printf("FDC_RW|MOTOROFF\n");
535 motoroff();
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;
546 p_blockbuff++;
547 p_tbaddr++;
550 //else{printf("FLOPPY | ERROR\n");}
551 return (tries != 3);
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;
558 BYTE tmpbuff[256];
559 unsigned char *p_tbaddr = (unsigned char *)0x8000;
560 unsigned int copycount = 0;
562 /* check geometry */
563 if (g->spt != DG144_SPT && g->spt != DG168_SPT)
564 return false;
566 /* spin up the disk */
567 motoron ();
569 /* program data rate (500K/s) */
570 outportb (FDC_CCR, 0);
572 seek (track); /* seek to track */
574 /* precalc some constants for interleave calculation */
575 split = g->spt / 2;
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)
583 dchange = true;
584 seek (1); /* clear "disk change" status */
585 recalibrate ();
586 motoroff ();
587 return false;
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) */
596 r_id = r / 2 + 1;
597 if (r & 1) r_id += split;
599 /* add some head skew (2 sectors should be enough) */
600 if (h & 1)
602 r_id -= 2;
603 if (r_id < 1) r_id += g->spt;
605 /* add some track skew (1/2 a revolution) */
606 if (track & 1)
608 r_id -= g->spt / 2;
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;
616 tmpbuff[i++] = h;
617 tmpbuff[i++] = r_id;
618 tmpbuff[i++] = 2;
621 /* copy sector ID's to track buffer */
622 for(copycount = 0; copycount < (unsigned) i; copycount ++)
624 *p_tbaddr = tmpbuff[copycount];
625 p_tbaddr++;
627 //movedata(_my_ds(),(long)tmpbuff,_dos_ds,tbaddr,i);
629 /* start dma xfer */
630 dma_xfer (2, tbaddr, i, true);
632 /* prepare "format track" command */
633 sendbyte (CMD_FORMAT);
634 sendbyte (h << 2);
635 sendbyte (2);
636 sendbyte (g->spt);
637 if (g->spt == DG144_SPT)
638 sendbyte (DG144_GAP3FMT); /* gap3 size for 1.44M format */
639 else
640 sendbyte (DG168_GAP3FMT); /* gap3 size for 1.68M format */
641 sendbyte (0); /* filler byte */
643 /* wait for command to finish */
644 if (!waitfdc(false))
645 return false;
646 if (status[0] & 0xc0)
648 motoroff();
649 return false;
652 motoroff();
653 return true;
658 bool floppy_acthandler (unsigned act, partition_t *p, char *block, char *more, int n)
660 switch (act) {
661 case DEV_ACT_INIT:
663 if (!floppy_detect ())
664 return 0;
666 floppy_install ();
667 __asm__ __volatile__ ("sti");
668 init_floppy ();
670 return 1;
672 break;
673 case DEV_ACT_READ:
675 read_block (n, block, 1);
676 return 1;
678 break;
679 case DEV_ACT_MOUNT:
681 int f = 0, t = 0;
682 read_dir (-1);
684 if (strlen (more)) {
685 if (!strcmp (more, ".")) {
686 read_dir (0);
687 return 1;
690 if (!strcmp (more, "..")) {
691 read_dir (1);
692 return 1;
695 while (strlen (dir[f].name)) {
696 if (!strncmp (dir[f].name, more, strlen (more))) {
697 read_dir (f);
698 t = 1;
699 break;
702 f ++;
705 if (!t)
706 return 0;
709 f = 0;
710 unsigned fl = strlen (dir[f].name);
712 while (fl) {
713 unsigned attrib = VFS_FILEATTR_MOUNTED;
715 /*unsigned x = fl;
716 while (x) {
717 if (dir[f].name[x] != ' ')
719 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;
738 if (dir[f].read) {
739 attrib |= VFS_FILEATTR_READ;
740 attrib |= VFS_FILEATTR_WRITE;
742 if (dir[f].hidden)
743 attrib |= VFS_FILEATTR_HIDDEN;
744 if (dir[f].system)
745 attrib |= VFS_FILEATTR_SYSTEM;
746 if (dir[f].dir)
747 attrib |= VFS_FILEATTR_DIR;
748 else
749 attrib |= VFS_FILEATTR_FILE;
750 if (dir[f].bin)
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);
757 f ++;
758 fl = strlen (dir[f].name);
761 return 1;
763 break;
766 return 0;
770 asm (
771 ".globl _int1c \n"
772 "_int1c: \n"
773 " pusha \n" /* Save all registers */
774 " pushw %ds \n" /* Set up the data segment */
775 " pushw %es \n"
776 " pushw %ss \n" /* Note that ss is always valid */
777 " pushw %ss \n"
778 " popw %ds \n"
779 " popw %es \n"
780 " \n"
781 " call int1c \n"
782 " \n"
783 " popw %es \n"
784 " popw %ds \n" /* Restore registers */
785 " popa \n"
786 " iret \n" /* Exit interrupt */
789 asm (
790 ".globl floppy_ISR \n"
791 "floppy_ISR: \n"
792 " pusha \n" /* Save all registers */
793 " pushw %ds \n" /* Set up the data segment */
794 " pushw %es \n"
795 " pushw %ss \n" /* Note that ss is always valid */
796 " pushw %ss \n"
797 " popw %ds \n"
798 " popw %es \n"
799 " \n"
800 " call irq6 \n"
801 " \n"
802 " popw %es \n"
803 " popw %ds \n" /* Restore registers */
804 " popa \n"
805 " iret \n" /* Exit interrupt */