PCI IDE Controller: Difference between revisions

[unchecked revision][unchecked revision]
Content deleted Content added
Line 802:
 
===Read From ATAPI Drive===
Let's move to a part which is quite easier, it is to read from ATAPI Drive, i will not make the function write to ATAPI Drive, because the write Operation is very complix and it should done by third-party tools (like Nero in Windows, and Brasero in Linux).
 
ATAPI Drive is different from ATA Drives, as it doesn't use ATA Commands, but it use the SCSI-Command-Set. Parameters are sent into a Packet, so it is Called: ATA-Packet Interface [ATAPI].
 
Notice also that ATAPI drives should always use IRQs, you can't disable them, so we should create a function which waits for an IRQ to be caused:
 
<source lang="c">
void ide_wait_irq() {
while (!ide_irq_invoked);
ide_irq_invoked = 0;
}
</source>
 
when an IRQ happens, the following function should be executed by ISR:
 
<source lang="c">
void ide_irq() {
ide_irq_invoked = 1;
}
</source>
 
by this way, ide_wait_irq() will go into a while loop, which waits for the variable ide_irq_invoked to be set, then it reclears it.
 
<source lang="c">
unsigned char ide_atapi_read(unsigned char drive, unsigned int lba, unsigned char numsects,
unsigned short selector, unsigned int edi) {
</source>
 
* drive, is the drive number, which is from 0 to 3.
* lba, the lba address.
* numsects, number of sectors, it should always be 1, and if you wanna read more than one sector, re-execute this fucntion with updated LBA address.
* selector, Segment Selector.
* edi, offset in the selector.
 
Let's read the parameters of the drive:
 
<source lang="c">
unsigned int channel = ide_devices[drive].channel;
unsigned int slavebit = ide_devices[drive].drive;
unsigned int bus = channels[channel].base;
unsigned int words = 1024; // Sector Size in Words, ATAPI Drives has a sector size of 2048 bytes.
unsigned char err; int i;
</source>
 
We need IRQs:
 
<source lang="c">
// Enable IRQs:
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN = ide_irq_invoked = 0x0);
</source>
 
Let's setup the SCSI Packet, Which is 6-Words long [12-Bytes]:
 
<source lang="c">
// (I): Setup SCSI Packet:
// ------------------------------------------------------------------
atapi_packet[ 0] = ATAPI_CMD_READ;
atapi_packet[ 1] = 0x0;
atapi_packet[ 2] = (lba>>24) & 0xFF;
atapi_packet[ 3] = (lba>>16) & 0xFF;
atapi_packet[ 4] = (lba>> 8) & 0xFF;
atapi_packet[ 5] = (lba>> 0) & 0xFF;
atapi_packet[ 6] = 0x0;
atapi_packet[ 7] = 0x0;
atapi_packet[ 8] = 0x0;
atapi_packet[ 9] = numsects;
atapi_packet[10] = 0x0;
atapi_packet[11] = 0x0;
</source>
 
Now we should select the drive:
 
<source lang="c">
// (II): Select the Drive:
// ------------------------------------------------------------------
ide_write(channel, ATA_REG_HDDEVSEL, slavebit<<4);
</source>
 
400 nanoseconds delay after this select is a good idea:
 
<source lang="c">
// (III): Delay 400 nanoseconds for select to complete:
// ------------------------------------------------------------------
ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
</source>
 
<source lang="c">
// (IV): Inform the Controller that we use PIO mode:
// ------------------------------------------------------------------
ide_write(channel, ATA_REG_FEATURES, 0); // PIO mode.
</source>
 
Controller wants to know what do we think of the size of buffer!, we will allow him to know that:
 
<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.
</source>
 
Now we want to send the packet, we should first send the command "Packet":
 
<source lang="c">
// (VI): Send the Packet Command:
// ------------------------------------------------------------------
ide_write(channel, ATA_REG_COMMAND, ATA_CMD_PACKET); // Send the Command.
</source>
 
<source lang="c">
// (VII): Waiting for the driver to finish or invoke an error:
// ------------------------------------------------------------------
if (err = ide_polling(channel, 1)) return err; // Polling and return if error.
</source>
 
<source lang="c">
// (VIII): Sending the packet data:
// ------------------------------------------------------------------
asm("rep outsw"::"c"(6), "d"(bus), "S"(atapi_packet)); // Send Packet Data
</source>
 
Here we cannot Poll, We should wait for an IRQ, then read the sectors. these two operations should be repeated as the number of sectors, but we are said before that numsects should be 1. But I have put a for loop, i don't know why.
 
<source lang="c">
// (IX): Recieving Data:
// ------------------------------------------------------------------
for (i = 0; i < numsects; i++) {
ide_wait_irq(); // Wait for an IRQ.
if (err = ide_polling(channel, 1)) return err; // Polling and return if error.
asm("pushw %es");
asm("mov %%ax, %%es"::"a"(selector));
asm("rep insw"::"c"(words), "d"(bus), "D"(edi));// Receive Data.
asm("popw %es");
edi += (words*2);
}
</source>
 
Now we should wait for an IRQ and Poll for Busy and DRQ bits to be clear:
 
<source lang="c">
// (X): Waiting for an IRQ:
// ------------------------------------------------------------------
ide_wait_irq();
 
// (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?
}
</source>
 
===Standard Function For Reading from ATA/ATAPI Drive===