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.硬件相关的代码,比如中断服务程序上报事件











中断-----按键中断驱动程序

中断处理 1:外设的处理速度一般慢于CPU。2:CPU不能一直等待外部事件,所以设备必须有一种方法来通知CPU它的工作进度,这个方法就是中断,外设与CPU信息交互的机制,提高CPU利用率。处理之外还有...

(DT系列三)系统启动时, dts 是怎么被加载的

一,主要问题: 系统在启动的时候,是怎么加载 dts的; Lk,kernel中都需要调查。 二:参考文字 dts加载流程如下图所示: 启动过程中,bootloader...

linux驱动——input输入子系统(3)——evdev

linux驱动——input输入子系统(1)—输入子系统核心层(Input Core)的地址链接 linux驱动——input输入子系统(2)——handler的地址链接 ...

Linux驱动之输入子系统框架

好记性不如烂笔头,整理一下笔记~ Linux驱动之输入子系统框架 输入子系统将该类驱动划分为3部分 1、核心层 input.c 2、设备层 Gpio_keys.c ... 3、事件处理层 Evd...

linux驱动之输入子系统

对于驱动开发者来说,对按键 触摸屏 鼠标等设备分别进行文件操作显得很繁琐,他们具有一些相同的规律,即内核负责记录数据,应用负责读取数据,因此,内核开发者为了简化驱动开发者的工作,特地创造了输入子系统。...

Linux驱动学习笔记----------input输入子系统(基本概念与流程)

很久没有没有动博客,这些日子想把之前学的一些干货分享一下。首先关于输入子系统: 关于input输入子系统,流程图如下: 在输入子系统中我们将其分为4部分(设备驱动层,核心层,事件层,用户空间)...

linux驱动之输入子系统概念

以前的博文介绍linux驱动程序的时候一直遵循以下步骤: 确定主设备号定义一个file_operation结构,填充函数如:open  write  read poll等设备注册入口函数出口函数 以...

linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(2)

linux驱动—input输入子系统—The simplest example(一个最简单的实例)分析(1)的地址链接 1、上一篇说到了input_match_device函数,这篇接着...

嵌入式Linux驱动笔记(六)------浅析input输入子系统框架

你好!这里是风筝的博客, 欢迎和我一起多多交流。 基于设备驱动分层的思想,其实理解了platform总线,输入子系统也是可以很好理解的。 以kernel  4.8.17为例: input...

嵌入式linux驱动-输入子系统笔记

一、开发环境 1、内核:Linux 2.6.22.6; 2、JZ2440 3、ubuntu 9.10 二、过程 1、分配input_dev结构体    使用input_alloc...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:linux驱动之输入子系统
举报原因:
原因补充:

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