PCI IDE Controller: Difference between revisions

m
Bot: fixing lint errors, replacing obsolete HTML tags: <font> (2x)
[unchecked revision][unchecked revision]
m (Bot: fixing lint errors, replacing obsolete HTML tags: <font> (2x))
 
(5 intermediate revisions by 4 users not shown)
Line 3:
 
[[ATAPI]] is an extension to ATA (recently renamed to PATA) which adds support for the SCSI command set.
 
== Parallel/Serial ATA/ATAPI ==
IDE can connect up to 4 drives. Each drive can be one of the following:
 
* ATA (Serial): Used for most modern hard drives.
* ATA (Parallel): Commonly used for hard drives.
* ATAPI (Serial): Used for most modern optical drives.
* ATAPI (Parallel): Commonly used for optical drives.
 
Accessing an ATA/PATA drive works the same way as accessing a SATA drive. This also implicitly states that accessing a PATAPI ODD is the same as accessing a SATAPI ODD. An IDE driver does not need to know whether a drive is parallel or serial, it only has to know whether it's using ATA or ATAPI.
 
== IDE Interface ==
 
If you open your case up and take a look at your motherboard, you will most likely see one or two (or possibly more) of the slots.
 
Line 21 ⟶ 16:
 
Each port can have a PATA cable connected to it. One master drive, or two drives (master and slave), can be connected to one PATA cable. So that leaves us with the following possibilities:
 
* Primary Master Drive.
* Primary Slave Drive.
* Secondary Master Drive.
* Secondary Slave Drive.
 
Each drive can be either PATA or PATAPI.
 
== Serial IDE ==
Almost every modern (this article is probably written in early 2010 so it assumes motherboards still have ide/ahci modes) motherboard has a Serial IDE channel which allows [[SATA]] and SATAPI Drives to be connected to it. There are 4 Serial IDE Ports. Each port is connected to a drive with a SATA Cable. Basically you can only have one drive connected to the Serial IDE port. Each pair of ports (every 2 ports) form one channel.
 
Serial IDE also has a few possibilities:
 
* Primary Master, also called SATA1.
* Primary Slave, also called SATA2.
* Secondary Master, also called SATA3.
* Secondary Slave, also called SATA4.
 
== Detecting a PCI IDE Controller ==
Each IDE controller appears as a device on the [[PCI]] bus and can be identified by reading the configuration space. If the class code is 0x01 (Mass Storage Controller) and the subclass code is 0x01 (IDE), the device is an IDE controller. The programming interface byte(Prog If) determines how you'll access it.
Line 46 ⟶ 36:
* Bit 3: When set, you can modify bit 2 to switch between PCI native and compatibility mode. When clear, you cannot modify bit 2.
* Bit 7: When set, this is a bus master IDE controller. When clear, this controller doesn't support DMA.
 
If you want to access an IDE channel in PCI native mode or use the bus master function, you must additionally read the BARs to find which I/O ports to use.
* BAR0: Base address of primary channel in PCI native mode (8 ports)
Line 56 ⟶ 45:
 
If either IDE channel is in PCI native mode, you must also read the interrupt line or interrupt pin register to determine which interrupt to use. If both channels are in PCI native mode, they'll both share the same interrupt. The interrupt line field is only valid when using the [[8259 PIC|PIC]].
 
== Detecting IDE Drives ==
 
