P/S2键盘驱动分析

初始化

在本驱动中,我们只需要关注有差异的地方,拿出来讲一下就行了。

static iostatus_t keyboard_enter(driver_object_t *driver)
{
    iostatus_t status;

    device_object_t *devobj;
    device_extension_t *devext;

    /* 初始化一些其它内容 */
    status = io_create_device(driver, sizeof(device_extension_t), DEV_NAME, DEVICE_TYPE_KEYBOARD, &devobj);

    if (status != IO_SUCCESS) {
        keprint(PRINT_ERR "keyboard_enter: create device failed!\n");
        return status;
    }
    /* neither io mode */
    devobj->flags = 0;
    devext = (device_extension_t *)devobj->device_extension;
    devext->device_object = devobj;

    devext->irq = IRQ1;
    devext->flags = 0;
    devext->opened = 0;

    /* 初始化私有数据 */
    devext->code_with_e0 = 0;

    devext->shift_left    = devext->shift_right = 0;
    devext->alt_left    = devext->alt_right   = 0;
    devext->ctl_left    = devext->ctl_right  = 0;

    devext->caps_lock   = 0;
    devext->num_lock    = 1;
    devext->scroll_lock = 0;
    unsigned char *buf = mem_alloc(DEV_FIFO_BUF_LEN);
    if (buf == NULL) {
        status = IO_FAILED;
        keprint(PRINT_DEBUG "keyboard_enter: alloc buf failed!\n");
        return status;
    }
    fifo_io_init(&devext->fifoio, buf, DEV_FIFO_BUF_LEN);

    input_even_init(&devext->evbuf);

    /* 初始化键盘控制器 */

    /* 发送写配置命令 */
    WAIT_KBC_WRITE();
    out8(KBC_CMD, KBC_CMD_WRITE_CONFIG);

    /* 往数据端口写入配置值 */
    WAIT_KBC_WRITE();
    out8(KBC_WRITE_DATA, KBC_CONFIG);

    /* 注册时钟中断并打开中断,因为设定硬件过程中可能产生中断,所以要提前打开 */    
    irq_register(devext->irq, keyboard_handler, IRQF_DISABLED, "IRQ1", DRV_NAME, (void *) devext);

    /* 启动一个内核线程来处理数据 */
    task_create("kbd", TASK_PRIO_LEVEL_REALTIME, kbd_thread, devext);

    return status;
}

键盘驱动进入的时候就进行了初始化,在io_create_device创建设备的时候指定了扩展结构体大小,那么就会为这个扩展结构分配内存,然后记录到devobj->device_extension中。在后面就可以直接使用这个地址了。于是devext = (device_extension_t *)devobj->device_extension;这行就把devext变量执行了扩展区域。

再来看一下他的扩展结构:

typedef struct _device_extension {
    device_object_t *device_object; /* 设备对象 */
    char irq;           /* irq号 */

    int    code_with_e0;    /* 携带E0的值 */
    int    shift_left;    /* l shift state */
    int    shift_right;    /* r shift state */
    int    alt_left;    /* l alt state     */
    int    alt_right;    /* r left state     */
    int    ctl_left;    /* l ctrl state     */
    int    ctl_right;    /* l ctrl state     */
    int    caps_lock;    /* Caps Lock     */
    int    num_lock;    /* Num Lock     */
    int    scroll_lock;    /* Scroll Lock     */
    int    column;        /* 数据位于哪一列 */

    fifo_io_t fifoio;
    input_even_buf_t evbuf;     /* 事件缓冲区 */
    uint32_t flags;
    uint32_t opened;
} device_extension_t;

每一个设备都可以有一个扩展结构来存放设备自己的数据,尽量保持每个驱动的扩展结构都一致为device_extension_t,当然你也可以使用其它结构,这是自由的,因为是属于这个驱动自己的内容。在这个驱动中,devobj->flags = 0;意味着执行读写操作时使用ioreq->user_buffer

读操作

iostatus_t keyboard_read(device_object_t *device, io_request_t *ioreq)
{
    device_extension_t *ext = device->device_extension;
    iostatus_t status = IO_SUCCESS;

    /* 直接返回读取的数据 */
    ioreq->io_status.infomation = ioreq->parame.read.length;
    input_event_t *even = (input_event_t *) ioreq->user_buffer;
    /* 参数正确 */
    if (even && ioreq->parame.read.length == sizeof(input_event_t)) {

        if (ext->flags & DEV_NOWAIT) {
            // 解析
            if (input_even_get(&ext->evbuf, even) < 0) {
                status = IO_FAILED;
            } else {
                #ifdef DEBUG_DRV
                keprint(PRINT_DEBUG "key even get: type=%d code=%x value=%d\n", even->type, even->code, even->value);
                keprint(PRINT_DEBUG "key even buf: head=%d tail=%d\n", ext->evbuf.head, ext->evbuf.tail);
                #endif        
            }
        } else {
            while (1) {
                // 解析
                if (!input_even_get(&ext->evbuf, even))
                    break;
                task_yield();
            }
        }
    } else {
        status = IO_FAILED;
    }
    #ifdef DEBUG_DRV
    keprint(PRINT_DEBUG "key even get: type=%d code=%x value=%d\n", even->type, even->code, even->value);
    keprint(PRINT_DEBUG "key even buf: head=%d tail=%d\n", ext->evbuf.head, ext->evbuf.tail);
    #endif     
    ioreq->io_status.status = status;
    /* 调用完成请求 */
    io_complete_request(ioreq);

    return status;
}

我们可以通过ioreq->parame.read.length来获取要读取数据的长度。因为device flags是0,所以通过ioreq->user_buffer来访问用户缓冲区。读取完成后,调用io_complete_request(ioreq);来完成读取。

设备控制

iostatus_t keyboard_devctl(device_object_t *device, io_request_t *ioreq)
{
    device_extension_t *extension = device->device_extension;

    unsigned int ctlcode = ioreq->parame.devctl.code;
    unsigned int leds;
    iostatus_t status;

    switch (ctlcode) {
    case EVENIO_GETLED:
        leds =  extension->num_lock | 
            (extension->caps_lock << 1) |
            (extension->scroll_lock << 2);
        *(unsigned int *) ioreq->parame.devctl.arg = leds;
        break;
    case EVENIO_SETFLG:
        extension->flags = *(unsigned int *) ioreq->parame.devctl.arg;
        break;
    case EVENIO_GETFLG:
        *(unsigned int *) ioreq->parame.devctl.arg = extension->flags;
        break;
    default:
        status = IO_FAILED;
        break;
    }
    ioreq->io_status.status = status;
    ioreq->io_status.infomation = 0;
    io_complete_request(ioreq);
    return status;
}

设备控制是对设备进行一个IO控制,可以从设备获取或者写入配置。在这里,我们通过ioreq->parame.devctl.code获取控制码,在用switch根据不同的控制码执行不同的操作。除此之外,还可以传一个参数地址ioreq->parame.devctl.arg,然后可以从参数地址上读取或者写入数据。

中断处理

static int keyboard_handler(irqno_t irq, void *data)
{
    device_extension_t *ext = (device_extension_t *) data;
    ...xxx...
    return 0;
}
irq_register(devext->irq, keyboard_handler, IRQF_DISABLED, "IRQ1", DRV_NAME, (void *) devext);

在这个键盘驱动例子中,flagsIRQF_DISABLED,表示在键盘中断函数处理期间需要关闭中断,datadevext,那么就可以在keyboard_handler中通过data传入,从而可以解析出对应的接口体,来满足我们的使用。

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

results matching ""

    No results matching ""