以前,看过国嵌关于input子系统的视频课程,说实话,我看完后脑子里很乱,给我的印象好像是input子系统驱动是一个全新的驱动架构,疑惑相当多。前几天在网上,看到有很多人介绍韦东山老师的linux驱动课程很不错,于是,我就买了第二期的视频,看了韦老师讲解的input子系统视频课程后,我完全明白了整个input子系统的工作机制。为了方便以后查阅,对input子系统的整体框架总结如下:

典型的输入设备(如键盘、鼠标)的工作机制都是差不多的,都是在设备有动作时,向CPU产生一个中断,通知它读取相应的数据。Linux为了方便开发这一类驱动,它实现了这类驱动的通用部分,只留下与设备相关的部分,这样使得开发这一类驱动更加方便。

在Linux中,Input子系统由三大部分组成,它们是Input子系统核心层、Input子系统事件处理层和Input子系统设备驱动层。在通常情况下,Input子系统核心层和Input子系统事件处理层都已经实现了,而作为驱动开发者,我们仅仅只需要完成Input子系统设备驱动层。

对于一个完整的驱动程序,我们首先需要确定设备的主设备号,次设备号,然后向系统注册该设备,最后实现file_operations结构体中的函数。在Input子系统中,这些步骤会分布到不同的层中,最后三个层通过一些联系构成了一个完整的驱动程序。

在input子系统中有三个比较中要的结构体,它们分别是input_handler结构体、input_dev结构体、input_handle结构体。它们的具体代码如下:

struct input_handler {

void *private; //driver相关数据

void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); //input_event函数会调用此函数
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 file_operations *fops; //file_operation结构体,会替换input.c里的file_operation结构体
int minor;
const char *name; //input_handler名称

const struct input_device_id *id_table;
const struct input_device_id *blacklist;

struct list_head h_list; //与该handler相关的handle
struct list_head node;
};

struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;

unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);

struct ff_device *ff;

unsigned int repeat_key;
struct timer_list timer;

int sync;

int abs[ABS_MAX + 1];
int rep[REP_MAX + 1];

unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];

int absmax[ABS_MAX + 1];
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int absres[ABS_MAX + 1];

int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

struct input_handle *grab;

spinlock_t event_lock;
struct mutex mutex;

unsigned int users;
bool going_away;

struct device dev;

struct list_head h_list;  //与该input_dev相关的handle
struct list_head node;
};

struct input_handle {

void *private;

int open;
const char *name;

struct input_dev *dev;  //指向input_dev
struct input_handler *handler; //指向input_handler

struct list_head d_node;
struct list_head h_node;
};

在Input.c(drivers/input)的input_init函数中,有:

err=register_chrdev(INPUT_MAJOR, "input", &input_fops)

它向系统中注册了一个主设备号为INPUT_MAJOR(13),名为input的设备。而它的file_operations结构体,如下所示:

static const struct file_operations input_fops = {

.owner = THIS_MODULE,

.open = input_open_file,

};

它仅仅实现了open函数,为没有实现其他函数。其实其他函数会在其open函数中实现。

在input_open_file函数中,会把Input子系统的事件处理层相关结构体handler的file_operations赋值给input_fops,从而完整的实现了file_operations结构体。具体实现的代码:

new_fops = fops_get(handler->fops)

file->f_op = new_fops;

从这里我们可以看出一个Input子系统的驱动的file_operations结构体是在handler结构体中实现的。

对于input_handler结构体,它是input子系统中事件处理层相关的结构体,对于一个具体的事件处理,需要向事件处理层注册这样一个结构体,如在鼠标的事件处理程序mousedev.c的mousedev_init函数中,会调用input_register_handler函数,注册一个具体的handler。而在input_register_handler函数中,会将handler添加到input_handler_list中,然后在input_dev_list中寻找相匹配的dev。具体代码:

list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);

因而,要形成一个完成的驱动,则必须有相应的dev。

对于input_dev结构体,它是input子系统中设备驱动层相关的结构体,对于一个具体的设备,需要向设备驱动层注册这样一个结构体,如在鼠标的设备驱动程序usbmouse.c的usb_mouse_probe函数中,会调用input_register_device函数,注册一个具体的dev。而在input_register_device函数中,会将dev添加到input_dev_list中,然后再input_handler_list寻找相匹配的handler。具体代码:

list_add_tail(&dev->node, &input_dev_list);

list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);

在input_attach_handler函数中,会调用input_match_device函数,将input_dev_list中的dev与handler相匹配或将input_handler_list中的handler与dev相匹配,匹配成功后就会调用handler中的connect函数。而在connect函数中,会通过handle结构体将匹配好的handler和dev联系起来。如mousedev.c中,connect函数中调用了mousedev_create函数,而在mousedev_create函数中,将handle结构中的handler指向匹配好的handler,而dev指向匹配好的dev,然后会调用input_register_handle函数。

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

