PCI IDE Controller: Difference between revisions

m
Cleanup, part 2
[unchecked revision][unchecked revision]
m (Large cleanup, needs grammar and technical checks. Part one)
m (Cleanup, part 2)
Line 64:
// The device doesn't use IRQs, check if this is an Parallel IDE:
if (class == 0x01 && subclass == 0x01 && (ProgIF == 0x8A || ProgIF == 0x80)) {
// This is a Parallel IDE Controller which useduses IRQs 14 and 15.
}
}
Line 84:
 
We can assume that BAR4 is 0x0 because we are not going to use it yet.
We will return to ide_initialize function, which searches for drives connected to the IDE, but. beforeBefore we are goinggo into this function, we should write some support functions and definitions which will help us a lot. But before that, we should write some definitions:
 
<source lang="c">
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
</source>
There is a port called theThe Command/Status Port, whenreturns ita isbit readmask referring to the status of a channel iswhen returned; the above bit mask expresses these statesread.
 
<source lang="c">
#define ATA_ER_BBK 0x80
#define ATA_ER_UNC 0x40
#define ATA_ER_MC 0x20
#define ATA_ER_IDNF 0x10
#define ATA_ER_MCR 0x08
#define ATA_ER_ABRT 0x04
#define ATA_ER_TK0NF 0x02
#define ATA_ER_AMNF 0x01
</source>
There is also port called theThe Features/Error Port, which returns the most recent error upon read;, thehas above definitions express thethese possible bit masks
 
<source lang="c">
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_READ_PIO_EXT 0x24
#define ATA_CMD_READ_DMA 0xC8
#define ATA_CMD_READ_DMA_EXT 0x25
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_WRITE_PIO_EXT 0x34
#define ATA_CMD_WRITE_DMA 0xCA
#define ATA_CMD_WRITE_DMA_EXT 0x35
#define ATA_CMD_CACHE_FLUSH 0xE7
#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
#define ATA_CMD_PACKET 0xA0
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_CMD_IDENTIFY 0xEC
</source>
When you write to the Command/Status port, you are executing one of the commands above.
Line 131:
#define ATAPI_CMD_EJECT 0x1B
</source>
The commands above are for ATAPI devices, which will be understood soon.
 
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.
The commands above are for ATAPI Devices which will be understood soon.
 
ATA_CMD_IDENTIFY_PACKET and ATA_CMD_IDENTIFY return a buffer of 512 bytes called Identification space; the following definitions are used to read information from the identification space.
<source lang="c">
#define ATA_IDENT_DEVICETYPE 0
#define ATA_IDENT_CYLINDERS 2
#define ATA_IDENT_HEADS 6
#define ATA_IDENT_SECTORS 12
#define ATA_IDENT_SERIAL 20
#define ATA_IDENT_MODEL 54
#define ATA_IDENT_CAPABILITIES 98
#define ATA_IDENT_FIELDVALID 106
#define ATA_IDENT_MAX_LBA 120
#define ATA_IDENT_COMMANDSETS 164
#define ATA_IDENT_MAX_LBA_EXT 200
</source>
 
When you select a drive, you should specify if it is the master drive or the slave one:
<source lang="c">
#define ATA_MASTER 0x00
#define ATA_SLAVE 0x01
</source>
 
<source lang="c">
#define IDE_ATA 0x00
#define IDE_ATAPI 0x01
</source>
 
<source lang="c">
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
#define ATA_REG_SECCOUNT1 0x08
#define ATA_REG_LBA3 0x09
#define ATA_REG_LBA4 0x0A
#define ATA_REG_LBA5 0x0B
#define ATA_REG_CONTROL 0x0C
#define ATA_REG_ALTSTATUS 0x0C
#define ATA_REG_DEVADDRESS 0x0D
</source>
 
 
Task File is a range of ports [8 ports] which are usedoffsets byfrom BAR0 (primary channel) [BAR0] and/or SecondaryBAR2 Channel(secondary [BAR2]channel). To exemplify:
 
* BAR0 + 0 is first port.
* BAR0 + 1 is second port.
* BAR0 + 32 is the third ... etc ...
 
