上一节大概了解了输入子系统的流程

这一节认真追踪一下代码

input.c:

input_init(void)函数

 static int __init input_init(void)
{
int err; err = class_register(&input_class);
if (err) {
printk(KERN_ERR "input: unable to register input_dev class\n");
return err;
} err = input_proc_init();
if (err)
goto fail1; err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
if (err) {
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
goto fail2;
} return ; fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}

err = register_chrdev(INPUT_MAJOR, "input", &input_fops);     /* 注册字符设备(主设备号INPUT_MAJOR = 13) */

input_fops-->

static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];
const struct file_operations *old_fops, *new_fops = NULL;
int err; /* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops)))
return -ENODEV; /*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {
fops_put(new_fops);
return -ENODEV;
}
old_fops = file->f_op;
file->f_op = new_fops; err = new_fops->open(inode, file); if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
return err;
}

  

 知识点之一:struct input_handler *handler = input_table[iminor(inode) >> 5];
/* struct input_handler是用来定义一个handler的结构体,一个handler就对应一个struct input_handler结构体变量 */ 

----->input_table    ----->
static struct input_handler *input_table[8];
既然是静态的就肯定在input.c中 ------>>>int input_register_handler(struct input_handler *handler)
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev; INIT_LIST_HEAD(&handler->h_list); if (handler->fops != NULL) {
if (input_table[handler->minor >> ])
return -EBUSY; input_table[handler->minor >> 5] = handler;
} list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);//每个input_dev调用该函数并根据input_handler的id_table判断能不能支持这个inpt_dev了 input_wakeup_procfs_readers();
return ;
}
  知识点之二:handler = input_table[iminor(inode) >> 5];    /* input_table是input子系统中用来管理handler的一个数组,里面存放的是handler的指针。通过次设备号找到本次应用层打开的输入设备对应的handler结构体 */

可知   input_table[]是由这个函数完成构造的    继续全局搜索该函数input_register_handler

可得到:

这些函数即分别对应上层的各个不同的handler的源代码。
evdev.c:
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
input_register_handler(&evdev_handler);
在evdev_init函数中直接调用核心层提供的handler注册函数注册event这个handler,evdev_handler这个struct input_handler类型的变量就是对这个handler的一个描述,
evdev_handler:
static struct input_handler evdev_handler = {
.event = evdev_event, /* 这个函数的作用就是实现将下层的事件包进行封装,然后存放在缓冲区中待read函数读取,并唤醒阻塞等待读取数据的进程 */
.connect = evdev_connect, /* 匹配成功之后,就会调用这个函数进行连接 */
.disconnect = evdev_disconnect, /* 断开连接 */
.fops = &evdev_fops, /* 这个就是应用open/read/write...时对应的接口函数封装 file_operations */
.minor = EVDEV_MINOR_BASE, /* 这个就是本handler下的设备的次设备号的起始设备号(基设备号) */
.name = "evdev", /* handler的名字 */
.id_table = evdev_ids, /* 一个handler描述自己支持的输入设备的特征的一个结构体,一个变量就描述了该halder支持的一类设备 */
};

evdev_handler变量就是本次分析的handler对应的结构体变量,变量中填充最重要的有3个:

evdev_event函数:

evdev_connect函数:

evdev_fops变量:这个变量是struct  fileoperations类型的结构体变量,将来应用层通过调用open函数时,这个结构体封装的函数会被核心层中注册字符设备时封装的open函数调用

说明:

input_handler中id_table 会和input_register_device中的struct input_dev类型的结构体进行匹配

全局搜索input_register_device可以知道有很多鼠标键盘等设备都会调用这个函数进行一个注册

