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

这一节认真追踪一下代码

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. django面试六

    Redis缓存击穿.缓存雪崩.缓存重建 回答参考: 缓存击穿: 当一个连接访问缓存数据库中不存在的数据时,会直接通过缓存数据库到后端数据库中查找数据,但如果有大量连接在查询一个不存在的数据,就会有大量 ...

  2. php 安装过程 第二次探索

    由于第一次安装过程写的比较乱,再做整理 1.phpstudy 才是正确姿势. http://phpstudy.php.cn/ 官网下载.  phpstrom 中配置 php为 phpstudy目录下 ...

  3. 小技巧, 批处理修改IP

    相信很多人都有这样的麻烦, 工作单位的IP网段与住的不一致, 自己的笔记本在单位和回家的时候每次都要更改IP, 很麻烦,  菜鸟小罗偷个懒, 做了个批处理来修改IP,方便一点. 还有就是可以把工作的时 ...

  4. 【leetcode】350. Intersection of Two Arrays II

    problem 350. Intersection of Two Arrays II 不是特别明白这道题的意思,例子不够说明问题: 是按顺序把相同的元素保存下来,还是排序,但是第二个例子没有重复... ...

  5. aliyun服务器对象存储oss

    aliyun OSS 使用简单.方便. 官方网址 aliyun.com 首先通过aliyun管理控制台申请OSS服务.通过AccessKeys分配Access Key ID和Access Key Se ...

  6. C语言--第二周作业评分和总结(5班)

    作业链接:https://edu.cnblogs.com/campus/hljkj/CS2017-5/homework/1026 一.评分要求 要求1 阅读指定博客+阅读收获+例子.(5分) 要求2 ...

  7. Blender界面及模式统计

    Blender2.79b安装后默认界面分布: 所有窗口: 窗口: 3D View的9种模式:

  8. (13)自定意义标签和过滤器 (templatetags)

    过滤器分内置和自定意义 PS:过滤器可以用在for循环和if判断后,但是标签不能使用在for循环和if判断后 内置过滤器: add(在模板中实现加减法) default(就是当传入的变量是False的 ...

  9. Js 判断输入的验证码是否一致

    实现效果: 判断输入的验证码是否一致 如果不同,alert出验证码输入有误~, 输入正确输出登录成功. <!DOCTYPE html> <html lang="en&quo ...

  10. linux ssh的安装与配置以及实现密钥登录

    安装ssh服务器: yum install openssh ssh 配置文件位于/etc/ssh/sshd_config 如果需要允许root用户远程登录,那么vi /etc/ssh/sshd_con ...