The ALTSTATUS/CONTROL port returns the alternate status when read and controls a channel when written to.
if BAR0 is 0x1F0:
* For the primary channel, ALTSTATUS/CONTROL port is BAR1 + 2.
* The Data Port of the Primary Channel is 0x1F0.
* For the secondary channel, ALTSTATUS/CONTROL port is BAR3 + 2.
* The Features/Error Port of the Primary Channel is 0x1F1.
* etc ...
The same with the secondary channel.
 
We can now say that each channel has 13 registers. For the primary channel, we use these values:
There is a port which is called "ALTSTATUS/CONTROL PORT", when is read, you read alternate status, when this port is written to, you are controlling a channel.
* Data Register: BAR0 + 0; // Read-Write
* Error Register: BAR0 + 1; // Read Only
* Features Register: BAR0 + 1; // Write Only
* SECCOUNT0: BAR0 + 2; // Read-Write
* LBA0: BAR0 + 3; // Read-Write
* LBA1: BAR0 + 4; // Read-Write
* LBA2: BAR0 + 5; // Read-Write
* HDDEVSEL: BAR0 + 6; // Read-Write, used to select a drive in the channel.
* Command Register: BAR0 + 7; // Write Only.
* Status Register: BAR0 + 7; // Read Only.
* Alternate Status Register: BAR1 + 2; // Read Only.
* Control Register: BAR1 + 2; // Write Only.
* DEVADDRESS: BAR1 + 2; // 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.
* For the Primary Channel, ALTSTATUS/CONTROL Port is BAR1 + 2.
* For the Secondary Channel, ALTSTATUS/CONTROL Port is BAR3 + 2.
 
We can know say that Each Channel has 13 Register, for a primary channel:
 
* Data Register: BAR0[0]; // Read and Write
* Error Register: BAR0[1]; // Read Only
* Features Register: BAR0[1]; // Write Only
* SECCOUNT0: BAR0[2]; // Read and Write
* LBA0: BAR0[3]; // Read and Write
* LBA1: BAR0[4]; // Read and Write
* LBA2: BAR0[5]; // Read and Write
* HDDEVSEL: BAR0[6]; // Read and Write, this port is used to select a drive in the channel.
* Command Register: BAR0[7]; // Write Only.
* Status Register: BAR0[7]; // Read Only.
* Alternate Status Register: BAR1[2]; // Read Only.
* Control Register: BAR1[2]; // Write Only.
* DEVADDRESS: BAR1[2]; // I don't know what is the benefit from this register.
 
The map above is the same with the secondary channel, but it is using BAR2 and BAR3 instead of BAR0 and BAR1.
 
<source lang="c">
Line 228 ⟶ 218:
</source>
 
We have had defined all definitionseverything needed by the driver, now lets move to an important part,. weWe said that
* BAR0 is the Basestart of the I/O Portsports used by Primarythe primary Channelchannel.
* BAR1 is the Basestart of the I/O Portsports which control Primarythe primary Channelchannel.
* BAR2 is the Basestart of the I/O Portsports used by Secondarysecondary Channelchannel.
* BAR3 is the Basestart of the I/O Portsports which control Secondarysecondary Channelchannel.
* BAR4 is the Basestart of 8 I/O Portsports controls Primarythe Channelprimary channel's Bus Master IDE [BMIDE].
* BAR4 + 8 is the Base of 8 I/O Portsports controls Secondarysecondary Channelchannel's Bus Master IDE [BMIDE].
 
So we can make this global structure:
 
