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

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
proxy
Member
Member
Posts: 108
Joined: Wed Jan 19, 2005 12:00 am
Contact:

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

Post 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?
Last edited by proxy on Sun Mar 15, 2020 10:37 pm, edited 2 times in total.
User avatar
proxy
Member
Member
Posts: 108
Joined: Wed Jan 19, 2005 12:00 am
Contact:

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

Post 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 :-(.
Last edited by proxy on Sun Mar 15, 2020 10:35 pm, edited 1 time in total.
User avatar
proxy
Member
Member
Posts: 108
Joined: Wed Jan 19, 2005 12:00 am
Contact:

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

Post 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!
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

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

Post 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
User avatar
proxy
Member
Member
Posts: 108
Joined: Wed Jan 19, 2005 12:00 am
Contact:

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

Post 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!
Post Reply