Nucleus
Barry System headers (remove libc dependency) 18495cf (3 years, 2 months ago)
/*
* This file is the driver for the PCI IDE devices. It handles detecting drives
* and accessing them through the IDE interface. The ATA and ATAPI drivers
* handle reading and writing to the devices individually through this.
* This driver will access the drives in the best available way, presenting a
* uniform interface for any other code to use. It also adds a device node to
* the devfs mount for every device it finds, to provide access for the rest of
* the OS easily.
*/
#include <stdint.h>
#include <nucleus/io.h>
#include <nucleus/pci.h>
#include "ide.h"
struct IDEChannelRegs ideChannels[2];
struct IDEDevice ideDevices[4];
uint8_t ideBuf[2048] = {0};
int ideIrqInvoked = 0;
/* Read identification space */
static void
ide_read_buffer(uint8_t channel, void *buffer, uint32_t dwords)
{
struct IDEChannelRegs *c = &ideChannels[channel];
insl(c->base, buffer, dwords);
}
/* Initialise an IDE drive */
static void
ide_drive_init(uint32_t bar0, uint32_t bar1, uint32_t bar2, uint32_t bar3,
uint32_t bar4)
{
ideChannels[IDE_PRIMARY].base = bar0 & ~3;
ideChannels[IDE_PRIMARY].ctrl = bar1 & ~3;
ideChannels[IDE_PRIMARY].busmaster = bar4 & ~3;
ideChannels[IDE_SECONDARY].base = bar2 & ~3;
ideChannels[IDE_SECONDARY].ctrl = bar3 & ~3;
ideChannels[IDE_SECONDARY].busmaster = (bar4 & ~3) + 8;
/* Disable IRQs */
ide_write(IDE_PRIMARY, IDE_REG_CONTROL, 2);
ide_write(IDE_SECONDARY, IDE_REG_CONTROL, 2);
/* Iterate all possible devices */
uint32_t count = 0;
uint8_t channel, drive, err, type, status;
struct IDEDevice *dev;
for (count = 0; count < 4; count++) {
dev = &ideDevices[count];
dev->reserved = 0;
channel = count / 2;
drive = count % 2;
type = IDE_ATA;
err = 0;
ide_write(channel, IDE_REG_HDDEVSEL, 0xA0 | (drive << 4));
ide_write(channel, IDE_REG_COMMAND, IDE_CMD_IDENTIFY);
if (ide_read(channel, IDE_REG_STATUS) == 0)
continue;
/* Wait for response */
while (1) {
status = ide_read(channel, IDE_REG_STATUS);
if ((status & IDE_SR_ERROR)) {
err = 1;
break;
}
if (!(status & IDE_SR_BUSY)
&& (status & IDE_SR_DATA_READY))
break;
}
/* Check type */
if (err) {
uint8_t cl = ide_read(channel, IDE_REG_LBA1);
uint8_t ch = ide_read(channel, IDE_REG_LBA2);
if (cl == 0x14 && ch == 0xEB)
type = IDE_ATAPI;
else if (cl == 0x69 && ch == 0x96)
type = IDE_ATAPI;
else
continue;
ide_write(channel, IDE_REG_COMMAND,
IDE_CMD_IDENTIFY_PACKET);
}
/* Fill in IDE Device structure */
ide_read_buffer(channel, ideBuf, 128);
uint32_t size;
uint16_t sig, cap, set;
sig = *(uint16_t *) (ideBuf + IDE_IDENT_DEVICETYPE);
cap = *(uint16_t *) (ideBuf + IDE_IDENT_CAPABILITIES);
set = *(uint16_t *) (ideBuf + IDE_IDENT_COMMANDSETS);
if (set & (1 << 26))
size = *(uint32_t *) (ideBuf + IDE_IDENT_MAX_LBA_EXT);
else
size = *(uint32_t *) (ideBuf + IDE_IDENT_MAX_LBA);
dev->reserved = 1;
dev->type = type;
dev->channel = channel;
dev->drive = drive;
dev->signature = sig;
dev->capabilities = cap;
dev->commandSets = set;
dev->size = size;
uint32_t k;
for (k = 0; k < 40; k += 2) {
dev->model[k] = ideBuf[IDE_IDENT_MODEL + k + 1];
dev->model[k + 1] = ideBuf[IDE_IDENT_MODEL + k];
}
dev->model[40] = 0;
}
}
/* Read channel */
uint8_t
ide_read(uint8_t channel, uint8_t reg)
{
uint8_t result;
struct IDEChannelRegs *c = &ideChannels[channel];
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, IDE_REG_CONTROL, 0x80 | c->noint);
if (reg < 0x08)
result = inb(c->base + reg - 0);
else if (reg < 0x0C)
result = inb(c->base + reg - 6);
else if (reg < 0x0E)
result = inb(c->ctrl + reg - 10);
else if (reg < 0x16)
result = inb(c->busmaster + reg - 14);
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, IDE_REG_CONTROL, c->noint);
return result;
}
/* Write channel */
void
ide_write(uint8_t channel, uint8_t reg, uint8_t data)
{
struct IDEChannelRegs *c = &ideChannels[channel];
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, IDE_REG_CONTROL, 0x80 | c->noint);
if (reg < 0x08)
outb(c->base + reg - 0, data);
else if (reg < 0x0C)
outb(c->base + reg - 6, data);
else if (reg < 0x0E)
outb(c->ctrl + reg - 10, data);
else if (reg < 0x16)
outb(c->busmaster + reg - 14, data);
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, IDE_REG_CONTROL, c->noint);
}
/* Poll for status after command */
uint8_t
ide_poll_status(uint8_t channel, uint32_t check)
{
uint8_t i, state;
/* Wait for BUSY */
for (i = 0; i < 4; i++)
ide_read(channel, IDE_REG_ALTSTATUS);
/* Wait for BUSY to clear */
while (ide_read(channel, IDE_REG_STATUS) & IDE_SR_BUSY);
/* Check the status */
if (check) {
state = ide_read(channel, IDE_REG_STATUS);
if (state & IDE_SR_ERROR)
return ERROR_GENERIC;
if (state & IDE_SR_WRITE_FAULT)
return ERROR_WRITE_FAULT;
if ((state & IDE_SR_DATA_READY) == 0)
return ERROR_NOT_READY;
}
return ERROR_NONE;
}
/* Initialise a PCI IDE controller */
void
pci_init_ide(uint8_t bus, uint8_t dev, uint8_t func)
{
uint8_t class, subclass, progif, header;
class = pci_read_byte(bus, dev, func, 11);
subclass = pci_read_byte(bus, dev, func, 10);
progif = pci_read_byte(bus, dev, func, 9);
header = pci_read_byte(bus, dev, func, 14);
/* Parallel IDE defaults */
uint32_t bar[5] = {0x1F0, 0x3F6, 0x170, 0x376, 0x00};
/* Primary Native */
if (progif & (1 << 0)) {
bar[0] = pci_read_dword(bus, dev, func, 0x10);
bar[1] = pci_read_dword(bus, dev, func, 0x14);
}
/* Secondary Native */
if (progif & (1 << 2)) {
bar[2] = pci_read_dword(bus, dev, func, 0x18);
bar[3] = pci_read_dword(bus, dev, func, 0x1C);
}
/* Bus Master */
if (progif & (1 << 7))
bar[4] = pci_read_dword(bus, dev, func, 0x20);
ide_drive_init(bar[0], bar[1], bar[2], bar[3], bar[4]);
uint8_t i;
for (i = 0; i < 4; i++) {
if (!ideDevices[i].reserved)
continue;
if (ideDevices[i].type == IDE_ATA)
ata_init(&ideDevices[i]);
// if (ideDevices[i].type == IDE_ATAPI)
// atapi_init(&ideDevices[i]);
}
}