quinn-os/pata.c
2022-07-21 14:52:54 +10:00

800 lines
30 KiB
C

#include <stddef.h>
#include "inttypes.h"
#include "pvec.h"
#include "pci.h"
#include "io.h"
#include "pata.h"
#include "ahci.h"
#include "hd.h"
#include "memory.h"
#include "console.h"
#include "string.h"
extern void cli();
extern void sti();
struct ide_dev **ide_devices;
void ide_write(struct ide_dev *device, uint8_t channel, uint8_t reg, uint8_t data);
void init_pata() {
// find pci device
struct pci_device *pci_dev;
int devcount = 0;
struct ide_dev *ide_device;
while (pci_find_device(0x01, 0x01, &pci_dev, devcount)) {
devcount++;
if (devcount == 1) {
ide_devices = (struct ide_dev **)dbmalloc(sizeof(struct ide_dev *), "init pata 1");
} else {
ide_devices = (struct ide_dev **)dbrealloc(ide_devices, sizeof(struct ide_dev *) * devcount, "init pata 4");
}
ide_device = (struct ide_dev *)dbmalloc(sizeof(struct ide_dev), "init pata 3");
ide_device->pci_dev = pci_dev;
ide_device->use_dma = 1;
irq_install_handler(14, ide_irq_isr, 0);
irq_install_handler(15, ide_irq_isr, 0);
ide_initialize(ide_device);
ide_devices[devcount - 1] = ide_device;
}
kprintf("Found %d IDE contollers\n", devcount);
}
uint8_t ide_read(struct ide_dev *device, uint8_t channel, uint8_t reg) {
uint8_t result = 0;
if (reg > 0x07 && reg < 0x0C)
ide_write(device, channel, ATA_REG_CONTROL, 0x80 | device->channels[channel].nIEN);
if (reg < 0x08)
result = inportb(device->channels[channel].base + reg - 0x00);
else if (reg < 0x0C)
result = inportb(device->channels[channel].base + reg - 0x06);
else if (reg < 0x0E)
result = inportb(device->channels[channel].ctrl + reg - 0x0A);
else if (reg < 0x16)
result = inportb(device->channels[channel].bmide + reg - 0x0E);
if (reg > 0x07 && reg < 0x0C)
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN);
return result;
}
void ata_wait(struct ide_dev *device, uint8_t channel) {
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
}
void ide_write(struct ide_dev *device, uint8_t channel, uint8_t reg, uint8_t data) {
if (reg > 0x07 && reg < 0x0C)
ide_write(device, channel, ATA_REG_CONTROL, 0x80 | device->channels[channel].nIEN);
if (reg < 0x08)
outportb(device->channels[channel].base + reg - 0x00, data);
else if (reg < 0x0C)
outportb(device->channels[channel].base + reg - 0x06, data);
else if (reg < 0x0E)
outportb(device->channels[channel].ctrl + reg - 0x0A, data);
else if (reg < 0x16)
outportb(device->channels[channel].bmide + reg - 0x0E, data);
if (reg > 0x07 && reg < 0x0C)
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN);
}
void ide_read_buffer(struct ide_dev *device, uint8_t channel, uint8_t reg, uint32_t buffer, uint32_t quads) {
if (reg > 0x07 && reg < 0x0C)
ide_write(device, channel, ATA_REG_CONTROL, 0x80 | device->channels[channel].nIEN);
asm("pushw %es; movw %ds, %ax; movw %ax, %es");
if (reg < 0x08)
inportsl(device->channels[channel].base + reg - 0x00, (uint32_t *)buffer, quads);
else if (reg < 0x0C)
inportsl(device->channels[channel].base + reg - 0x06, (uint32_t *)buffer, quads);
else if (reg < 0x0E)
inportsl(device->channels[channel].ctrl + reg - 0x0A, (uint32_t *)buffer, quads);
else if (reg < 0x16)
inportsl(device->channels[channel].bmide + reg - 0x0E, (uint32_t *)buffer, quads);
asm("popw %es;");
if (reg > 0x07 && reg < 0x0C)
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN);
}
void ide_initialize(struct ide_dev *device) {
int i, j, k, count = 0;
device->channels[ATA_PRIMARY].base = (device->pci_dev->base[0] & 0xFFFFFFFC) + 0x1F0 * (!device->pci_dev->base[0]);
device->channels[ATA_PRIMARY].ctrl = (device->pci_dev->base[1] & 0xFFFFFFFC) + 0x3F6 * (!device->pci_dev->base[1]);
device->channels[ATA_SECONDARY].base = (device->pci_dev->base[2] & 0xFFFFFFFC) + 0x170 * (!device->pci_dev->base[2]);
device->channels[ATA_SECONDARY].ctrl = (device->pci_dev->base[3] & 0xFFFFFFFC) + 0x376 * (!device->pci_dev->base[3]);
device->channels[ATA_PRIMARY].bmide = (device->pci_dev->base[4] & 0xFFFFFFFC) + 0; // Bus Master IDE
device->channels[ATA_SECONDARY].bmide = (device->pci_dev->base[4] & 0xFFFFFFFC) + 8; // Bus Master IDE
ide_write(device, ATA_PRIMARY, ATA_REG_CONTROL, 2);
ide_write(device, ATA_SECONDARY, ATA_REG_CONTROL, 2);
// 3- Detect ATA-ATAPI Devices:
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++) {
uint8_t err = 0, type = IDE_ATA, status;
device->ide_devices[count].reserved = 0; // Assuming that no drive here.
// (I) Select Drive:
ide_write(device, i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); // Select Drive.
ata_wait(device, ATA_PRIMARY);
// (II) Send ATA Identify Command:
ide_write(device, i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
ata_wait(device, ATA_PRIMARY);
// (III) Polling:
if (!(ide_read(device, i, ATA_REG_STATUS)))
continue; // If Status = 0, No Device.
while (1) {
status = ide_read(device, 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.
}
// (IV) Probe for ATAPI Devices:
if (err) {
uint8_t cl = ide_read(device, i, ATA_REG_LBA1);
uint8_t ch = ide_read(device, i, ATA_REG_LBA2);
if (cl == 0x14 && ch == 0xEB)
type = IDE_ATAPI;
else if (cl == 0x69 && ch == 0x96)
type = IDE_ATAPI;
else
continue; // Unknown Type (And always not be a device).
ide_write(device, i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
ata_wait(device, ATA_PRIMARY);
}
// (V) Read Identification Space of the Device:
ide_read_buffer(device, i, ATA_REG_DATA, (uint32_t)device->buffer, 128);
// (VI) Read Device Parameters:
device->ide_devices[count].reserved = 1;
device->ide_devices[count].type = type;
device->ide_devices[count].channel = i;
device->ide_devices[count].drive = j;
device->ide_devices[count].sign = ((uint16_t *)(device->buffer + ATA_IDENT_DEVICETYPE))[0];
device->ide_devices[count].capabilities = ((uint16_t *)(device->buffer + ATA_IDENT_CAPABILITIES))[0];
device->ide_devices[count].commandsets = ((uint32_t *)(device->buffer + ATA_IDENT_COMMANDSETS))[0];
// (VII) Get Size:
if (device->ide_devices[count].commandsets & (1 << 26)) {
// Device uses 48-Bit Addressing:
device->ide_devices[count].size = ((uint32_t *)(device->buffer + ATA_IDENT_MAX_LBA_EXT))[0];
// Note that Quafios is 32-Bit Operating System, So last 2 Words are ignored.
} else {
// Device uses CHS or 28-bit Addressing:
device->ide_devices[count].size = ((uint32_t *)(device->buffer + ATA_IDENT_MAX_LBA))[0];
}
// (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...):
for (k = ATA_IDENT_MODEL; k < (ATA_IDENT_MODEL + 40); k += 2) {
device->ide_devices[count].model[k - ATA_IDENT_MODEL] = device->buffer[k + 1];
device->ide_devices[count].model[(k + 1) - ATA_IDENT_MODEL] = device->buffer[k];
}
device->ide_devices[count].model[40] = 0; // Terminate String.
// device->ide_devices[count].dma_prdt = (struct prdt_t *)malloc(sizeof(struct prdt_t));
device->ide_devices[count].dma_prdt_phys = (uint32_t)mem_alloc();
device->ide_devices[count].dma_prdt = (struct prdt_t *)mem_pci_sbrk(4096);
mem_map_page(device->ide_devices[count].dma_prdt_phys, (uint32_t)device->ide_devices[count].dma_prdt, 3);
device->ide_devices[count].dma_start_phys = (uint32_t)mem_alloc();
device->ide_devices[count].dma_start = mem_pci_sbrk(4096);
mem_map_page(device->ide_devices[count].dma_start_phys, device->ide_devices[count].dma_start, 3);
device->ide_devices[count].dma_prdt[0].offset = device->ide_devices[count].dma_start_phys;
device->ide_devices[count].dma_prdt[0].bytes = 512;
device->ide_devices[count].dma_prdt[0].last = 0x8000;
count++;
}
pci_set_master(device->pci_dev, 1);
// pci_set_mem_enable(device->pci_dev, 1);
// pci_set_io_enable(device->pci_dev, 1);
// 4- Print Summary:
for (i = 0; i < 4; i++)
if (device->ide_devices[i].reserved == 1) {
kprintf("Found %s Drive %dGB - %s\n", (const char *[]){"ATA", "ATAPI"}[device->ide_devices[i].type], /* Type */
device->ide_devices[i].size / 1024 / 1024 / 2, /* Size */
device->ide_devices[i].model);
}
init_ide_hd(device);
}
uint8_t ide_polling(struct ide_dev *device, uint8_t channel, uint32_t advanced_check) {
// (I) Delay 400 nanosecond for BSY to be set:
// -------------------------------------------------
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
// (II) Wait for BSY to be cleared:
// -------------------------------------------------
while (ide_read(device, channel, ATA_REG_STATUS) & ATA_SR_BSY)
; // Wait for BSY to be zero.
if (advanced_check) {
uint8_t state = ide_read(device, channel, ATA_REG_STATUS); // Read Status Register.
// (III) Check For Errors:
// -------------------------------------------------
if (state & ATA_SR_ERR)
return 2; // Error.
// (IV) Check If Device fault:
// -------------------------------------------------
if (state & ATA_SR_DF)
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 set
}
return 0; // No Error.
}
uint8_t ide_irq_invoked = 0;
void ide_wait_irq() {
while (!ide_irq_invoked) {
__asm__ volatile("hlt");
}
ide_irq_invoked = 0;
}
void ide_irq_isr(struct regs *r) { ide_irq_invoked = 1; }
uint8_t ide_ata_dma_access(struct ide_dev *device, uint8_t direction, uint8_t drive, uint32_t lba, uint8_t numsects, uint16_t selector, uint32_t edi) {
uint32_t channel = device->ide_devices[drive].channel; // Read the Channel.
uint32_t slavebit = device->ide_devices[drive].drive; // Read the Drive [Master/Slave]
// enable interrupts
if (direction == 0) {
// DMA Read.
outportb(device->channels[channel].bmide, 0x00);
outportl(device->channels[channel].bmide + 0x04, device->ide_devices[drive].dma_prdt_phys);
outportb(device->channels[channel].bmide + 0x02, inportb(device->channels[channel].bmide + 0x02) | 0x04 | 0x02);
outportb(device->channels[channel].bmide, 0x08);
outportb(device->channels[channel].base + ATA_REG_CONTROL, 0x00);
outportb(device->channels[channel].base + ATA_REG_HDDEVSEL, 0xe0 | slavebit << 4);
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN = ide_irq_invoked = 0x0);
ata_wait(device, channel);
for (int sc = 0; sc < numsects; sc++) {
// irq ON
// sti();
outportb(device->channels[channel].base + ATA_REG_FEATURES, 0x00);
outportb(device->channels[channel].base + ATA_REG_SECCOUNT0, 1);
outportb(device->channels[channel].base + ATA_REG_LBA0, (lba & 0x000000ff) >> 0);
outportb(device->channels[channel].base + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8);
outportb(device->channels[channel].base + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16);
// outportb(bus + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
while (1) {
uint8_t status = inportb(device->channels[channel].base + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
break;
}
outportb(device->channels[channel].base + ATA_REG_COMMAND, ATA_CMD_READ_DMA);
ata_wait(device, channel);
outportb(device->channels[channel].bmide, 0x08 | 0x01);
#if 0
ide_wait_irq();
#endif
while (1) {
int status = inportb(device->channels[channel].bmide + 0x02);
int dstatus = inportb(device->channels[channel].base + ATA_REG_STATUS);
if (!(status & 0x04)) {
continue;
}
if (!(dstatus & ATA_SR_BSY)) {
break;
}
}
// IRQ_OFF;
// cli();
/* Copy from DMA buffer to output buffer. */
outportb(device->channels[channel].bmide + 0x02, inportb(device->channels[channel].bmide + 0x02) | 0x04 | 0x02);
memcpy((char *)edi + (sc * 512), (char *)device->ide_devices[drive].dma_start, 512);
/* Inform device we are done. */
}
} else {
// DMA Write.
outportb(device->channels[channel].base + ATA_REG_CONTROL, 0x00);
outportb(device->channels[channel].base + ATA_REG_HDDEVSEL, 0xe0 | slavebit << 4);
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN = ide_irq_invoked = 0x0);
ata_wait(device, channel);
outportb(device->channels[channel].bmide, 0x00);
outportl(device->channels[channel].bmide + 0x04, device->ide_devices[drive].dma_prdt_phys);
outportb(device->channels[channel].bmide + 0x02, inportb(device->channels[channel].bmide + 0x02) | 0x04 | 0x02);
outportb(device->channels[channel].bmide, 0x00);
for (int sc = 0; sc < numsects; sc++) {
// irq ON
memcpy((char *)device->ide_devices[drive].dma_start, (char *)edi + (sc * 512), 512);
// sti();
outportb(device->channels[channel].base + ATA_REG_FEATURES, 0x00);
outportb(device->channels[channel].base + ATA_REG_SECCOUNT0, 1);
outportb(device->channels[channel].base + ATA_REG_LBA0, (lba & 0x000000ff) >> 0);
outportb(device->channels[channel].base + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8);
outportb(device->channels[channel].base + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16);
// outportb(bus + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
while (1) {
uint8_t status = inportb(device->channels[channel].base + ATA_REG_STATUS);
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
break;
}
outportb(device->channels[channel].base + ATA_REG_COMMAND, ATA_CMD_WRITE_DMA);
ata_wait(device, channel);
outportb(device->channels[channel].bmide, 0x01);
#if 0
ide_wait_irq();
#endif
while (1) {
int status = inportb(device->channels[channel].bmide + 0x02);
int dstatus = inportb(device->channels[channel].base + ATA_REG_STATUS);
if (!(status & 0x04)) {
continue;
}
if (!(dstatus & ATA_SR_BSY)) {
break;
}
}
// IRQ_OFF;
// cli();
/* Copy from DMA buffer to output buffer. */
outportb(device->channels[channel].bmide + 0x02, inportb(device->channels[channel].bmide + 0x02) | 0x04 | 0x02);
/* Inform device we are done. */
}
}
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN = (ide_irq_invoked = 0x0) + 0x2);
return 0;
}
uint8_t ide_ata_access(struct ide_dev *device, uint8_t direction, uint8_t drive, uint32_t lba, uint8_t numsects, uint16_t selector, uint32_t edi) {
uint8_t lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */, dma /* 0: No DMA, 1: DMA */, cmd;
uint8_t lba_io[6];
uint32_t channel = device->ide_devices[drive].channel; // Read the Channel.
uint32_t slavebit = device->ide_devices[drive].drive; // Read the Drive [Master/Slave]
uint32_t bus = device->channels[channel].base; // The Bus Base, like [0x1F0] which is also data port.
uint32_t words = 256; // Approximatly all ATA-Drives has sector-size of 512-byte.
uint16_t cyl, i;
uint8_t head, sect, err;
dma = 0;
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN = (ide_irq_invoked = 0x0) + 0x02);
// (I) Select one from LBA28, LBA48 or CHS;
if (lba >= 0x10000000) { // Sure Drive should support LBA in this case, or you are giving a wrong LBA.
// 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 lba is integer, so 32-bit are enough to access 2TB.
lba_io[5] = 0; // We said that we lba is integer, so 32-bit are enough to access 2TB.
head = 0; // Lower 4-bits of HDDEVSEL are not used here.
} else if (device->ide_devices[drive].capabilities & 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.
}
// (II) See if Drive Supports DMA or not;
// (III) Wait if the drive is busy;
while (ide_read(device, channel, ATA_REG_STATUS) & ATA_SR_BSY)
; // Wait if Busy.
// (IV) Select Drive from the controller;
if (lba_mode == 0)
ide_write(device, channel, ATA_REG_HDDEVSEL, 0xA0 | (slavebit << 4) | head); // Select Drive CHS.
else
ide_write(device, channel, ATA_REG_HDDEVSEL, 0xE0 | (slavebit << 4) | head); // Select Drive LBA.
// (V) Write Parameters;
if (lba_mode == 2) {
ide_write(device, channel, ATA_REG_SECCOUNT1, 0);
ide_write(device, channel, ATA_REG_LBA3, lba_io[3]);
ide_write(device, channel, ATA_REG_LBA4, lba_io[4]);
ide_write(device, channel, ATA_REG_LBA5, lba_io[5]);
}
ide_write(device, channel, ATA_REG_SECCOUNT0, numsects);
ide_write(device, channel, ATA_REG_LBA0, lba_io[0]);
ide_write(device, channel, ATA_REG_LBA1, lba_io[1]);
ide_write(device, channel, ATA_REG_LBA2, lba_io[2]);
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 == 2 && dma == 0 && direction == 0)
cmd = ATA_CMD_READ_PIO_EXT;
if (lba_mode == 0 && dma == 1 && direction == 0)
cmd = ATA_CMD_READ_DMA;
if (lba_mode == 1 && dma == 1 && direction == 0)
cmd = ATA_CMD_READ_DMA;
if (lba_mode == 2 && dma == 1 && direction == 0)
cmd = ATA_CMD_READ_DMA_EXT;
if (lba_mode == 0 && dma == 0 && direction == 1)
cmd = ATA_CMD_WRITE_PIO;
if (lba_mode == 1 && dma == 0 && direction == 1)
cmd = ATA_CMD_WRITE_PIO;
if (lba_mode == 2 && dma == 0 && direction == 1)
cmd = ATA_CMD_WRITE_PIO_EXT;
if (lba_mode == 0 && dma == 1 && direction == 1)
cmd = ATA_CMD_WRITE_DMA;
if (lba_mode == 1 && dma == 1 && direction == 1)
cmd = ATA_CMD_WRITE_DMA;
if (lba_mode == 2 && dma == 1 && direction == 1)
cmd = ATA_CMD_WRITE_DMA_EXT;
ide_write(device, channel, ATA_REG_COMMAND, cmd); // Send the Command.
if (direction == 0) {
// PIO Read.
for (i = 0; i < numsects; i++) {
if ((err = ide_polling(device, channel, 1)))
return err; // Polling, then 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(device, 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(device, channel, ATA_REG_COMMAND, (uint8_t[]){ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH_EXT}[lba_mode]);
ide_polling(device, channel, 0); // Polling.
}
return 0; // Easy, ... Isn't it?
}
uint8_t ide_atapi_read(struct ide_dev *device, uint8_t drive, uint32_t lba, uint8_t numsects, uint16_t selector, uint32_t edi) {
uint32_t channel = device->ide_devices[drive].channel;
uint32_t slavebit = device->ide_devices[drive].drive;
uint32_t bus = device->channels[channel].base;
uint32_t words = 2048 / 2; // Sector Size in Words, Almost All ATAPI Drives has a sector size of 2048 bytes.
uint8_t err;
int i;
uint8_t atapi_packet[12];
// Enable IRQs:
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN = ide_irq_invoked = 0x0);
// (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;
// (II): Select the Drive:
// ------------------------------------------------------------------
ide_write(device, channel, ATA_REG_HDDEVSEL, slavebit << 4);
// (III): Delay 400 nanosecond for select to complete:
// ------------------------------------------------------------------
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
// (IV): Inform the Controller that we use PIO mode:
// ------------------------------------------------------------------
ide_write(device, channel, ATA_REG_FEATURES, 0); // PIO mode.
// (V): Tell the Controller the size of buffer:
// ------------------------------------------------------------------
ide_write(device, channel, ATA_REG_LBA1, (words * 2) & 0xFF); // Lower Byte of Sector Size.
ide_write(device, channel, ATA_REG_LBA2, (words * 2) >> 8); // Upper Byte of Sector Size.
// (VI): Send the Packet Command:
// ------------------------------------------------------------------
ide_write(device, channel, ATA_REG_COMMAND, ATA_CMD_PACKET); // Send the Command.
// (VII): Waiting for the driver to finish or invoke an error:
// ------------------------------------------------------------------
if ((err = ide_polling(device, channel, 1)))
return err; // Polling and return if error.
// (VIII): Sending the packet data:
// ------------------------------------------------------------------
asm("rep outsw" ::"c"(6), "d"(bus), "S"(atapi_packet)); // Send Packet Data
// (IX): Recieving Data:
// ------------------------------------------------------------------
for (i = 0; i < numsects; i++) {
ide_wait_irq(); // Wait for an IRQ.
if ((err = ide_polling(device, 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);
}
// (X): Waiting for an IRQ:
// ------------------------------------------------------------------
ide_wait_irq();
// (XI): Waiting for BSY & DRQ to clear:
// ------------------------------------------------------------------
while (ide_read(device, channel, ATA_REG_STATUS) & (ATA_SR_BSY | ATA_SR_DRQ))
;
return 0; // Easy, ... Isn't it?
}
uint8_t ide_print_error(struct ide_dev *device, uint32_t drive, uint8_t err) {
if (err == 0)
return err;
kprintf(" IDE:");
if (err == 1) {
kprintf("- Device Fault\n ");
err = 19;
} else if (err == 2) {
uint8_t st = ide_read(device, device->ide_devices[drive].channel, ATA_REG_ERROR);
if (st & ATA_ER_AMNF) {
kprintf("- No Address Mark Found\n ");
err = 7;
}
if (st & ATA_ER_TK0NF) {
kprintf("- No Media or Media Error\n ");
err = 3;
}
if (st & ATA_ER_ABRT) {
kprintf("- Command Aborted\n ");
err = 20;
}
if (st & ATA_ER_MCR) {
kprintf("- No Media or Media Error\n ");
err = 3;
}
if (st & ATA_ER_IDNF) {
kprintf("- ID mark not Found\n ");
err = 21;
}
if (st & ATA_ER_MC) {
kprintf("- No Media or Media Error\n ");
err = 3;
}
if (st & ATA_ER_UNC) {
kprintf("- Uncorrectable Data Error\n ");
err = 22;
}
if (st & ATA_ER_BBK) {
kprintf("- Bad Sectors\n ");
err = 13;
}
} else if (err == 3) {
kprintf("- Reads Nothing\n ");
err = 23;
} else if (err == 4) {
kprintf("- Write Protected\n ");
err = 8;
}
kprintf("- [%s %s] %s\n", (const char *[]){"Primary", "Secondary"}[device->ide_devices[drive].channel],
(const char *[]){"Master", "Slave"}[device->ide_devices[drive].drive], device->ide_devices[drive].model);
return err;
}
void ide_read_sectors(struct ide_dev *device, uint8_t drive, uint8_t numsects, uint32_t lba, uint16_t es, uint32_t edi) {
int i;
// 1: Check if the drive presents:
// ==================================
if (drive > 3 || device->ide_devices[drive].reserved == 0) {
}
// 2: Check if inputs are valid:
// ==================================
else if (((lba + numsects) > device->ide_devices[drive].size) && (device->ide_devices[drive].type == IDE_ATA)) {
}
// 3: Read in PIO Mode through Polling & IRQs:
// ============================================
else {
uint8_t err = 0;
if (device->ide_devices[drive].type == IDE_ATA) {
if (device->use_dma) {
err = ide_ata_dma_access(device, ATA_READ, drive, lba, numsects, es, edi);
} else {
err = ide_ata_access(device, ATA_READ, drive, lba, numsects, es, edi);
}
} else if (device->ide_devices[drive].type == IDE_ATAPI) {
for (i = 0; i < numsects; i++)
err = ide_atapi_read(device, drive, lba + i, 1, es, edi + (i * 2048));
} else {
}
ide_print_error(device, drive, err);
}
}
void ide_write_sectors(struct ide_dev *device, uint8_t drive, uint8_t numsects, uint32_t lba, uint16_t es, uint32_t edi) {
// 1: Check if the drive presents:
// ==================================
if (drive > 3 || device->ide_devices[drive].reserved == 0) {
} // Drive Not Found!
// 2: Check if inputs are valid:
// ==================================
else if (((lba + numsects) > device->ide_devices[drive].size) && (device->ide_devices[drive].type == IDE_ATA)) {
}
// 3: Read in PIO Mode through Polling & IRQs:
// ============================================
else {
uint8_t err = 0;
if (device->ide_devices[drive].type == IDE_ATA) {
if (device->use_dma) {
err = ide_ata_dma_access(device, ATA_WRITE, drive, lba, numsects, es, edi);
} else {
err = ide_ata_access(device, ATA_WRITE, drive, lba, numsects, es, edi);
}
} else if (device->ide_devices[drive].type == IDE_ATAPI)
err = 4; // Write-Protected.
ide_print_error(device, drive, err);
}
}
void ide_atapi_eject(struct ide_dev *device, uint8_t drive) {
uint32_t channel = device->ide_devices[drive].channel;
uint32_t slavebit = device->ide_devices[drive].drive;
uint32_t bus = device->channels[channel].base;
uint8_t err;
uint8_t atapi_packet[12];
ide_irq_invoked = 0;
// 1: Check if the drive presents:
// ==================================
if (drive > 3 || device->ide_devices[drive].reserved == 0) {
} // Drive Not Found!
// 2: Check if drive isn't ATAPI:
// ==================================
else if (device->ide_devices[drive].type == IDE_ATA) {
} // Command Aborted.
// 3: Eject ATAPI Driver:
// ============================================
else {
// Enable IRQs:
ide_write(device, channel, ATA_REG_CONTROL, device->channels[channel].nIEN = ide_irq_invoked = 0x0);
// (I): Setup SCSI Packet:
// ------------------------------------------------------------------
atapi_packet[0] = ATAPI_CMD_EJECT;
atapi_packet[1] = 0x00;
atapi_packet[2] = 0x00;
atapi_packet[3] = 0x00;
atapi_packet[4] = 0x02;
atapi_packet[5] = 0x00;
atapi_packet[6] = 0x00;
atapi_packet[7] = 0x00;
atapi_packet[8] = 0x00;
atapi_packet[9] = 0x00;
atapi_packet[10] = 0x00;
atapi_packet[11] = 0x00;
// (II): Select the Drive:
// ------------------------------------------------------------------
ide_write(device, channel, ATA_REG_HDDEVSEL, slavebit << 4);
// (III): Delay 400 nanosecond for select to complete:
// ------------------------------------------------------------------
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
ide_read(device, channel, ATA_REG_ALTSTATUS); // Reading Alternate Status Port wastes 100ns.
// (IV): Send the Packet Command:
// ------------------------------------------------------------------
ide_write(device, channel, ATA_REG_COMMAND, ATA_CMD_PACKET); // Send the Command.
// (V): Waiting for the driver to finish or invoke an error:
// ------------------------------------------------------------------
if ((err = ide_polling(device, channel, 1)))
; // Polling and stop if error.
// (VI): Sending the packet data:
// ------------------------------------------------------------------
else {
asm("rep outsw" ::"c"(6), "d"(bus), "S"(atapi_packet)); // Send Packet Data
ide_wait_irq(); // Wait for an IRQ.
err = ide_polling(device, channel, 1); // Polling and get error code.
if (err == 3)
err = 0; // DRQ is not needed here.
}
ide_print_error(device, drive, err); // Return;
}
}