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

在这个驱动案例中,flagsIRQF_SHARED,表示这是一个共享中断,datadriver驱动本身。

在中断处理函数中虽然没有用到data这个变量,但是必须传入一个地址,来表明它不同于其它驱动,这是共享中断必须的。

ahci_handler中,如果中断被处理了,就返回IRQ_HANDLED,没有被处理就返回IRQ_NEXTONE,表示需要其它驱动处理中断。

Copyright © BookOS-developers 2021 all right reserved,powered by Gitbook修订时间: 2021-06-16

results matching ""

    No results matching ""