feat: add ataIoDelay and ataWaitClear
This commit is contained in:
1 parent
c970e99fc9
commit
54242aeae3
1 file changed
+199
@@ -0,0 +1,199 @@
|
|||||||
|
#include "ata.h"
|
||||||
|
#include "ports.h"
|
||||||
|
|
||||||
|
#define ataRegData 0x1F0
|
||||||
|
#define ataRegError 0x1F1
|
||||||
|
#define ataRegSectorCnt 0x1F2
|
||||||
|
#define ataRegLbaLow 0x1F3
|
||||||
|
#define ataRegLbaMid 0x1F4
|
||||||
|
#define ataRegLbaHigh 0x1F5
|
||||||
|
#define ataRegDriveHead 0x1F6
|
||||||
|
#define ataRegStatus 0x1F7
|
||||||
|
#define ataRegCommand 0x1F7
|
||||||
|
#define ataRegControl 0x3F6
|
||||||
|
|
||||||
|
#define ataStatusErr 0x01
|
||||||
|
#define ataStatusDrq 0x08
|
||||||
|
#define ataStatusDf 0x20
|
||||||
|
#define ataStatusBsy 0x80
|
||||||
|
|
||||||
|
#define ataCmdReadSectors 0x20
|
||||||
|
#define ataCmdWriteSectors 0x30
|
||||||
|
#define ataCmdCacheFlush 0xE7
|
||||||
|
#define ataCmdIdentify 0xEC
|
||||||
|
|
||||||
|
#define ataMaxSectorsPerCommand 255
|
||||||
|
#define ataPollTimeout 1000000u
|
||||||
|
|
||||||
|
static bool diskPresent;
|
||||||
|
static uint64 totalSectors;
|
||||||
|
static char modelBuffer[41];
|
||||||
|
|
||||||
|
static void ataIoDelay(void)
|
||||||
|
{
|
||||||
|
/* Selecting a drive needs ~400ns to settle; reading the alternate
|
||||||
|
status register is a documented no-op read that burns exactly that
|
||||||
|
long without disturbing the main status register. */
|
||||||
|
inb(ataRegControl);
|
||||||
|
inb(ataRegControl);
|
||||||
|
inb(ataRegControl);
|
||||||
|
inb(ataRegControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ataWaitClear(uint8 mask)
|
||||||
|
{
|
||||||
|
for (uint32 i = 0; i < ataPollTimeout; i++)
|
||||||
|
if (!(inb(ataRegStatus) & mask))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ataWaitDrqOrErr(bool* err)
|
||||||
|
{
|
||||||
|
for (uint32 i = 0; i < ataPollTimeout; i++) {
|
||||||
|
uint8 status = inb(ataRegStatus);
|
||||||
|
if (status & (ataStatusErr | ataStatusDf)) {
|
||||||
|
*err = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (status & ataStatusDrq) {
|
||||||
|
*err = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ataSelect(uint64 lba, uint8 count)
|
||||||
|
{
|
||||||
|
outb(ataRegDriveHead, (uint8)(0xE0 | ((lba >> 24) & 0x0F)));
|
||||||
|
ataIoDelay();
|
||||||
|
outb(ataRegSectorCnt, count);
|
||||||
|
outb(ataRegLbaLow, (uint8)(lba & 0xFF));
|
||||||
|
outb(ataRegLbaMid, (uint8)((lba >> 8) & 0xFF));
|
||||||
|
outb(ataRegLbaHigh, (uint8)((lba >> 16) & 0xFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ataInit(void)
|
||||||
|
{
|
||||||
|
diskPresent = false;
|
||||||
|
totalSectors = 0;
|
||||||
|
modelBuffer[0] = 0;
|
||||||
|
|
||||||
|
outb(ataRegControl, 0x02); /* nIEN: this driver only ever polls */
|
||||||
|
|
||||||
|
outb(ataRegDriveHead, 0xA0); /* master, no chs/lba bits needed yet */
|
||||||
|
ataIoDelay();
|
||||||
|
outb(ataRegSectorCnt, 0);
|
||||||
|
outb(ataRegLbaLow, 0);
|
||||||
|
outb(ataRegLbaMid, 0);
|
||||||
|
outb(ataRegLbaHigh, 0);
|
||||||
|
outb(ataRegCommand, ataCmdIdentify);
|
||||||
|
|
||||||
|
if (inb(ataRegStatus) == 0)
|
||||||
|
return false; /* floating bus: no drive wired up on this channel */
|
||||||
|
|
||||||
|
if (!ataWaitClear(ataStatusBsy))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* A plain PATA hard disk leaves LBA mid/high at 0 after IDENTIFY; an
|
||||||
|
ATAPI or SATA-bridge device stamps a signature there instead. This
|
||||||
|
driver only supports the former. */
|
||||||
|
if (inb(ataRegLbaMid) != 0 || inb(ataRegLbaHigh) != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool err = false;
|
||||||
|
if (!ataWaitDrqOrErr(&err) || err)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint16 identifyWords[256];
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
identifyWords[i] = inw(ataRegData);
|
||||||
|
|
||||||
|
totalSectors = (uint64)identifyWords[60] | ((uint64)identifyWords[61] << 16);
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
uint16 word = identifyWords[27 + i];
|
||||||
|
modelBuffer[i * 2] = (char)(word >> 8);
|
||||||
|
modelBuffer[i * 2 + 1] = (char)(word & 0xFF);
|
||||||
|
}
|
||||||
|
modelBuffer[40] = 0;
|
||||||
|
for (int i = 39; i >= 0 && modelBuffer[i] == ' '; i--)
|
||||||
|
modelBuffer[i] = 0;
|
||||||
|
|
||||||
|
diskPresent = totalSectors > 0;
|
||||||
|
return diskPresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ataPresent(void)
|
||||||
|
{
|
||||||
|
return diskPresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 ataSectorCount(void)
|
||||||
|
{
|
||||||
|
return totalSectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ataModelString(void)
|
||||||
|
{
|
||||||
|
return modelBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ataReadSectors(uint64 lba, uint32 count, void* buffer)
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
return true;
|
||||||
|
if (!diskPresent || count > ataMaxSectorsPerCommand || lba + count > totalSectors)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ataWaitClear(ataStatusBsy))
|
||||||
|
return false;
|
||||||
|
ataSelect(lba, (uint8)count);
|
||||||
|
outb(ataRegCommand, ataCmdReadSectors);
|
||||||
|
|
||||||
|
uint16* dst = (uint16*)buffer;
|
||||||
|
for (uint32 s = 0; s < count; s++) {
|
||||||
|
if (!ataWaitClear(ataStatusBsy))
|
||||||
|
return false;
|
||||||
|
bool err = false;
|
||||||
|
if (!ataWaitDrqOrErr(&err) || err)
|
||||||
|
return false;
|
||||||
|
for (int w = 0; w < 256; w++)
|
||||||
|
dst[w] = inw(ataRegData);
|
||||||
|
dst += 256;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ataWriteSectors(uint64 lba, uint32 count, const void* buffer)
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
return true;
|
||||||
|
if (!diskPresent || count > ataMaxSectorsPerCommand || lba + count > totalSectors)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ataWaitClear(ataStatusBsy))
|
||||||
|
return false;
|
||||||
|
ataSelect(lba, (uint8)count);
|
||||||
|
outb(ataRegCommand, ataCmdWriteSectors);
|
||||||
|
|
||||||
|
const uint16* src = (const uint16*)buffer;
|
||||||
|
for (uint32 s = 0; s < count; s++) {
|
||||||
|
if (!ataWaitClear(ataStatusBsy))
|
||||||
|
return false;
|
||||||
|
bool err = false;
|
||||||
|
if (!ataWaitDrqOrErr(&err) || err)
|
||||||
|
return false;
|
||||||
|
for (int w = 0; w < 256; w++)
|
||||||
|
outw(ataRegData, src[w]);
|
||||||
|
src += 256;
|
||||||
|
ataIoDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(ataRegCommand, ataCmdCacheFlush);
|
||||||
|
if (!ataWaitClear(ataStatusBsy))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user