📄 fdc.c
字号:
/*GazOS Operating SystemCopyright (C) 1999 Gareth Owen <gaz@athene.co.uk>This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, 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; loop<nosectors; loop++) result = fdc_rw(block+loop, blockbuff+(loop*512), TRUE, 1); return result; } return fdc_rw(block,blockbuff,TRUE,nosectors);
}
/* write block (blockbuff is 512 byte buffer) */
BOOL write_block(int block,BYTE *blockbuff, unsigned long nosectors)
{
return fdc_rw(block,blockbuff,FALSE, nosectors);
}
/*
* since reads and writes differ only by a few lines, this handles both. This
* function is called by read_block() and write_block()
*/
BOOL fdc_rw(int block,BYTE *blockbuff,BOOL read,unsigned long nosectors)
{
int head,track,sector,tries, copycount = 0;
unsigned char *p_tbaddr = (char *)0x80000; unsigned char *p_blockbuff = blockbuff;
/* convert logical address into physical address */
block2hts(block,&head,&track,§or);
/* spin up the disk */
motoron();
if (!read && blockbuff) {
/* copy data from data buffer into track buffer */
for(copycount=0; copycount<(nosectors*512); copycount++) { *p_tbaddr = *p_blockbuff; p_blockbuff++; p_tbaddr++; } }
for (tries = 0;tries < 3;tries++) {
/* check for diskchange */
if (inportb(FDC_DIR) & 0x80) {
dchange = TRUE;
seek(1); /* clear "disk change" status */
recalibrate();
motoroff();
kprint("FDC: Disk change detected. Trying again.\n"); return fdc_rw(block, blockbuff, read, nosectors); }
/* move head to right track */
if (!seek(track)) {
motoroff();
kprint("FDC: Error seeking to track\n"); return FALSE;
}
/* program data rate (500K/s) */
outportb(FDC_CCR,0);
/* send command */
if (read) {
dma_xfer(2,tbaddr,nosectors*512,FALSE);
sendbyte(CMD_READ);
} else {
dma_xfer(2,tbaddr,nosectors*512,TRUE);
sendbyte(CMD_WRITE);
}
sendbyte(head << 2);
sendbyte(track);
sendbyte(head);
sendbyte(sector);
sendbyte(2); /* 512 bytes/sector */
sendbyte(geometry.spt);
if (geometry.spt == DG144_SPT)
sendbyte(DG144_GAP3RW); /* gap 3 size for 1.44M read/write */
else
sendbyte(DG168_GAP3RW); /* gap 3 size for 1.68M read/write */
sendbyte(0xff); /* DTL = unused */
/* wait for command completion */
/* read/write don't need "sense interrupt status" */
if (!waitfdc(TRUE)) {
kprint("Timed out, trying operation again after reset()\n"); reset(); return fdc_rw(block, blockbuff, read, nosectors); }
if ((status[0] & 0xc0) == 0) break; /* worked! outta here! */
recalibrate(); /* oops, try again... */
}
/* stop the motor */
motoroff();
if (read && blockbuff) {
/* copy data from track buffer into data buffer */
p_blockbuff = blockbuff; p_tbaddr = (char *) 0x80000; for(copycount=0; copycount<(nosectors*512); copycount++) { *p_blockbuff = *p_tbaddr; p_blockbuff++; p_tbaddr++; } }
return (tries != 3);
}
/* this formats a track, given a certain geometry */
BOOL format_track(BYTE track,DrvGeom *g)
{
int i,h,r,r_id,split;
BYTE tmpbuff[256];
unsigned char *p_tbaddr = (char *)0x8000; unsigned int copycount = 0;
/* check geometry */
if (g->spt != 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; copycount<i; copycount++) { *p_tbaddr = tmpbuff[copycount]; p_tbaddr++; }// movedata(_my_ds(),(long)tmpbuff,_dos_ds,tbaddr,i);
/* start dma xfer */
dma_xfer(2,tbaddr,i,TRUE);
/* prepare "format track" command */
sendbyte(CMD_FORMAT);
sendbyte(h << 2);
sendbyte(2);
sendbyte(g->spt);
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 */);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -