Input子系统是linux kernel中与部分外围器件驱动联系比较紧密的模块,常用于Sensor,TP(touch panel),power key等器件的驱动。这类模块有个共同特点:字符设备,且数据量都不大,比如sensor一般最多只有xyz三个维度的数据。

整体来看,Input子系统有一个主线,那就是题目中这三个结构体的关系(下面简称为三方关系),input_dev对应于实际的device端,input_handler从名字也可以猜出来是对device的处理。“处理”这个词语不单单指的是对device数据的处理,比如report等;它其实可以包括系统在该device事件发生时想做的任何动作。至于input_handle,它是连接input_dev与input_handler的,该设计后面也会详细分析。在这里请记住,我们最终的目的是,通过input_dev,可以遍历所有与它有关的input_handler;通过input_handler,也可以遍历所有与它有关的input_dev。

为了更加透彻地讲述Input子系统,本博文将分两篇介绍,第一篇就来分析上面这个主线,第二篇分析Input子系统的A/B两个协议(B协议又称为Slot Protocol)。下面从input_device说起。

驱动端通过input_allocate_device来allocate对应的input_dev结构体,之后持有该指针,并完成对应的初始化(name, set_bit等等)。

点击(此处)折叠或打开

struct input_dev *input_allocate_device(void)

{

struct input_dev *dev;

dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);

if (dev) {

...//I deleted many lines here.

INIT_LIST_HEAD(&dev->h_list);//this is the head of the list which consists of related input_handles.

...

}

return dev;

}

结合这段代码,看下input_dev结构体(这里只关心主线相关的结构体成员)

点击(此处)折叠或打开

struct input_dev {

...//detailed info of this device

struct list_head         h_list;//这是与input_dev相关联的input_handle的链表的表头

struct list_head node;//链入全局链表

};

驱动完成初始化后,调用input_register_device来注册已经初始化的input_dev,这个函数是三方关系的核心,它调用了input_attach_handler,而恰恰是在input_attach_handler这个函数内dev, handler和handle这三者确定了关系。看下这个函数究竟做了哪些关键的事情,

点击(此处)折叠或打开

int input_register_device(struct input_dev *dev)

{

static atomic_t input_no = ATOMIC_INIT(0);

struct input_handler *handler;

...//此次省略一千行O(∩_∩)O~

dev_set_name(&dev->dev, "input%ld",

(unsigned long) atomic_inc_return(&input_no) - 1);//set the dev name here: input0,input1,...

list_add_tail(&dev->node, &input_dev_list);//input_dev_list is a global list! So every input_dev will be listed.

list_for_each_entry(handler, &input_handler_list, node)

input_attach_handler(dev, handler);//dev and handler, we fould it.

...//同上

return 0;

}

上面的代码,对dev做进一步的设置,同时也将dev链接到全局链表中;但它最重要的功能还是体现在11、12行。

到了这里不得介绍下input_handler_list与input_handler,它是系统中所有handler挂载的链表的表头,自定义的handler必须挂载到该链表才有可能被系统所用(调用input_register_handler)。input_handler的作用上面简单提了,定义如下

点击(此处)折叠或打开

struct input_handler {

...

void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//important

bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

bool (*match)(struct input_handler *handler, struct input_dev *dev);

int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);

void (*disconnect)(struct input_handle *handle);

void (*start)(struct input_handle *handle);

...

const struct input_device_id *id_table;

struct list_head    h_list;//这是与input_handler相关联的input_handle的链表的表头

struct list_head    node;//链入全局链表   h_list node~是否注意到这与input_dev的最后两个一模一样呢,事实上他们名字与作用都一样

};

完成了必要的背景介绍,继续input_attach_handler,这个函数的逻辑与具体的handler是强相关的,下面就以input子系统默认的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,

};

点击(此处)折叠或打开

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)

{

const struct input_device_id *id;

int error;

id = input_match_device(handler, dev);//判断handler是否与dev match.通过handler的id_table、match等实现,比较简单,不做展开。

if (!id)

return -ENODEV;

error = handler->connect(handler, dev, id);//这之前dev与handler还是彼此独立的,connect直接产生我们关注的三方关系

if (error && error != -ENODEV)

pr_err("failed to attach handler %s to device %s, error: %d\n",

handler->name, kobject_name(&dev->dev.kobj), error);

return error;

}

