As Brendan states, and which are very valid and needful questions and statements, you do need some more work.
However, as a newbie, which I am sure you are, you probably aren't "excited" about that part yet. The enjoyment from the newbies point of view, is reading an actual sector from the disk; seeing that it worked, and you are able to communicate with the hardware. The rest comes later (hopefully, and with Brendan's statements, a lot sooner than later).
So, after you have detected that there is an actual hard drive at that location, along with a lot of other things, you now have come to the point to read a sector. Let's do that first, so that your curiosity is fulfilled, and your keep the excitement of the project.
Code: Select all
void read_ata(unsigned long addr) {
int first=0;
int second=0;
int readbuffer=0;
// you might wish to create #defines, such as ATA_FEATURES and use
// outb(base_addr + ATA_FEATURES, 0x00)
// instead of the below (then you don't need the comments as sort)
outb(0x1F1, 0x00); // write 0x00 to the FEATURES register
outb(0x1F2, 0x01); // write 0x01 to the Sector Count register
outb(0x1F3, (unsigned char)addr); // Sector number or LBA Low, most likely LBA Low (but see comments below)
outb(0x1F4, (unsigned char)(addr >> 8)); // Cyl Low number or LBA Mid
outb(0x1F5, (unsigned char)(addr >> 16)); // Cyl High number or LBA High
outb(0x1F6, 0x40); // select the drive. See note [1] below
outb(0x1F7, 0x20); // Send command, See note [2] below
tp("reading");
// see note [3] below
for (int idx = 0; idx < 256; idx++)
... snip
}
[1] Selecting the drive is usually done first, before you set the other registers. For example, what if the selection fails?
[2] As Brendan states, you need to make sure the controller is ready to receive a command first. This kind of comes back to note 1 above, making sure it doesn't fail. Select the drive, then make sure the Abort bit does not become set, i.e.: read the Error register and see that there was no error after the selection of the drive. Then wait for the drive to become ready before you modify any of the other registers as well as sending the command.
[3] You need to wait for the drive to be ready for the transfer. Usually, an interrupt will fire (remembering that an interrupt will fire for every sector, even if you told it to read more than one, you still have to wait for the interrupt at each sector). Read the Status register waiting for the ready bit.
For a proper ATA driver, you need to do a lot more than just read and write to the drive. As Brendan stated, this could be an ATAPI device which requires you to use a different form of command transfer. The technique to see if it is an ATA device (older/most hard drives) or an ATAPI device (some modern hard drives/all CDROM's), you have to send the Identify command, *TWICE*. If you send the ATA Identify to an ATAPI device, it will purposely fail to indicate that it is an ATAPI device. However, you then have to send the ATAPI Identify just to make sure the failure was correct, and not just a general failure. (A interesting note is that the ATA Identify requires you to wait for Ready, while the ATAPI Identify does not...)
Once you have sent the ATA Identify and ATAPI Identify, you can then successfully indicate which type of device it is. You can also get an idea at reset time, due to the register settings after reset. However, you *must* use the Identify technique to be sure.
If it is an ATAPI device, it can use 12- or 16-byte command blocks, something you will have to extract from the buffer returned and use throughout your ATAPI driver.
So, for now, and to keep your interest alive, add the few things noted above to your read code, checking for controller/device ready, and you should be able to read a sector. Once you have this done, you should, as Brendan has suggested:
- scan through the PCI space looking for ATA controllers.
- is the controller in Legacy mode or Native mode?
- reset the controller. (remember that resetting one channel will reset the other channel depending on the combination of the drives attached. i.e.: IIRC, resetting the Slave Channel with no Master attached resets both, but resetting the Master with a Slave attached does not, or visa-versa. Read the specs.)
- detect if a drive is attached on each channel. A PCI controller will have up to two channels, two drives each.
- if a drive is detected, detect if ATA or ATAPI (using the Identify commands)
- Now you can see if the drive supports CHS only, LBA, or long LBA addressing (28-bit or 48 bit).
- Does the drive support DMA transfers? Are they PCI Bus Mastering DMA or old ISA DMA transfers?
- How many cylinders, heads, sectors does the drive have, now usually only LBAs are important. Which field of the Identify buffer do you use for count of LBAs (there are a few of them).
- Does the drive expect a "Spin me up" command before you can use it.
There are a lot of things you must do for a proper ATA(PI) driver.
If you wish, I have a book at
http://www.fysnet.net/media_storage_devices.htm that explains all of this. However, if this isn't of interest to you, please continue to post here. We will help all we can, *as long as you try first, yourself*. (We don't do homework)
- Ben
P.S. Just for fun, a few days ago I plugged in an old Seagate ST351A/X drive I have. This was an interesting drive of its time. It (supposedly) only had one platter (2 heads) with top-of-the-line formatting/etc. to allow a whopping 40Meg of space to be used on one platter. However, it used less-than-the-best actuator tech to do it... The Identify command returns that it has 5 heads, not 2, and though it isn't completely insane, usually the head count is an even count, one per side. However, there is a difference between physical head count and firmware head count in a lot of devices... It is interesting to see what was used at the dawn of hard drives to squeeze out every megabyte. Today it is Terabytes.... Also, the A/X part meant that it has a jumper that you could use it on an XT or an AT machine (difference was 8- or 16-bit transfers).