To initialise the IDE driver, we call ide_initialise:
<sourcesyntaxhighlight lang="c">
void ide_initialize(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3,
unsigned int BAR4) {
</syntaxhighlight>
</source>
 
If you only want to support the parallel IDE, you can use these parameters:
<sourcesyntaxhighlight lang="c">
ide_initialize(0x1F0, 0x3F6, 0x170, 0x376, 0x000);
</syntaxhighlight>
</source>
 
We can assume that BAR4 is 0x0 because we are not going to use it yet.
We will return to ide_initialize, which searches for drives connected to the IDE. Before we go into this function, we should write some support functions and definitions which will help us a lot.
 
=== Status ===
 
The Command/Status Port returns a bit mask referring to the status of a channel when read.
<syntaxhighlight lang="c">
 
<source lang="c">
#define ATA_SR_BSY 0x80 // Busy
#define ATA_SR_DRDY 0x40 // Drive ready
Line 86 ⟶ 68:
#define ATA_SR_IDX 0x02 // Index
#define ATA_SR_ERR 0x01 // Error
</syntaxhighlight>
</source>
 
=== Errors ===
 
The Features/Error Port, which returns the most recent error upon read, has these possible bit masks
<syntaxhighlight lang="c">
 
<source lang="c">
#define ATA_ER_BBK 0x80 // Bad block
#define ATA_ER_UNC 0x40 // Uncorrectable data
Line 101 ⟶ 80:
#define ATA_ER_TK0NF 0x02 // Track 0 not found
#define ATA_ER_AMNF 0x01 // No address mark
</syntaxhighlight>
</source>
 
=== Commands ===
 
When you write to the Command/Status port, you are executing one of the commands below.
<syntaxhighlight lang="c">
 
<source lang="c">
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_READ_PIO_EXT 0x24
Line 121 ⟶ 97:
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_CMD_IDENTIFY 0xEC
</syntaxhighlight>
</source>
 
The commands below are for ATAPI devices, which will be understood soon.
<syntaxhighlight lang="c">
 
<source lang="c">
#define ATAPI_CMD_READ 0xA8
#define ATAPI_CMD_EJECT 0x1B
</syntaxhighlight>
</source>
 
ATA_CMD_IDENTIFY_PACKET and ATA_CMD_IDENTIFY return a buffer of 512 bytes called the identification space; the following definitions are used to read information from the identification space.
<sourcesyntaxhighlight lang="c">
#define ATA_IDENT_DEVICETYPE 0
#define ATA_IDENT_CYLINDERS 2
Line 143 ⟶ 116:
#define ATA_IDENT_COMMANDSETS 164
#define ATA_IDENT_MAX_LBA_EXT 200
</syntaxhighlight>
</source>
 
When you select a drive, you should specify the interface type and whether it is the master or slave:
<sourcesyntaxhighlight lang="c">
#define IDE_ATA 0x00
#define IDE_ATAPI 0x01
Line 152 ⟶ 124:
#define ATA_MASTER 0x00
#define ATA_SLAVE 0x01
</syntaxhighlight>
</source>
 
Task File is a range of 8 ports which are offsets from BAR0 (primary channel) and/or BAR2 (secondary channel). To exemplify:
* BAR0 + 0 is first port.
* BAR0 + 1 is second port.
* BAR0 + 2 is the third
<sourcesyntaxhighlight lang="c">
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
Line 176 ⟶ 147:
#define ATA_REG_ALTSTATUS 0x0C
#define ATA_REG_DEVADDRESS 0x0D
</syntaxhighlight>
</source>
 
The ALTSTATUS/CONTROL port returns the alternate status when read and controls a channel when written to.
* For the primary channel, ALTSTATUS/CONTROL port is BAR1 + 2.
* For the secondary channel, ALTSTATUS/CONTROL port is BAR3 + 2.
 
We can now say that each channel has 13 registers. For the primary channel, we use these values:
* Data Register: BAR0 + 0; // Read-Write
Line 196 ⟶ 165:
* Control Register: BAR1 + 2; // Write Only.
* DEVADDRESS: BAR1 + 3; // I don't know what is the benefit from this register.
 
The map above is the same with the secondary channel, but it uses BAR2 and BAR3 instead of BAR0 and BAR1.
<syntaxhighlight lang="c">
 
<source lang="c">
// Channels:
#define ATA_PRIMARY 0x00
Line 207 ⟶ 174:
#define ATA_READ 0x00
#define ATA_WRITE 0x01
</syntaxhighlight>
</source>
 
We have defined everything needed by the driver, now lets move to an important part. We said that
* BAR0 is the start of the I/O ports used by the primary channel.
Line 216 ⟶ 182:
* BAR4 is the start of 8 I/O ports controls the primary channel's Bus Master IDE.
* BAR4 + 8 is the Base of 8 I/O ports controls secondary channel's Bus Master IDE.
 
So we can make this global structure:
<sourcesyntaxhighlight lang="c">
struct IDEChannelRegisters {
unsigned short base; // I/O Base.
Line 225 ⟶ 190:
unsigned char nIEN; // nIEN (No Interrupt);
} channels[2];
</syntaxhighlight>
</source>
 
We also need a buffer to read the identification space into, we need a variable that indicates if an irq is invoked or not, and finally we need an array of 6 words [12 bytes] for ATAPI Drives:
<syntaxhighlight lang="c">
 
<source lang="c">
unsigned char ide_buf[2048] = {0};
volatile unsigned static char ide_irq_invoked = 0;
unsigned static char atapi_packet[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
</syntaxhighlight>
</source>
 
We said the the IDE can contain up to 4 drives:
<syntaxhighlight lang="c">
 
<source lang="c">
struct ide_device {
unsigned char Reserved; // 0 (Empty) or 1 (This Drive really exists).
Line 249 ⟶ 210:
unsigned char Model[41]; // Model in string.
} ide_devices[4];
</syntaxhighlight>
</source>
 
When we read a register in a channel, like STATUS Register, it is easy to execute:
<syntaxhighlight lang="c">
 
<source lang="c">
ide_read(channel, ATA_REG_STATUS);
 
Line 272 ⟶ 231:
return result;
}
</syntaxhighlight>
</source>
 
We also need a function for writing to registers:
<sourcesyntaxhighlight lang="c">
void ide_write(unsigned char channel, unsigned char reg, unsigned char data) {
if (reg > 0x07 && reg < 0x0C)
Line 290 ⟶ 248:
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
}
</syntaxhighlight>
</source>
 
To read the identification space, we should read the Data Register as a double word 128 times. We can then copy them to our buffer.
<syntaxhighlight lang="c">
 
<source lang="c">
void ide_read_buffer(unsigned char channel, unsigned char reg, unsigned int buffer,
unsigned int quads) {
Line 316 ⟶ 272:
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
}
</syntaxhighlight>
</source>
 
When we send a command, we should wait for 400 nanosecond, then read the Status port. If the Busy bit is on, we should read the status port again until the Busy bit is 0; then we can read the results of the command. This operation is called "Polling". We can also use IRQs instead of polling.
 
After many commands, if the Device Fault bit is set, there is a failure; if DRQ is not set, there is an error. If the ERR bit is set, there is an error which is described in Error port.
<syntaxhighlight lang="c">
 
<source lang="c">
unsigned char ide_polling(unsigned char channel, unsigned int advanced_check) {
 
Line 359 ⟶ 313:
 
}
</syntaxhighlight>
</source>
 
If there is an error, we have a function which prints errors on screen:
<sourcesyntaxhighlight lang="c">
unsigned char ide_print_error(unsigned int drive, unsigned char err) {
if (err == 0)
Line 388 ⟶ 341:
return err;
}
</syntaxhighlight>
</source>
 
Now let's return to the initialization function:
<sourcesyntaxhighlight lang="c">
void ide_initialize(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3,
unsigned int BAR4) {
Line 404 ⟶ 356:
channels[ATA_PRIMARY ].bmide = (BAR4 & 0xFFFFFFFC) + 0; // Bus Master IDE
channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFFFFFC) + 8; // Bus Master IDE
</syntaxhighlight>
</source>
Then we should disable IRQs in both channels by setting bit 1 (nIEN) in the Control port:
<syntaxhighlight lang="c">
 
<source lang="c">
// 2- Disable IRQs:
ide_write(ATA_PRIMARY , ATA_REG_CONTROL, 2);
ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2);
</syntaxhighlight>
</source>
 
