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