int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT();
struct input_handler *handler;
const char *path;
int error; set_bit(EV_SYN, dev->evbit); /*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/ 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] = ;
dev->rep[REP_PERIOD] = ;
} if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode; list_add_tail(&dev->node, &input_dev_list);//置入到链表
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - ); if (!dev->cdev.dev)
dev->cdev.dev = dev->dev.parent; error = class_device_add(&dev->cdev);
if (error)
return error; path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path); list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);//input_handler_list链表中的每个都调用这个函数
该函数根据input_handler的id_table判断能不能支持这个input_dev
input_wakeup_procfs_readers(); return ;
}
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id;
int error; if (handler->blacklist && input_match_device(handler->blacklist, dev))
return -ENODEV; id = input_match_device(handler->id_table, dev);
if (!id)
return -ENODEV; error = handler->connect(handler, dev, id);
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->cdev.kobj), error); return error;
}

  


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, &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函数建立"连接


怎么建立连接?

在 evdec.c中找这个例子怎么建立连接

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
dev_t devt;
int minor;
int error; for (minor = ; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
} evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM; INIT_LIST_HEAD(&evdev->client_list);
init_waitqueue_head(&evdev->wait); evdev->exist = ;
evdev->minor = minor;
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor); evdev_table[minor] = evdev; devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), cdev = class_device_create(&input_class, &dev->cdev, devt,
dev->cdev.dev, evdev->name);
if (IS_ERR(cdev)) {
error = PTR_ERR(cdev);
goto err_free_evdev;
} /* temporary symlink to keep userspace happy */
error = sysfs_create_link(&input_class.subsys.kobj,
&cdev->kobj, evdev->name);
if (error)
goto err_cdev_destroy; error = input_register_handle(&evdev->handle);
if (error)
goto err_remove_link; return ; err_remove_link:
sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
err_cdev_destroy:
class_device_destroy(&input_class, devt);
err_free_evdev:
kfree(evdev);
evdev_table[minor] = NULL;
return error;
}

1. 分配一个input_handle结构体

struct evdev *evdev;

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

2.
input_handle.dev = input_dev; // 指向左边的输入设备input_dev
input_handle.handler = input_handler; // 指向右边的input_handler

3. 注册这个handle:

error = input_register_handle(&evdev->handle);

int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler; list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handler->h_list); if (handler->start)
handler->start(handle); return 0;
}

  连接的时候构建一个input_handle结构体其中刚有个成员.dev  这个成员指向输入设备即input_device

还有一个.handler成员   他指向input_handler

input_handler有个h_list  指向了input_handle;   对于input_handler来讲就可以由h_list 找到input_handle然后找到dev然后找到相应的设备了
inpu_dev      有个  h_list 指向了input_handle;   对输入设备inpu_dev 来讲就可以由h_list 找到input_handle 然后input_handle 发现到inpu_dev 所对应的处理input_handler

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


怎么读按键?
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->wait里休眠  那就在这里唤醒  全局搜索evdev->wait
evdev_event时间处理函数函数:
wake_up_interruptible(&evdev->wait);

evdev_event来唤醒,那么evdev_event被谁调用?

猜:应该是硬件相关的代码,input_dev那层调用的
在设备的中断服务程序里,确定事件是什么,然后调用相应的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);  evdev_event被调用

evdev_event函数分析:

当设备驱动层发生一个输入事件的时候,设备驱动层就会去读取设备硬件,获取事件信息,然后上报给核心层,核心层索引到与当前设备匹配成功的所有上层handler,然后再上报给

这些handler,上层就会调用event函数将事件包封装放入缓冲区,然后唤醒进程。  我对这个函数的内部细节并不了解,只能知道大概加上自己的一些猜想.

其实下层可以上报的事件都在我们的内核中是定义好的,我们都可以上报这些事,但是input子系统的上层输入事件驱动层的各个handler只能够处理某一些事件(event除外),

例如joy handler只能处理摇杆类型的事件,key handler只能处理键盘,内部实现的原理就是会在核心层做handler和device匹配的过程。如果我们的上报的事件与多个handler都能够匹配成功,那么绑定之后核心层会向这多个handler都上报事件,再由handler上报给应用层。

同一个input设备可以对应多个次设备号,因为对于一个输入设备来说,他在进行匹配的时候可能会与多个handler匹配成功,匹配成功就会在连接过程中创建设备文件,而不同的handler创建的设备文件的次设备号是不一样的,所以就是导致一个设备对应多个次设备号。