Now we need to check for drives which could be connected to each channel. We will select the master drive of each channel, and send the ATA_IDENTIFY command (which is supported by ATA Drives). If there's no error, there are values returned in registers which determine the type of Drive; if no drive is present, there will be strange values.
 
Notice that if bit 4 in HDDEVSEL is set to 1, we are selecting the slave drive, if set to 0, we are selecting the master drive.
<syntaxhighlight lang="c">
 
<source lang="c">
// 3- Detect ATA-ATAPI Devices:
for (i = 0; i < 2; i++)
Line 449 ⟶ 398:
unsigned char ch = ide_read(i, ATA_REG_LBA2);
 
if (cl == 0x14 && ch == 0xEB)
type = IDE_ATAPI;
else if (cl == 0x69 && ch == 0x96)
Line 498 ⟶ 447:
}
}
</syntaxhighlight>
</source>
 
== Read/Write From ATA Drive ==
Now we're moving to a slightly more advanced part, it is to read and write from/to an ATA drive.
Line 506 ⟶ 454:
* LBA28: Accessing a sector by its 28-bit LBA address. All ATA drives should support this way of addressing, the problem with LBA28 Addressing is that it only allows access 128GB to be accessed, so if the disk is bigger than 128GB, it should support the LBA48 Feature Set.
* LBA48: Accessing a sector by its 48-bit LBA address. As we use integers in GCC, our maximum address in this tutorial is 32-bit long, which allows accessing a drive with a size of up to 2TB.
 