<source lang="c">
struct channelIDEChannelRegisters {
unsigned short base; // I/O Base.
unsigned short ctrl; // Control Base
Line 247 ⟶ 236:
</source>
 
We also need a buffer to read the identification space in itinto, 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:
 
<source lang="c">
Line 259 ⟶ 248:
<source lang="c">
struct ide_device {
unsigned char reservedReserved; // 0 (Empty) or 1 (This Drive really exists).
unsigned char channelChannel; // 0 (Primary Channel) or 1 (Secondary Channel).
unsigned char driveDrive; // 0 (Master Drive) or 1 (Slave Drive).
unsigned short typeType; // 0: ATA, 1:ATAPI.
unsigned short signSignature; // Drive Signature
unsigned short capabilitiesCapabilities;// Features.
unsigned int commandsetsCommandSets; // Command Sets Supported.
unsigned int sizeSize; // Size in Sectors.
unsigned char modelModel[41]; // Model in string.
} ide_devices[4];
</source>
Line 273 ⟶ 262:
When we read a register in a channel, like STATUS Register, it is easy to execute:
 
<source lang="c">
ide_read(channel, ATA_REG_STATUS);
 
<source lang="c">
unsigned char ide_read(unsigned char channel, unsigned char reg) {
unsigned char result;
if (reg > 0x07 && reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
if ide_write(regchannel, <ATA_REG_CONTROL, 0x08)0x80 result| = inb(channels[channel].base + reg - 0x00nIEN);
if (reg < 0x08)
else if (reg < 0x0C) result = inb(channels[channel].base + reg - 0x06);
else if (reg < 0x0E) result = inb(channels[channel].ctrl base + reg - 0x0A0x00);
else if (reg < 0x16) result = inb(channels[channel].bmide + reg - 0x0E0x0C);
if (regresult >= 0x07 && reg < 0x0C) ide_writeinb(channel, ATA_REG_CONTROL, channels[channel].nIENbase + reg - 0x06);
else if (reg < 0x0E)
result = inb(channels[channel].ctrl + reg - 0x0A);
else if (reg < 0x16)
result = inb(channels[channel].bmide + reg - 0x0E);
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
return result;
}
</source>
 
AndWe Alsoalso there isneed a function for writing to registers:
 
<source lang="c">
void ide_write(unsigned char channel, unsigned char reg, unsigned char data) {
if (reg > 0x07 && reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
if ide_write(regchannel, <ATA_REG_CONTROL, 0x08)0x80 outb(data,| channels[channel].base + reg - 0x00nIEN);
if (reg < 0x08)
else if (reg < 0x0C) outb(data, channels[channel].base + reg - 0x06);
else if (reg < 0x0E) outb(data, channels[channel].ctrlbase + reg - 0x0A0x00);
else if (reg < 0x16) outb(data, channels[channel].bmide + reg - 0x0E0x0C);
outb(data, channels[channel].base + reg - 0x06);
if (reg > 0x07 && reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
else if (reg < 0x0E)
outb(data, channels[channel].ctrl + reg - 0x0A);
else if (reg < 0x16)
outb(data, channels[channel].bmide + reg - 0x0E);
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
}
</source>
 
If We want toTo read the identification space, we should read the Data Register as Doublea Worddouble forword 128 times. the first read is the first dword, the second read is the second dword, and so on. weWe can read the 128 dwords andthen copy them to our buffer.
 
<source lang="c">
void ide_read_buffer(unsigned char channel, unsigned char reg, unsigned int buffer,
unsigned int quads) {
if (reg > 0x07 && reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
asm("pushw %es; movw %ds, %ax; movw %ax, %es");
if (reg < 0x08) insl(channels[channel].base + reg - 0x00, buffer, quads);
else if (reg < 0x0C) insl(channels[channel].base + reg - 0x060x00, buffer, quads);
else if (reg < 0x0E) insl(channels[channel].ctrl + reg - 0x0A, buffer, quads0x0C);
else if (reg < 0x16) insl(channels[channel].bmidebase + reg - 0x0E0x06, buffer, quads);
else if (reg < 0x0E)
insl(channels[channel].ctrl + reg - 0x0A, buffer, quads);
else if (reg < 0x16)
insl(channels[channel].bmide + reg - 0x0E, buffer, quads);
asm("popw %es;");
if (reg > 0x07 && reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
}
</source>
 
When we send a command, we should wait for 400 nanosecond, then we should read the Status Port,port. ifIf the Busy Bitbit is on, so we should read the status port again, until the Busy Bitbit is 0,; in this case,then we can read the results of the command. thisThis operation is called "Polling",. weWe can also use IRQs instead of polling, and IRQs are suitable for.
Multi-Tasking Environments, but i think Polling is much faster than IRQs.
 
After Manymany Commandscommands, if DFthe is set [Device Fault Bit],bit is soset, there is a failure, and; if DRQ is not set, so there is an error. ifIf the ERR bit is set, so there is an error which is described in Error Portport.
 
<source lang="c">
Line 327 ⟶ 332:
// (I) Delay 400 nanosecond for BSY to be set:
// -------------------------------------------------
for(int i = 0; i < 4; i++)
ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status Portport wastes 100ns; loop four times.
ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
 
// (II) Wait for BSY to be cleared:
// -------------------------------------------------
while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY); // Wait for BSY to be zero.
; // Wait for BSY to be zero.
 
if (advanced_check) {
 
unsigned char state = ide_read(channel, ATA_REG_STATUS); // Read Status Register.
 
// (III) Check For Errors:
// -------------------------------------------------
if (state & ATA_SR_ERR) return 2; // Error.
return 2; // Error.
 
// (IV) Check If Device fault:
// -------------------------------------------------
if (state & ATA_SR_DF ) return 1; // Device Fault.
return 1; // Device Fault.
 
// (V) Check DRQ:
// -------------------------------------------------
// BSY = 0; DF = 0; ERR = 0 so we should check for DRQ now.
if (!(state & ATA_SR_DRQ)) return== 3; // DRQ should be set0)
return 3; // DRQ should be set
 
}
Line 360 ⟶ 366:
</source>
 
ifIf there is an error, we have a functionsfunction which printprints errors on screen:
 
<source lang="c">
unsigned char ide_print_error(unsigned int drive, unsigned char err) {
if (err == 0)
if (err == 0) return err;
 
printk(" IDE:");
if (err == 1) {printk("- Device Fault\n "); err = 19;}
else if (err == 2) {
Line 382 ⟶ 387:
else if (err == 4) {printk("- Write Protected\n "); err = 8;}
printk("- [%s %s] %s\n",
(const char *[]){"Primary", "Secondary"}[ide_devices[drive].channel], // Use the channel as an index into the array
(const char *[]){"Master", "Slave"}[ide_devices[drive].drive], // Same as above, using the drive
ide_devices[drive].model);
 
Line 390 ⟶ 395:
</source>
 
Now letslet's return to the initialization function:
 
<source lang="c">
void ide_initialize(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3,
Line 399 ⟶ 403:
 
// 1- Detect I/O Ports which interface IDE Controller:
channels[ATA_PRIMARY ].base = (BAR0 &= 0xFFFFFFFC) + 0x1F0 * (!BAR0);
channels[ATA_PRIMARY ].ctrl = (BAR1 &= 0xFFFFFFFC) + 0x3F4 * (!BAR1);
channels[ATA_SECONDARY].base = (BAR2 &= 0xFFFFFFFC) + 0x170 * (!BAR2);
channels[ATA_SECONDARY].ctrl = (BAR3 &= 0xFFFFFFFC) + 0x374 * (!BAR3);
channels[ATA_PRIMARY ].bmide = (BAR4 &= 0xFFFFFFFC) + 0; // Bus Master IDE
channels[ATA_SECONDARY].bmide = (BAR4 &= 0xFFFFFFFC) + 8; // Bus Master IDE
</source>
Then we should disable IRQs in both channels by setting bit 1 [nIEN] in Control Port:
 
<source lang="c">
 
Then We Should Disable IRQs in the both channels [This is temporary]:
This happens by setting bit 1 [nIEN] in Control Port:
 
Code:
// 2- Disable IRQs:
ide_write(ATA_PRIMARY , ATA_REG_CONTROL, 2);
Line 416 ⟶ 418:
</source>
 
Now we need to check for drives which could be connected to each channel,. weWe will select the master drive of each channel, and send the command ATA_IDENTIFY command (Whichwhich is supported by ATA Drives). ifIf there's no error, there isare values returned in registers determineswhich determine the type of Drive,; if no drive is present, there will be strange values.
 
Notice that bit4if bit 4 in HDDEVSEL, ifis set to 1, we are selecting the slave drive, if set to 0, we are selecting the master drive.
 
<source lang="c">
Line 426 ⟶ 428:
 
unsigned char err = 0, type = IDE_ATA, status;
ide_devices[count].reserved Reserved = 0; // Assuming that no drive here.
 
// (I) Select Drive:
ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); // Select Drive.
sleep(1); // Wait 1ms for drive select to work.
 
Line 438 ⟶ 440:
 
// (III) Polling:
if (!(ide_read(i, ATA_REG_STATUS)) == 0) continue; // If Status = 0, No Device.
 
while(1) {
status = ide_read(i, ATA_REG_STATUS);
if ( (status & ATA_SR_ERR)) {err = 1; break;} // If Err, Device is not ATA.
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; // Everything is right.
}
Line 448 ⟶ 450:
// (IV) Probe for ATAPI Devices:
 
if (err != 0) {
unsigned char cl = ide_read(i, ATA_REG_LBA1);
unsigned char ch = ide_read(i, ATA_REG_LBA2);
 
if (cl == 0x14 && ch ==0xEB) type = IDE_ATAPI;
else if (cl == 0x69 && ch ==0x96) type = IDE_ATAPI;
else continue;if //(cl Unknown Type (And== always0x69 not&& bech a== device0x96).
type = IDE_ATAPI;
else
continue; // Unknown Type (may not be a device).
 
ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
Line 464 ⟶ 469:
 
// (VI) Read Device Parameters:
ide_devices[count].reservedReserved = 1;
ide_devices[count].typeType = type;
ide_devices[count].channelChannel = i;
ide_devices[count].driveDrive = j;
ide_devices[count].sign Signature = ((unsigned short *)(ide_buf + ATA_IDENT_DEVICETYPE ))[0];
ide_devices[count].capabilitiesCapabilities = ((unsigned short *)(ide_buf + ATA_IDENT_CAPABILITIES))[0];
ide_devices[count].commandsetsCommandSets = ((unsigned int *)(ide_buf + ATA_IDENT_COMMANDSETS ))[0];
 
// (VII) Get Size:
if (ide_devices[count].commandsetsCommandSets & (1 << 26)){
// Device uses 48-Bit Addressing:
ide_devices[count].sizeSize = ((unsigned int *) (ide_buf + ATA_IDENT_MAX_LBA_EXT ))[0];
else
// Note that Quafios is 32-Bit Operating System, So last 2 Words are ignored.
} else {
// Device uses CHS or 28-bit Addressing:
ide_devices[count].sizeSize = ((unsigned int *) (ide_buf + ATA_IDENT_MAX_LBA ))[0];
}
 
// (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...):
for(k = ATA_IDENT_MODEL0; k < (ATA_IDENT_MODEL+40); k += 2) {
ide_devices[count].modelModel[k - ATA_IDENT_MODEL] = ide_buf[ATA_IDENT_MODEL + k + 1];
ide_devices[count].modelModel[(k +1) - ATA_IDENT_MODEL1] = ide_buf[ATA_IDENT_MODEL + k];}
ide_devices[count].modelModel[40] = 0; // Terminate String.
 
count++;
Line 493 ⟶ 496:
// 4- Print Summary:
for (i = 0; i < 4; i++)
if (ide_devices[i].reservedReserved == 1) {
printk(" Found %s Drive %dGB - %s\n",
(const char *[]){"ATA", "ATAPI"}[ide_devices[i].typeType], /* Type */
ide_devices[i].sizeSize / 1024 / 1024 / 2, /* Size */
ide_devices[i].modelModel);
}
}
Line 503 ⟶ 506:
 
===Read/Write From ATA Drive===
Now we are're moving to a bitslightly more advanced part, it is to read and write from/to an ATA Drivedrive.
There is 3 ways of addressing a sector:
* CHS (Cylinder-Head-Sector): an old way of addressing sectors in ATA drives, I think all ATA-Drives drives should support this way of addressing.
* LBA28: Accessing a sector by its LBA Address. but the address should be 28-bit longLBA address. iAll thinkATA all ATA-Drivesdrives should support this way of addressing, the problem ofwith LBA28 Addressing is that it allows only toallows access 128GB fromto thebe ATA-Diskaccessed, so if ATA-Diskthe disk is morebigger than 128GB, it should support the LBA48 Feature Set.
* LBA48: Accessing a sector by its LBA Address. but the address should be 48-bit longLBA address. asAs we use integers in GCC, so our maximum address in this tutorial is 32-bit long, which allows accessing ana ATA-Drivedrive with a size of up to 2TB.
 
So We can conclude an algorithm to determine which type of Addressing we are going to use:
Line 515 ⟶ 518:
// Use CHS.
else (if the LBA Sector Address > 0x0FFFFFFF)
// The Sector We are going to read is above 128GB Boundary, Use LBA48.
else
// Use LBA28.
Line 521 ⟶ 524:
 
Reading the buffer may be done by polling or DMA.
PIO: After sending the command [Read or Write Sectors], we read Data Port [as words], or write to Data Port [as words]. thisThis is the same way of reading identification space.
DMA: After sending the command, you should wait for an IRQ, while you are waiting, Buffer is written directly to memory automatically.
 
We are going to use PIO as it isn't going to be complex, and i want to be far from IRQs as they are very slower than Polling after PIO.
 
We can conclude also this table:
Line 533 ⟶ 536:
* Addressing Modes:
* ================
* - LBA28 Mode. (+)
* - LBA48 Mode. (+)
* - CHS. (+)
* Reading Modes:
Line 545 ⟶ 548:
* ================
* - IRQs
* - Polling Status (+) // Suitable for Singletasking
*/
</source>
 
There is something needed to be expressed here, I have told before that Task-File is like that:
* Register 0: [Word] Data Register. [Readable & Writable](Read-Write).
* Register 1: [Byte] Error Register. [Readable](Read).
* Register 1: [Byte] Features Register. [Writable](Write).
* Register 2: [Byte] SECCOUNT0 Register. [Readable & Writable](Read-Write).
* Register 3: [Byte] LBA0 Register. [Readable & Writable](Read-Write).
* Register 4: [Byte] LBA1 Register. [Readable & Writable](Read-Write).
* Register 5: [Byte] LBA2 Register. [Readable & Writable](Read-Write).
* Register 6: [Byte] HDDEVSEL Register. [Readable & Writable](Read-Write).
* Register 7: [Byte] Command Register. [Writable](Write).
* Register 7: [Byte] Status Register. [Readable](Read).
 
So each oneregister of Registers frombetween 2 to 5 should be 8-bits long. but reallyReally each one of them isare 16-bitbits long.
 
* Register 2: [Bits 0-7] SECCOUNT0, [Bits 8-15] SECOUNT1
Line 568 ⟶ 571:
* 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 ismakes up bits Bits[0- : 7] of the LBA Addressaddress when you read in LBA28 or LBA48,; it can also be the sector number of CHS.
* LBA1 ismakes up bits Bits[8- : 15] of the LBA Addressaddress when you read in LBA28 or LBA48,; it can also be the low 8byte bitsof ofthe cylinder number of CHS.
* LBA2 ismakes up bits Bits[16- : 23] of the LBA Addressaddress when you read in LBA28 or LBA48,; it can also be the high 8byte bitsof ofthe cylinder number of CHS.
* LBA3 ismakes Bits[24-31]up ofbits LBA24 Address: when31 youof read inthe LBA48 address.
* LBA4 ismakes Bits[32-39]up ofbits LBA32 Address: when39 youof read inthe LBA48 address.
* LBA5 ismakes Bits[40-47]up ofbits LBA40 Address: when47 youof readLBA48 in LBA48address.
 
Notice that according to that,the LBA0, 1, and 2 registers [8-bitsare +24 8-bits +long 8-bits]in are 24-bit longtotal, which is not enough for LBA28, so; the higher 4-bits can be written to the lower 4-bits of the HDDEVSEL Registerregister.
 
Also notice that if we set bit 6 of this register, we are going to use LBA, if not, we are going to use CHS.
notice that there is a mode which is called extended CHS, but i don't wanna be exposed to that.
 
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:
Line 591 ⟶ 592:
</source>
 
This Functionfunction reads/writes sectors from ATA-Drive. ifIf (direction =is 0) so we are reading, else we are writing.
* drive, is the drive number which can be from 0 to 3.
* lba, is the LBA Addressaddress which allows us to access disks up to 2TB.
* numsects, is the number of sectors to be read, it is a char, as reading more than 256 sector immediately may causeperformance the OS to hangissues. notice that ifIf numsects =is 0, the ATA controller will know that we want 256 sectors.
* selector, is the segment selector to read from, or write to.
* edi, is the offset in thethat segment.
 
<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];
unsigned int channel = ide_devices[drive].channelChannel; // Read the Channel.
unsigned int slavebit = ide_devices[drive].driveDrive; // Read the Drive [Master/Slave]
unsigned int bus = channels[channel].baseBase; // Bus Base, like 0x1F0 which is also data port.
unsigned int words = 256; // ApproximatelyAlmost allevery ATA-Drives drive has a sector-size of 512-byte.
unsigned short cyl, i; unsigned char head, sect, err;
unsigned char head, sect, err;
</source>
 
We don't need IRQs, so we should disable it to disallowprevent problems tofrom happen,happening. weWe said before that if bit 1 of the Control Register (Whichwhich is called nIEN bit), if it is set, so no IRQs will be invoked from any drives on this channel, either from Master Drivemaster or from Slave Driveslave.
 
<source lang="c">
Line 622 ⟶ 624:
// LBA48:
lba_mode = 2;
lba_io[0] = (lba & 0x000000FF) >> 0;
lba_io[1] = (lba & 0x0000FF00) >> 8;
lba_io[2] = (lba & 0x00FF0000) >> 16;
lba_io[3] = (lba & 0xFF000000) >> 24;
lba_io[4] = 0; // We said that we lbaLBA28 is integer, so 32-bitbits are enough to access 2TB.
lba_io[5] = 0; // We said that we lbaLBA28 is integer, so 32-bitbits are enough to access 2TB.
head = 0; // Lower 4-bits of HDDEVSEL are not used here.
} else if (ide_devices[drive].capabilitiesCapabilities & 0x200) { // Drive supports LBA?
// LBA28:
lba_mode = 1;
lba_io[0] = (lba & 0x00000FF) >> 0;
lba_io[1] = (lba & 0x000FF00) >> 8;
lba_io[2] = (lba & 0x0FF0000) >> 16;
lba_io[3] = 0; // These Registers are not used here.
lba_io[4] = 0; // These Registers are not used here.
lba_io[5] = 0; // These Registers are not used here.
head = (lba & 0xF000000) >> 24;
} else {
// CHS:
lba_mode = 0;
sect = (lba % 63) + 1;
cyl = (lba + 1 - sect) / (16 * 63);
lba_io[0] = sect;
lba_io[1] = (cyl >> 0) & 0xFF;
lba_io[2] = (cyl >> 8) & 0xFF;
lba_io[3] = 0;
lba_io[4] = 0;
lba_io[5] = 0;
head = (lba + 1 - sect) % (16 * 63) / (63); // Head number is written to HDDEVSEL lower 4-bits.
}
</source>
Line 657 ⟶ 659:
 
<source lang="c">
// (II) See if Drivedrive Supportssupports DMA or not;
dma = 0; // Supports or doesn't, weWe don't support !!!DMA
</source>
 
Lets Pollpoll the statusStatus port if the channel is busy:
 
<source lang="c">
// (III) Wait if the drive is busy;
while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY); // Wait if Busy.
; // Wait if Busy.
</source>
 
The HDDDEVSEL Registerregister now looks like this:
 
* Bits 0- :3: Head Number for CHS.
* Bit 4: Slave Bit. (0: Selecting Master Drive, 1: Selecting Slave Drive).
* Bit 5: Obsolete and isn't used, but should be set.
Line 680 ⟶ 683:
<source lang="c">
// (IV) Select Drive from the controller;
if (lba_mode == 0)
if (lba_mode==0) ide_write(channel,ATA_REG_HDDEVSEL,0xA0|(slavebit<<4)|head); // Drive & CHS.
else ide_write(channel, ATA_REG_HDDEVSEL,0xE0 0xA0 | (slavebit << 4) | head); // Drive & LBACHS.
else
ide_write(channel, ATA_REG_HDDEVSEL, 0xE0 | (slavebit << 4) | head); // Drive & LBA
</source>
 
Line 700 ⟶ 705:
</source>
 
By this way, ifIf you are using LBA48 and want to write to the LBA0 Register [Register 3 in Task-File], and want to write to LBA3 Register [Register 3 also in Task-File]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 full-fullly 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:
Line 715 ⟶ 720:
</source>
 
There isn't a command for Doing indoing CHS with DMA.
 
<source lang="c">
Line 733 ⟶ 738:
</source>
 
This Command "ATA_CMD_READ_PIO" command is rightused for reading in LBA28 or CHS, and the IDE controller refers to bit 6 of the HDDEVSEL Registerregister to knowfind out the mode of reading (LBA or CHS).
 
After sending the command, we should poll, then we read/write a sector, then we should poll, then we read/write a sector, until we read/write all sectors needed, if an error ishas happened, we the function will return a specific error code.
 
Notice that after writing, we should execute the CACHE FLUSH Commandcommand, and we should poll after it, but without checking for errors.
 
<source lang="c">
if (dma)
if (direction == 0);
// DMA Read.
else; // DMA Write.
// DMA Write.
else
if (direction == 0)
// PIO Read.
for (i = 0; i < numsects; i++) {
if (err = ide_polling(channel, 1)) return err; // Polling, set error and exit if there is.
return err; // Polling, set error and exit if there is.
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);
} else {
// PIO Write.
for (i = 0; i < numsects; i++) {
ide_polling(channel, 0); // Polling.
asm("pushw %ds");
asm("mov %%ax, %%ds"::"a"(selector));
asm("rep outsw"::"c"(words), "d"(bus), "S"(edi)); // Send Data
asm("popw %ds");
edi += (words*2);
}
ide_write(channel, ATA_REG_COMMAND, (char []) { ATA_CMD_CACHE_FLUSH,
ATA_CMD_CACHE_FLUSH,
ATA_CMD_CACHE_FLUSH_EXT}[lba_mode]);
ide_polling(channel, 0); // Polling.
}
 
return 0; // Easy, ... Isnisn't it?
}
</source>
 
===Read From ATAPI Drive===
Let's move to a part which is quitean easier, itpart is- to readreading from an ATAPI Drive,drive. iI will not make the function write to an ATAPI Drivedrive, because the write Operation is very complex and itis shouldoutside doneof bythe third-party tools (like Nero in Windows, andscope Braseroof inthis Linux)tutorial.
 
An ATAPI Drivedrive is different from an ATA Drivesdrive, as it doesn'tuses usethe ATASCSI command Commandsset, butnot itthe useATA thecommand SCSI-Command-Setset. Parameters are sent into aas Packetpackets, so it is Called:called the ATA-Packet Interface [ATAPI].
 
Notice also that ATAPI drives should always use IRQs, you can't disable them, so. weWe 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;
}
Line 796 ⟶ 804:
</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 clears it.
 
<source lang="c">
Line 803 ⟶ 811:
</source>
 
* drive, is the drive number, which is from 0 to 3.
* lba, is the lbaLBA address.
* numsects, is the number of sectors,. itIt should always be 1, and if you wannawant to read more than one sector, re-execute this function with th updated LBA address.
* selector, is the Segment Selector.
* edi, is the offset in the selector.
 
Let's read the parameters of the drive:
 
<source lang="c">
unsigned int channel = ide_devices[drive].channelChannel;
unsigned int slavebit = ide_devices[drive].driveDrive;
unsigned int bus = channels[channel].baseBase;
unsigned int words = 1024; // Sector Size. ATAPI Drives has a sector size of 2048 bytes.
unsigned char err; int i;
int i;
</source>
 
Line 826 ⟶ 835:
</source>
 
Let's setup the SCSI Packet, Whichwhich is 6-Words longwords [(12-Bytes] bytes) long:
 
<source lang="c">
Line 833 ⟶ 842:
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;
Line 850 ⟶ 859:
// (II): Select the Drive:
// ------------------------------------------------------------------
ide_write(channel, ATA_REG_HDDEVSEL, slavebit << 4);
</source>
 
Line 858 ⟶ 867:
// (III): Delay 400 nanoseconds for select to complete:
// ------------------------------------------------------------------
for(int i = 0; i < 4; i++)
ide_read(channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status Portport 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>
 
Line 870 ⟶ 877:
</source>
 
Tell the controller the size of the buffer
Controller wants to know what do we think of the size of buffer!, we will allow him to know that:
 
<source lang="c">
Line 876 ⟶ 883:
// ------------------------------------------------------------------
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>
 
Line 896 ⟶ 903:
// (VIII): Sending the packet data:
// ------------------------------------------------------------------
asm("rep outsw" : : "c"(6), "d"(bus), "S"(atapi_packet)); // Send Packet Data
</source>
 
Anonymous user