Page 1 of 1

ATA: Why don't I ever see DRQ in my status bits?

Posted: Fri Mar 13, 2020 8:33 am
by proxy
I have an ATA driver that "works"... but according to everything I've read, it shouldn't and I want to understand why. I **never** see DRQ in the status, ever. So right now I have

Code: Select all

(device_status & STATUS_DRQ) != 0
commented out, because it causes a hang. As I said, it seems to work with that bit removed... but I want to do it right.

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;
}
Any thoughts?

Re: ATA: Why don't I ever see DRQ in my status bits?

Posted: Fri Mar 13, 2020 8:34 am
by proxy
I also have a follow-up question:

If I manually loop and read, I get the data that I expect. But according to the wiki, at least for reading, I should be able to do a REP INSW... but that simply doesn't work for me :-(.

Re: ATA: Why don't I ever see DRQ in my status bits?

Posted: Sun Mar 15, 2020 10:34 pm
by proxy
No one has any ideas? :-(

I can say that it continues to be strange... If I add this code to reset the controller the beginning of the read-routine, suddenly, the REP INSW method works:

Code: Select all

	io::write<uint8_t>(ctrl_, 4);
	io::write<uint8_t>(ctrl_, 0);

	uint8_t s = 0xff;
	for (int i = 0; i < 5; ++i) {
		s = io::read<uint8_t>(ctrl_ | 0);
	}

	while ((s & (STATUS_BSY | STATUS_RDY)) != STATUS_RDY) {
		s = io::read<uint8_t>(ctrl_);
	}
But why should I need to reset the controller every time when reading it word-by-word "works"? I don't see anyone else's code doing that kind of thing.

And I still have no idea why I never see the DRQ bit set in the status byte, despite it serving up data!

Re: ATA: Why don't I ever see DRQ in my status bits?

Posted: Sun Mar 15, 2020 11:04 pm
by BenLunt
When you read the status register, are you reading the actual status register or the alternate status register. It looks to me like you are reading the alternate status register.

Reading the alternate will not acknowledge the interrupt. The drive won't be ready for the next read if you don't acknowledge the interrupt, even if you have interrupts disabled.

Also, doing a 'rep insw' is not recommended. I have seen where this is too fast for the controller and have received data in error. Placing a single read word instruction within a loop allows the controller the necessary time to get ready for the next read. Yes it is a minute amount of time, but it is still required by most controllers, especially older ones.

I think your problem is that you are not reading the status register after reading (or writing) 256 words. This is a requirement after every sector no matter how many sectors you are reading. The controller will fire an interrupt after ever sector transferred. If interrupts are off, you still have to do all of the process of acknowledging the interrupt as if it had fired.

Ben
- http://www.fysnet.net/media_storage_devices.htm

Re: ATA: Why don't I ever see DRQ in my status bits?

Posted: Tue Mar 17, 2020 10:34 pm
by proxy
@BenLunt,

That solved it! Thanks so much.

Also... interestingly enough, I actually had a copy of your FYSOS: Storage Media Devices! So I was able to use it to clear up some details along the way.

Thanks again!