Page 1 of 1

Non-PCI Probing Of ATA/IDE

Posted: Tue Aug 21, 2007 6:46 pm
by Kevin McGuire
Non-PCI Probing Of ATA/IDE
I have spent about a day in total tinkering with the ATA driver's disk detection routine in the Mattise kernel. It was actual a little tricky, and I am sure others have had problem.

Since I got it working I wanting to share it, and wondering if it actually works well with different hardware and maybe some of your views on better ways to do this.

The idea is to send a value which represents the head and drive to select on a controller, and check for the appropriate values.

Where here we have some defines and a data structure holding all the default controller addresses.

Code: Select all

#define ATA_DATA				0
#define ATA_ERROR				1
#define ATA_SECTORCOUNT			2
#define ATA_SECTORNUMBER		3
#define ATA_CYLINDERLOW			4
#define ATA_CYLINDERHIGH		5
#define ATA_DRIVEHEAD			6
#define ATA_STATUS			7
#define ATA_COMMAND			8

typedef struct{
	unsigned short		base[2];
}Tata_ctl_info;
static Tata_ctl_info ata_ctl_info[] = {
	{ .base = {0x1f0, 0x3f0} },
	{ .base = {0x170, 0x370} },
	{ .base = {0x1e8, 0x3e0} },
	{ .base = {0x168, 0x360} }
};
What I am doing here is going through each Tata_ctl_info structure. While in each info structure I check both controller base addresses. I simply issue a bit pattern to the controller's correct port using the base port for a drive and head selection (ATA_DRIVEHEAD). Then I delay, and finally read the status for a correct determination.

Code: Select all

	for(drvi = 0, x = 0; x < (sizeof(ata_ctl_info) / sizeof(Tata_ctl_info)); ++x)
	{
		for(z = 0; z < 2; ++z)
		{
			for(y = 0; y < 2; ++y)
			{
				outportb(ata_ctl_info[x].base[z] + ATA_DRIVEHEAD, (0xa0 | (y << 4)));
				delay(ATA_DELAY_NORMAL);
				printf("[ATA] ctl:%x alt:%x drv:%x (%02x)\n",
				x, z, y, inportb(ata_ctl_info[x].base[z] + ATA_STATUS));
				delay(ATA_DELAY_NORMAL);

				if((((unsigned char)inportb(ata_ctl_info[x].base[z] + ATA_STATUS) & (0x40 | 0x10 | 0x20 | 0x01)) == 0x50))
				{
					printf("[ATA] present ata/%1x:%1x:%1x\n", x, z, y);
				}
			}
		}
	}
The trick was not just to check the ready bit, but also the:
  • drive seek completion bit.(TRUE)
  • write fault. (FALSE)
  • previous command was error. (FALSE)
If you only check for the ready bit it seems that on BOCHS you will be presented also with a write fault which you will miss and detect the drive as being present. Also with out the delays it seems things will not work as intended -- which was also a problem in the original ATA code. Possibly someone with a slower computer may not have delay troubles for some reason when running their detection code under a emulator?? Not sure, but I did have the original author of the code who said it worked perfect on his machine -- but not mind, so I made these additions for better detection.

Posted: Tue Aug 21, 2007 7:52 pm
by Dex
Why not just do a " IDENTIFY command" (eg: 0xEC ), read 256 words into buffer and test for error etc ?.

Posted: Tue Aug 21, 2007 8:03 pm
by Kevin McGuire
Well. I think what I am doing is essentially what you do before issuing a identify command. I base this information from viewing the source found here:
http://www.gelato.unsw.edu.au/lxr/sourc ... de-probe.c

But, you are correct. Why not just go straight ahead and starting writing command bytes to the command port? -- unless you want to select the drive and check for error during selection.

Ironically, adding the identify command after setting the drive-head port and checking status appears to be exactly what most disk drivers do. So I suppose this is on the right track. Thanks, Dex.

I forgot to mention a thanks for the information you post on this link to:
http://www.osdev.org/phpBB2/viewtopic.php?t=12268 (Dex)
http://www.osdev.org/wiki/ATA/ (Pype.Clicker And Wiki Editors)

Posted: Wed Aug 22, 2007 9:00 am
by Pype.Clicker
I must say that i do not find the code in the "ATA" page of the wiki very helpful. Back in time, i indeed studied this problem and wrote down my notes in http://clicker.sourceforge.net/wiclicke ... taIdentify ...

It is indeed good practice to make sure you have a device present before sending out identify commands, but that's probably something very pedantic, and the pre-identify checks are black-magic-numbers at best.

Yet, iirc, i finally managed to have it work in Clicker ^_^

Posted: Wed Aug 22, 2007 11:06 am
by Dex
Your welcome Kevin McGuire, The way i do it in DexOS is to issue a IDENTIFY command with the address set to 0x1f0 0x00, i then loop through call the DENTIFY command If it get to the end, the address is saved and the good bits from the 256 words of info is stored, it then moves on to the next address and repeated, once its done this, i check the commend var for IDENTIFY if it =, i change the command for 0xA1 (ATA_ATAPIIDENTIFY) and repeat the above loop then exit.

Now this code has been run on many differant PC configurations and if the device is not present it returns with a error and has worked 100% so far.

PS: Nice to see you back Pype.Clicker

Posted: Wed Aug 22, 2007 2:54 pm
by Brynet-Inc
I think using the PCI method first may be more common....

On OpenBSD for example an ATA device is detected as:

Code: Select all

pci0 at mainbus0: configuration mode 1 (bios)
pciide0 at pci0 dev 1 function 1 "Intel 82371SB IDE" rev 0x00: DMA, channel 0 wired to compatibility, channel 1 wired to compatibility
wd0 at pciide0 channel 0 drive 0: <QEMU HARDDISK>
wd0: 16-sector PIO, LBA48, 1024MB, 2097152 sectors
wd0(pciide0:0:0): using PIO mode 0, DMA mode 2
But if you disable the pciide* devices via UKC(User Kernel Config) it'll just probe the ISA bus..

Code: Select all

wdc0 at isa0 port 0x1f0/8 irq 14
wd0 at wdc0 channel 0 drive 0: <QEMU HARDDISK>
wd0: 16-sector PIO, LBA48, 1024MB, 2097152 sectors
wd0(wdc0:0:0): using BIOS timings
I'd like to hear a few opinions, wouldn't the PCI method be more portable? :?

Posted: Wed Aug 22, 2007 6:32 pm
by exkor
You can't find IDE controller without pci these days - will not know which ports to probe.
Wondering how do you probe ata/ide drive with the PCI bus?

By the way in my experience completely empty IDE cntrls default to -1 and 7fh. Half empty(or if only 1 chanel implemented.) to 0 .

A bit more about detection you can read here http://board.flatassembler.net/topic.php?t=2703

Posted: Thu Aug 23, 2007 11:08 am
by Dex
As i pointed out in the link you posted (I was Called ASHLEY4 then), i find the code i posted always works, as long as you call it in realmode (not sure why).

Posted: Wed Aug 29, 2007 9:40 am
by Candy
I identify the controller by writing to (command + 0x3) and (command + 0x4) and then checking whether the written bytes are still there. If they are, there are registers there which means that there's most likely an IDE controller there.

I identify drives by sending them an ATA Identify command and then (if given a negative response) an ATAPI identify command. If either of these returns ok, there's a device there (of the given type) and I construct an object to represent it. If they both return failure, I ignore the connection.