PCI IDE Controller: Difference between revisions
Jump to navigation
Jump to search
[unchecked revision] | [unchecked revision] |
Content deleted Content added
m bugged me |
m Bot: Replace deprecated source tag with syntaxhighlight |
||
Line 60: | Line 60: | ||
To initialise the IDE driver, we call ide_initialise: |
To initialise the IDE driver, we call ide_initialise: |
||
< |
<syntaxhighlight lang="c"> |
||
void ide_initialize(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3, |
void ide_initialize(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3, |
||
unsigned int BAR4) { |
unsigned int BAR4) { |
||
</syntaxhighlight> |
|||
</source> |
|||
If you only want to support the parallel IDE, you can use these parameters: |
If you only want to support the parallel IDE, you can use these parameters: |
||
< |
<syntaxhighlight lang="c"> |
||
ide_initialize(0x1F0, 0x3F6, 0x170, 0x376, 0x000); |
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 can assume that BAR4 is 0x0 because we are not going to use it yet. |
||
Line 77: | Line 77: | ||
The Command/Status Port returns a bit mask referring to the status of a channel when read. |
The Command/Status Port returns a bit mask referring to the status of a channel when read. |
||
< |
<syntaxhighlight lang="c"> |
||
#define ATA_SR_BSY 0x80 // Busy |
#define ATA_SR_BSY 0x80 // Busy |
||
#define ATA_SR_DRDY 0x40 // Drive ready |
#define ATA_SR_DRDY 0x40 // Drive ready |
||
Line 86: | Line 86: | ||
#define ATA_SR_IDX 0x02 // Index |
#define ATA_SR_IDX 0x02 // Index |
||
#define ATA_SR_ERR 0x01 // Error |
#define ATA_SR_ERR 0x01 // Error |
||
</syntaxhighlight> |
|||
</source> |
|||
=== Errors === |
=== Errors === |
||
Line 92: | Line 92: | ||
The Features/Error Port, which returns the most recent error upon read, has these possible bit masks |
The Features/Error Port, which returns the most recent error upon read, has these possible bit masks |
||
< |
<syntaxhighlight lang="c"> |
||
#define ATA_ER_BBK 0x80 // Bad block |
#define ATA_ER_BBK 0x80 // Bad block |
||
#define ATA_ER_UNC 0x40 // Uncorrectable data |
#define ATA_ER_UNC 0x40 // Uncorrectable data |
||
Line 101: | Line 101: | ||
#define ATA_ER_TK0NF 0x02 // Track 0 not found |
#define ATA_ER_TK0NF 0x02 // Track 0 not found |
||
#define ATA_ER_AMNF 0x01 // No address mark |
#define ATA_ER_AMNF 0x01 // No address mark |
||
</syntaxhighlight> |
|||
</source> |
|||
=== Commands === |
=== Commands === |
||
Line 107: | Line 107: | ||
When you write to the Command/Status port, you are executing one of the commands below. |
When you write to the Command/Status port, you are executing one of the commands below. |
||
< |
<syntaxhighlight lang="c"> |
||
#define ATA_CMD_READ_PIO 0x20 |
#define ATA_CMD_READ_PIO 0x20 |
||
#define ATA_CMD_READ_PIO_EXT 0x24 |
#define ATA_CMD_READ_PIO_EXT 0x24 |
||
Line 121: | Line 121: | ||
#define ATA_CMD_IDENTIFY_PACKET 0xA1 |
#define ATA_CMD_IDENTIFY_PACKET 0xA1 |
||
#define ATA_CMD_IDENTIFY 0xEC |
#define ATA_CMD_IDENTIFY 0xEC |
||
</syntaxhighlight> |
|||
</source> |
|||
The commands below are for ATAPI devices, which will be understood soon. |
The commands below are for ATAPI devices, which will be understood soon. |
||
< |
<syntaxhighlight lang="c"> |
||
#define ATAPI_CMD_READ 0xA8 |
#define ATAPI_CMD_READ 0xA8 |
||
#define ATAPI_CMD_EJECT 0x1B |
#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. |
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. |
||
< |
<syntaxhighlight lang="c"> |
||
#define ATA_IDENT_DEVICETYPE 0 |
#define ATA_IDENT_DEVICETYPE 0 |
||
#define ATA_IDENT_CYLINDERS 2 |
#define ATA_IDENT_CYLINDERS 2 |
||
Line 143: | Line 143: | ||
#define ATA_IDENT_COMMANDSETS 164 |
#define ATA_IDENT_COMMANDSETS 164 |
||
#define ATA_IDENT_MAX_LBA_EXT 200 |
#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: |
When you select a drive, you should specify the interface type and whether it is the master or slave: |
||
< |
<syntaxhighlight lang="c"> |
||
#define IDE_ATA 0x00 |
#define IDE_ATA 0x00 |
||
#define IDE_ATAPI 0x01 |
#define IDE_ATAPI 0x01 |
||
Line 152: | Line 152: | ||
#define ATA_MASTER 0x00 |
#define ATA_MASTER 0x00 |
||
#define ATA_SLAVE 0x01 |
#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: |
Task File is a range of 8 ports which are offsets from BAR0 (primary channel) and/or BAR2 (secondary channel). To exemplify: |
||
Line 158: | Line 158: | ||
* BAR0 + 1 is second port. |
* BAR0 + 1 is second port. |
||
* BAR0 + 2 is the third |
* BAR0 + 2 is the third |
||
< |
<syntaxhighlight lang="c"> |
||
#define ATA_REG_DATA 0x00 |
#define ATA_REG_DATA 0x00 |
||
#define ATA_REG_ERROR 0x01 |
#define ATA_REG_ERROR 0x01 |
||
Line 176: | Line 176: | ||
#define ATA_REG_ALTSTATUS 0x0C |
#define ATA_REG_ALTSTATUS 0x0C |
||
#define ATA_REG_DEVADDRESS 0x0D |
#define ATA_REG_DEVADDRESS 0x0D |
||
</syntaxhighlight> |
|||
</source> |
|||
The ALTSTATUS/CONTROL port returns the alternate status when read and controls a channel when written to. |
The ALTSTATUS/CONTROL port returns the alternate status when read and controls a channel when written to. |
||
Line 199: | Line 199: | ||
The map above is the same with the secondary channel, but it uses BAR2 and BAR3 instead of BAR0 and BAR1. |
The map above is the same with the secondary channel, but it uses BAR2 and BAR3 instead of BAR0 and BAR1. |
||
< |
<syntaxhighlight lang="c"> |
||
// Channels: |
// Channels: |
||
#define ATA_PRIMARY 0x00 |
#define ATA_PRIMARY 0x00 |
||
Line 207: | Line 207: | ||
#define ATA_READ 0x00 |
#define ATA_READ 0x00 |
||
#define ATA_WRITE 0x01 |
#define ATA_WRITE 0x01 |
||
</syntaxhighlight> |
|||
</source> |
|||
We have defined everything needed by the driver, now lets move to an important part. We said that |
We have defined everything needed by the driver, now lets move to an important part. We said that |
||
Line 218: | Line 218: | ||
So we can make this global structure: |
So we can make this global structure: |
||
< |
<syntaxhighlight lang="c"> |
||
struct IDEChannelRegisters { |
struct IDEChannelRegisters { |
||
unsigned short base; // I/O Base. |
unsigned short base; // I/O Base. |
||
Line 225: | Line 225: | ||
unsigned char nIEN; // nIEN (No Interrupt); |
unsigned char nIEN; // nIEN (No Interrupt); |
||
} channels[2]; |
} 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: |
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"> |
||
unsigned char ide_buf[2048] = {0}; |
unsigned char ide_buf[2048] = {0}; |
||
volatile unsigned static char ide_irq_invoked = 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}; |
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: |
We said the the IDE can contain up to 4 drives: |
||
< |
<syntaxhighlight lang="c"> |
||
struct ide_device { |
struct ide_device { |
||
unsigned char Reserved; // 0 (Empty) or 1 (This Drive really exists). |
unsigned char Reserved; // 0 (Empty) or 1 (This Drive really exists). |
||
Line 249: | Line 249: | ||
unsigned char Model[41]; // Model in string. |
unsigned char Model[41]; // Model in string. |
||
} ide_devices[4]; |
} ide_devices[4]; |
||
</syntaxhighlight> |
|||
</source> |
|||
When we read a register in a channel, like STATUS Register, it is easy to execute: |
When we read a register in a channel, like STATUS Register, it is easy to execute: |
||
Line 362: | Line 362: | ||
If there is an error, we have a function which prints errors on screen: |
If there is an error, we have a function which prints errors on screen: |
||
< |
<syntaxhighlight lang="c"> |
||
unsigned char ide_print_error(unsigned int drive, unsigned char err) { |
unsigned char ide_print_error(unsigned int drive, unsigned char err) { |
||
if (err == 0) |
if (err == 0) |
||
Line 388: | Line 388: | ||
return err; |
return err; |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
Now let's return to the initialization function: |
Now let's return to the initialization function: |
||
< |
<syntaxhighlight lang="c"> |
||
void ide_initialize(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3, |
void ide_initialize(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3, |
||
unsigned int BAR4) { |
unsigned int BAR4) { |
||
Line 404: | Line 404: | ||
channels[ATA_PRIMARY ].bmide = (BAR4 & 0xFFFFFFFC) + 0; // Bus Master IDE |
channels[ATA_PRIMARY ].bmide = (BAR4 & 0xFFFFFFFC) + 0; // Bus Master IDE |
||
channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFFFFFC) + 8; // 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: |
Then we should disable IRQs in both channels by setting bit 1 (nIEN) in the Control port: |
||
< |
<syntaxhighlight lang="c"> |
||
// 2- Disable IRQs: |
// 2- Disable IRQs: |
||
ide_write(ATA_PRIMARY , ATA_REG_CONTROL, 2); |
ide_write(ATA_PRIMARY , ATA_REG_CONTROL, 2); |
||
ide_write(ATA_SECONDARY, 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. |
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. |
||
Line 526: | Line 526: | ||
We can conclude also this table: |
We can conclude also this table: |
||
< |
<syntaxhighlight lang="c"> |
||
/* ATA/ATAPI Read/Write Modes: |
/* ATA/ATAPI Read/Write Modes: |
||
* ++++++++++++++++++++++++++++++++ |
* ++++++++++++++++++++++++++++++++ |
||
Line 545: | Line 545: | ||
* - Polling Status (+) // Suitable for Singletasking |
* - 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: |
There is something needed to be expressed here, I have told before that Task-File is like that: |
||
Line 582: | Line 582: | ||
Lets go into the code: |
Lets go into the code: |
||
< |
<syntaxhighlight lang="c"> |
||
unsigned char ide_ata_access(unsigned char direction, unsigned char drive, unsigned int lba, |
unsigned char ide_ata_access(unsigned char direction, unsigned char drive, unsigned int lba, |
||
unsigned char numsects, unsigned short selector, unsigned int edi) { |
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. |
This function reads/writes sectors from ATA-Drive. If direction is 0 we are reading, else we are writing. |
||
Line 594: | Line 594: | ||
* edi is the offset in that segment. (the memory address for the data buffer) |
* edi is the offset in that segment. (the memory address for the data buffer) |
||
< |
<syntaxhighlight lang="c"> |
||
unsigned char lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd; |
unsigned char lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd; |
||
unsigned char lba_io[6]; |
unsigned char lba_io[6]; |
||
Line 603: | Line 603: | ||
unsigned short cyl, i; |
unsigned short cyl, i; |
||
unsigned char head, sect, err; |
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. |
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"> |
||
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = (ide_irq_invoked = 0x0) + 0x02); |
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = (ide_irq_invoked = 0x0) + 0x02); |
||
</syntaxhighlight> |
|||
</source> |
|||
Now lets read the parameters: |
Now lets read the parameters: |
||
< |
<syntaxhighlight lang="c"> |
||
// (I) Select one from LBA28, LBA48 or CHS; |
// (I) Select one from LBA28, LBA48 or CHS; |
||
if (lba >= 0x10000000) { // Sure Drive should support LBA in this case, or you are |
if (lba >= 0x10000000) { // Sure Drive should support LBA in this case, or you are |
||
Line 649: | Line 649: | ||
head = (lba + 1 - sect) % (16 * 63) / (63); // Head number is written to HDDEVSEL lower 4-bits. |
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]: |
Now we are going to choose the way of reading the buffer [PIO or DMA]: |
||
< |
<syntaxhighlight lang="c"> |
||
// (II) See if drive supports DMA or not; |
// (II) See if drive supports DMA or not; |
||
dma = 0; // We don't support DMA |
dma = 0; // We don't support DMA |
||
</syntaxhighlight> |
|||
</source> |
|||
Lets poll the Status port while the channel is busy: |
Lets poll the Status port while the channel is busy: |
||
< |
<syntaxhighlight lang="c"> |
||
// (III) Wait if the drive is busy; |
// (III) Wait if the drive is busy; |
||
while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY){ |
while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY){ |
||
} // Wait if busy. |
} // Wait if busy. |
||
</syntaxhighlight> |
|||
</source> |
|||
The HDDDEVSEL register now looks like this: |
The HDDDEVSEL register now looks like this: |
||
Line 687: | Line 687: | ||
Let's write the parameters to registers: |
Let's write the parameters to registers: |
||
< |
<syntaxhighlight lang="c"> |
||
// (V) Write Parameters; |
// (V) Write Parameters; |
||
if (lba_mode == 2) { |
if (lba_mode == 2) { |
||
Line 699: | Line 699: | ||
ide_write(channel, ATA_REG_LBA1, lba_io[1]); |
ide_write(channel, ATA_REG_LBA1, lba_io[1]); |
||
ide_write(channel, ATA_REG_LBA2, lba_io[2]); |
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. |
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. |
||
Line 705: | Line 705: | ||
Now, we have a great set of commands described in ATA/ATAPI-8 Specification, we should choose the suitable command to execute: |
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"> |
||
// (VI) Select the command and send it; |
// (VI) Select the command and send it; |
||
// Routine that is followed: |
// Routine that is followed: |
||
Line 714: | Line 714: | ||
// If (!DMA & LBA28) DO_PIO_LBA; |
// If (!DMA & LBA28) DO_PIO_LBA; |
||
// If (!DMA & !LBA#) DO_PIO_CHS; |
// If (!DMA & !LBA#) DO_PIO_CHS; |
||
</syntaxhighlight> |
|||
</source> |
|||
There isn't a command for doing CHS with DMA. |
There isn't a command for doing CHS with DMA. |
||
< |
<syntaxhighlight lang="c"> |
||
if (lba_mode == 0 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO; |
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; |
if (lba_mode == 1 && dma == 0 && direction == 0) cmd = ATA_CMD_READ_PIO; |
||
Line 732: | Line 732: | ||
if (lba_mode == 2 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA_EXT; |
if (lba_mode == 2 && dma == 1 && direction == 1) cmd = ATA_CMD_WRITE_DMA_EXT; |
||
ide_write(channel, ATA_REG_COMMAND, cmd); // Send the Command. |
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). |
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 784: | Line 784: | ||
Notice also that ATAPI drives always use IRQs and you can't disable them. We should create a function that waits for an IRQ: |
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"> |
||
void ide_wait_irq() { |
void ide_wait_irq() { |
||
while (!ide_irq_invoked) |
while (!ide_irq_invoked) |
||
Line 790: | Line 790: | ||
ide_irq_invoked = 0; |
ide_irq_invoked = 0; |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
When an IRQ happens, the following function should be executed by ISR: |
When an IRQ happens, the following function should be executed by ISR: |
||
< |
<syntaxhighlight lang="c"> |
||
void ide_irq() { |
void ide_irq() { |
||
ide_irq_invoked = 1; |
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. |
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"> |
||
unsigned char ide_atapi_read(unsigned char drive, unsigned int lba, unsigned char numsects, |
unsigned char ide_atapi_read(unsigned char drive, unsigned int lba, unsigned char numsects, |
||
unsigned short selector, unsigned int edi) { |
unsigned short selector, unsigned int edi) { |
||
</syntaxhighlight> |
|||
</source> |
|||
* drive is the drive number, which is from 0 to 3. |
* drive is the drive number, which is from 0 to 3. |
||
Line 815: | Line 815: | ||
Let's read the parameters of the drive: |
Let's read the parameters of the drive: |
||
< |
<syntaxhighlight lang="c"> |
||
unsigned int channel = ide_devices[drive].Channel; |
unsigned int channel = ide_devices[drive].Channel; |
||
unsigned int slavebit = ide_devices[drive].Drive; |
unsigned int slavebit = ide_devices[drive].Drive; |
||
Line 822: | Line 822: | ||
unsigned char err; |
unsigned char err; |
||
int i; |
int i; |
||
</syntaxhighlight> |
|||
</source> |
|||
We need IRQs: |
We need IRQs: |
||
< |
<syntaxhighlight lang="c"> |
||
// Enable IRQs: |
// Enable IRQs: |
||
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = ide_irq_invoked = 0x0); |
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: |
Let's setup the SCSI Packet, which is 6 words (12 bytes) long: |
||
< |
<syntaxhighlight lang="c"> |
||
// (I): Setup SCSI Packet: |
// (I): Setup SCSI Packet: |
||
// ------------------------------------------------------------------ |
// ------------------------------------------------------------------ |
||
Line 848: | Line 848: | ||
atapi_packet[10] = 0x0; |
atapi_packet[10] = 0x0; |
||
atapi_packet[11] = 0x0; |
atapi_packet[11] = 0x0; |
||
</syntaxhighlight> |
|||
</source> |
|||
Now we should select the drive: |
Now we should select the drive: |
||
Line 867: | Line 867: | ||
</source> |
</source> |
||
< |
<syntaxhighlight lang="c"> |
||
// (IV): Inform the Controller that we use PIO mode: |
// (IV): Inform the Controller that we use PIO mode: |
||
// ------------------------------------------------------------------ |
// ------------------------------------------------------------------ |
||
ide_write(channel, ATA_REG_FEATURES, 0); // PIO mode. |
ide_write(channel, ATA_REG_FEATURES, 0); // PIO mode. |
||
</syntaxhighlight> |
|||
</source> |
|||
Tell the controller the size of the buffer |
Tell the controller the size of the buffer |
||
< |
<syntaxhighlight lang="c"> |
||
// (V): Tell the Controller the size of buffer: |
// (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_LBA1, (words * 2) & 0xFF); // Lower Byte of Sector Size. |
||
ide_write(channel, ATA_REG_LBA2, (words * 2) >> 8); // Upper 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": |
Now that we want to send the packet, we should first send the command "Packet": |
||
< |
<syntaxhighlight lang="c"> |
||
// (VI): Send the Packet Command: |
// (VI): Send the Packet Command: |
||
// ------------------------------------------------------------------ |
// ------------------------------------------------------------------ |
||
Line 896: | Line 896: | ||
// ------------------------------------------------------------------ |
// ------------------------------------------------------------------ |
||
asm("rep outsw" : : "c"(6), "d"(bus), "S"(atapi_packet)); // Send Packet Data |
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. |
Here we cannot poll. We should wait for an IRQ, then read the sectors. These two operations should be repeated for each sector. |
||
Line 917: | Line 917: | ||
Now we should wait for an IRQ and poll until the Busy and DRQ bits are clear: |
Now we should wait for an IRQ and poll until the Busy and DRQ bits are clear: |
||
< |
<syntaxhighlight lang="c"> |
||
// (X): Waiting for an IRQ: |
// (X): Waiting for an IRQ: |
||
// ------------------------------------------------------------------ |
// ------------------------------------------------------------------ |
||
Line 929: | Line 929: | ||
return 0; // Easy, ... Isn't it? |
return 0; // Easy, ... Isn't it? |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
== Reading from an ATA/ATAPI Drive == |
== Reading from an ATA/ATAPI Drive == |
||
Line 961: | Line 961: | ||
== Writing to an ATA drive == |
== Writing to an ATA drive == |
||
< |
<syntaxhighlight lang="c"> |
||
void ide_write_sectors(unsigned char drive, unsigned char numsects, unsigned int lba, |
void ide_write_sectors(unsigned char drive, unsigned char numsects, unsigned int lba, |
||
unsigned short es, unsigned int edi) { |
unsigned short es, unsigned int edi) { |
||
Line 984: | Line 984: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
== Ejecting an ATAPI Drive == |
== Ejecting an ATAPI Drive == |