BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / blob / master / drivers / storage / ide.c

// Related

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]);
	}
}