So we can conclude an algorithm to determine which type of Addressing we are going to use:
 
<pre>
if (No LBA support)
Line 517 ⟶ 463:
Use LBA28.
</pre>
 
Reading the buffer may be done by polling or DMA.
PIO: After sending the command to read or write sectors, we read or write to the Data Port (as words). This is the same way of reading identification space.
Line 525 ⟶ 470:
 
We can conclude also this table:
<syntaxhighlight lang="c">
 
<source lang="c">
/* ATA/ATAPI Read/Write Modes:
* ++++++++++++++++++++++++++++++++
Line 545 ⟶ 489:
* - Polling Status (+) // Suitable for Singletasking
*/
</syntaxhighlight>
</source>
 
There is something needed to be expressed here, I have told before that Task-File is like that:
* Register 0: [Word] Data Register. (Read-Write).
Line 558 ⟶ 501:
* Register 7: [Byte] Command Register. (Write).
* Register 7: [Byte] Status Register. (Read).
 
So each register between 2 to 5 should be 8-bits long. Really each of them are 16-bits long.
 
* Register 2: [Bits 0-7] SECCOUNT0, [Bits 8-15] SECOUNT1
* Register 3: [Bits 0-7] LBA0, [Bits 8-15] LBA3
* Register 4: [Bits 0-7] LBA1, [Bits 8-15] LBA4
* Register 5: [Bits 0-7] LBA2, [Bits 8-15] LBA5
 
The word [(SECCOUNT1 << 8) | SECCOUNT0] expresses the number of sectors which can be read when you access by LBA48.
When you access in CHS or LBA28, SECCOUNT0 only expresses number of sectors.
 
* LBA0 makes up bits 0 : 7 of the LBA address when you read in LBA28 or LBA48; it can also be the sector number of CHS.
* LBA1 makes up bits 8 : 15 of the LBA address when you read in LBA28 or LBA48; it can also be the low byte of the cylinder number of CHS.
Line 575 ⟶ 514:
* LBA4 makes up bits 32 : 39 of the LBA48 address.
* LBA5 makes up bits 40 : 47 of LBA48 address.
 
Notice that the LBA0, 1 and 2 registers are 24 bits long in total, which is not enough for LBA28; the higher 4-bits can be written to the lower 4-bits of the HDDEVSEL register.
 
Also notice that if bit 6 of this register is set, we are going to use LBA, if not, we are going to use CHS. There is a mode which is called extended CHS.
 
Lets go into the code:
<syntaxhighlight lang="c">
 
