Nucleus
Barry Device driver interface 7864e7f (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/stat.h>
#include <io.h>
#include <nucleus/driver.h>
#include <nucleus/panic.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);
FileOps ataFileOps = {
.read = ata_dev_read,
.write = ata_dev_write,
};
/* 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 void *ataDmaArea = NULL;
if (!ataDmaArea)
ataDmaArea = (void *) alloc_frame();
static int ataMajor = 0;
if (!ataMajor)
ataMajor = register_driver(1, &ataFileOps);
/* Create device node */
char name[16];
int minor = drive * 16;
sprintf(name, "/dev/hd%d", drive);
mknod(name, S_IFBLK | 0600, MKDEV(ataMajor, minor));
kprintf(" ATA drive [%s] (%d MB) %s", name,
dev->size / 1024 / 2, dev->model);
/* 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));
kprintf(" Partition [%s] %#.8x - %#.8x", name,
part[i].lba*512, (part[i].lba + part[i].size) * 512);
}
drive++;
}
/* Read from an ATA drive */
size_t
ata_dev_read(File *file, char *buf, size_t size, off_t offset)
{
}
/* Write to an ATA drive */
size_t
ata_dev_write(File *file, char *buf, size_t size, off_t offset)
{
}