error = input_register_handle(&mousedev->handle);

在 input_register_handle函数中,将input_handle结构体中的d_node和h_node成员分别添加到了匹配好的dev结构体的h_list中和匹配好的handler结构体的h_list中。

struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;

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

list_add_tail(&handle->h_node, &handler->h_list);

这样,Input驱动程序中的设备驱动和事件处理就这样无缝连接起来了,无论是我们先有了handler还是先有了dev,input子系统都会找了与之匹配的dev或handler,使得三部分之间更加整体化。

linux驱动学习之Input输入子系统的更多相关文章

  1. linux-2.6内核驱动学习——jz2440之输入子系统

    如果按照上一篇记录的那样,只有本公司的人或者自己才能使用驱动.想写出一个通用的驱动程序,让其他应用程序来无缝移植,需要使用现成的驱动——输入子系统. /drivers/input/input.c #d ...

  2. 嵌入式Linux驱动学习之路(十六)输入子系统

    以前写的一些输入设备的驱动都是采用字符设备处理的.问题由此而来,Linux开源社区的大神们看到了这大量输入设备如此分散不堪,有木有可以实现一种机制,可以对分散的.不同类别的输入设备进行统一的驱动,所以 ...

  3. ARM Linux内核Input输入子系统浅解

    --以触摸屏驱动为例 第一章.了解linux input子系统         Linux输入设备总类繁杂,常见的包括有按键.键盘.触摸屏.鼠标.摇杆等等,他们本身就是字符设备,而linux内核将这些 ...

  4. linux input输入子系统分析《四》:input子系统整体流程全面分析

    1      input输入子系统整体流程 本节分析input子系统在内核中的实现,包括输入子系统(Input Core),事件处理层(Event Handler)和设备驱动层.由于上节代码讲解了设备 ...

  5. input输入子系统

    一.什么是input输入子系统? 1.Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型.不同原理.不同的输入信 ...

  6. INPUT输入子系统【转】

    转自:https://www.cnblogs.com/deng-tao/p/6094049.html 1.Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等 ...

  7. INPUT输入子系统——按键

    一.什么是input输入子系统? 1.1. Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型.不同原理.不同的 ...

  8. linux 驱动学习笔记01--Linux 内核的编译

    由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...

  9. linux驱动学习(二) Makefile高级【转】

    转自:http://blog.csdn.net/ghostyu/article/details/6866863 版权声明:本文为博主原创文章,未经博主允许不得转载. 在我前一篇写的[ linux驱动学 ...

随机推荐

  1. 【转】Javascript+css 实现网页换肤功能

    来源:http://www.php100.com/html/webkaifa/DIV_CSS/2008/1014/2326.html Html代码部分: 1.要有一个带id的样式表链接,我们要通过操作 ...

  2. XMLHttpRequest 对象

    XMLHttpRequest 对象 XML XSLTXML 解析器XMLHttpRequest 对象用于在后台与服务器交换数据. 什么是 XMLHttpRequest 对象? XMLHttpReque ...

  3. Golang_test

    package main import ( "fmt" "time" ) func GetName() { //没事玩一下循环 for i := 0; i &l ...

  4. 运用cookie登陆人人网爬取数据

    浏览器访问WEB服务器的过程 在用户访问网页时,不论是通过URL输入域名或IP,还是点击链接,浏览器向WEB服务器发出了一个HTTP请求(Http Request),WEB服务器接收到客户端浏览器的请 ...

  5. 基于LBS的地理位置附近的搜索以及由近及远的排序

    Nosql学习之Redis资料(一) http://redis.io/download 目前基于LBS地理位置的搜索已经应用非常广了,的确是个很方便的东西. 我们做程序的就是要考虑如何通过这些功能,来 ...

  6. sqlserver快照,启用基于行版本控制的隔离级别

    在sqlserver标准的已提交读(read committed)隔离级别下,读写操作相互阻塞.未提交读(read uncommitted)虽然不会有这种阻塞,但是读操作可能会读到脏数据,这是大部分用 ...

  7. android模拟器不能用键盘

    android模拟器不能用键盘?咋整啊?

  8. mysql四种事务隔离级的说明

    ·未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据 ·提交读(Read Committed):只能读取到已经提交的数据.Oracle等多数数据库默 ...

  9. [linux]BASH 的基本语法

    最简单的例子 -- Hello World! 关于输入.输出和错误输出 BASH 中对变量的规定(与 C 语言的异同) BASH 中的基本流程控制语法 函数的使用 2.1     最简单的例子 -- ...

  10. 剑指offer系列46---和为s的连续正数序列

    [题目]输出所有和为S的连续正数序列.序列为:1,2,3,4,5,6,7,8................ * 序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序 package com.e ...