arm 驱动进阶:输入子系统概念及架构

输入子系统由 驱动层、输入子系统核心、事件处理层 三部分组成,个输入事件,如鼠标移动、键盘按下等通过Driver->Inputcore->Event handler->userspace的顺序到达用户控件的应用程序。

 

驱动层:将底层的硬件输入转化为统一事件形式,想输入核心(Input Core)汇报。

输入子系统核心:承上启下。为驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;在/Proc下产生相应的设备信息

事件处理层:主要是和用户空间交互。(Linux中在用户空间将所有的设备都当初文件来处理,由于在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,这些操作在输入子系统中由事件处理层完成)

 

设备描述:

input_dev结构

实现设备驱动核心工作是:向系统报告按键、触摸屏等输入事件(event,通过input_event结构描述),不再需要关心文件操作接口。驱动报告事件经过inputCore和Eventhandler到达用户空间。

 

注册输入设备函数:

int input_register_device(struct input_dev *dev)

 

注销输入设备函数:

void input_unregister_device(struct input_dev *dev)

 

驱动实现——初始化(事件支持):

set_bit()告诉input输入子系统支持哪些事件,哪些按键。例如:

 

set_bit(EV_KEY,button_dev.evbit)  (其中button_dev是struct input_dev类型)

struct input_dev中有两个成员为:

evbit:

事件类型(包括 EV_RST,EV_REL,EV_MSC,EV_KEY,EV_ABS,EV_REP等)

keybit:

按键类型(当事件类型为EV_KEY时包括 BTN_LEFT,BTN_0,BTN_1,BTN_MIDDLE等)

 

驱动实现——报告事件:

用于报告EV_KEY,EV_REL,EV_ABS事件的函数分别为

void input_report_key(struct input_dev *dev,unsigned int code,int value)

void input_report_rel(struct input_dev *dev,unsigned int code,int value)

void input_report_abs(struct input_dev *dev,unsigned int code,int value)

 

驱动实现——报告结束:

input_sync()同步用于告诉input core子系统报告结束。

 

实例:触摸屏设备驱动中,一次点击的整个报告过程如下:

input_reprot_abs(input_dev,ABS_X,x);   //x坐标

input_reprot_abs(input_dev,ABS_Y,y);   // y坐标

input_reprot_abs(input_dev,ABS_PRESSURE,1);

input_sync(input_dev);//同步结束

 

 

输入子系统实现原理图:

 

内核代码实现分析:

curcial_code
drivers/input/input.c:
    input_init > err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
    
static const struct file_operations input_fops = {
    .owner = THIS_MODULE,
    .open = input_open_file,
};

问:怎么读按键?

input_open_file
    struct input_handler *handler = input_table[iminor(inode) >> 5];
    new_fops = fops_get(handler->fops)  //  =>&evdev_fops
    file->f_op = new_fops;
    err = new_fops->open(inode, file);

app: read > ... > file->f_op->read  

input_table数组由谁构造?

input_register_handler


注册input_handler:
input_register_handler
    // 放入数组
    input_table[handler->minor >> 5] = handler;
    
    // 放入链表
    list_add_tail(&handler->node, &input_handler_list);

    // 对于每个input_dev,调用input_attach_handler
    list_for_each_entry(dev(注意与“注册输入设备”对应handler), &input_dev_list, node)
        input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev
    
    


注册输入设备:
input_register_device
    // 放入链表
    list_add_tail(&dev->node, &input_dev_list);
    
    // 对于每一个input_handler,都调用input_attach_handler
    list_for_each_entry(handler, &input_handler_list, node)
        input_attach_handler(dev, handler); // 根据input_handler的id_table判断能否支持这个input_dev


input_attach_handler
    id = input_match_device(handler->id_table, dev);
    
    error = handler->connect(handler, dev, id);


注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接"

怎么建立连接?
1. 分配一个input_handle结构体
2. 
    input_handle.dev = input_dev;  // 指向左边的input_dev
    input_handle.handler = input_handler;  // 指向右边的input_handler
3. 注册:
   input_handler->h_list = &input_handle;
   inpu_dev->h_list      = &input_handle;


evdev_connect
    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 分配一个input_handle
    
    // 设置
    evdev->handle.dev = dev;  // 指向左边的input_dev
    evdev->handle.name = evdev->name;
    evdev->handle.handler = handler;  // 指向右边的input_handler
    evdev->handle.private = evdev;
    
    // 注册
    error = input_register_handle(&evdev->handle);

 

 

输入子系统read调用过程原理图:

 

 

 

内核代码实现分析:

View Code
app: read
--------------------------
   .......
           evdev_read
               // 无数据并且是非阻塞方式打开,则立刻返回
            if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
                return -EAGAIN;
            
            // 否则休眠
            retval = wait_event_interruptible(evdev->wait,
                client->head != client->tail || !evdev->exist);
               

谁来唤醒?
evdev_event
    wake_up_interruptible(&evdev->wait);


//evdev_event被谁调用?input_dev->h_list->handler->event,在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
gpio_keys_isr
    // 上报事件
    input_event(input, type, button->code, !!state);
    input_sync(input);
    
input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
    struct input_handle *handle;

    list_for_each_entry(handle, &dev->h_list, d_node)
        if (handle->open)
            handle->handler->event(handle, type, code, value);

 

 

 

 

 

 

posted @ 2012-05-22 11:25  ITMelody  阅读(1419)  评论(0编辑  收藏  举报