嵌入式之input驱动框架源码分析

这里写图片描述

/////////////////////////////////////////////////////////////////////
总结:三层
我们写的驱动层
我们写的案例:中断方式按键中断
gpio_request申请gpio,因为按键与gpio有关
s3c_gpio_cfgpin设置gpio模式为中断模式
gpio_get_value读取引脚的值,注意读取引脚的值要将gpio设为输入或输出模式读取,好点

request_irq申请中断号并绑定中断程序和设置中断触发情况
中断号都在irqs.h文件中
flag设置中断触发情况可以si搜到
在绑定的中断程序中发送input结构体input_report_key,向上汇报硬件触发事件

input_allocate_device申请input结构体的内存
set_bit设置允许向上汇报的事件类型
input_register_device注册input设备

九鼎移植的button_x210.c驱动层轮询方式
从module_init开始
module_init
    s3c_button_init
        platform_device_register    完成平台总线的设备和驱动注册,就是将我们这个设备放到平台
        platform_driver_register    总线上去管理,即当设备和驱动相遇会调用pore函数

.probe      = s3c_button_probe,
s3c_button_probe
    gpio_request        向gpiolib申请gpio
    s3c_gpio_setpull    设置上下拉
    s3c_gpio_cfgpin     设置成输入模式,则表示用轮询方式
    s3c_button_history[]记录以前引脚的值,因为我们采用轮询方式,如果采集的数据与上次一样我们就不进行处理
    input_allocate_device   申请内存
    set_bit(EV_KEY, input->evbit);设置该驱动支持向上汇报硬件触发事件的类型
    set_bit(s3c_Keycode[i], input->keybit);设置该类型的具体事件,就一个对应type一个core
    input_register_device   注册
    init_timer定时,定轮询时间,因为定时是单次定时,因此我们要在每次定时绑定的处理函数结束前把定时更新。
    timer.function = s3cbutton_timer_handler;绑定定时函数
    timer.expires = jiffies + (HZ/100);HZ是一个值表示在内核里面的1s。分成100,就是10ms
    add_timer(&timer);//添加计时
绑定的定时函数
s3cbutton_timer_handler
    gpio_get_value获取值
    input_report_key要与之前的值对比一下再向上汇报
    mod_timer更新定时

/////////////////////////////////////////////////////////////////////
核心层input.c
subsys_initcall同样从这里开始,定义为一个模块,满足linux的可裁剪性
    input_init
        class_register//在sys下注册一个input类
        input_proc_init//在proc下添加一个接口
        register_chrdev//在/dev下注册一个设备文件夹吧,只支持open,那些次input设备就在下面

对下面驱动层提高的接口为
input_allocate_device 
input_set_capability 
input_register_device
解析
input_allocate_device   返回的就是申请好的内存地址,然后驱动层不用定义该结构体变量只用定义指针即可
    dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);就是为input_dev结构体分配内存
    还进行一些input设备共有的属性的初始化
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
内部其实就是一些__set_bit。
input_register_device
    还进行一些input设备共同信息设置
    device_add 设备添加这个就是对应的dev/input下的设备节点
    list_add_tail将注册好的设备放到管理输入设备的链表里
    list_for_each_entry(handler, &input_handler_list, node),input_attach_handler输入事件handler也是用链表绑定的,然后遍历并比对找出能够绑定的,就是将设备和已经注册了的handle进行匹配
        input_match_device进行配对
        handler->connect(handler, dev, id);handler规定的连接方法进行连接绑定



对上面输入事件驱动层提供的接口为
input_register_handler  对应写上层输入事件驱动的人调用注册的如evdev.c和mousedev.c和joydev.c属于这一层 
input_register_handle   handle结构体就是绑定好的设备和handler进行绑定用链表链接
解析
input_register_handler
    input_table[handler->minor >> 5] = handler;
        input_table是用来管理所有的handler的只能支持8个,
        handler->minor都会赋值一个该handler支持的设备的次设备号的基准在除以32,表示一个hander
        最多支持32个设备,每个handler都对应一个基准次设备号
    list_add_tail(&handler->node, &input_handler_list);
        内核中handler使用链表管理的,将注册好的设备添加到该链表中
    list_for_each_entry(dev, &input_dev_list, node)
        input_attach_handler(dev, handler);
        同样添加一个handler就要把以前注册过的设备进行遍历看看有哪些可以与该handler进行绑定


input_register_handle
struct input_handle {handle是这种类型的

    void *private;

    int open;
    const char *name;

    //对应设备和handler的绑定。
    struct input_dev *dev;
    struct input_handler *handler;
    //对应的链表
    struct list_head    d_node;
    struct list_head    h_node;
};
/////////////////////////////////////////////////////////////////////
输入事件驱动层,就是注册handler的,我们以evdevhandler为例,在evdev.c文件
module_init模块
    evdev_init
        input_register_handler(&evdev_handler);直接注册,然后就没了,我们主要研究该结构体


static struct input_handler evdev_handler = {
    .event      = evdev_event,      //关键;给应用层返回一个event结构体
    .connect    = evdev_connect,    //handler和设备连接的函数
    .disconnect = evdev_disconnect,
    .fops       = &evdev_fops,      //对用应用层操作设备文件时的函数的接口open,read
    .minor      = EVDEV_MINOR_BASE,//对应该handler支持的设备的次设备号的基准号,一个handler最大支持32个
    .name       = "evdev",
    .id_table   = evdev_ids,    //对对应的handler所支持的设备的特征数组,这里没有,表示所有设备都支持
};
我们分析;event,   fops,  id_table
evdev_event
    因为已经确定handler,则先进行一些该handler有的初始化
    rcu_dereference看该信息是传给一个handler直接调用发送还是有多个handler遍历要发送的hander发送
    evdev_pass_event向上层发送
        client->buffer[client->head++] = *event;将信息从内核发送到应用层
        wake_lock_timeout还要进行一些解锁,唤醒的堵塞的read

.read       = evdev_read,
    if (count < input_event_size())return -EINVAL;保证传参正确
    wait_event_interruptible  read堵塞就堵塞在这里,唤醒在evdev_event这里的
    input_event_to_user //将数据放到应用层去。

.id_table   = evdev_ids,//对对应的handler所支持的设备的特征数组,这里没有,表示所有设备都支持
//因为eventhandle是配对所有设备的,
static const struct input_device_id evdev_ids[] = {
    { .driver_info = 1 },   /* Matches all devices */
    { },            /* Terminating zero entry */
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值