/* * 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 #include #include #include #include #include #include #include #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; }