input子系统详解2的更多相关文章

  1. input子系统详解

    一.初识linux输入子系统 linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Inpu ...

  2. linux input子系统详解

    首先,什么是linux的子系统: 输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动通过Driver->Input core->Event handler- ...

  3. Linux内核驱动学习(十)Input子系统详解

    文章目录 前言 框架 如何实现`input device` 设备驱动? 头文件 注册input_dev设备 上报按键值 dev->open()和dev->close() 其他事件类型,处理 ...

  4. driver: Linux设备模型之input子系统详解

    本节从整体上讲解了输入子系统的框架结构.有助于读者从整体上认识linux的输入子系统.在陷入代码分析的过程中,通过本节的知识能够找准方向,明白原理. 本节重点: 输入子系统的框架结构 各层对应内核中的 ...

  5. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  6. 嵌入式Linux内核I2C子系统详解

    1.1 I2C总线知识 1.1.1  I2C总线物理拓扑结构     I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成.通信原理是通过对SCL和SDA线高 ...

  7. PhoenixFD插件流体模拟——UI布局【Input】详解

    Liquid Input 流体输入 本文主要讲解Input折叠栏中的内容.原文地址:https://docs.chaosgroup.com/display/PHX3MAX/Liquid+Input 主 ...

  8. vue中ref在input中详解

    当我们在项目中遇见文本输入框的时候,获取时刻输入框中的值 1.v-model <template> <input type="text" v-model=&quo ...

  9. input表单的type属性详解,不同type不同属性之间区别

    目标:详解表单input标签type属性常用的属性值 一.input标签和它的type属性 PS:input 元素可以用来生成一个供用户输入数据的简单文本框. 在默认的情况下, 什么样的数据均可以输入 ...

随机推荐

  1. 解决 canvas 将图片转为base64报错

    var canvas=document.getElementById("canvas"),//获取canvas ctx = canvas.getContext("2d&q ...

  2. element.dataset API

    不久之前我向大家展示了非常有用的classList API,它是一种HTML5里提供的原生的对页面元素的CSS类进行增.删改的接口,完全可以替代jQuery里的那些CSS类操作方法.而另外一个非常有用 ...

  3. [转]使用keepalived搭建主备切换环境

    使用keepalived搭建主备切换环境 时间 2016-09-15 08:00:00 cpper 原文  http://cpper.info/2016/09/15/keepalived-for-ma ...

  4. WEBBASE篇: 第二篇, HTML知识2

    HTML知识2 <!doctype html> <html lang="en"> <head> <meta charset="U ...

  5. 二叉树求逆序对(伪AC 23333)

    成链的时候 是最坏情况 O(n^2)的复杂度呢! 按照输入的数据 一个一个的插入建树 然后维护左右儿子的个数  (我们规定, 左儿子 小于  父亲  右儿子大于父亲) 往左走 说明存在逆序对 逆序对的 ...

  6. jQuery 中json字符串与对象互转

    json字符串转json对象:jQuery.parseJSON(jsonStr); json对象转json字符串:JSON.stringify(jsonObj);

  7. 20155208徐子涵 2016-2017-2 《Java程序设计》第8周学习总结

    20155208徐子涵 2016-2017-2 <Java程序设计>第8周学习总结 教材学习内容总结 第14章 NIO与NIO2 14.1 认识NIO NIO概述 NIO使用频道(Chan ...

  8. Tree Recovery

    #include<stdio.h> #include<string.h> void build(int n,char*s1,char*s2) { )return ; ])-s2 ...

  9. YIT-CTF—Web

    一:背后 打开传送门——>查看网页源代码——>1b0679be72ad976ad5d491ad57a5eec0——>用MD5解密 二:一种编码 [][(![]+[])[+[]]+([ ...

  10. 树莓派ssh服务

    从官网下载的镜像更新raspberry pi 3 B,但默认是不支持SSH的,即不可外部通过SSH登陆到板子里. 解决办法很简单,在SD卡的根目录下创建一个"ssh"的文件夹即可.