linux输入子系统驱动工作原理分析

本文主要从输入子系统的 输入设备注册和事件上报 来分析!
  一: 输入设备注册
          要分析注册就要从注册函数input_register_device(struct input_dev *dev)开始  主要工作有:
        ① 对input_dev中的一些成员初始化
       init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}

if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;

if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;

dev_set_name(&dev->dev, "input%ld",
    (unsigned long) atomic_inc_return(&input_no) - 1);
       
        ② 在 list_for_each_entry(handler, &input_handler_list, node)中用input_attach_handler(dev, handler)函数
  遍历input_handler中的与此设备相关的处理操作函数 函数中的具体操作如下:
  用 input_match_device(handler, dev)函数来匹配input_dev中的id和input_handler中的id(此id的具体在evdev_handler中),
  若匹配成功则连接设备与操作处理函数handler->connect(handler, dev, id); 此函数对于本次具体实现在evdev_handler结构体中
     evdev_handler具体成员定义如下:
        static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
  在evdev_connect函数中主要的工作是创建设备文件  
       evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free; 

error = device_add(&evdev->dev);  
 通过以上几步就可以自动的创建一个设备文件而不需要我们自己去手动的创建!
 虽然上面的工作都做得很完美 但却缺少了字符设备驱动,如果没有这个东东的话
 那么前面做的都将无法实现 所以我们要注册一个字符设备驱动  
    在input_init(void)函数中有个class_register(&input_class);函数,它就是
    注册字符设备驱动的函数(注:这个函数是老版本的注册函数 新版本的注册函数是cdev_add()).  
    
 总结以上的工作流程:
     首先 我们在开始就要注册一个字符设备驱动 以便于以后使用!
     其次 输入子系统input_dev想要实现一些功能就需要借助一个handler来实现 即通过input_handler中的一个具体的结构体匹配它们之间相应的的id来建立连接,
          那么这个具体的结构体就是input_handler *evdev_handler, 既然已建立连接那么就要负责任 即要创建一个设备文件来供设备使用。
          
  
  二:事件上报(以按键为例)
     要分析事件上报就从input_report_key()开始!
       ① 在此函数里的input_event()zhong 首先判断设备是否注册 is_event_supported(type, dev->evbit, EV_MAX)
          若注册则主要调用input_handle_event(dev, type, code, value)函数来
          进行进一步的处理
       ② 在input_handle_event(dev, type, code, value)函数中:
            首先会做的是通过对比type和code来做相关工作!
              switch (type) {


case EV_SYN:  //同步事件
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;

case SYN_REPORT:
if (!dev->sync) {
dev->sync = true;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = false;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;

case EV_KEY: //按键事件
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
   !!test_bit(code, dev->key) != value) {

if (value != 2) {
__change_bit(code, dev->key);
if (value)
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev);
}

disposition = INPUT_PASS_TO_HANDLERS;
}
break;

case EV_SW: //
if (is_event_supported(code, dev->swbit, SW_MAX) &&
   !!test_bit(code, dev->sw) != value) {

__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;

case EV_ABS://绝对坐标事件
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, code, &value);

break;

case EV_REL:// Reset事件
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;

break;

case EV_MSC: // 其它事件
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;

break;

case EV_LED: //LED事件
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
   !!test_bit(code, dev->led) != value) {

__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;

case EV_SND: //声音事件
if (is_event_supported(code, dev->sndbit, SND_MAX)) {

if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;

case EV_REP: //Repeat事件
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;

case EV_FF: ///力反馈事件
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;

case EV_PWR:  
disposition = INPUT_PASS_TO_ALL;
break;
}
            
            下来主要的工作是调用input_pass_event()函数
               做了这么多我们其实最终要做的就是要找到handler来处理我们的事件 所幸的是
               我们做到了。在这个函数中有这么一句:
                 handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
     这就是我们需要的处理函数event(),它的一个具体定义是在evdev_handler中
     的  .event = evdev_event,,那么接下来我们分析evdev_event()函数:
        ③ 在evdev_event函数中:(一下才是重点)
            首先会把事件的相关信息保存到我们重新定义的一个input_dev结构体中:
                event.type = type;
event.code = code;
event.value = value;
   然后主要是使用evdev_pass_event(client, &event);函数来把已经保存好的
   信息放到一个buffer中供应用程序使用;
     client->buffer[client->head++] = *event;
     client->head &= client->bufsize - 1;
        ④ 既然我们将事件的信息放到了一个buffer中来让应用程序使用,那么我们就自然而然的
           要看看应用程序是怎样来拿到事件信息的
               一般应用程序都是从file_operations中来工作的 本应用程序也不例外 那么它在哪呢?
               在input_init()函数中的注册字符设备函数register_chrdev(INPUT_MAJOR, "input", &input_fops);
               我们看到有个input_fops,它就是我们苦苦寻觅的file_operations,
               static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
               要想对设备文件进行读写等操作就要先打开设备文件 input_open_file()
               在input_open_file()函数中 :
                   其是我们主要的就是找到操作读,写的入口,在函数中有这么两行代码
             if (handler)
new_fops = fops_get(handler->fops);
     这个是将evdev_handler中
        static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};的fops复值给新的一个file_operations;
    file->f_op = new_fops; 有将刚才的fops中的信息给了file->f_op,由于在fops中有应用程序
    要操作的相关函数入口所以应用程序真正的操作就在这里!
       evdev_fops中的文件系统
                           static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush,
.llseek = no_llseek,
}; 
               既然已经找到了操作入口 那么就可以访问事件了
               在evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)函数中:
                 我们可以看到它调用了一个函数evdev_fetch_next_event();这个函数里面主要的工作就是将我们前面
                 放到buffer中的事件信息拿出来放到input_event结构体中 代码如下:
  if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= client->bufsize - 1;
}
     最后通过input_event_to_user()函数再调用copy_to_user()将数据送入用户空间!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值