BarryServer : Git

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

// Related

Nucleus

Barry System headers (remove libc dependency) 18495cf (3 years, 2 months ago)
/*
 * This is the driver for ATA storage devices.  It controls access to the drives
 * and contains the devfs functions.  It uses the IDE driver for access to the
 * actual hardware.
 */

#include <stdint.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <nucleus/driver.h>
#include <nucleus/io.h>
#include <nucleus/lib.h>
#include <nucleus/vfs.h>
#include "ide.h"

/* Structure for a MBR Partition entry */
struct Partition {
	uint8_t drive;
	uint8_t shead, ssect, scyl;
	uint8_t type;
	uint8_t ehead, esect, ecyl;
	uint32_t lba;
	uint32_t size;
};

size_t ata_dev_read(File *file, char *buf, size_t size, off_t offset);
size_t ata_dev_write(File *file, char *buf, size_t size, off_t offset);

/* File Operations for the ATA device nodes */
FileOps ataFileOps = {
	.read = ata_dev_read,
	.write = ata_dev_write,
};

static void *ataDmaArea = NULL;

/* Access an ATA drive */
static uint8_t
ata_access(int write, struct IDEDevice *dev, uint32_t lba, uint8_t sectors,
           void *buf)
{
	struct IDEChannelRegs *channel = &ideChannels[dev->channel];
	uint8_t lbaMode, dma, cmd;
	uint8_t lbaIO[6];
	uint16_t cyl, i;
	uint8_t head, sect, err;

//	ideIrqInvoked = 0;
	channel->noint = 2;
	ide_write(dev->channel, IDE_REG_CONTROL, channel->noint);

	/* Configure access mode */
	if (lba >= 0x10000000) {
		lbaMode = LBA_48;
		lbaIO[0] = (lba & 0x000000FF) >> 0;
		lbaIO[1] = (lba & 0x0000FF00) >> 8;
		lbaIO[2] = (lba & 0x00FF0000) >> 16;
		lbaIO[3] = (lba & 0xFF000000) >> 24;
		lbaIO[4] = 0;
		lbaIO[5] = 0;
		head = 0;
	} else if (dev->capabilities & 0x200) {
		lbaMode = LBA_28;
		lbaIO[0] = (lba & 0x000000FF) >> 0;
		lbaIO[1] = (lba & 0x0000FF00) >> 8;
		lbaIO[2] = (lba & 0x00FF0000) >> 16;
		lbaIO[3] = 0;
		lbaIO[4] = 0;
		lbaIO[5] = 0;
		head = (lba & 0x0F000000) >> 24;
	} else {
		lbaMode = LBA_CHS;
		sect = (lba % 63) + 1;
		cyl = (lba + 1 - sect) / (16 * 63);
		lbaIO[0] = sect;
		lbaIO[1] = (cyl >> 0) & 0xFF;
		lbaIO[2] = (cyl >> 8) & 0xFF;
		lbaIO[3] = 0;
		lbaIO[4] = 0;
		lbaIO[5] = 0;
		head = (lba + 1 - sect) % (16 * 63) / 63;
	}

	dma = 0; //dev->busmaster;

	/* Wait for device to become available */
	while (ide_read(dev->channel, IDE_REG_STATUS) & IDE_SR_BUSY);

	/* Select drive */
	if (lbaMode == LBA_CHS)
		ide_write(dev->channel, IDE_REG_HDDEVSEL,
		          0xA0 | (dev->drive << 4) | head);
	else
		ide_write(dev->channel, IDE_REG_HDDEVSEL,
		          0xE0 | (dev->drive << 4) | head);

	/* Specify sectors to read */
	if (lbaMode = LBA_48) {
		ide_write(dev->channel, IDE_REG_SECCOUNT1, 0);
		ide_write(dev->channel, IDE_REG_LBA3, lbaIO[3]);
		ide_write(dev->channel, IDE_REG_LBA4, lbaIO[4]);
		ide_write(dev->channel, IDE_REG_LBA5, lbaIO[5]);
	}
	ide_write(dev->channel, IDE_REG_SECCOUNT0, sectors);
	ide_write(dev->channel, IDE_REG_LBA0, lbaIO[0]);
	ide_write(dev->channel, IDE_REG_LBA1, lbaIO[1]);
	ide_write(dev->channel, IDE_REG_LBA2, lbaIO[2]);

	/* Send command */
	if (lbaMode == LBA_CHS && !dma && !write) cmd = IDE_CMD_READ_PIO;
	if (lbaMode == LBA_28  && !dma && !write) cmd = IDE_CMD_READ_PIO;
	if (lbaMode == LBA_48  && !dma && !write) cmd = IDE_CMD_READ_PIO_EXT;
	if (lbaMode == LBA_CHS &&  dma && !write) cmd = IDE_CMD_READ_DMA;
	if (lbaMode == LBA_28  &&  dma && !write) cmd = IDE_CMD_READ_DMA;
	if (lbaMode == LBA_48  &&  dma && !write) cmd = IDE_CMD_READ_DMA_EXT;
	if (lbaMode == LBA_CHS && !dma &&  write) cmd = IDE_CMD_WRITE_PIO;
	if (lbaMode == LBA_28  && !dma &&  write) cmd = IDE_CMD_WRITE_PIO;
	if (lbaMode == LBA_48  && !dma &&  write) cmd = IDE_CMD_WRITE_PIO_EXT;
	if (lbaMode == LBA_CHS &&  dma &&  write) cmd = IDE_CMD_WRITE_DMA;
	if (lbaMode == LBA_28  &&  dma &&  write) cmd = IDE_CMD_WRITE_DMA;
	if (lbaMode == LBA_48  &&  dma &&  write) cmd = IDE_CMD_WRITE_DMA_EXT;
	ide_write(dev->channel, IDE_REG_COMMAND, cmd);

	/* Read/Write data */
	if (dma) {
		/* Use DMA */
		if (write) {
			/* TODO */
		} else {
			/* TODO */
		}
	} else {
		/* Use PIO */
		if (write) {
			for (i = 0; i < sectors; i++) {
				ide_poll_status(dev->channel, 0);
				outsw(channel->base, buf + (i * 512), 256);
			}
			ide_write(dev->channel, IDE_REG_COMMAND,
			          (lbaMode == LBA_48)
			          ? IDE_CMD_CACHE_FLUSH_EXT
			          : IDE_CMD_CACHE_FLUSH);
			ide_poll_status(dev->channel, 0);
		} else {
			for (i = 0; i < sectors; i++) {
				if (err = ide_poll_status(dev->channel, 1))
					return err;
				insw(channel->base, buf + (i * 512), 256);
			}
		}
	}

	return 0;
}