<source lang="c">
unsigned char ide_ata_access(unsigned char direction, unsigned char drive, unsigned int lba,
unsigned char numsects, unsigned short selector, unsigned int edi) {
</syntaxhighlight>
</source>
 
This function reads/writes sectors from ATA-Drive. If direction is 0 we are reading, else we are writing.
* drive is the drive number which can be from 0 to 3.
Line 593 ⟶ 529:
* selector is the segment selector to read from, or write to.
* edi is the offset in that segment. (the memory address for the data buffer)
<syntaxhighlight lang="c">
 
<source lang="c">
unsigned char lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd;
unsigned char lba_io[6];
Line 603 ⟶ 538:
unsigned short cyl, i;
unsigned char head, sect, err;
</syntaxhighlight>
</source>
 
We don't need IRQs, so we should disable it to prevent problems from happening. We said before that if bit 1 of the Control Register (which is called nIEN bit), is set, no IRQs will be invoked from any drives on this channel, either master or slave.
<syntaxhighlight lang="c">
 
<source lang="c">
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = (ide_irq_invoked = 0x0) + 0x02);
</syntaxhighlight>
</source>
 
Now lets read the parameters:
<syntaxhighlight lang="c">
 
<source lang="c">
// (I) Select one from LBA28, LBA48 or CHS;
if (lba >= 0x10000000) { // Sure Drive should support LBA in this case, or you are
Line 649 ⟶ 580:
head = (lba + 1 - sect) % (16 * 63) / (63); // Head number is written to HDDEVSEL lower 4-bits.
}
</syntaxhighlight>
</source>
 
Now we are going to choose the way of reading the buffer [PIO or DMA]:
<syntaxhighlight lang="c">
 
<source lang="c">
// (II) See if drive supports DMA or not;
dma = 0; // We don't support DMA
</syntaxhighlight>
</source>
 
Lets poll the Status port while the channel is busy:
<syntaxhighlight lang="c">
 
<source lang="c">
// (III) Wait if the drive is busy;
while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY){
} // Wait if busy.
</syntaxhighlight>
</source>
 
The HDDDEVSEL register now looks like this:
 
* Bits 0 : 3: Head Number for CHS.
* Bit 4: Slave Bit. (0: Selecting Master Drive, 1: Selecting Slave Drive).
Line 674 ⟶ 599:
* Bit 6: LBA (0: CHS, 1: LBA).
* Bit 7: Obsolete and isn't used, but should be set.
 
Lets write all these information to the register, while the obsolete bits are set (0xA0):
<syntaxhighlight lang="c">
 
<source lang="c">
// (IV) Select Drive from the controller;
if (lba_mode == 0)
Line 683 ⟶ 606:
else
ide_write(channel, ATA_REG_HDDEVSEL, 0xE0 | (slavebit << 4) | head); // Drive & LBA
</syntaxhighlight>
</source>
 
Let's write the parameters to registers:
<syntaxhighlight lang="c">
 
