input子系统详解2

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

这一节认真追踪一下代码

input.c:

input_init(void)函数

 1 static int __init input_init(void)
 2 {
 3     int err;
 4
 5     err = class_register(&input_class);
 6     if (err) {
 7         printk(KERN_ERR "input: unable to register input_dev class\n");
 8         return err;
 9     }
10
11     err = input_proc_init();
12     if (err)
13         goto fail1;
14
15     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
16     if (err) {
17         printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
18         goto fail2;
19     }
20
21     return 0;
22
23  fail2:    input_proc_exit();
24  fail1:    class_unregister(&input_class);
25     return err;
26 }

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 >> 5])
            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 0;
}
  知识点之二: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(0);
    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] = 250;
        dev->rep[REP_PERIOD] = 33;
    }

    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) - 1);

    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 0;
}
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 = 0; 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 = 1;
    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 0;

 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被调用

时间: 2024-11-10 01:35:19

input子系统详解2的相关文章

input子系统详解

一.初识linux输入子系统 linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputCore)和输入子系统设备驱动层. 对于输入子系统设备驱动层而言,主要实现对硬件设备的读写访问,中断设置,并把硬件产生的事件转换为核心层定义的规范提交给事件处理层.即将底层的硬件输入转化为统一事件形式,想输入核心(Input Core)汇报. 对于核心层而言,为设备驱动层提供了规范和接口.设备驱动

vue中ref在input中详解

当我们在项目中遇见文本输入框的时候,获取时刻输入框中的值 1.v-model <template> <input type="text" v-model="inputval"> </template> export default { data(){ return { inputval:'', } }, watch:{ inputval(){ console.log(this.inputval) } } } 2.ref自定义一个方

OS X系统审计子系统详解

OS X是下了基本安全模块BSM.审计系统的作用是跟踪用户和进程的操作.类似于Windows系统里面的日志功能. 出于安全性考虑,审计系统必须在内核层次执行,在OS X中,审计是通过Mach实现的. 默认情况下,审计日志存放在/var/audit目录下.命名方式是start_time.stop_time.其中的start_time是起始时间戳,精度为秒.最后一个日志文件的stop_time是not_terminated. 在terminal输入以下命令可以看到/var/aduit目录的基本信息:

嵌入式Linux内核I2C子系统详解

1.1 I2C总线知识 1.1.1  I2C总线物理拓扑结构     I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成.通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递.在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平. 1.1.2  I2C总线特征    I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从I2C器件的数据手册得知),主

Java笔记---Hadoop 2.7.1下WordCount程序详解

一.前言 在之前我们已经在 CenOS6.5 下搭建好了 Hadoop2.x 的开发环境.既然环境已经搭建好了,那么现在我们就应该来干点正事嘛!比如来一个Hadoop世界的HelloWorld,也就是WordCount程序(一个简单的单词计数程序) 二.WordCount 官方案例的运行 2.1 程序简介 WordCount程序是hadoop自带的案例,我们可以在 hadoop 解压目录下找到包含这个程序的 jar 文件(hadoop-mapreduce-examples-2.7.1.jar),

Android4.0 input事件输入流程详解(中间层到应用层)

在Android系统中,类似于键盘按键.触摸屏等事件是由WindowManagerService服务来管理的,然后再以消息的形式来分发给应用程序进行处理.系统启动时,窗口管理服务也会启动,该服务启动过程中,会通过系统输入管理器InputManager来负责监控键盘消息.当某一个Activity激活时,会在该Service下注册一个接收消息的通道,表明可以处理具体的消息,然后当有消息时,InputManager就会分发给当前处于激活状态下的Activity进行处理. InputManager的启动

Angularjs 事件指令 input 相关指令 和样式指令 DOM 操作指令详解

Angularjs 事件指令 input 相关指令 和样式指令DOM 操作指令详解学习要点:1. AngularJs 事件指令2. input 相关指令3. 样式指令4. DOM 操作指令5. ngBind/ngBindHtml/ngBindTemplate 重点6. ng-init ng-mode ng-model-options ng-controler 1. Angularjs 事件指令自己研究:ng-click/dbclickng-mousedown/upng-mouseenter/le

25.Unity3D手机中Input类touch详解-Unity触屏事件解析到底(Twisted Fate)

首先贴一下Unity支持的模型文件类型,以前没有收集过. Unity支持两种类型的3D文件格式: 1.  通用的"出口型"3D文件 如.fbx..dae..3ds..dxf..obj等文件格式. 2.  3D软件专用的3D文件格式 如Max, Maya, Blender,Cinema4D, Modo, Lightwave & Cheetah3D 等软件所支持的格式,如.MAX, .MB, .MA等等. Unity3D手机中Input类touch详解: 1.Input.touch

13.Linux键盘按键驱动 (详解)

版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一节分析输入子系统内的intput_handler软件处理部分后,接下来我们开始写input_dev驱动 本节目标: 实现键盘驱动,让开发板的4个按键代表键盘中的L.S.空格键.回车键 1.先来介绍以下几个结构体使用和函数,下面代码中会用到 1)input_dev驱动设备结构体中常用成员如下: struct input_dev { void *private; const char *name; //设备名字 const char *ph