看看evdev_handler的connect究竟为这个三方关系做了什么吧,

点击(此处)折叠或打开

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,

const struct input_device_id *id)

{

struct evdev *evdev;

...

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

if (!evdev)

return -ENOMEM;

...

evdev->handle.dev = input_get_device(dev);//这里的handle就是input_handle,它的dev成员指向input_dev

evdev->handle.name = dev_name(&evdev->dev);

evdev->handle.handler = handler;//它的handler成员指向input_handler

evdev->handle.private = evdev;

...

error = input_register_handle(&evdev->handle);//10、12行完成了handle到dev和handler,这个函数完善了所有的三方关系

return 0;

...//error handling

}

点击(此处)折叠或打开

struct input_handle {

...

struct input_dev *dev;//上段代码的第10行

struct input_handler *handler;//上段代码的第12行

struct list_head    d_node;//链入input_dev的h_list代表的链表

struct list_head    h_node;//链入input_handler的h_list代表的链表

};

点击(此处)折叠或打开

int input_register_handle(struct input_handle *handle)

{

struct input_handler *handler = handle->handler;

struct input_dev *dev = handle->dev;

...

if (handler->filter)

list_add_rcu(&handle->d_node, &dev->h_list);//上段代码的第5行的注释

else

list_add_tail_rcu(&handle->d_node, &dev->h_list);

...

list_add_tail_rcu(&handle->h_node, &handler->h_list);//上段代码的第6行的注释

...

return 0;

}

至此,三方关系形成完毕。我们实现了最终的目的,通过input_dev,可以遍历所有与它有关的input_handler;通过input_handler,也可以遍历所有与它有关的input_dev。

图解如下:图中单向箭头表示指针,双向箭头表示list_head。可以看出,从任何一个双向箭头出发,通过handle的过度,完全实现了我们的最终目标。掌握了这点,再看input_report那些流程的时候就非常容易了,dev想要report数据的时候无非是调用了handler的event函数指针指向的函数,我们可以在这个函数里定义任何想让系统去做的任务,比如cpu调频等,而不仅限于数据上报。熟悉面向对象编程的人可能想到了,其实这个设计运用了面向对象的observer设计模式。

至此,本文主要内容完结。掌握了这些知识固然重要,但还需要简单分析下这个三方关系的设计思路。

从本质上讲,input_dev与input_handler是一个多对多的关系,一个dev可以对应多个handler,一个handler也可以对应多个dev(参考上图)。

针对这种多对多的关系,也许有人会想,为什么不将input_handle的dnode,hnode分别内嵌到dev和handler内,这样也可以节省空间;实际上,内嵌的方式最终实现的是混乱的一对多的关系,因为指针的指向是唯一的,所以当两个不同的dev有一个共同的handler的时候,两个链表相交,那么后注册的dev会改变前一个dev的链表,导致混乱。

实际上,input_handle可以拆成两部分,dnode一部分,hnode一部分,dnode来表达一个dev可以对应多个handler,hnode来表达一个handler也可以对应多个dev。这两部分独立存在也一样可以实现input子系统的功能,而且理解起来更加简单;将他们合并起来节省了空间。

希望本文能让看了它的人,遇到多对多模型的类似问题时,能够记起这个三方关系。

