📄 hdd.c
字号:
*/
/* reset the HDC - hd_reset resets the controller (which controlls
both drives). We have to do it once, then try both physical drives.
If the second drive is not there, some controllers will lock-up
(the el-cheapos). In this case we have to reset it again so it
will work. It seems like a lot of work, but to make it function
with the widest range of IDE and MFM controllers this is the
only way I have found that works.
*/
hd_reset(); /* no error is returned */
/* Now we attempt to select and recal both drives.
The driver MUST be able to recal the first physical drive
or the Driver won't install.
*/
erc = hd_recal(0); /* try to recal */
if (erc) { /* try one more time! */
hd_reset();
erc = hd_recal(0); /* try to recal */
if (erc) {
hdcb[0].last_erc = erc;
hd0_type = 0; /* Must not be a valid drive */
return(ErcNoDrive0);
}
}
/* if we got here, drive 0 looks OK and the controller is
functioning. Now we try drive 1 if type > 0.
*/
if (hd1_type) {
erc = hd_recal(1); /* try to recal if CMOS says it's there */
if (erc) {
hdcb[1].last_erc = erc;
hd1_type = 0; /* Guess it's not a valid drive */
if (!erc)
/* We must redo drive 0 cause some cheap controllers lockup
on us if drive 1 is not there. They SHOULD simply return
a Bad Command bit set in the Error register, but they don't. */
hd_reset();
erc = hd_recal(0); /* recal drive 0 */
hdcb[0].last_erc = erc;
}
}
return(erc = InitDevDr(12, &hdcb, 2, 1));
}
/************************************************************
Reset the HD controller. This should only be called by
DeviceInit or hdisk_setup. This resets the controller
and reloads parameters for both drives (if present) and
attempts to recal them.
*************************************************************/
void hd_reset(void)
{
U32 i;
UnMaskIRQ(14); /* enable the IRQ */
OutByte(4, HD_REG_PORT); /* reset the controller */
MicroDelay(4); /* Delay 60us */
/* bit 3 of HD_REG must be 1 for access to heads 8-15 */
/* Clear "MUCHO" heads bit, and clear the reset bit */
OutByte(hd_control & 0x0f, HD_REG_PORT);
Sleep(20); /* 200ms - seems some controllers are SLOW!! */
i = CheckMsg(hd_exch, &hd_msg); /* Eat Int if one came back */
hdstatus.ResetStatByte = statbyte; /* The ISR gets statbyte */
if (i) hdstatus.fIntOnReset = 1;
else hdstatus.fIntOnReset = 0;
}
/*************************************************************
The ISR is VERY simple. It just waits for an interrupt, gets
the single status byte from the controller (which clears the
interrupt condition) then sends an empty message to the
exchange where the HD Driver task will be waiting.
This tells the HD task currently running that it's got
some status to act on!
****************************************************************/
void interrupt hdisk_isr(void)
{
statbyte = InByte(HD_PORT+7);
HDDInt = 1;
ISendMsg(hd_exch, 0xfffffff0, 0xfffffff0);
EndOfIRQ(14);
}
/*************************************************************
This checks the HDC controller to see if it's busy so we can
send it commands or read the rest of the registers.
We will wait up to 3 seconds then error out.
The caller should call check_busy and check the error.
If it's 0 then the controller became ready in less than
3 seconds. ErcNotReady will be returned otherwise.
It leaves the status byte in the global statbyte.
****************************************************************/
U32 check_busy(void)
{
S16 count;
count = 0;
while (count++ < 60) {
statbyte = InByte(HD_PORT+7);
if ((statbyte & BUSY) == 0) return(ok);
Sleep(5); /* 50ms shots */
}
return(ErcNotReady); /* controller out to lunch! */
}
/*************************************************************
This sends the SetParams command to the controller to set
up the drive geometry (nHeads, nSectors, etc.).
****************************************************************/
U32 hd_init(U8 drive)
{
U32 erc;
/* set max heads, sectors and cylinders */
if (drive == 0) { /* Drive 0 */
hd_Cmd[2] = hd0_secpertrk; /* sector count */
hd_Cmd[6] = (drive << 4) | ((hd0_heads-1) & 0x0f) | 0xa0; /* hds & drv */
}
else { /* Drive 1 */
hd_Cmd[2] = hd1_secpertrk; /* sector count */
hd_Cmd[6] = (drive << 4) | ((hd1_heads-1) & 0x0f) | 0xa0; /* hds & drv */
}
hd_Cmd[1] = 0;
hd_Cmd[3] = 0;
hd_Cmd[4] = 0; /* cyl = 0 for init */
hd_Cmd[5] = 0; /* cyl = 0 for init */
erc = send_command(HDC_SET_PARAMS); /* Send the command */
erc = hd_wait(); /* wait for interrupt */
if (!erc)
erc = hd_status(HDC_SET_PARAMS);
return(erc);
}
/******************************************
Wait for the hardware interrupt to occur.
Time-out and return if no interrupt.
********************************************/
U32 hd_wait(void)
{
U32 erc;
U8 fMsg;
/* Set alarm for 3 seconds */
HDDInt = 0;
KillAlarm(hd_exch); /* kill any pending alarm */
erc = Alarm(hd_exch, 300); /* Set it up again */
if (erc)
return(erc); /* bad problem */
/* We call check in a loop with a small sleep in between because
the ISR is using ISend which doesn't force a Task Switch */
/*
fMsg = 0;
while (!fMsg) {
erc = CheckMsg(hd_exch, &hd_msg);
if (!erc)
fMsg = 1;
else if (erc == ErcNoMsg)
Sleep(2);
else {
KillAlarm(hd_exch);
return (erc);
}
}
*/
erc = WaitMsg(hd_exch, &hd_msg);
KillAlarm(hd_exch);
if (hd_msg != 0xfffffff0) { /* HD interrupt sends fffffff0 */
if (HDDInt)
return(ErcMissHDDInt);
else
return(ErcHDCTimeOut); /* Alarm sends 0xffffffff */
}
else {
KillAlarm(hd_exch);
return(ok);
}
}
/********************************************
Recalibrate the drive.
*********************************************/
U32 hd_recal(U8 drive)
{
U32 erc;
hd_Cmd[6] = (drive << 4) | (hd_head & 0x0f) | 0xa0;
erc = send_command(HDC_RECAL);
if (!erc)
erc = hd_wait(); /* wait for interrupt */
if (!erc)
erc = hd_status(HDC_RECAL);
if (drive)
hdstatus.LastRecalErc1 = erc;
else
hdstatus.LastRecalErc0 = erc;
return(erc);
}
/********************************************
Send the command to the controller.
Clear the Echange of any left over
alarm or int messages before we
send a command.
*********************************************/
U32 send_command(U8 Cmd)
{
U32 erc, msg[2];
while (CheckMsg(hd_exch, &msg) == 0); /* Empty it */
/* bit 3 of HD_REG must be 1 for access to heads 8-15 */
if (hd_head > 7) {
hd_control |= 0x08;
OutByte(hd_control, HD_REG_PORT); /* set bit for head > 7 */
hd_control &= 0xf7;
}
erc = check_busy();
if (!erc) OutByte(hd_Cmd[1], HD_PORT+1);
if (!erc) erc = check_busy();
if (!erc) OutByte(hd_Cmd[2], HD_PORT+2);
if (!erc) erc = check_busy();
if (!erc) OutByte(hd_Cmd[3], HD_PORT+3);
if (!erc) erc = check_busy();
if (!erc) OutByte(hd_Cmd[4], HD_PORT+4);
if (!erc) erc = check_busy();
if (!erc) OutByte(hd_Cmd[5], HD_PORT+5);
if (!erc) erc = check_busy();
if (!erc) OutByte(hd_Cmd[6], HD_PORT+6);
if (!erc) erc = check_busy();
if (!erc) OutByte(Cmd, HD_PORT+7);
return(erc);
}
/*************************************************************
This sets up the cylinder, head and sector variables for all
commands that require them (read, write, verify, format, seek).
nBlks ca NOT be greater than the hardware can handle. For
IDE/MFM controllers this is 128 sectors.
The caculated values are placed in the proper command byte
in anticipation of the command being sent.
*************************************************************/
U32 setupseek(U32 dLBA, U32 nBlks)
{
U32 j;
U16 cyl;
if (nBlks > 256) return ErcTooManyBlks;
if (nBlks == 0) return ErcZeroBlks;
hd_nsectors = nBlks;
if (hd_nsectors == 256) hd_nsectors = 0; /* 0==256 for controller */
if (hd_drive == 0) { /* drive 0 */
cyl = dLBA / (hd0_heads * hd0_secpertrk);
j = dLBA % (hd0_heads * hd0_secpertrk); /* remainder */
/* we now know what cylinder, calculate head and sector */
hd_head = j / hd0_secpertrk;
hd_sector = j % hd0_secpertrk + 1; /* sector number start at 1 !!! */
}
else { /* drive 1 */
cyl = dLBA / (hd1_heads * hd1_secpertrk);
j = dLBA % (hd1_heads * hd1_secpertrk); /* remainder */
/* we now know what cylinder, calculate head and sector */
hd_head = j / hd1_secpertrk;
hd_sector = j % hd1_secpertrk + 1; /* sector number start at 1 !!! */
}
hd_Cmd[2] = nBlks; /* How many sectors */
hd_Cmd[3] = hd_sector; /* Which sector to start on */
hd_Cmd[4] = cyl & 0xff; /* cylinder lobyte */
hd_Cmd[5] = (cyl >> 8) & 0xff; /* cylinder hibyte */
hd_Cmd[6] = (hd_drive << 4) | (hd_head & 0x0f) | 0xa0;
return ok;
}
/*******************************************************
Move the head to the selected track (cylinder).
*********************************************************/
U32 hd_seek(U32 dLBA)
{
U32 erc;
erc = setupseek(dLBA, 1); /* sets up for implied seek */
if (!erc) erc = send_command(HDC_SEEK); /* Not implied anymore... */
if (!erc) erc = hd_wait(); /* wait for interrupt */
if (!erc) erc = hd_status(HDC_SEEK);
hdstatus.LastSeekErc0 = erc;
return(erc);
}
/*******************************************************
Called to read status and errors from the controller
after an interrupt generated by a command we sent.
The error checking is based on the command that we sent.
This is done because certain bits in the status and error
registers are actually not errors, but simply indicate
status or indicate an action we must take next.
ZERO returned indicates no errors for the command status
we are checking.
*********************************************************/
U32 hd_status(U8 LastCmd)
{
U32 erc;
U8 statbyte, errbyte;
/* We shouldn't see the controller busy. After all,
he interrupted us with status.
*/
erc = check_busy(); /* puts status byte into global StatByte */
if (!erc)
statbyte = InByte(HD_PORT+7);
else return(erc);
if (hd_drive)
hdstatus.LastStatByte1 = statbyte;
else
hdstatus.LastStatByte0 = statbyte;
if ((statbyte & ERROR) == 0) { /* Error bit not set in status reg */
erc = ok; /* default */
switch (LastCmd) {
case HDC_READ:
case HDC_READ_LONG:
case HDC_WRITE:
case HDC_WRITE_LONG:
case HDC_SEEK:
case HDC_RECAL:
if (statbyte & WRITE_FAULT) erc = ErcWriteFault;
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -