FDD problem
Posted: Tue Dec 20, 2011 6:52 pm
Hi everyone,
I've very recently decided to pick up OSdev again as another project is on hold. I decided to write the FDD driver for my os, in bochs it works perfectly yet on physical hardware the sector isn't read (I'm testing with sector 0). Before trying to read everything I first call init_FDD().
On two laptops the sector doesn't get read, and ST0 is 0x40 where ST1 is 0x01 after the read command.
Does anyone know what's wrong here?
Thank you for the effort.
I've very recently decided to pick up OSdev again as another project is on hold. I decided to write the FDD driver for my os, in bochs it works perfectly yet on physical hardware the sector isn't read (I'm testing with sector 0). Before trying to read everything I first call init_FDD().
On two laptops the sector doesn't get read, and ST0 is 0x40 where ST1 is 0x01 after the read command.
Does anyone know what's wrong here?
Thank you for the effort.
Code: Select all
#include "fdd.h"
#include "port.h"
#include "sleep.h"
#include "vidstub.h"
#define SECTORS 18
#define HEADS 2
#define CYLINDERS 63
#define FDD_DOR 0x3F2
#define FDD_MSR 0x3F4
#define FDD_FIFO 0x3F5
#define FDD_CTRL 0x3F7
char FDD_int;
char init_FDD()
{
FDD_int = 0;
//Reset the controller
outb(FDD_DOR,0);
//Restart the controller
outb(FDD_DOR,0x0c); //DMA + enable
while(FDD_int == 0){}; //Include a time-out someday
string_out("Reset--IRQ\n\0"); //DEBUG
unsigned int lc;
for(lc=0;lc<4;lc++)
{
FDD_send_cmd(FDD_CMD_SENSE_INT);
FDD_recv_cmd();
FDD_recv_cmd();
}
string_out("Reset SIs handled\n\0");
//Configure
FDD_send_cmd(0x13);
FDD_send_cmd(0);
FDD_send_cmd(0x28);
FDD_send_cmd(0);
//Send the specify command
FDD_send_cmd(FDD_CMD_SPECIFY);
FDD_send_cmd(0xbf); /* SRT = 5ms, HUT = 240ms */
FDD_send_cmd(0x02); /* HLT = 16ms, ND = 0 */ //Got these from GazOS
string_out("END_SPECIFY\n\0"); //DEBUG
return 0;
}
char get_sector(unsigned short LBA,unsigned int address)
{
if((address & 0xFF000000) > 0)
{
string_out("Memory address too high for DMA\n\0");
return 1; //Memory address too high
};
CHS_FDD_t my_CHS;
my_CHS = LBA_to_CHS(LBA); //Get CHS value
//Initiate the motor (aftewards, wait for 300 ms)
outb(FDD_DOR,FDD_DOR_MASK_DRIVE0_MOTOR | FDD_DOR_MASK_RESET | FDD_DOR_MASK_DMA);
b_sleep(600); //Sleep for 300 milliseconds
string_out("START_MOTOR\n\0"); //DEBUG
unsigned char loop_count;
//Calibrate
for(loop_count = 0;loop_count < 10;loop_count++)
{
if(FDD_calibrate() == 0)
{
loop_count = 0;
break;
}
}
if(loop_count != 0)
{
string_out("Error with calibrate command\n\0");
return 1;
}
//Seek
for(loop_count = 0;loop_count < 10;loop_count++)
{
if(FDD_seek(my_CHS) == 0)
{
loop_count = 0;
break;
}
}
if(loop_count != 0)
{
string_out("Error with seek command\n\0");
return 1;
}
//Actual read
for(loop_count = 0;loop_count < 10;loop_count++)
{
init_dma(address);
if(FDD_read(my_CHS) == 0)
{
loop_count = 0;
break;
}
}
if(loop_count != 0)
{
string_out("Error with read command\n\0");
return 1;
}
return 0;
}
CHS_FDD_t LBA_to_CHS(unsigned short LBA)
{
CHS_FDD_t new_CHS;
new_CHS.cylinder = (LBA / (SECTORS * HEADS)); //Wikipedia
new_CHS.head = ((LBA / SECTORS)%HEADS);
new_CHS.sectors = ((LBA % SECTORS) + 1);
return new_CHS;
}
char FDD_calibrate()
{
FDD_int = 0;
FDD_send_cmd(FDD_CMD_CALIBRATE);
FDD_send_cmd(0);
while(FDD_int == 0){}; //Include a time-out someday
string_out("Calibrate--IRQ\n\0"); //DEBUG
FDD_send_cmd(FDD_CMD_SENSE_INT); //Must be done to check status of calibrate command
char PCN,ST0;
ST0 = FDD_recv_cmd();
PCN = FDD_recv_cmd();
hint_out(ST0,3);
b_sleep(1000);
if(PCN == 0 && ST0 == 0x20)
{
return 0;
}
else
{
return 1;
}
}
char FDD_seek(CHS_FDD_t my_CHS)
{
FDD_int = 0;
FDD_send_cmd(FDD_CMD_SEEK);
unsigned char cmd_byte = 0;
cmd_byte = my_CHS.head << 2;
FDD_send_cmd(cmd_byte); //Send the x00 with x = head;
cmd_byte = my_CHS.cylinder;
FDD_send_cmd(cmd_byte); //Send the New Cylinder Number
while(FDD_int == 0){}; //Include a time-out someday
string_out("Seek--IRQ\n\0"); //DEBUG
FDD_send_cmd(FDD_CMD_SENSE_INT); //Must be done to check status of seek command
char ST0 = FDD_recv_cmd();
char PCN = FDD_recv_cmd();
if(PCN != my_CHS.cylinder || ST0 != 0x20)
{
return 1;
}
else
{
return 0;
}
}
char FDD_read(CHS_FDD_t my_CHS)
{
//Read command
string_out("bla\n\0");
hint_out(my_CHS.cylinder,3);
hint_out(my_CHS.head,3);
hint_out(my_CHS.sectors,3);
FDD_int = 0;
FDD_send_cmd(0xE0 | FDD_CMD_READ_SECT); //0xC0 == double density + MT
unsigned char cmd_byte = my_CHS.head << 2;
FDD_send_cmd(cmd_byte); //Send the x00 with x = head;
string_out(" cmd_byte head\n\0");
hint_out(cmd_byte,3);
cmd_byte = my_CHS.cylinder;
FDD_send_cmd(cmd_byte); //Send the New Cylinder Number
cmd_byte = my_CHS.head;
FDD_send_cmd(cmd_byte); //Send head again (stupid controller)
cmd_byte = my_CHS.sectors;
FDD_send_cmd(cmd_byte); //Send the Sector
FDD_send_cmd(2); //Sector size, 2 = 512
FDD_send_cmd(SECTORS); //EOT
FDD_send_cmd(0x1B); //GPL
FDD_send_cmd(0xFF); //DTL
b_sleep(1000);
while(FDD_int == 0){}; //Include a time-out someday
string_out("Read--IRQ\n\0"); //DEBUG
b_sleep(500);
unsigned char ST0,ST1,ST2,C,H,R,N;
ST0 = FDD_recv_cmd();
ST1 = FDD_recv_cmd();
ST2 = FDD_recv_cmd();
C = FDD_recv_cmd();
H = FDD_recv_cmd();
R = FDD_recv_cmd();
N = FDD_recv_cmd();
hint_out(C,3);
hint_out(H,3);
hint_out(R,3);
string_out(" ST0/1/2\n\0");
hint_out(ST0,3);
hint_out(ST1,3);
hint_out(ST2,3);
if(my_CHS.sectors == SECTORS)
{
if(my_CHS.head == 0)
{
if(my_CHS.head != H && R == 1) //success
{
return 0;
}
}
else if(my_CHS.head != H && R == 1 && C == (my_CHS.cylinder+1)) //success
{
return 0;
}
}
if(my_CHS.sectors < SECTORS)
{
if(R == (my_CHS.sectors+1)) //Success
{
return 0;
}
}
return 1;
}
char FDD_send_cmd(unsigned char command)
{
unsigned char MSR_Status;
while(1)
{
MSR_Status = inb(FDD_MSR);
if(MSR_Status & FDD_MSR_MASK_DATAREG)
{
break;
}
}
outb(FDD_FIFO,command);
b_sleep(10);
return 0;
}
unsigned char FDD_recv_cmd()
{
unsigned char MSR_Status;
while(1)
{
MSR_Status = inb(FDD_MSR);
if(MSR_Status & FDD_MSR_MASK_DATAREG)
{
break;
}
}
unsigned char retval;
retval = inb(FDD_FIFO);
b_sleep(10);
return retval;
}
void FDD_IRQ()
{
FDD_int = 1;
outb(0x20, 0x20); //Reset the master
return;
}
char init_fdd()
{
return 0;
}
char init_dma(unsigned int address)
{
outb(0x0A,0x06); //Mask DMA channel
//reset the flip-flop
outb(0xD8,0xFF);
outb(0x0C,0xFF);
unsigned char addr_part;
addr_part = address; //Get lower part
outb(0x04,addr_part);
addr_part = address >> 8; //Get higher part
outb(0x04,addr_part);
addr_part = address >> 16; //Get page
outb(0x81,addr_part);
//reset the flip-flop
outb(0xD8,0xFF);
outb(0x0C,0xFF);
//Set the sector size (0x1FF)
outb(0x05,0xFF);
outb(0x05,0x01);
//Mode of operation -- 00000110, demand transfer,to mem, channel 2
outb(0x0b,0x06);
//unmask the channel
outb(0x0A,0x02);
return 0;
}