Cleanup in elf.c with .bss section clean; adm command mounts cdrom instead of floppy...
[ZeXOS.git] / kernel / drivers / block / floppy.c
blob6a478ac35ed96162e2ec242164ac27b60b13b484
1 /*
2 * ZeX/OS
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/>.
22 #include <config.h>
23 #include <build.h>
25 #ifdef ARCH_i386
26 #include <system.h>
27 #include <arch/io.h>
28 #include <string.h>
29 #include <dev.h>
30 #include <vfs.h>
31 #include <partition.h>
33 #define MAX_DISKCHANGE 16
35 /* FDC_H */
36 /* globals */
37 static volatile bool done = false;
38 static bool dchange = false;
39 static bool motor = false;
40 static int mtick = 0;
41 static volatile int tmout = 0;
42 static char status[7] = { 0 };
43 static char statsz = 0;
44 static char sr0 = 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 */
51 /* prototypes */
52 void sendbyte (int byte);
53 int getbyte ();
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",
62 "2.88mb 3.5in" };
64 /* helper functions */
66 unsigned int floppy_detect (void)
68 unsigned char c;
69 int a,b;
70 outb (0x70, 0x10);
71 c = inb (0x71);
72 a = c >> 4;
73 b = c & 0xF;
75 if (!a)
76 return 0;
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]);
83 return 1;
87 /* This is the IRQ6 handler */
88 void floppy_handler (void *r)
90 /* signal operation finished */
91 done = true;
92 /* EOI the PIC */
93 //outb (0x20,0x20);
96 /* init driver */
97 unsigned int floppy_install (void)
99 irq_install_handler (6, (void *) floppy_handler);
101 return 1;
104 unsigned int init_floppy (void)
106 int i;
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 */
112 done = false;
114 reset ();
116 /* get floppy controller version */
117 sendbyte (CMD_VERSION);
118 i = getbyte ();
120 if (i == 0x80)
121 kprintf ("NEC765 controller found\n");
122 else
123 kprintf ("Enhanced controller found\n");
125 /* spin up the disk */
126 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|MOTORON");
127 //motoron ();
129 floppy_tbaddr = (unsigned char *) kmalloc (1024);
131 return 1;
134 /* sendbyte() routine from intel manual */
135 void sendbyte (int byte)
137 volatile int msr;
138 int tmo;
140 for (tmo = 0; tmo < 128; tmo ++) {
141 msr = inb (FDC_MSR);
143 if ((msr & 0xc0) == 0x80) {
144 outb (FDC_DATA, byte);
145 return;
148 inb (0x80); /* delay */
152 /* getbyte() routine from intel manual */
153 int getbyte ()
155 volatile int msr;
156 int tmo;
158 for (tmo = 0; tmo < 128; tmo ++) {
159 msr = inb (FDC_MSR);
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)
172 tmout = 80000;
173 /* wait for IRQ6 handler to signal command finished */
174 /* while (!done && tmout) {
175 schedule ();
176 tmout --;
177 delay (10);
180 while (!done && tmout) {
181 kprintf("");
182 schedule ();
185 /* read in command result bytes */
186 statsz = 0;
188 while ((statsz < 7) && (inb(FDC_MSR) & 0x10)) {
189 status[statsz ++] = getbyte ();
192 if (sensei) {
193 /* send a "sense interrupt status" command */
194 sendbyte (CMD_SENSEI);
195 sr0 = getbyte ();
196 fdc_track = getbyte ();
199 done = false;
201 if (!tmout)
203 if (inb(FDC_DIR) & 0x80)
204 dchange = true;
205 return false;
207 else*/
208 return true;
213 /* This is the timer (int 1ch) handler */
214 void int1c (void)
216 if (tmout)
217 -- tmout; /* bump timeout */
219 if (mtick > 0)
220 -- mtick;
221 else if (!mtick && motor) {
222 outb (FDC_DOR, 0x0c); /* turn off floppy motor */
223 motor = false;
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 */
246 void reset (void)
248 /* stop the motor and disable IRQ */
249 motoroff ();
250 outb (FDC_DOR, 0);
251 mtick = 0;
252 motor = false;
254 /* program data rate (500K/s) */
255 outb (FDC_DRS, 0);
257 /* re-enable interrupts */
258 outb (FDC_DOR, 0x0c);
260 /* resetting triggered an interrupt - handle it */
261 done = true;
262 waitfdc (true);
263 done = true;
264 waitfdc (true);
265 done = true;
266 waitfdc (true);
267 done = true;
268 waitfdc (true);
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");
277 seek (5);
279 recalibrate ();
281 dchange = false;
284 /* this returns whether there was a disk change */
285 bool diskchange (void)
287 return dchange;
290 /* this turns the motor on */
291 void motoron (void)
293 if (!motor) {
294 mtick = -1; /* stop motor kill countdown */
295 outb (FDC_DOR, 0x1c);
297 delay (100); /* delay 500ms for motor to spin up */
298 motor = true;
302 /* this turns the motor off */
303 void motoroff (void)
305 if (motor) {
306 // mtick = 13500; /* start motor kill countdown: 36 ticks ~ 2s */
308 outb (FDC_DOR, 0x0c);
309 outb (0x3f2, 0);
310 motor = false;
311 delay (200);
315 /* recalibrate the drive */
316 void recalibrate (void)
318 /* turn the motor on */
319 //motoron();
321 /* send actual command bytes */
322 sendbyte (CMD_RECAL);
323 sendbyte (0);
324 /* wait until seek finished */
325 waitfdc (true);
327 /* turn the motor off */
328 //motoroff();
332 /* seek to track */
333 bool seek (int track)
336 if (fdc_track == track) /* already there? */
337 return true;
339 //motoron();
340 /* send actual command bytes */
341 sendbyte (CMD_SEEK);
342 sendbyte (0);
343 sendbyte (track);
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 */
351 delay (20);
352 //motoroff();
353 /* check that seek worked */
354 if ((sr0 != 0x20) || (fdc_track != track)) {
355 if (sr0 != 0x20)
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");
361 return false;
363 else
364 return true;
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 */
371 reset();
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 */
380 if (g) {
381 g->heads = geometry.heads;
382 g->tracks = geometry.tracks;
383 g->spt = geometry.spt;
386 return true;
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 */
396 if (g) {
397 g->heads = geometry.heads;
398 g->tracks = geometry.tracks;
399 g->spt = geometry.spt;
402 return true;
405 /* it's not 1.44M or 1.68M - we don't support it */
406 return false;
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;
414 reset ();
416 //The FDC can read multiple sides at once but not multiple tracks
417 //printf("READ 1\n");
418 block2hts (block, &head, &track, &sector);
419 block2hts (block+nosectors, &head, &track2, &sector);
421 if(track != track2) {
422 for(loop = 0; (unsigned) loop < nosectors; loop ++)
423 result = fdc_rw (block+loop, blockbuff + (loop*512), true, 1);
425 return result;
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, &sector);
450 /* spin up the disk */
451 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|MOTORON");
452 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;
458 p_blockbuff++;
459 p_tbaddr++;
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 */
469 if (!seek (track)) {
470 motoroff ();
471 DPRINT (DBG_DRIVER | DBG_BLOCK, "FLOPPY | CANNOT SEEK\n");
472 return false;
475 /* check for diskchange */
476 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|DISKCHANGE");
478 if (inb (FDC_DIR) & 0x80) {
479 dchange = true;
480 seek (1); /* clear "disk change" status */
481 recalibrate ();
482 motoroff ();
483 /*fdc_diskchange ++;
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");
489 reset ();
490 motoroff();
491 return false;*/
493 //fdc_diskchange = 0;
495 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|RATE");
497 /* program data rate (500K/s) */
498 outb (FDC_CCR, 0);
500 /* send command */
501 if (read) {
502 //DPRINT (DBG_DRIVER | DBG_BLOCK, "FDC_RW|DMA");
503 dma_xfer (2, (unsigned long) floppy_tbaddr, nosectors * 512, false);
504 sendbyte (CMD_READ);
505 } else {
506 dma_xfer (2, (unsigned long) floppy_tbaddr, nosectors * 512, true);
507 sendbyte (CMD_WRITE);
510 sendbyte (head << 2);
511 sendbyte (track);
512 sendbyte (head);
513 sendbyte (sector);
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 */
519 else
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" */
527 if (!waitfdc(1)) {
528 kprintf ("Timed out, trying operation again after reset()\n");
529 reset ();
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! */
538 if (tries)
539 DPRINT (DBG_DRIVER | DBG_BLOCK, "Try %d error => read from floppy\n", tries);
541 recalibrate (); /* oops, try again... */
545 /* stop the motor */
546 //printf("FDC_RW|MOTOROFF\n");
547 motoroff ();
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;
557 p_blockbuff ++;
558 p_tbaddr ++;
560 } else
561 DPRINT (DBG_DRIVER | DBG_BLOCK, "FLOPPY | ERROR\n");
563 return (tries != 3);
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;
570 char tmpbuff[256];
571 unsigned char *p_tbaddr = (unsigned char *) floppy_tbaddr;
572 unsigned int copycount = 0;
574 /* check geometry */
575 if (g->spt != DG144_SPT && g->spt != DG168_SPT)
576 return false;
578 /* spin up the disk */
579 motoron ();
581 /* program data rate (500K/s) */
582 outb (FDC_CCR, 0);
584 seek (track); /* seek to track */
586 /* precalc some constants for interleave calculation */
587 split = g->spt / 2;
589 if (g->spt & 1)
590 split ++;
592 for (h = 0; h < g->heads; h ++) {
593 /* for each head... */
594 /* check for diskchange */
595 if (inb (FDC_DIR) & 0x80) {
596 dchange = true;
597 seek (1); /* clear "disk change" status */
598 recalibrate ();
599 motoroff ();
601 return false;
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) */
609 r_id = r / 2 + 1;
611 if (r & 1)
612 r_id += split;
614 /* add some head skew (2 sectors should be enough) */
615 if (h & 1) {
616 r_id -= 2;
618 if (r_id < 1)
619 r_id += g->spt;
621 /* add some track skew (1/2 a revolution) */
622 if (track & 1) {
623 r_id -= g->spt / 2;
625 if (r_id < 1)
626 r_id += g->spt;
629 /**** interleave now calculated - sector ID is stored in r_id ****/
631 /* fill in sector ID's */
632 tmpbuff[i ++] = track;
633 tmpbuff[i ++] = h;
634 tmpbuff[i ++] = r_id;
635 tmpbuff[i ++] = 2;
638 /* copy sector ID's to track buffer */
639 for (copycount = 0; copycount < (unsigned) i; copycount ++) {
640 *p_tbaddr = tmpbuff[copycount];
641 p_tbaddr ++;
643 //movedata(_my_ds(),(long)tmpbuff,_dos_ds,tbaddr,i);
645 /* start dma xfer */
646 dma_xfer (2, (unsigned long) floppy_tbaddr, i, true);
648 /* prepare "format track" command */
649 sendbyte (CMD_FORMAT);
650 sendbyte (h << 2);
651 sendbyte (2);
652 sendbyte (g->spt);
654 if (g->spt == DG144_SPT)
655 sendbyte (DG144_GAP3FMT); /* gap3 size for 1.44M format */
656 else
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))
663 return false;
665 if (status[0] & 0xc0) {
666 motoroff ();
667 return false;
671 motoroff ();
673 return true;
678 bool floppy_acthandler (unsigned act, partition_t *p, char *block, char *more, int n)
680 switch (act) {
681 case DEV_ACT_INIT:
683 if (!floppy_detect ())
684 return 0;
686 int_disable ();
687 floppy_install ();
688 int_enable ();
689 init_floppy ();
691 return 1;
693 break;
694 case DEV_ACT_READ:
696 read_block (n, block, 1);
697 return 1;
699 break;
700 case DEV_ACT_MOUNT:
702 int f = 0, t = 0;
703 if (!read_dir (-1))
704 return 0;
706 if (strlen (more)) {
707 if (!strcmp (more, "."))
708 return read_dir (0);
710 if (!strcmp (more, ".."))
711 return read_dir (1);
713 while (strlen (dir[f].name)) {
714 if (!strncmp (dir[f].name, more, strlen (more))) {
715 if (!read_dir (f))
716 return 0;
718 t = 1;
719 break;
722 f ++;
725 if (!t)
726 return 0;
729 f = 0;
730 unsigned fl = strlen (dir[f].name);
732 while (fl) {
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;
752 if (dir[f].read) {
753 attrib |= VFS_FILEATTR_READ;
754 attrib |= VFS_FILEATTR_WRITE;
756 if (dir[f].hidden)
757 attrib |= VFS_FILEATTR_HIDDEN;
758 if (dir[f].system)
759 attrib |= VFS_FILEATTR_SYSTEM;
760 if (dir[f].dir)
761 attrib |= VFS_FILEATTR_DIR;
762 else
763 attrib |= VFS_FILEATTR_FILE;
764 if (dir[f].bin)
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);
771 f ++;
772 fl = strlen (dir[f].name);
775 return 1;
777 break;
780 return 0;
782 #endif