/* GazOS Operating System Copyright (C) 1999 Gareth Owen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "dma.h" #include "fdc.h" #include "video.h" #include "delay.h" #include "gdt.h" #include "idt.h" #include "8259.h" #include "io.h" /* globals */ static volatile BOOL done = FALSE; static BOOL dchange = FALSE; static BOOL motor = FALSE; static int mtick = 0; static volatile int tmout = 0; static BYTE status[7] = { 0 }; static BYTE statsz = 0; static BYTE sr0 = 0; static BYTE fdc_track = 0xff; static DrvGeom geometry = { DG144_HEADS,DG144_TRACKS,DG144_SPT }; unsigned long tbaddr = 0x80000L; /* physical address of track buffer located below 1M */ /* prototypes */ extern void floppy_ISR(); extern void _int1c(); void sendbyte(int byte); int getbyte(); void irq6(void); void int1c(void); BOOL waitfdc(BOOL sensei); BOOL fdc_rw(int block,BYTE *blockbuff,BOOL read,unsigned long nosectors); /* helper functions */ /* init driver */ void init_floppy(void) { int i; /* allocate track buffer (must be located below 1M) */ /* see above for address assignment, floppy DMA buffer is at 0x80000) */ /* install IRQ6 handler */ set_vector(floppy_ISR, M_VEC+6, (D_INT+D_PRESENT+D_DPL1)); enable_irq(6); set_vector(_int1c, 0x1c, (D_INT+D_PRESENT+D_DPL1)); reset(); /* get floppy controller version */ sendbyte(CMD_VERSION); i = getbyte(); kprint("Initialising Floppy driver... "); if (i == 0x80) kprint("NEC765 controller found\n"); else kprint("Enhanced controller found\n"); } /* deinit driver */ //void deinit(void) //{ // set_irq_handler(6,NULL,&oldirq6); // kprint("uninstalling IRQ6 handler\n"); // set_irq_handler(0x1c,NULL,&oldint1c); // kprint("uninstalling timer handler\n"); // // /* stop motor forcefully */ // outportb(FDC_DOR,0x0c); //} /* sendbyte() routine from intel manual */ void sendbyte(int byte) { volatile int msr; int tmo; for (tmo = 0;tmo < 128;tmo++) { msr = inportb(FDC_MSR); if ((msr & 0xc0) == 0x80) { outportb(FDC_DATA,byte); return; } inportb(0x80); /* delay */ } } /* getbyte() routine from intel manual */ int getbyte() { volatile int msr; int tmo; for (tmo = 0;tmo < 128;tmo++) { msr = inportb(FDC_MSR); if ((msr & 0xd0) == 0xd0) { return inportb(FDC_DATA); } inportb(0x80); /* delay */ } return -1; /* read timeout */ } /* this waits for FDC command to complete */ BOOL waitfdc(BOOL sensei) { tmout = 50000; /* set timeout to 1 second */ /* wait for IRQ6 handler to signal command finished */ while (!done && tmout) ; /* read in command result bytes */ statsz = 0; while ((statsz < 7) && (inportb(FDC_MSR) & (1<<4))) { status[statsz++] = getbyte(); } if (sensei) { /* send a "sense interrupt status" command */ sendbyte(CMD_SENSEI); sr0 = getbyte(); fdc_track = getbyte(); } done = FALSE; if (!tmout) { /* timed out! */ if (inportb(FDC_DIR) & 0x80) /* check for diskchange */ dchange = TRUE; return FALSE; } else return TRUE; } /* This is the IRQ6 handler */ void irq6(void) { /* signal operation finished */ done = TRUE; /* EOI the PIC */ outportb(0x20,0x20); } /* This is the timer (int 1ch) handler */ void int1c(void) { if (tmout) --tmout; /* bump timeout */ if (mtick> 0) --mtick; else if (!mtick && motor) { outportb(FDC_DOR,0x0c); /* turn off floppy motor */ motor = FALSE; } } /* * converts linear block address to head/track/sector * * blocks are numbered 0..heads*tracks*spt-1 * blocks 0..spt-1 are serviced by head #0 * blocks spt..spt*2-1 are serviced by head 1 * * WARNING: garbage in == garbage out */ void block2hts(int block,int *head,int *track,int *sector) { *head = (block % (geometry.spt * geometry.heads)) / (geometry.spt); *track = block / (geometry.spt * geometry.heads); *sector = block % geometry.spt + 1; } /**** disk operations ****/ /* this gets the FDC to a known state */ void reset(void) { /* stop the motor and disable IRQ/DMA */ outportb(FDC_DOR,0); mtick = 0; motor = FALSE; /* program data rate (500K/s) */ outportb(FDC_DRS,0); /* re-enable interrupts */ outportb(FDC_DOR,0x0c); /* resetting triggered an interrupt - handle it */ done = TRUE; waitfdc(TRUE); /* specify drive timings (got these off the BIOS) */ sendbyte(CMD_SPECIFY); sendbyte(0xdf); /* SRT = 3ms, HUT = 240ms */ sendbyte(0x02); /* HLT = 16ms, ND = 0 */ /* clear "disk change" status */ seek(1); recalibrate(); dchange = FALSE; } /* this returns whether there was a disk change */ BOOL diskchange(void) { return dchange; } /* this turns the motor on */ void motoron(void) { if (!motor) { mtick = -1; /* stop motor kill countdown */ outportb(FDC_DOR,0x1c); delay(500); /* delay 500ms for motor to spin up */ motor = TRUE; } } /* this turns the motor off */ void motoroff(void) { if (motor) { mtick = 13500; /* start motor kill countdown: 36 ticks ~ 2s */ } } /* recalibrate the drive */ void recalibrate(void) { /* turn the motor on */ motoron(); /* send actual command bytes */ sendbyte(CMD_RECAL); sendbyte(0); /* wait until seek finished */ waitfdc(TRUE); /* turn the motor off */ motoroff(); } /* seek to track */ BOOL seek(int track) { if (fdc_track == track) /* already there? */ return TRUE; // motoron(); /* send actual command bytes */ sendbyte(CMD_SEEK); sendbyte(0); sendbyte(track); /* wait until seek finished */ if (!waitfdc(TRUE)) return FALSE; /* timeout! */ /* now let head settle for 15ms */ delay(15); // motoroff(); /* check that seek worked */ if ((sr0 != 0x20) || (fdc_track != track)) return FALSE; else return TRUE; } /* checks drive geometry - call this after any disk change */ BOOL log_disk(DrvGeom *g) { /* get drive in a known status before we do anything */ reset(); /* assume disk is 1.68M and try and read block #21 on first track */ geometry.heads = DG168_HEADS; geometry.tracks = DG168_TRACKS; geometry.spt = DG168_SPT; if (read_block(20,NULL,1)) { /* disk is a 1.68M disk */ if (g) { g->heads = geometry.heads; g->tracks = geometry.tracks; g->spt = geometry.spt; } return TRUE; } /* OK, not 1.68M - try again for 1.44M reading block #18 on first track */ geometry.heads = DG144_HEADS; geometry.tracks = DG144_TRACKS; geometry.spt = DG144_SPT; if (read_block(17,NULL,1)) { /* disk is a 1.44M disk */ if (g) { g->heads = geometry.heads; g->tracks = geometry.tracks; g->spt = geometry.spt; } return TRUE; } /* it's not 1.44M or 1.68M - we don't support it */ return FALSE; } /* read block (blockbuff is 512 byte buffer) */ BOOL read_block(int block,BYTE *blockbuff, unsigned long nosectors) { int track=0, sector=0, head=0, track2=0, result=0, loop=0; // The FDC can read multiple sides at once but not multiple tracks block2hts(block, &head, &track, §or); block2hts(block+nosectors, &head, &track2, §or); if(track!=track2) { for(loop=0; loopspt != DG144_SPT && g->spt != DG168_SPT) return FALSE; /* spin up the disk */ motoron(); /* program data rate (500K/s) */ outportb(FDC_CCR,0); seek(track); /* seek to track */ /* precalc some constants for interleave calculation */ split = g->spt / 2; if (g->spt & 1) split++; for (h = 0;h < g->heads;h++) { /* for each head... */ /* check for diskchange */ if (inportb(FDC_DIR) & 0x80) { dchange = TRUE; seek(1); /* clear "disk change" status */ recalibrate(); motoroff(); return FALSE; } i = 0; /* reset buffer index */ for (r = 0;r < g->spt;r++) { /* for each sector... */ /* calculate 1:2 interleave (seems optimal in my system) */ r_id = r / 2 + 1; if (r & 1) r_id += split; /* add some head skew (2 sectors should be enough) */ if (h & 1) { r_id -= 2; if (r_id < 1) r_id += g->spt; } /* add some track skew (1/2 a revolution) */ if (track & 1) { r_id -= g->spt / 2; if (r_id < 1) r_id += g->spt; } /**** interleave now calculated - sector ID is stored in r_id ****/ /* fill in sector ID's */ tmpbuff[i++] = track; tmpbuff[i++] = h; tmpbuff[i++] = r_id; tmpbuff[i++] = 2; } /* copy sector ID's to track buffer */ for(copycount = 0; copycountspt); if (g->spt == DG144_SPT) sendbyte(DG144_GAP3FMT); /* gap3 size for 1.44M format */ else sendbyte(DG168_GAP3FMT); /* gap3 size for 1.68M format */ sendbyte(0); /* filler byte */ /* wait for command to finish */ if (!waitfdc(FALSE)) return FALSE; if (status[0] & 0xc0) { motoroff(); return FALSE; } } motoroff(); return TRUE; } asm ( ".globl _int1c \n" "_int1c: \n" " pusha \n" /* Save all registers */ " pushw %ds \n" /* Set up the data segment */ " pushw %es \n" " pushw %ss \n" /* Note that ss is always valid */ " pushw %ss \n" " popw %ds \n" " popw %es \n" " \n" " call int1c \n" " \n" " popw %es \n" " popw %ds \n" /* Restore registers */ " popa \n" " iret \n" /* Exit interrupt */ ); asm ( ".globl floppy_ISR \n" "floppy_ISR: \n" " pusha \n" /* Save all registers */ " pushw %ds \n" /* Set up the data segment */ " pushw %es \n" " pushw %ss \n" /* Note that ss is always valid */ " pushw %ss \n" " popw %ds \n" " popw %es \n" " \n" " call irq6 \n" " \n" " popw %es \n" " popw %ds \n" /* Restore registers */ " popa \n" " iret \n" /* Exit interrupt */ );

AltStyle によって変換されたページ (->オリジナル) /