Code: Select all
(device_status & STATUS_DRQ) != 0
Here are the notable parts of my ATA driver:
You can assume the following:
Code: Select all
port_ = 0x1f0;
ctrl_ = 0x3f6;
Code: Select all
void ndelay(uint32_t ms) {
__asm__ __volatile__(
"jmp 1f \n"
".align 16 \n"
"1: \n"
"jmp 2f \n"
".align 16 \n"
"2: \n"
"decl %0 \n"
"jns 2b \n"
:
: "a"(ms));
}
namespace io {
template <>
inline uint8_t read<uint8_t>(uint16_t port) {
uint8_t r;
__asm__ __volatile__("inb %1, %0"
: "=a"(r)
: "Nd"(port));
return r;
}
template <>
inline uint16_t read<uint16_t>(uint16_t port) {
uint16_t r;
__asm__ __volatile__("inw %1, %0"
: "=a"(r)
: "Nd"(port));
return r;
}
}
uint8_t Ata::status() const {
return io::read<uint8_t>(ctrl_ | 0);
}
uint8_t Ata::waitForData() const {
// Read the Regular Status port until bit 7 (BSY, value = 0x80) clears,
// and bit 3 (DRQ, value = 8) sets -- or until bit 0 (ERR, value = 1)
// or bit 5 (DF, value = 0x20) sets. If neither error bit is set, the
// device is ready right then.
while(true) {
ndelay(400);
const uint8_t device_status = status();
// an error occured
if ((device_status & (STATUS_ERR | STATUS_DF)) != 0) {
return device_status;
}
// not busy and ready for data
// NOTE(eteran): why don't i ever see the DRQ bit get set?! This "works", but if I put back the "wait for DRQ" part, I hang :-(
if((device_status & STATUS_BSY) == 0 /*&& (device_status & STATUS_DRQ) != 0*/) {
return device_status;
}
}
}
// Almost identical to waitForData, just doesn't care about DRQ
uint8_t Ata::waitForCommand() const {
while (true) {
ndelay(400);
const uint8_t device_status = status();
// an error occured
if ((device_status & (STATUS_ERR | STATUS_DF)) != 0) {
return device_status;
}
// not busy
if ((device_status & STATUS_BSY) == 0) {
return device_status;
}
}
}
uint32_t Ata::readSectorsLba28(void *buffer, size_t count, uint64_t lba) {
assert(buffer);
assert(count != 0);
assert(count < 256);
assert(lba < (1 << 28));
auto ptr = static_cast<uint16_t *>(buffer);
// do next part in limited scope so ScopedLock
// can do its magic
const auto lock = make_scoped_lock(&mutex_);
// wait till drive is ready...
if (waitForCommand() & (STATUS_ERR | STATUS_DF)) {
return 0;
}
const uint8_t drive = ((lba >> 0x18) & 0x0f) | 0xe0 | ((drive_number_ << 4) & 0xff);
const uint8_t lba_lo = ((lba >> 0x00) & 0xff);
const uint8_t lba_mid = ((lba >> 0x08) & 0xff);
const uint8_t lba_hi = ((lba >> 0x10) & 0xff);
io::write<uint8_t>(port_ | 2, count & 0xff);
io::write<uint8_t>(port_ | 3, lba_lo);
io::write<uint8_t>(port_ | 4, lba_mid);
io::write<uint8_t>(port_ | 5, lba_hi);
io::write<uint8_t>(port_ | 6, drive);
io::write<uint8_t>(port_ | 7, ATA_READ_SECTORS_RETRY);
// wait till drive drops busy bit and says data is ready
if (waitForData() & (STATUS_ERR | STATUS_DF)) {
return 0;
}
// read in the sectors
uint32_t sectors = 0;
for (size_t i = 0; i < count; ++i) {
/* NOTE(eteran): Why doesn't REP INS work?*/
for(int i = 0; i < 256; ++i) {
*ptr++ = io::read<uint16_t>(port_);
}
++sectors;
ndelay(400);
// wait for still ready (necessary?)
if (waitForData() & (STATUS_ERR | STATUS_DF)) {
return sectors;
}
}
return sectors;
}