linux驱动之输入子系统

原创 2017年03月03日 11:35:04

输入子系统框架,把内核打开 搜索input.c

输入子系统的代码在/driver/input目录下面 最上一层,我们称它为核心层

要看一个驱动程序我们应该从他的入口函数开始看


有一行:err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),INPUT_MAX_CHAR_DEVICES, "input");//注册字符设备,以前是我们自己写的,现在这套代码里面已经有了


在 2.4 的内核我们使用 register_chrdev(0, "hello", &hello_fops) 来进行字符设备设备节点的分配,这种方式每一个主设备号只能存放一种设备,它们使用相同的 file_operation 结构体,也就是说内核最多支持 256 个字符设备驱动程序。

在 2.6 的内核之后,新增了一个 register_chrdev_region 函数,它支持将同一个主设备号下的次设备号进行分段,每一段供给一个字符设备驱动程序使用,使得资源利用率大大提升,同时,2.6 的内核保留了原有的 register_chrdev 方法。在 2.6 的内核中这两种方法都会调用


下一层是evdev.c,keyboard.c mousedev.c


static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}

他们向上注册input_register_handler 向input.c 

可以看下这个evdev_handler这个里面有什么东西


这里面有个fops


handler是个纯软件的 handler处理


还一个层,设备层

设备层向input.c注册input_register_device  上面是纯软件的东西,这里就代表硬件


这个handler能不能处理这个device呢 ,这里肯定会有个联系 我们先看一下这个handler

这里有个.id_table= evdev_ids,就是表示支持哪一些设备

当你注册handler和注册设备的时候,他们会比较一下,看看这个handler能不能支持这个设备


在这里可以看出来,如果能够支持的话就会调用这个connect函数

搜索input_register_device会发现各种外设如鼠标什么的都调用了这个函数

我们input_register_device这个主要做了什么事情,大概看一眼

list_add_tail(&dev->node, &input_dev_list);会把device结构体放到链表里面去


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

handler也会有一个链表,对这个链表里面的每一项都调用input_attach_handler(dev, handler);这个函数会根据input_handler的id_tabler判断能否支持这个input_dev。


我们来看看另外一边input_register_handler这里面做了什么事情

list_add_tail(&handler->node, &input_handler_list);//放到一个链表里面去

list_for_each_entry(dev, &input_dev_list, node)//对于每个input_device调用input_attach_handler
input_attach_handler(dev, handler);这个函数会根据input_handler的id_tabler判断能否支持这个input_dev。


来看看input_attach_handler这个函数

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的id_table和输入设备比较一下,看看能不能支持,是否匹配


if (!id)
return -ENODEV;


error = handler->connect(handler, dev, id);//如果可以的话调用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;
}

注册input_dev和input_handler时,会两两比较左边的input_dev和右边的input_handler

根据input_handler的id_table判断这个input_handler能否支持这个input_dev

如果能支持,则调用input_handler的connect函数建立“连接”


怎么建立连接的

我们来看看 

evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);这里分配一个evdev结构体,分配了一个input_handle,注意没有r,跟之前相比没有r,

然后来设置它

evdev->handle.dev = input_get_device(dev);指向左边的input_device结构体
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler; 指向右边的input_handler结构体
evdev->handle.private = evdev;


evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;


error = input_register_handle(&evdev->handle);//然后注册这个handle


然后我们进去看一看这个函数

if (handler->filter)
list_add_rcu(&handle->d_node, &dev->h_list); 
else
list_add_tail_rcu(&handle->d_node, &dev->h_list); 把这个handle放到一个输入设备的链表里面


list_add_tail_rcu(&handle->h_node, &handler->h_list); 把这个handle放到handler的链表里面



连接的时候构造一个input_handle 里面有个dev指向输入设备,里面有个handler指向handler



input_devie里面有个h_list, handler也有一个h_list

这两个h_list都指向这个handle 所以我们device可以通过这个h_list找到这个handle,又可以通过这个handle找到这个handler,反过来也是一样的