<source lang="c">
// (V) Write Parameters;
if (lba_mode == 2) {
Line 699 ⟶ 620:
ide_write(channel, ATA_REG_LBA1, lba_io[1]);
ide_write(channel, ATA_REG_LBA2, lba_io[2]);
</syntaxhighlight>
</source>
 
If you are using LBA48 and want to write to the LBA0 and LBA3 registers, you should write LBA3 to Register 3, then write LBA0 to Register 3. ide_write function makes it quite simple, refer to the function and you will fully understand the code.
 
Now, we have a great set of commands described in ATA/ATAPI-8 Specification, we should choose the suitable command to execute:
<syntaxhighlight lang="c">
 
<source lang="c">
// (VI) Select the command and send it;
// Routine that is followed:
Line 714 ⟶ 633:
// If (!DMA & LBA28) DO_PIO_LBA;
// If (!DMA & !LBA#) DO_PIO_CHS;
</syntaxhighlight>
</source>
 
There isn't a command for doing CHS with DMA.
<syntaxhighlight lang="c">
 
<source lang="c">
if (lba_mode == 0 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO;
if (lba_mode == 1 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO;
Line 732 ⟶ 649:
if (lba_mode == 2 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA_EXT;
ide_write(channel, ATA_REG_COMMAND, cmd); // Send the Command.
</syntaxhighlight>
</source>
 
This ATA_CMD_READ_PIO command is used for reading in LBA28 or CHS, and the IDE controller refers to bit 6 of the HDDEVSEL register to find out the mode of reading (LBA or CHS).
 
Line 739 ⟶ 655:
 
Notice that after writing, we should execute the CACHE FLUSH command, and we should poll after it, but without checking for errors.
<syntaxhighlight lang="c">
 
<source lang="c">
if (dma)
if (direction == 0);
Line 775 ⟶ 690:
return 0; // Easy, isn't it?
}
</syntaxhighlight>
</source>
 
== Reading from an ATAPI Drive ==
Let's move to an easier part - reading from an ATAPI drive. I will not make the function that writes to an ATAPI drive, because writing to it is very complex and is outside of the scope of this tutorial.
Line 783 ⟶ 697:
 
Notice also that ATAPI drives always use IRQs and you can't disable them. We should create a function that waits for an IRQ:
<syntaxhighlight lang="c">
 
<source lang="c">
void ide_wait_irq() {
while (!ide_irq_invoked)
Line 790 ⟶ 703:
ide_irq_invoked = 0;
}
</syntaxhighlight>
</source>
 
When an IRQ happens, the following function should be executed by ISR:
<syntaxhighlight lang="c">
 
<source lang="c">
void ide_irq() {
ide_irq_invoked = 1;
}
</syntaxhighlight>
</source>
 
ide_wait_irq will go into a while loop, which waits for the variable ide_irq_invoked to be set, then clears it.
<syntaxhighlight lang="c">
 
<source lang="c">
unsigned char ide_atapi_read(unsigned char drive, unsigned int lba, unsigned char numsects,
unsigned short selector, unsigned int edi) {
</syntaxhighlight>
</source>
 
* drive is the drive number, which is from 0 to 3.
* lba is the LBA address.
Line 812 ⟶ 720:
* selector is the Segment Selector.
* edi is the offset in the selector.
 
Let's read the parameters of the drive:
<syntaxhighlight lang="c">
 
<source lang="c">
unsigned int channel = ide_devices[drive].Channel;
unsigned int slavebit = ide_devices[drive].Drive;
Line 822 ⟶ 728:
unsigned char err;
int i;
</syntaxhighlight>
</source>
 
We need IRQs:
<syntaxhighlight lang="c">
 
<source lang="c">
// Enable IRQs:
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = ide_irq_invoked = 0x0);
</syntaxhighlight>
</source>
 
Let's setup the SCSI Packet, which is 6 words (12 bytes) long:
<syntaxhighlight lang="c">
 
<source lang="c">
// (I): Setup SCSI Packet:
// ------------------------------------------------------------------
Line 848 ⟶ 750:
atapi_packet[10] = 0x0;
atapi_packet[11] = 0x0;
</syntaxhighlight>
</source>
 
Now we should select the drive:
<syntaxhighlight lang="c">
 
<source lang="c">
// (II): Select the drive:
// ------------------------------------------------------------------
ide_write(channel, ATA_REG_HDDEVSEL, slavebit << 4);
</syntaxhighlight>
</source>
 
400 nanoseconds delay after this select is a good idea:
<syntaxhighlight lang="c">
 
<source lang="c">
// (III): Delay 400 nanoseconds for select to complete:
// ------------------------------------------------------------------
for(int i = 0; i < 4; i++)
ide_read(channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns.
</syntaxhighlight>
</source>
<syntaxhighlight lang="c">
 
<source lang="c">
// (IV): Inform the Controller that we use PIO mode:
// ------------------------------------------------------------------
ide_write(channel, ATA_REG_FEATURES, 0); // PIO mode.
</syntaxhighlight>
</source>
 
Tell the controller the size of the buffer
<syntaxhighlight lang="c">
 
<source lang="c">
// (V): Tell the Controller the size of buffer:
// ------------------------------------------------------------------
ide_write(channel, ATA_REG_LBA1, (words * 2) & 0xFF); // Lower Byte of Sector Size.
ide_write(channel, ATA_REG_LBA2, (words * 2) >> 8); // Upper Byte of Sector Size.
</syntaxhighlight>
</source>
 
Now that we want to send the packet, we should first send the command "Packet":
<syntaxhighlight lang="c">
 
<source lang="c">
// (VI): Send the Packet Command:
// ------------------------------------------------------------------
Line 896 ⟶ 789:
// ------------------------------------------------------------------
asm("rep outsw" : : "c"(6), "d"(bus), "S"(atapi_packet)); // Send Packet Data
</syntaxhighlight>
</source>
 
Here we cannot poll. We should wait for an IRQ, then read the sectors. These two operations should be repeated for each sector.
<syntaxhighlight lang="c">
 
<source lang="c">
// (IX): Receiving Data:
// ------------------------------------------------------------------
Line 913 ⟶ 804:
edi += (words * 2);
}
</syntaxhighlight>
</source>
 
Now we should wait for an IRQ and poll until the Busy and DRQ bits are clear:
<syntaxhighlight lang="c">
 
<source lang="c">
// (X): Waiting for an IRQ:
// ------------------------------------------------------------------
Line 924 ⟶ 813:
// (XI): Waiting for BSY & DRQ to clear:
// ------------------------------------------------------------------
while (ide_read(channel, ATA_REG_STATUS) & (ATA_SR_BSY | ATA_SR_DRQ)){
;
 
}
 
 
return 0; // Easy, ... Isn't it?
}
</syntaxhighlight>
</source>
 
== Reading from an ATA/ATAPI Drive ==
<sourcesyntaxhighlight lang="c">
void ide_read_sectors(unsigned char drive, unsigned char numsects, unsigned int lba,
unsigned short es, unsigned int edi) {
Line 960 ⟶ 846:
}
// package[0] is an entry of an array. It contains the Error Code.
</syntaxhighlight>
</source>
 
== Writing to an ATA drive ==
<sourcesyntaxhighlight lang="c">
void ide_write_sectors(unsigned char drive, unsigned char numsects, unsigned int lba,
unsigned short es, unsigned int edi) {
Line 986 ⟶ 871:
}
}
</syntaxhighlight>
</source>
 
== Ejecting an ATAPI Drive ==
<sourcesyntaxhighlight lang="c">
void ide_atapi_eject(unsigned char drive) {
unsigned int channel = ide_devices[drive].Channel;
Line 1,055 ⟶ 939:
}
}
</syntaxhighlight>
</source>
 
When this method is invoked, the optical device on the given channel is ejected.
 
== See Also ==
=== Wiki Pages ===
* [[MBR_(x86)|Master Boot Record (x86)]]
* [[Partition_Table|Partition Table (x86)]]
 
=== Threads ===
* [http://www.osdev.org/phpBB2/viewtopic.php?t=12268 How to w/r harddisk in pmode? (ASM Code from Dex)]
* [http://www.osdev.org/phpBB2/viewtopic.php?t=15314 ATA PIO code library (ASM code from XCHG)]
* [http://forum.osdev.org/viewtopic.php?f=1&p=167798#p167798 IDE Tutorial (C code from ''mostafazizo'')]
 
=== External Links ===
* [http://www.t13.org T13] -- The group that creates the ATA standard
Line 1,076 ⟶ 956:
* [http://www.bswd.com/pciide.pdf PCI IDE Controller Specification] -- Specification containing information on "compatibility mode" and "PCI native mode" (and switching between them)
* [http://bswd.com/idems100.pdf Programming Interface for Bus Master IDE Controller] -- Bus Master IDE specification
 
[[Category:ATA]]
[[Category:Storage]]
[[de:AT Attachment]]
40

edits