os_kernel_lab/labcodes_answer/lab3_result/kern/driver/ide.c

215 lines
6.3 KiB
C
Raw Normal View History

2014-08-20 15:42:20 +08:00
#include <defs.h>
#include <stdio.h>
#include <trap.h>
#include <picirq.h>
#include <fs.h>
#include <ide.h>
#include <x86.h>
#include <assert.h>
#define ISA_DATA 0x00
#define ISA_ERROR 0x01
#define ISA_PRECOMP 0x01
#define ISA_CTRL 0x02
#define ISA_SECCNT 0x02
#define ISA_SECTOR 0x03
#define ISA_CYL_LO 0x04
#define ISA_CYL_HI 0x05
#define ISA_SDH 0x06
#define ISA_COMMAND 0x07
#define ISA_STATUS 0x07
#define IDE_BSY 0x80
#define IDE_DRDY 0x40
#define IDE_DF 0x20
#define IDE_DRQ 0x08
#define IDE_ERR 0x01
#define IDE_CMD_READ 0x20
#define IDE_CMD_WRITE 0x30
#define IDE_CMD_IDENTIFY 0xEC
#define IDE_IDENT_SECTORS 20
#define IDE_IDENT_MODEL 54
#define IDE_IDENT_CAPABILITIES 98
#define IDE_IDENT_CMDSETS 164
#define IDE_IDENT_MAX_LBA 120
#define IDE_IDENT_MAX_LBA_EXT 200
#define IO_BASE0 0x1F0
#define IO_BASE1 0x170
#define IO_CTRL0 0x3F4
#define IO_CTRL1 0x374
#define MAX_IDE 4
#define MAX_NSECS 128
#define MAX_DISK_NSECS 0x10000000U
#define VALID_IDE(ideno) (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid))
static const struct {
unsigned short base; // I/O Base
unsigned short ctrl; // Control Base
} channels[2] = {
{IO_BASE0, IO_CTRL0},
{IO_BASE1, IO_CTRL1},
};
#define IO_BASE(ideno) (channels[(ideno) >> 1].base)
#define IO_CTRL(ideno) (channels[(ideno) >> 1].ctrl)
static struct ide_device {
unsigned char valid; // 0 or 1 (If Device Really Exists)
unsigned int sets; // Commend Sets Supported
unsigned int size; // Size in Sectors
unsigned char model[41]; // Model in String
} ide_devices[MAX_IDE];
static int
ide_wait_ready(unsigned short iobase, bool check_error) {
int r;
while ((r = inb(iobase + ISA_STATUS)) & IDE_BSY)
/* nothing */;
if (check_error && (r & (IDE_DF | IDE_ERR)) != 0) {
return -1;
}
return 0;
}
void
ide_init(void) {
static_assert((SECTSIZE % 4) == 0);
unsigned short ideno, iobase;
for (ideno = 0; ideno < MAX_IDE; ideno ++) {
/* assume that no device here */
ide_devices[ideno].valid = 0;
iobase = IO_BASE(ideno);
/* wait device ready */
ide_wait_ready(iobase, 0);
/* step1: select drive */
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4));
ide_wait_ready(iobase, 0);
/* step2: send ATA identify command */
outb(iobase + ISA_COMMAND, IDE_CMD_IDENTIFY);
ide_wait_ready(iobase, 0);
/* step3: polling */
if (inb(iobase + ISA_STATUS) == 0 || ide_wait_ready(iobase, 1) != 0) {
continue ;
}
/* device is ok */
ide_devices[ideno].valid = 1;
/* read identification space of the device */
unsigned int buffer[128];
insl(iobase + ISA_DATA, buffer, sizeof(buffer) / sizeof(unsigned int));
unsigned char *ident = (unsigned char *)buffer;
unsigned int sectors;
unsigned int cmdsets = *(unsigned int *)(ident + IDE_IDENT_CMDSETS);
/* device use 48-bits or 28-bits addressing */
if (cmdsets & (1 << 26)) {
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA_EXT);
}
else {
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA);
}
ide_devices[ideno].sets = cmdsets;
ide_devices[ideno].size = sectors;
/* check if supports LBA */
assert((*(unsigned short *)(ident + IDE_IDENT_CAPABILITIES) & 0x200) != 0);
unsigned char *model = ide_devices[ideno].model, *data = ident + IDE_IDENT_MODEL;
unsigned int i, length = 40;
for (i = 0; i < length; i += 2) {
model[i] = data[i + 1], model[i + 1] = data[i];
}
do {
model[i] = '\0';
} while (i -- > 0 && model[i] == ' ');
cprintf("ide %d: %10u(sectors), '%s'.\n", ideno, ide_devices[ideno].size, ide_devices[ideno].model);
}
// enable ide interrupt
pic_enable(IRQ_IDE1);
pic_enable(IRQ_IDE2);
}
bool
ide_device_valid(unsigned short ideno) {
return VALID_IDE(ideno);
}
size_t
ide_device_size(unsigned short ideno) {
if (ide_device_valid(ideno)) {
return ide_devices[ideno].size;
}
return 0;
}
int
ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs) {
assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno);
ide_wait_ready(iobase, 0);
// generate interrupt
outb(ioctrl + ISA_CTRL, 0);
outb(iobase + ISA_SECCNT, nsecs);
outb(iobase + ISA_SECTOR, secno & 0xFF);
outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF);
outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF);
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF));
outb(iobase + ISA_COMMAND, IDE_CMD_READ);
int ret = 0;
for (; nsecs > 0; nsecs --, dst += SECTSIZE) {
if ((ret = ide_wait_ready(iobase, 1)) != 0) {
goto out;
}
insl(iobase, dst, SECTSIZE / sizeof(uint32_t));
}
out:
return ret;
}
int
ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs) {
assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno);
ide_wait_ready(iobase, 0);
// generate interrupt
outb(ioctrl + ISA_CTRL, 0);
outb(iobase + ISA_SECCNT, nsecs);
outb(iobase + ISA_SECTOR, secno & 0xFF);
outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF);
outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF);
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF));
outb(iobase + ISA_COMMAND, IDE_CMD_WRITE);
int ret = 0;
for (; nsecs > 0; nsecs --, src += SECTSIZE) {
if ((ret = ide_wait_ready(iobase, 1)) != 0) {
goto out;
}
outsl(iobase, src, SECTSIZE / sizeof(uint32_t));
}
out:
return ret;
}