/* Read from an ATA drive */
static inline uint8_t
ata_read(struct IDEDevice *dev, uint32_t lba, uint8_t sectors, void *buf)
{
	ata_access(0, dev, lba, sectors, buf);
}

/* Write to an ATA drive */
static inline uint8_t
ata_write(struct IDEDevice *dev, uint32_t lba, uint8_t sectors, void *buf)
{
	ata_access(1, dev, lba, sectors, buf);
}

/* Initialise an ATA drive */
void
ata_init(struct IDEDevice *dev)
{
	static int drive = 0;
	static int ataMajor = 0;
	if (!ataMajor)
		ataMajor = register_driver(1, &ataFileOps);

	if (!ataDmaArea)
		ataDmaArea = (void *) alloc_frame();

	/* Create device node */
	char name[16];
	int minor = drive * 16;
	sprintf(name, "/dev/hd%d", drive);
	mknod(name, S_IFBLK | 0600, MKDEV(ataMajor, minor));

	/* Attempt to read partition table */
	int i;
	struct Partition *part = ataDmaArea + 446;
	if (ata_read(dev, 0, 1, ataDmaArea))
		return;
	for (i = 0; i < 4; i++) {
		if (!part[i].type)
			continue;
		sprintf(name, "/dev/hd%dp%d", drive, i);
		mknod(name, S_IFBLK | 0600, MKDEV(ataMajor, minor + i + 1));
	}

	drive++;
}

/* Read from an ATA drive */
size_t
ata_dev_read(File *file, char *buf, size_t size, off_t offset)
{
	uint16_t min, bufOffset = offset % 512;
	size_t count = 0;
	uint16_t parti = MINOR(file->inode->dev) % 16;
	uint32_t lba;
	struct Partition *part;

	struct IDEDevice *dev = &ideDevices[MINOR(file->inode->dev) / 16];

	if (parti) {
		ata_read(dev, 0, 1, ataDmaArea);
		part = (void *) ataDmaArea + 446
		     + ((parti - 1) * sizeof(struct Partition));
		if (offset > part->size * 512)
			return 0;
		if (offset + size > part->size * 512)
			size = (part->size * 512) - offset;
	} else {
		if (offset > dev->size * 512)
			return 0;
		if (offset + size > dev->size * 512)
			size = (dev->size * 512) - offset;
	}

	while (size + bufOffset) {
		min = (size > PAGE_SIZE) ? PAGE_SIZE : size;
		lba = (parti ? part->lba : 0) + ((offset + count) / 512);
		ata_read(dev, lba, PAGE_SIZE / 512, ataDmaArea);
		memcpy((void *) (buf + count),
		       (void *) ataDmaArea + bufOffset,
		       min);
		size -= min;
		count += min;
		bufOffset = 0;
	}

	return count;
}

/* Write to an ATA drive */
size_t
ata_dev_write(File *file, char *buf, size_t size, off_t offset)
{
	
}

/* Open an ATA drive */
int
ata_dev_open(File *file)
{
	struct IDEDevice *dev = &ideDevices[MINOR(file->inode->dev) / 16];
	if (!dev->reserved)
		return -ENODEV;
	if (dev->type != 0)
		return -EINVAL;
	return 0;
}