所以怎么建立连接总结起来就是

1.分配一个input_handle结构体

2.input_handle.dev=input_dev;//指向input_device

    input_handle.handler=input_handler;//指向input_handler

3.注册

注册里面干了什么事了 就是input_handler->h_list =&input_handle

input_device>h_list =&input_handle


所以我们handler可以通过h_list找到input_handle input_handle里面有个input_handle.handler = handler;可以找到handler 反过来一样的



我们拿按键驱动来举例子

我们应用程序来读,最终会导致相应的handler里面的里面的fops里面的read函数被调用

我们拿evdev_read来说明




有休眠就有唤醒,那么被谁唤醒呢,被谁唤醒不好找,那我们来搜索evdev->wait这个东西,

结果可以发现在.event= evdev_event,这个里面唤醒 从这个单词就可以知道它是发生了某些事件



那么问题有来了,被它唤醒,但是这个东西又是被谁调用

我们可以猜测应该是硬件相关的代码,应该是input_device这一层调用的

有个例子比较好,搜索gpio_keys.c这个,这只是一个例子



如此可以看出 在设备的中断服务程序里面,确定事件是什么,然后调用相应的input_handler的event处理函数

实际上这就是我们的核心 input_event就是用来上报事件的

void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value) 这个函数有个参数是input_device

函数里有个成员是input_handler


list_for_each_entry(handle,&dev->h_list,d_node)

if(handle->open)

handle->handler->event(handle,type,code,value);



怎么写一个符合输入子系统框架的驱动程序

1.分配一个input_dev结构体

2.设置

3.注册

4.硬件相关的代码,比如中断服务程序上报事件











版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Apache 使用ssl模块配置HTTPS

Web服务器在默认情况下使用HTTP,这是一个纯文本的协议。正如其名称所暗示的,纯文本协议不会对传输中的数据进行任何形式的加密。而基于HTTP的Web服务器是非常容易配置,它在安全方面有重大缺陷。任何...

XAMPP各个版本配置

XAMPP这集成环境也很不错 如果你的PHP代码有加密部分,而Zend Guard 5.2加密的代码不能用5.3解密, 只有下载最后一个PHP5.2版本的xampp 1.7.1,并改动...

xamarin android异步更新UI线程

UI线程简单了解 一些从事web开发的同学,可能对UI线程没有这个概念,没办法,毕竟“UI线程”这个概念只存在一些客户端(window客户端软件、app等)。其实android在子线程中更新UI线程,...

夕拾朝花——我的2016

夕拾朝花——我的2016走在北航的林间小道上,两边稀稀落落侥幸残留几片枯叶在冷风中瑟瑟抖动,北京 的冬日就是这样,异显凄凉。我常想在纷扰中寻出一点闲静来,然而委实不容易。趁着圣诞的间隙,从快节奏的生活...
  • yzzst
  • yzzst
  • 2016-12-24 23:53
  • 12811

异步赠书:7月重磅大咖新书机器学习/Android/python

异步赠书:6月重磅大咖新书机器学习/Android/python           今天小编开启了大咖重磅新书赠送活动,如果你热爱读书、热爱技术,参与到异步赠书活动中来,都有机会得到新书中的一本哦...

大型分布式数据库中间件MyCat的安装与使用

官网资料MyCat官网:http://www.mycat.io/MyCat权威指南:http://www.mycat.io/document/Mycat_V1.6.0.pdfMyCat下载地址:htt...

[centos] 安装php扩展<stomp>

安装步骤 1>.打开php官网:http://php.net/ 2>.在搜索框搜索要安装的扩展,如下图 [举例stomp] 3>.在搜索出的页面中,有 简介,需求,安装,运行时配置等...
  • mecho
  • mecho
  • 2014-02-19 11:10
  • 4788

[笔记]如何解决Your project contains C++ files but it is not using a supported native build system

最近因为项目需要下载Android终端模拟器(Android-Terminal-Emulator)源码进行调试编译,编译过程中出现报错 Error:Execution failed for ta...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)