linux的自定义input,Linux Input子系统之第一篇(input_dev/input_handle/input_handler)相关推荐

  1. linux新建自定义命令,Linux 创建自定义命令

    Linux 创建自定义命令 Linux 可以创建自定义使用命令 这里我们采取使用"alias"命令.这里我们首先了解两个文件,通过这两个文件我们可以根据环境配置相应的自定义命令. ...

  2. linux c 自定义 sigaction,Linux sigaction信号机制积累

    1.Linxu下使用signal()函数获取信号: signal()函数: #include typedef void (*sighandler_t)(int); sighandler_t signa ...

  3. Linux 贪吃蛇游戏 -C语言(本人的第一篇博文)

    一.Ncurses库简介: 1.curses是一个在Linux/Unix下广泛应用的图形函数库, 作用是可以在终端内绘制简单的图形用户界面. 2.Nurses库头文件:#include <cur ...

  4. python 自定义模块加密_Python开发【第一篇】Python基础之自定义模块和内置模块...

    为什么要有模块,将代码归类.模块,用一砣代码实现了某个功能的代码集合. Python中叫模块,其他语言叫类库. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代 ...

  5. 第一篇:linux(ubuntu)系统实操学习

    系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录 前言 一.linux的认识 1.Linux的应用领域 2.Linux学习建 ...

  6. linux 设备驱动 百度,Linux设备驱动之input子系统

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 作者:武汉华嵌嵌入式培训中心 讲师 李家凯 对于输入类设备如键盘.鼠标.触摸屏之类的Linux驱动,内核提供input子系统,使得这类设备的处理变得非常便 ...

  7. platform框架--Linux MISC杂项框架--Linux INPUT子系统框架--串行集成电路总线I2C设备驱动框架--串行外设接口SPI 设备驱动框架---通用异步收发器UART驱动框架

    platform框架 input. pinctrl. gpio 子系统都是 Linux 内核针对某一类设备而创建的框架, input子系统是管理输入的子系统 pinctrl 子系统重点是设置 PIN( ...

  8. SCALA Linux安装:JAVA.LANG.NUMBERFORMATEXCEPTION: FOR INPUT FOR INPUT STRING: “0X100“

    SCALA安装:JAVA.LANG.NUMBERFORMATEXCEPTION: FOR INPUT FOR INPUT STRING: "0X100" 问题描述 Linux终端输 ...

  9. linux系统中pinctrl 和gpio子系统使用方法(教你点灯)

    如何使用pinctrl和gpio子系统点亮led pinctrl 子系统作用 设备树PIN配置 gpio子系统介绍 配置gpio相关 编写驱动程序 编写应用程序 pinctrl 子系统作用 pinct ...

最新文章

  1. 【OpenCV 4开发详解】图像上绘制几何图形
  2. java上传ftp数据丢失_Java:将文件上传到FTP问题(数据包丢失) - java
  3. mysql partition赋权_mysql 创建用户及赋权
  4. 自然语言处理在医学领域的应用
  5. android流程化步骤样式,Android RecyclerView 解析之绘制流程篇
  6. 通俗易懂,快速幂基本思想
  7. 动画-animation
  8. 在c语言程序中无论是整数还是实数,C语言基础题及参考答案
  9. 用户行为变迁 行业垂直深耕——疫情下的2020年移动互联网报告
  10. 索罗斯说,我投机了,但我不觉得我做错了什么,我做的都是合法的。
  11. 使用NVivo完善定性编码的艺术
  12. Ubuntu 14.04解决minidwep-gtk无法启动问题
  13. C++17 并行排序初体验
  14. 超级实用——用word与谷歌翻译将英文PDF文档翻译成中文,免费无限制之美
  15. Excel转xml简单方法
  16. android 剪贴板管理器,Clipper一个强大的剪贴板管理器为Android | MOS86
  17. android订餐系统app、android购物商城系统app 手机端+服务器端 mysql数据库,界面简单,功能齐全 安卓购物商城 安卓在线订餐系统
  18. 手机远程管理服务器软件,远程控制软件 手机远程管理服务器
  19. 起源鸿蒙虚无等级,《刺客信条:起源》或为开放世界游戏 最高等级只有40
  20. Windows 10的中文用户名怎么改成英文

热门文章

  1. Linux上chown命令的高级用法
  2. 使用希捷DiscWizard格式化3TB硬盘并分区
  3. 在Blender中创建惊人的低多边形动画
  4. uboot引导kernel - 3 -uboot给内核传参详解
  5. nicstat命令安装与分析
  6. Java面试题之多线程同步和互斥有几种实现方法,都是什么?
  7. Python学习笔记——全局变量声明
  8. MVC3项目依赖文件错误解决
  9. visio 画类图时 方法里如何加参数
  10. 如何将Java源代码文件的编码从GBK转为UTF-8?