#include <string.h>
#include <malloc.h>
#include <thread.h>
#include <nullfs.h>
#include <printf.h>
#include <dev/pci.h>
#include <dev/pciide.h>
static void ide_register_device(ide_controller_t *c, uint8_t device, fs_t *iofs);
// ================================ //
// HELPER FUNCTIONS FOR PCI IDE I/O //
// ================================ //
static void ide_write(ide_controller_t *c, uint8_t channel, uint8_t reg, uint8_t data) {
if (reg > 0x07 && reg < 0x0C) {
ide_write(c, channel, ATA_REG_CONTROL, 0x80 | c->channels[channel].nIEN);
}
if (reg < 0x08) {
outb(c->channels[channel].base + reg - 0x00, data);
} else if (reg < 0x0C) {
outb(c->channels[channel].base + reg - 0x06, data);
} else if (reg < 0x0E) {
outb(c->channels[channel].ctrl + reg - 0x0A, data);
} else if (reg < 0x16) {
outb(c->channels[channel].bmide + reg - 0x0E, data);
}
if (reg > 0x07 && reg < 0x0C) {
ide_write(c, channel, ATA_REG_CONTROL, c->channels[channel].nIEN);
}
}
static uint8_t ide_read(ide_controller_t *c, uint8_t channel, uint8_t reg) {
uint8_t result = 0;
if (reg > 0x07 && reg < 0x0C) {
ide_write(c, channel, ATA_REG_CONTROL, 0x80 | c->channels[channel].nIEN);
}
if (reg < 0x08) {
result = inb(c->channels[channel].base + reg - 0x00);
} else if (reg < 0x0C) {
result = inb(c->channels[channel].base + reg - 0x06);
} else if (reg < 0x0E) {
result = inb(c->channels[channel].ctrl + reg - 0x0A);
} else if (reg < 0x16) {
result = inb(c->channels[channel].bmide + reg - 0x0E);
}
if (reg > 0x07 && reg < 0x0C) {
ide_write(c, channel, ATA_REG_CONTROL, c->channels[channel].nIEN);
}
return result;
}
static void ide_read_buffer(ide_controller_t *c, uint8_t channel, uint8_t reg, void* buffer, size_t quads) {
if (reg > 0x07 && reg < 0x0C) {
ide_write(c, channel, ATA_REG_CONTROL, 0x80 | c->channels[channel].nIEN);
}
if (reg < 0x08) {
insl(c->channels[channel].base + reg - 0x00, buffer, quads);
} else if (reg < 0x0C) {
insl(c->channels[channel].base + reg - 0x06, buffer, quads);
} else if (reg < 0x0E) {
insl(c->channels[channel].ctrl + reg - 0x0A, buffer, quads);
} else if (reg < 0x16) {
insl(c->channels[channel].bmide + reg - 0x0E, buffer, quads);
}
if (reg > 0x07 && reg < 0x0C) {
ide_write(c, channel, ATA_REG_CONTROL, c->channels[channel].nIEN);
}
}
static int ide_polling(ide_controller_t *c, uint8_t channel, bool advanced_check) {
// (I) Delay 400 nanosecond for BSY to be set:
for(int i = 0; i < 4; i++)
ide_read(c, channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns; loop four times.
// (II) Wait for BSY to be cleared:
while (ide_read(c, channel, ATA_REG_STATUS) & ATA_SR_BSY);
if (advanced_check) {
uint8_t state = ide_read(c, 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) == 0)
return 3; // DRQ should be set
}
return 0; // ok
}
static uint8_t ide_print_error(ide_controller_t *c, int drive, uint8_t err) {
if (err == 0)
return err;
dbg_printf("IDE: ");
if (err == 1) {
dbg_printf("- Device Fault\n ");
err = 19;
} else if (err == 2) {
uint8_t st = ide_read(c, c->devices[drive].channel, ATA_REG_ERROR);
if (st & ATA_ER_AMNF) {
dbg_printf("- No Address Mark Found\n ");
err = 7;
}
if (st & ATA_ER_TK0NF) {
dbg_printf("- No Media or Media Error\n ");
err = 3;
}
if (st & ATA_ER_ABRT) {
dbg_printf("- Command Aborted\n ");
err = 20;
}
if (st & ATA_ER_MCR) {
dbg_printf("- No Media or Media Error\n ");
err = 3;
}
if (st & ATA_ER_IDNF) {
dbg_printf("- ID mark not Found\n ");
err = 21;
}
if (st & ATA_ER_MC) {
dbg_printf("- No Media or Media Error\n ");
err = 3;
}
if (st & ATA_ER_UNC) {
dbg_printf("- Uncorrectable Data Error\n ");
err = 22;
}
if (st & ATA_ER_BBK) {
dbg_printf("- Bad Sectors\n ");
err = 13;
}
} else if (err == 3) {
dbg_printf("- Reads Nothing\n ");
err = 23;
} else if (err == 4) {
dbg_printf("- Write Protected\n ");
err = 8;
}
dbg_printf("- [%s %s] %s\n",
(const char *[]){"Primary", "Secondary"}[c->devices[drive].channel],
(const char *[]){"Master", "Slave"}[c->devices[drive].drive],
c->devices[drive].model);
return err;
}
// ============ //
// IRQ HANDLERS //
// ============ //
STATIC_MUTEX(on_pciirq);
STATIC_MUTEX(on_irq14);
STATIC_MUTEX(on_irq15);
static thread_t *wait_irq14 = 0, *wait_irq15 = 0, *wait_pciirq = 0;
void irq14_handler(registers_t *regs) {
if (wait_irq14) {
resume_thread(wait_irq14, true);
wait_irq14 = 0;
}
}
void irq15_handler(registers_t *regs) {
if (wait_irq15) {
resume_thread(wait_irq15, true);
wait_irq15 = 0;
}
}
void pciirq_handler(int pci_id) {
if (wait_pciirq) {
resume_thread(wait_pciirq, true);
wait_pciirq = 0;
}
}
static void ide_prewait_irq(ide_controller_t *c, int channel) {
int irq = c->channels[channel].irq;
if (irq == 14) {
mutex_lock(&on_irq14);
wait_irq14 = current_thread;
} else if (irq == 15) {
mutex_lock(&on_irq15);
wait_irq15 = current_thread;
} else {
mutex_lock(&on_pciirq);
wait_pciirq = current_thread;
}
}
static void ide_wait_irq(ide_controller_t *c, int channel) {
asm volatile("cli");
int irq = c->channels[channel].irq;
if (irq == 14) {
if (wait_irq14) pause();
mutex_unlock(&on_irq14);
} else if (irq == 15) {
if (wait_irq15) pause();
mutex_unlock(&on_irq15);
} else {
if (wait_pciirq) pause();
mutex_unlock(&on_pciirq);
}
asm volatile("sti");
}
// ===================================== //
// ACTUAL READING AND WRITING OF SECTORS //
// ===================================== //
static uint8_t ide_ata_access(ide_controller_t *c, int direction,
uint8_t drive, uint32_t lba, uint8_t numsects, void* ptr)
{
uint32_t channel = c->devices[drive].channel; // Read the Channel.
uint32_t slavebit = c->devices[drive].drive; // Read the Drive [Master/Slave]
uint32_t bus = c->channels[channel].base; // Bus Base, like 0x1F0 which is also data port.
const uint32_t words = 256; // Almost every ATA drive has a sector-size of 512-byte.
uint16_t cyl;
uint8_t head, sect, err;
// (I) Select one from LBA28, LBA48 or CHS;
uint8_t lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */;
uint8_t lba_io[6];
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;
lba_io[5] = 0;
head = 0; // Lower 4-bits of HDDEVSEL are not used here.
} else if (c->devices[drive].cap & 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;
bool dma = false; // We don't support DMA
// (III) Wait if the drive is busy;
while (ide_read(c, channel, ATA_REG_STATUS) & ATA_SR_BSY); // Wait if busy.
// (IV) Select Drive from the controller;
if (lba_mode == 0) {
ide_write(c, channel, ATA_REG_HDDEVSEL, 0xA0 | (slavebit << 4) | head); // Drive & CHS.
} else {
ide_write(c, channel, ATA_REG_HDDEVSEL, 0xE0 | (slavebit << 4) | head); // Drive & LBA
}
// (V) Write Parameters;
if (lba_mode == 2) {
ide_write(c, channel, ATA_REG_SECCOUNT1, 0);
ide_write(c, channel, ATA_REG_LBA3, lba_io[3]);
ide_write(c, channel, ATA_REG_LBA4, lba_io[4]);
ide_write(c, channel, ATA_REG_LBA5, lba_io[5]);
}
ide_write(c, channel, ATA_REG_SECCOUNT0, numsects);
ide_write(c, channel, ATA_REG_LBA0, lba_io[0]);
ide_write(c, channel, ATA_REG_LBA1, lba_io[1]);
ide_write(c, channel, ATA_REG_LBA2, lba_io[2]);
// (VI) Select the command and send it;
// Routine that is followed:
// If ( DMA & LBA48) DO_DMA_EXT;
// If ( DMA & LBA28) DO_DMA_LBA;
// If ( DMA & LBA28) DO_DMA_CHS;
// If (!DMA & LBA48) DO_PIO_EXT;
// If (!DMA & LBA28) DO_PIO_LBA;
// If (!DMA & !LBA#) DO_PIO_CHS;
uint8_t cmd;
if (lba_mode == 0 && !dma && direction == 0) cmd = ATA_CMD_READ_PIO;
if (lba_mode == 1 && !dma && direction == 0) cmd = ATA_CMD_READ_PIO;
if (lba_mode == 2 && !dma && direction == 0) cmd = ATA_CMD_READ_PIO_EXT;
if (lba_mode == 0 && dma && direction == 0) cmd = ATA_CMD_READ_DMA;
if (lba_mode == 1 && dma && direction == 0) cmd = ATA_CMD_READ_DMA;
if (lba_mode == 2 && dma && direction == 0) cmd = ATA_CMD_READ_DMA_EXT;
if (lba_mode == 0 && !dma && direction == 1) cmd = ATA_CMD_WRITE_PIO;
if (lba_mode == 1 && !dma && direction == 1) cmd = ATA_CMD_WRITE_PIO;
if (lba_mode == 2 && !dma && direction == 1) cmd = ATA_CMD_WRITE_PIO_EXT;
if (lba_mode == 0 && dma && direction == 1) cmd = ATA_CMD_WRITE_DMA;
if (lba_mode == 1 && dma && direction == 1) cmd = ATA_CMD_WRITE_DMA;
if (lba_mode == 2 && dma && direction == 1) cmd = ATA_CMD_WRITE_DMA_EXT;
ide_write(c, channel, ATA_REG_COMMAND, cmd); // Send the Command.
if (dma) {
if (direction == 0) {
// DMA Read.
ASSERT(false);
} else {
// DMA Write.
ASSERT(false);
}
} else {
if (direction == 0) {
// PIO Read.
for (int i = 0; i < numsects; i++) {
err = ide_polling(c, channel, true);
if (err) return err; // Polling, set error and exit if there is.
insw(bus, ptr, words);
ptr += (words*2);
}
} else {
// PIO Write.
for (int i = 0; i < numsects; i++) {
ide_polling(c, channel, false); // Polling.
outsw(bus, ptr, words);
ptr += (words*2);
}
ide_write(c, channel, ATA_REG_COMMAND,
(char []){ATA_CMD_CACHE_FLUSH,
ATA_CMD_CACHE_FLUSH,
ATA_CMD_CACHE_FLUSH_EXT}[lba_mode]);
ide_polling(c, channel, false); // Polling.
}
}
return 0; // Easy, isn't it?
}
static uint8_t ide_atapi_read(ide_controller_t *c, uint8_t drive, uint32_t lba,
uint8_t numsects, void* ptr)
{
uint32_t channel = c->devices[drive].channel;
uint32_t slavebit = c->devices[drive].drive;
uint32_t bus = c->channels[channel].base;
const uint32_t words = 1024; // Sector Size. ATAPI drives have a sector size of 2048 bytes.
uint8_t err;
// Enable IRQs:
c->channels[channel].nIEN = 0;
ide_write(c, channel, ATA_REG_CONTROL, 0);
// (I): Setup SCSI Packet:
uint8_t atapi_packet[12];
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(c, channel, ATA_REG_HDDEVSEL, slavebit << 4);
// (III): Delay 400 nanoseconds for select to complete:
for(int i = 0; i < 4; i++)
ide_read(c, channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns.
// (IV): Inform the Controller that we use PIO mode:
ide_write(c, channel, ATA_REG_FEATURES, 0); // PIO mode.
// (V): Tell the Controller the size of buffer:
ide_write(c, channel, ATA_REG_LBA1, (words * 2) & 0xFF); // Lower Byte of Sector Size.
ide_write(c, channel, ATA_REG_LBA2, (words * 2) >> 8); // Upper Byte of Sector Size.
// (VI): Send the Packet Command:
ide_write(c, channel, ATA_REG_COMMAND, ATA_CMD_PACKET); // Send the Command.
// (VII): Waiting for the driver to finish or return an error code:
err = ide_polling(c, channel, true);
if (err) return err; // Polling and return if error.
// (VIII): Sending the packet data:
ide_prewait_irq(c, channel);
outsw(bus, atapi_packet, 6);
// (IX): Receiving Data:
for (int i = 0; i < numsects; i++) {
ide_wait_irq(c, channel); // Wait for an IRQ.
ide_prewait_irq(c, channel);
err = ide_polling(c, channel, 1);
if (err) return err; // Polling and return if error.
insw(bus, ptr, words);
ptr += (words * 2);
}
// (X): Waiting for an IRQ:
ide_wait_irq(c, channel);
// (XI): Waiting for BSY & DRQ to clear:
while (ide_read(c, channel, ATA_REG_STATUS) & (ATA_SR_BSY | ATA_SR_DRQ));
return 0; // Easy, ... Isn't it?
}
static uint8_t ide_read_sectors(ide_controller_t *c, uint8_t drive,
uint32_t lba, uint8_t numsects, void* ptr)
{
// 1: Check if the drive presents:
if (drive > 3 || !c->devices[drive].present) return 1; // Drive Not Found!
// 2: Check if inputs are valid:
if (((lba + numsects) >= c->devices[drive].size) && (c->devices[drive].type == IDE_ATA))
return 2; // Seeking to invalid position.
// 3: Read in PIO Mode through Polling & IRQs:
if (c->devices[drive].type == IDE_ATA) {
uint8_t err = ide_ata_access(c, ATA_READ, drive, lba, numsects, ptr);
return ide_print_error(c, drive, err);
} else if (c->devices[drive].type == IDE_ATAPI) {
uint8_t err = 0;
// for (int i = 0; i < numsects; i++)
// err = ide_atapi_read(c, drive, lba + i, 1, ptr + (i*2048));
err = ide_atapi_read(c, drive, lba, numsects, ptr);
return ide_print_error(c, drive, err);
} else {
return 1;
}
}
static uint8_t ide_write_sectors(ide_controller_t *c, uint8_t drive,
uint8_t numsects, uint32_t lba, void* ptr)
{
// 1: Check if the drive presents:
if (drive > 3 || !c->devices[drive].present)
return 1;
// 2: Check if inputs are valid:
if (((lba + numsects) > c->devices[drive].size) && (c->devices[drive].type == IDE_ATA))
return 2; // Seeking to invalid position.
// 3: Read in PIO Mode through Polling & IRQs:
if (c->devices[drive].type == IDE_ATA) {
uint8_t err = ide_ata_access(c, ATA_WRITE, drive, lba, numsects, ptr);
return ide_print_error(c, drive, err);
} else if (c->devices[drive].type == IDE_ATAPI) {
return 4; // Write-Protected.
} else {
return 1;
}
}
// ============== //
// SETUP ROUTINES //
// ============== //
static void ide_init(int pci_id, ide_controller_t *c, fs_t *iofs) {
pci_devices[pci_id].data = c;
c->pci_id = pci_id;
// Set IRQ to PCI_IRQ_STORAGE
uint8_t prog_if = pci_read_config_byte(pci_id, PCI_CONFIG_PROG_IF);
if (prog_if == 0x8A || prog_if == 0x80) {
c->channels[ATA_PRIMARY].irq = IRQ14;
c->channels[ATA_SECONDARY].irq = IRQ15;
} else {
pci_set_irq(pci_id, PCI_IRQ_STORAGE);
c->channels[ATA_PRIMARY].irq = PCI_IRQ_STORAGE;
c->channels[ATA_SECONDARY].irq = PCI_IRQ_STORAGE;
}
// Set IRQ handler
pci_devices[pci_id].irq_handler = pciirq_handler;
// Detect IO ports
uint32_t bar0 = pci_read_config_long(pci_id, PCI_CONFIG_BAR0);
uint32_t bar1 = pci_read_config_long(pci_id, PCI_CONFIG_BAR1);
uint32_t bar2 = pci_read_config_long(pci_id, PCI_CONFIG_BAR2);
uint32_t bar3 = pci_read_config_long(pci_id, PCI_CONFIG_BAR3);
uint32_t bar4 = pci_read_config_long(pci_id, PCI_CONFIG_BAR4);
c->channels[ATA_PRIMARY ].base = (bar0 ? (bar0 & 0xFFFFFFFC) : 0x1F0);
c->channels[ATA_PRIMARY ].ctrl = (bar1 ? (bar1 & 0xFFFFFFFC) : 0x3F6);
c->channels[ATA_SECONDARY].base = (bar2 ? (bar2 & 0xFFFFFFFC) : 0x170);
c->channels[ATA_SECONDARY].ctrl = (bar3 ? (bar3 & 0xFFFFFFFC) : 0x376);
c->channels[ATA_PRIMARY ].bmide = (bar4 & 0xFFFFFFFC) + 0; // Bus Master IDE
c->channels[ATA_SECONDARY].bmide = (bar4 & 0xFFFFFFFC) + 8; // Bus Master IDE
// Disable IRQs:
ide_write(c, ATA_PRIMARY , ATA_REG_CONTROL, 2);
ide_write(c, ATA_SECONDARY, ATA_REG_CONTROL, 2);
// Detect drives
for (int i = 0; i < 4; i++) {
c->devices[i].present = 0;
}
int count = 0;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
uint8_t err = 0, type = IDE_ATA, status;
union {
char b[512];
uint16_t w[256];
uint32_t l[128];
} ide_buf;
// (I) Select Drive:
ide_write(c, i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4));
usleep(1000);
// (II) Send ATA Identify Command:
ide_write(c, i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
usleep(1000);
// (III) Polling:
if (ide_read(c, i, ATA_REG_STATUS) == 0) continue; // If Status = 0, No Device.
while(1) {
status = ide_read(c, i, ATA_REG_STATUS);
if ((status & ATA_SR_ERR)) { // If Err, Device is not ATA.
err = 1;
break;
}
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; // Everything is right.
}
// (IV) Probe for ATAPI Devices:
if (err != 0) {
uint8_t cl = ide_read(c, i, ATA_REG_LBA1);
uint8_t ch = ide_read(c, i, ATA_REG_LBA2);
if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) {
type = IDE_ATAPI;
} else {
continue; // Unknown Type (may not be a device).
}
ide_write(c, i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
usleep(1000);
}
// (V) Read Identification Space of the Device:
ide_read_buffer(c, i, ATA_REG_DATA, ide_buf.b, 128);
// (VI) Read Device Parameters:
c->devices[count].present = 1;
c->devices[count].type = type;
c->devices[count].channel = i;
c->devices[count].drive = j;
c->devices[count].sig = ide_buf.w[ATA_IDENT_DEVICETYPE / 2];
c->devices[count].cap = ide_buf.w[ATA_IDENT_CAPABILITIES / 2];
c->devices[count].cmdset = ide_buf.l[ATA_IDENT_COMMANDSETS / 4];
// (VII) Get Size:
if (c->devices[count].cmdset & (1 << 26)) {
// Device uses 48-Bit Addressing:
c->devices[count].size = ide_buf.l[ATA_IDENT_MAX_LBA_EXT / 4];
} else {
// Device uses CHS or 28-bit Addressing:
c->devices[count].size = ide_buf.l[ATA_IDENT_MAX_LBA / 4];
}
// (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...):
for(int k = 0; k < 40; k += 2) {
c->devices[count].model[k] = ide_buf.b[ATA_IDENT_MODEL + k + 1];
c->devices[count].model[k + 1] = ide_buf.b[ATA_IDENT_MODEL + k];
}
c->devices[count].model[40] = 0; // Terminate String.
count++;
}
}
dbg_printf("PCI IDE controller (PCI device %d)\n", pci_id);
for (int i = 0; i < 4; i++) {
if (c->devices[i].present) {
dbg_printf(" Found %s Drive (%d sectors) - %s (irq %d)\n",
(const char *[]){"ATA", "ATAPI"}[c->devices[i].type],
c->devices[i].size,
c->devices[i].model,
c->channels[c->devices[i].channel].irq);
ide_register_device(c, i, iofs);
}
}
}
void pciide_detect(fs_t *iofs) {
idt_set_irq_handler(IRQ14, irq14_handler);
idt_set_irq_handler(IRQ15, irq15_handler);
for (int i = 0; i < PCI_MAX_DEVICES; i++) {
if (pci_devices[i].vendor_id != 0xFFFF &&
pci_devices[i].base_class == PCI_BC_STORAGE &&
pci_devices[i].sub_class == PCI_SC_IDE)
{
ide_controller_t *c = (ide_controller_t*)malloc(sizeof(ide_controller_t));
if (c == 0) return;
memset(c, 0, sizeof(ide_controller_t));
ide_init(i, c, iofs);
}
}
}
// ======================== //
// INTERFACING WITH THE VFS //
// ======================== //
static int next_ata_id = 0;
static int next_atapi_id = 0;
typedef struct {
ide_controller_t *c;
uint32_t block_size; // ATA: 512, ATAPI: 2048
uint8_t device;
uint8_t type;
} ide_vfs_dev_t;
static bool ide_vfs_open(fs_node_ptr n, int mode, fs_handle_t *s);
static bool ide_vfs_stat(fs_node_ptr n, stat_t *st);
static size_t ide_vfs_read(fs_handle_ptr f, size_t offset, size_t len, char* buf);
static size_t ide_vfs_write(fs_handle_ptr f, size_t offset, size_t len, const char* buf);
static int ide_vfs_ioctl(fs_handle_ptr f, int command, void* data);
static void ide_vfs_close(fs_handle_ptr f);
static fs_node_ops_t ide_vfs_node_ops = {
.open = ide_vfs_open,
.stat = ide_vfs_stat,
.walk = 0,
.delete = 0,
.move = 0,
.create = 0,
.dispose = 0
};
static fs_handle_ops_t ide_vfs_handle_ops = {
.read = ide_vfs_read,
.write = ide_vfs_write,
.ioctl = ide_vfs_ioctl,
.close = ide_vfs_close,
.readdir = 0,
.get_page = 0,
.commit_page = 0
};
void ide_register_device(ide_controller_t *c, uint8_t device, fs_t *iofs) {
ide_vfs_dev_t *d = (ide_vfs_dev_t*)malloc(sizeof(ide_vfs_dev_t));
if (d == 0) return;
d->c = c;
d->device = device;
d->type = c->devices[device].type;
d->block_size = (d->type == IDE_ATAPI ? 2048 : 512);
char name[40];
if (d->type == IDE_ATAPI) {
snprintf(name, 40, "/atapi%d", next_atapi_id);
next_atapi_id++;
} else {
snprintf(name, 40, "/ata%d", next_ata_id);
next_ata_id++;
}
bool add_ok = nullfs_add_node(iofs, name, d, &ide_vfs_node_ops);
if (add_ok) {
dbg_printf(" Registered as %s.\n", name);
} else {
free(d);
}
}
bool ide_vfs_open(fs_node_ptr n, int mode, fs_handle_t *s) {
ide_vfs_dev_t *d = (ide_vfs_dev_t*)n;
int ok_modes = (FM_READ
| (d->type == IDE_ATA ? FM_WRITE : 0)
| FM_IOCTL);
if (mode & ~ok_modes) return false;
s->ops = &ide_vfs_handle_ops;
s->data = d;
s->mode = mode;
return true;
}
bool ide_vfs_stat(fs_node_ptr n, stat_t *st) {
ide_vfs_dev_t *d = (ide_vfs_dev_t*)n;
st->type = FT_BLOCKDEV;
st->access = (d->type == IDE_ATA ? FM_WRITE : 0) | FM_READ | FM_IOCTL;
st->size = d->c->devices[d->device].size * d->block_size;
return true;
}
size_t ide_vfs_read(fs_handle_ptr f, size_t offset, size_t len, char* buf) {
ide_vfs_dev_t *d = (ide_vfs_dev_t*)f;
if (offset % d->block_size != 0) return 0;
if (len % d->block_size != 0) return 0;
uint8_t err = ide_read_sectors(d->c, d->device, offset / d->block_size, len / d->block_size, buf);
if (err != 0) return 0;
return len;
}
size_t ide_vfs_write(fs_handle_ptr f, size_t offset, size_t len, const char* buf) {
ide_vfs_dev_t *d = (ide_vfs_dev_t*)f;
if (offset % d->block_size != 0) return 0;
if (len % d->block_size != 0) return 0;
uint8_t err = ide_write_sectors(d->c, d->device,
offset / d->block_size, len / d->block_size, (char*)buf);
if (err != 0) return 0;
return len;
}
int ide_vfs_ioctl(fs_handle_ptr f, int command, void* data) {
ide_vfs_dev_t *d = (ide_vfs_dev_t*)f;
int ret = 0;
if (command == IOCTL_BLOCKDEV_GET_BLOCK_SIZE)
ret = d->block_size;
if (command == IOCTL_BLOCKDEV_GET_BLOCK_COUNT)
ret = d->c->devices[d->device].size;
return ret;
}
void ide_vfs_close(fs_handle_ptr f) {
// nothing to do
}
/* vim: set ts=4 sw=4 tw=0 noet :*/