AHCI磁盘驱动分析
初始化
在本驱动中,我们只需要关注有差异的地方,拿出来讲一下就行了。
iostatus_t ahci_create_device(driver_object_t *driver, device_extension_t *dev)
{
iostatus_t status ;
device_object_t *devobj;
char devname[DEVICE_NAME_LEN] = {0};
sprintf(devname, "%s%c", DEV_NAME, 'a' + ahci_next_device);
ahci_next_device++;
/* 初始化一些其它内容 */
status = io_create_device(driver, 0, devname, DEVICE_TYPE_DISK, &devobj);
if (status != IO_SUCCESS) {
keprint(PRINT_ERR "[ahci]: create device on port %d failed!\n", dev->idx);
return status;
}
/* buffered io mode */
devobj->flags = DO_BUFFERED_IO;
devobj->device_extension = dev;
dev->device_object = devobj;
dev->created = 1;
dev->rwoffset = 0;
return status;
}
在io_create_device
创建设备的时候,我们把拓展设置为0,就不会创建扩展区域,那么devobj->device_extension
就是NULL,对于这里的扩展,我们是在后面直接绑定了扩展的指针devobj->device_extension = dev;
这里选用了DO_BUFFERED_IO
模式,因此磁盘驱动可能会在其它进程执行时产生中断来读写数据。
读写操作
iostatus_t ahci_read(device_object_t *device, io_request_t *ioreq)
{
long len;
iostatus_t status = IO_SUCCESS;
sector_t sectors = DIV_ROUND_UP(ioreq->parame.read.length, SECTOR_SIZE);
device_extension_t *ext = device->device_extension;
#ifdef DEBUG_AHCI
keprint(PRINT_DEBUG "ahci_read: buf=%x sectors=%d off=%x\n",
ioreq->system_buffer, sectors, ioreq->parame.read.offset);
#endif
unsigned long off;
if (ioreq->parame.read.offset == DISKOFF_MAX) {
off = ext->rwoffset;
} else {
off = ioreq->parame.read.offset;
}
len = ahci_read_sector(device->device_extension, off,
ioreq->system_buffer, sectors);
if (!len) { /* 执行失败 */
status = IO_FAILED;
len = 0;
}
ioreq->io_status.status = status;
ioreq->io_status.infomation = len;
io_complete_request(ioreq);
return status;
}
iostatus_t ahci_write(device_object_t *device, io_request_t *ioreq)
{
long len;
iostatus_t status = IO_SUCCESS;
sector_t sectors = DIV_ROUND_UP(ioreq->parame.write.length, SECTOR_SIZE);
device_extension_t *ext = device->device_extension;
#ifdef DEBUG_AHCI
keprint(PRINT_DEBUG "ahci_write: buf=%x sectors=%d off=%x\n",
ioreq->system_buffer, sectors, ioreq->parame.write.offset);
#endif
unsigned long off;
if (ioreq->parame.write.offset == DISKOFF_MAX) {
off = ext->rwoffset;
} else {
off = ioreq->parame.write.offset;
}
len = ahci_write_sector(device->device_extension, off,
ioreq->system_buffer, sectors);
if (!len) { /* 执行失败 */
status = IO_FAILED;
len = 0;
}
ioreq->io_status.status = status;
ioreq->io_status.infomation = len;
io_complete_request(ioreq);
return status;
}
在ahci_read
里面通过ioreq->parame.read.offset
来获得读取的偏移,就是从哪个扇区开始读取。因为device flags
用的是DO_BUFFERD_IO
,所以这里使用ioreq->system_buffer
来存放读取的数据。同理在ahci_write
里面通过ioreq->parame.write.offset
来获得读取的偏移,就是从哪个扇区开始写入数据。
完成操作后,执行ioreq->io_status.infomation = len;
来将读取的数据量返回,这样就可以知道读取了多少数据了。最后调用io_complete_request(ioreq);
完成请求。
设备控制
iostatus_t ahci_devctl(device_object_t *device, io_request_t *ioreq)
{
unsigned int ctlcode = ioreq->parame.devctl.code;
unsigned long arg = ioreq->parame.devctl.arg;
unsigned long off;
device_extension_t *ext = device->device_extension;
iostatus_t status = IO_SUCCESS;
int infomation = 0;
switch (ctlcode)
{
case DISKIO_GETSIZE:
*((unsigned int *) arg) = ext->size;
break;
case DISKIO_CLEAR:
//ahci_clean_disk(device->device_extension, arg);
break;
case DISKIO_SETOFF:
off = *((unsigned long *) arg);
if (off > ext->size - 1)
off = ext->size - 1;
ext->rwoffset = off;
break;
case DISKIO_GETOFF:
*((unsigned long *) arg) = ext->rwoffset;
default:
infomation = -1;
status = IO_FAILED;
break;
}
ioreq->io_status.status = status;
ioreq->io_status.infomation = infomation;
io_complete_request(ioreq);
return status;
}
设备控制是对设备进行一个IO控制,可以从设备获取或者写入配置。在这里,我们通过ioreq->parame.devctl.code
获取控制码,在用switch根据不同的控制码执行不同的操作。除此之外,还可以传一个参数地址ioreq->parame.devctl.arg
,然后可以从参数地址上读取或者写入数据。
中断处理
static int ahci_handler(irqno_t irq, void *data)
{
int intrhandled = IRQ_NEXTONE;
int i;
for(i=0;i<32;i++) {
if(hba_mem->interrupt_status & (1 << i)) {
dbgprint("ahci: interrupt %d occur!\n", i);
hba_mem->ports[i].interrupt_status = ~0;
hba_mem->interrupt_status = (1 << i);
ahci_flush_commands((struct hba_port *)&hba_mem->ports[i]);
intrhandled = IRQ_HANDLED;
}
}
return intrhandled;
}
irq_register(ahci_int, ahci_handler, IRQF_SHARED, "ahci", "ahci driver", (void *)driver);
在这个驱动案例中,flags
是IRQF_SHARED
,表示这是一个共享中断,data
为driver
驱动本身。
在中断处理函数中虽然没有用到data
这个变量,但是必须传入一个地址,来表明它不同于其它驱动,这是共享中断必须的。
在ahci_handler
中,如果中断被处理了,就返回IRQ_HANDLED
,没有被处理就返回IRQ_NEXTONE
,表示需要其它驱动处理中断。