800 lines
30 KiB
C
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;
|
|
}
|
|
} |