我是靠谱客的博主 平常康乃馨,最近开发中收集的这篇文章主要介绍input子系统,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、input子系统分析

1.输入子系统简介

同样的输入子系统也需要输入驱动的框架,好来辨认应用程序要打开的是哪个输入驱动

比如: 鼠标、键盘、游戏手柄等等这些都属于输入设备;这些输入设备的驱动都是通过输入子系统来实现的(当然,这些设备也依赖于usb子系统)

这些输入设备都各有不同,那么输入子系统也就只能实现他们的共性,差异性则由设备驱动来实现。差异性又体现在哪里?

最直观的就表现在这些设备功能上的不同了。对于我们写驱动的人来说在设备驱动中就只要使用输入子系统提供的工具(也就是函数)来完成这些“差异”就行了,其他的则是输入子系统的工作。这个思想不仅存在于输入子系统,其他子系统也是一样(比如:usb子系统、video子系统等)

所以我们先来分析下输入子系统input.c的代码,然后怎么来使用输入子系统(在内核中以input来形容输入子系统)

 

2.打开input.c,位于内核deivers/input

有以下这么两段:

subsys_initcall(input_init);
module_exit(input_exit);

显然输入子系统是作为一个模块存在,我们先来分析下input_int()入口函数

static int __init input_init(void)
{
	int err;

	input_init_abs_bypass();

	err = class_register(&input_class);
	if (err) {
		printk(KERN_ERR "input: unable to register input_dev classn");
		return err;
	}

	err = input_proc_init();
	if (err)
		goto fail1;

	err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
	if (err) {
		printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}

	return 0;

 fail2:	input_proc_exit();
 fail1:	class_unregister(&input_class);
	return err;
}

(1)class_register(&input_class),在/sys/class 里创建一个 input类,这里代码只创建类,没有使用device_create()创建驱动设备

(2)register_chrdev创建驱动设备,其中变量INPUT_MAJOR =13,所以创建了一个主设备为13的"input"设备

然后我们来看看它的操作结构体input_fops

static const struct file_operations input_fops = {
	.owner = THIS_MODULE,
	.open = input_open_file,
};

只有一个.open函数,比如当我们挂载一个新的input驱动,则内核便会调用该.open函数,接下来分析该.open函数

 

3.然后进入input_open_file函数(drivers/input/input.c)

static int input_open_file(struct inode *inode, struct file *file)
{
	struct input_handler *handler;
	const struct file_operations *old_fops, *new_fops = NULL;
	int err;

	err = mutex_lock_interruptible(&input_mutex);
	if (err)
		return err;

	/* No load-on-demand here? */
	handler = input_table[iminor(inode) >> 5];
	if (handler)
		new_fops = fops_get(handler->fops);

	mutex_unlock(&input_mutex);

	/*
	 * That's _really_ odd. Usually NULL ->open means "nothing special",
	 * not "no device". Oh, well...
	 */
	if (!new_fops || !new_fops->open) {
		fops_put(new_fops);
		err = -ENODEV;
		goto out;
	}

	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);
out:
	return err;
}

(1)其中iminor (inode)函数调用了MINOR(inode->i_rdev);读取子设备号,然后将子设备除以32,找到新挂载的input驱动的数组号,然后放在input_handler 驱动处理函数handler中 

(2)若handler有值,说明挂载有这个驱动,就将handler结构体里的成员file_operations * fops赋到新的file_operations *old_fops里面

(3)再将新的file_operations *old_fops赋到file-> file_operations  *f_op里, 此时input子系统的file_operations就等于新挂载的input驱动的file_operations结构体,实现一个偷天换日的效果.

(4)然后调用新挂载的input驱动的*old_fops里面的成员.open函数

 

4.上面代码的input_table[]数组在初始时是没有值的,在input.c函数(drivers/input/input.c)中搜索input_table

找到它在input_register_handler()函数中被赋值,代码如下:

int input_register_handler(struct input_handler *handler)
{
    ...

    input_table[handler->minor >> 5] = handler;
	
    ...

    list_add_tail(&handler->node, &input_handler_list);
}

就是将驱动处理程序input_handler注册到input_table[]中,然后放在input_handler_list链表中,后面会讲这个链表

 

5.继续搜索input_register_handler

发现这个函数被evdev.c(事件设备),joydev.c(joystick操作杆设备),keyboard.c(键盘设备),mousedev.c(鼠标设备) 等文件调用

我们以evdev.c为例

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

 

6.我们来看看这个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,
};

就是我们之前看的input_handler驱动处理结构体

(1)fops:文件操作结构体,evdev_fops就是自己的写的操作函数,然后赋到.fops中

(2)minor:用来存放次设备号,其中EVDEV_MINOR_BASE=64, 然后调用input_register_handler(&evdev_handler)后,由于EVDEV_MINOR_BASE/32=2,所以存到input_table[2]中

 所以当open打开这个input设备,就会进入 input_open_file()函数,执行evdev_handler-> evdev_fops -> open函数

static const struct file_operations evdev_fops = {
	.owner		= THIS_MODULE,
	.read		= evdev_read,
	.write		= evdev_write,
	.poll		= evdev_poll,
	.open		= evdev_open,
	.release	= evdev_release,
	.unlocked_ioctl	= evdev_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= evdev_ioctl_compat,
#endif
	.fasync		= evdev_fasync,
	.flush		= evdev_flush
};

(3)id_table : 表示能支持哪些输入设备,比如某个驱动设备的input_dev->id和某个input_handler->id_table相匹配,就会调用connect连接函数

(4)connect:连接函数,将设备input_dev和某个input_handler建立连接,如下图

 

 

7.我们先来看看上图的input_register_device()函数,如何创建驱动设备的

int input_register_device(struct input_dev *dev)
{
    ......


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

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

    ......
}

(1)将要注册的input_dev驱动设备放在input_dev_list链表中
(2)调用input_attach_handler()函数,将每个input_handle的id_table进行判断,若两者支持便进行连接。

然后我们在回过头来看注册input_handler的input_register_handler()函数,如下图所示

int input_register_handler(struct input_handler *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,都会进入input_attach_handler()判断两者id是否有支持, 若两者支持便进行连接。

我们来看看input_attach_handler()如何实现匹配两者id的:

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);
	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: %dn",
			handler->name, kobject_name(&dev->dev.kobj), error);

	return error;
}

若两者匹配成功,就会自动进入input_handler的connect函数建立连接

我们来看看input_match_device()是如何匹配的

static const struct input_device_id *input_match_device(const struct input_device_id *id,  
                            struct input_dev *dev)  
{  
    int i;  
  
    /*遍历handler的id_table与device进行匹配*/  
    for (; id->flags || id->driver_info; id++) {  
  
        /*根据flags的标志位,按需要匹配相应的字段*/  
        if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)  
            if (id->bustype != dev->id.bustype)//总线类型不匹配   
                continue;  
  
        if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)//生产厂商不匹配   
            if (id->vendor != dev->id.vendor)  
                continue;  
  
        if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)//产品不匹配   
            if (id->product != dev->id.product)  
                continue;  
  
        if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)//版本不匹配   
            if (id->version != dev->id.version)  
                continue;  
          
        MATCH_BIT(evbit,  EV_MAX);//匹配所有的事件类型   
  
        /*匹配所有事件的子事件*/  
        MATCH_BIT(keybit, KEY_MAX);  
        MATCH_BIT(relbit, REL_MAX);  
        MATCH_BIT(absbit, ABS_MAX);  
        MATCH_BIT(mscbit, MSC_MAX);  
        MATCH_BIT(ledbit, LED_MAX);  
        MATCH_BIT(sndbit, SND_MAX);  
        MATCH_BIT(ffbit,  FF_MAX);  
        MATCH_BIT(swbit,  SW_MAX);  
  
        return id;//匹配成功,返回id   
    }  
  
    return NULL;  
}  

如果相应的标志位或事件被设置了,则需要进行匹配。如果没设置则不用匹配

以evdev为例,什么标志位和事件都没有设置,也就意味着所有设备都能匹配上

static const struct input_device_id evdev_ids[] = {
	{ .driver_info = 1 },	/* Matches all devices */
	{ },			/* Terminating zero entry */
};

 

8.我们还是以evdev.c(事件驱动) 的evdev_handler->connect函数来分析是怎样建立连接的

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,
};

evdev_handler的.connect函数是evdev_connect(),代码如下:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
			 const struct input_device_id *id)
{
	struct evdev *evdev;
	int minor;
	int error;

	for (minor = 0; minor < EVDEV_MINORS; minor++)
		if (!evdev_table[minor])
			break;

	if (minor == EVDEV_MINORS) {
		printk(KERN_ERR "evdev: no more free evdev devicesn");
		return -ENFILE;
	}

	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
	if (!evdev)
		return -ENOMEM;

	INIT_LIST_HEAD(&evdev->client_list);
	spin_lock_init(&evdev->client_lock);
	mutex_init(&evdev->mutex);
	init_waitqueue_head(&evdev->wait);

	dev_set_name(&evdev->dev, "event%d", minor);
	evdev->exist = 1;
	evdev->minor = minor;

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

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

	error = input_register_handle(&evdev->handle);
	if (error)
		goto err_free_evdev;

	error = evdev_install_chrdev(evdev);
	if (error)
		goto err_unregister_handle;

	error = device_add(&evdev->dev);
	if (error)
		goto err_cleanup_evdev;

	return 0;

 err_cleanup_evdev:
	evdev_cleanup(evdev);
 err_unregister_handle:
	input_unregister_handle(&evdev->handle);
 err_free_evdev:
	put_device(&evdev->dev);
	return error;
}

(1) 保存驱动设备名字,名为event%d, 比如(键盘驱动)event2

(2)保存驱动设备的主次设备号,其中主设备号INPUT_MAJOR=13,因为EVDEV_MINOR_BASE=64,所以此设备号=64+驱动程序本事子设备号, 比如(键盘驱动)event2:  主次设备号就是13,66

(3)最终会进入input_register_handle()函数来注册

int input_register_handle(struct input_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);

    ......

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

    ......
}

(1)将handle->d_node放入到input_dev驱动设备的h_list链表中,即input_dev驱动设备的h_list链表就指向handle->d_node

(2) input_handler驱动处理结构体的h_list也指向了handle->h_node

最终如下图所示:

 

两者的.h_list都指向了同一个handle结构体,然后通过.h_list 来找到handle的成员.dev和handler,便能找到对方,便建立了连接

 

9.建立了连接后,又如何读取evdev.c(事件驱动) 的evdev_handler->.fops->.read函数?

事件驱动的.read函数是evdev_read()函数,我们来分析下:

static ssize_t evdev_read(struct file *file, char __user *buffer,
			  size_t count, loff_t *ppos)
{
    ......

    if (count < input_event_size())
        return -EINVAL;

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

    ......
}

如果是阻塞型并且没有事件发生就等待

 

10.read函数进入休眠状态,事件发生后通过evdev_event函数唤醒

static void evdev_event(struct input_handle *handle,
			unsigned int type, unsigned int code, int value)
{
    ......

    wake_up_interruptible(&evdev->wait);
}

 

 

二、接口说明

核心层提供给设备驱动层的接口函数

input设备驱动框架留给设备驱动层的接口函数主要有3个:

      input_allocate_device。分配一块input_dev结构体类型大小的内存

      input_set_capability。设置输入设备可以上报哪些输入事件

      input_register_device。向input核心层注册设备

(1)input_allocate_device函数

 1 struct input_dev *input_allocate_device(void)
 2 {
 3     struct input_dev *dev;                 //   定义一个 input_dev  指针
 4 
 5     dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);   //  申请分配内存
 6     if (dev) {
 7         dev->dev.type = &input_dev_type;          //  确定input设备的 设备类型     input_dev_type
 8         dev->dev.class = &input_class;                //  确定input设备所属的设备类   class
 9         device_initialize(&dev->dev);                   //  input设备的初始化
10         mutex_init(&dev->mutex);                        //  互斥锁初始化
11         spin_lock_init(&dev->event_lock);            //  自旋锁初始化
12         INIT_LIST_HEAD(&dev->h_list);                 //  input_dev -> h_list 链表初始化
13         INIT_LIST_HEAD(&dev->node);                 //  input_dev -> node 链表初始化
14 
15         __module_get(THIS_MODULE);
16     }
17 
18     return dev;
19 }

 

(2)input_set_capability函数:

函数原型:input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)

参数:dev就是设备的input_dev结构体变量

        type表示设备可以上报的事件类型

        code表示上报这类事件中的那个事件

注意:input_set_capability函数一次只能设置一个具体事件,如果设备可以上报多个事件,则需要重复调用这个函数来进行设置,例如:

input_set_capability(dev, EV_KEY, KEY_Q);         // 至于函数内部是怎么设置的,将会在后面进行分析。

input_set_capability(dev, EV_KEY, KEY_W);

input_set_capability(dev, EV_KEY, KEY_E);

具体的这些类下面有哪些具体的输入事件,请看 driversinputinput.h 这个文件。

 

(3)input_register_device函数:

 1 int input_register_device(struct input_dev *dev)      //  注册input输入设备
 2 {
 3     static atomic_t input_no = ATOMIC_INIT(0);
 4     struct input_handler *handler;                          //  定义一个  input_handler 结构体指针
 5     const char *path;
 6     int error;
 7 
 8     /* Every input device generates EV_SYN/SYN_REPORT events. */
 9     __set_bit(EV_SYN, dev->evbit);                  //   每一个input输入设备都会发生这个事件
10 
11     /* KEY_RESERVED is not supposed to be transmitted to userspace. */
12     __clear_bit(KEY_RESERVED, dev->keybit);  //  清除KEY_RESERVED 事件对应的bit位,也就是不传输这种类型的事件
13 
14     /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
15     input_cleanse_bitmasks(dev);           //   确保input_dev中的用来记录事件的变量中没有提到的位掩码是干净的。
16 
17     /*
18      * If delay and period are pre-set by the driver, then autorepeating
19      * is handled by the driver itself and we don't do it in input.c.
20      */
21     init_timer(&dev->timer);
22     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
23         dev->timer.data = (long) dev;
24         dev->timer.function = input_repeat_key;
25         dev->rep[REP_DELAY] = 250;
26         dev->rep[REP_PERIOD] = 33;
27     }
28 
29     if (!dev->getkeycode)
30         dev->getkeycode = input_default_getkeycode;
31 
32     if (!dev->setkeycode)
33         dev->setkeycode = input_default_setkeycode;
34 
35     dev_set_name(&dev->dev, "input%ld",                                  //   设置input设备对象的名字    input+数字
36              (unsigned long) atomic_inc_return(&input_no) - 1);
37 
38     error = device_add(&dev->dev);         //   添加设备       例如:          /sys/devices/virtual/input/input0     
39     if (error)
40         return error;
41 
42     path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);  //  获取input设备对象所在的路径      /sys/devices/virtual/input/input_xxx   
43     printk(KERN_INFO "input: %s as %sn",
44         dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
45     kfree(path);
46 
47     error = mutex_lock_interruptible(&input_mutex);
48     if (error) {
49         device_del(&dev->dev);
50         return error;
51     }
52 
53     list_add_tail(&dev->node, &input_dev_list);             //   链表挂接:    将 input_dev->node 作为节点挂接到 input_dev_list  链表上
54 
55     list_for_each_entry(handler, &input_handler_list, node)  //  遍历input_handler_list 链表上的所有handler
56         input_attach_handler(dev, handler);                        //  将handler与input设备进行匹配
57 
58     input_wakeup_procfs_readers();                //  更新proc 文件系统
59 
60     mutex_unlock(&input_mutex);
61 
62     return 0;
63 }

 

三、驱动代码

#include <linux/input.h> 
#include <linux/module.h> 
#include <linux/init.h>
#include <asm/irq.h> 
#include <asm/io.h>
#include <mach/irqs.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>

#define BUTTON_IRQ	IRQ_EINT2

static struct input_dev *button_dev;

static irqreturn_t button_interrupt(int irq, void *dummy) 
{ 
	int flag;

	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0));
	flag = gpio_get_value(S5PV210_GPH0(2));
	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));

	input_report_key(button_dev, KEY_LEFT, !flag);
	input_sync(button_dev);

	return IRQ_HANDLED; 
}

static int __init button_init(void) 
{ 
	int error;
	
	error = gpio_request(S5PV210_GPH0(2), "GPH0_2");
	if(error)
	{
		printk("key-s5pv210.c: request gpio GPH0(2) fail");
		return -EBUSY;
	}
	s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));
	
	if (request_irq(BUTTON_IRQ, button_interrupt, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "button", NULL)) 
	{ 
		printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %dn", BUTTON_IRQ);
		error = -EBUSY; 
		goto err_free_gpio; 
	}
	
	button_dev = input_allocate_device();
	if (!button_dev)
	{ 
		printk(KERN_ERR "key-s5pv210.c: Not enough memoryn");
		error = -ENOMEM;
		goto err_free_irq; 
	}
	
	button_dev->name = "keys";
	input_set_capability(button_dev, EV_KEY, KEY_LEFT);
	error = input_register_device(button_dev);
	if (error) 
	{ 
		printk(KERN_ERR "key-s5pv210.c: Failed to register devicen");
		goto err_free_dev; 
	}
	
	return 0;

 err_free_dev:
	input_free_device(button_dev);
 err_free_irq:
	free_irq(BUTTON_IRQ, NULL);
err_free_gpio:
	gpio_free(S5PV210_GPH0(2));
	
	return error; 
}

static void __exit button_exit(void) 
{
	input_unregister_device(button_dev);
	input_free_device(button_dev);
	free_irq(BUTTON_IRQ, NULL);
	gpio_free(S5PV210_GPH0(2));
}

module_init(button_init); 
module_exit(button_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");							// 描述模块的许可证
MODULE_AUTHOR("lsm");							// 描述模块的作者
MODULE_DESCRIPTION("s5pv210 button driver");	// 描述模块的介绍信息
MODULE_ALIAS("s5pv210_button");					// 描述模块的别名信息

 

四、应用程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <string.h>

#define X210_KEY			"/dev/input/event2"


int main(void)
{
	int fd = -1, ret = -1;
	struct input_event ev;
	
	fd = open(X210_KEY, O_RDONLY);
	if (fd < 0)
	{
		perror("open");
		return -1;
	}
	
	while (1)
	{
		memset(&ev, 0, sizeof(struct input_event));
		ret = read(fd, &ev, sizeof(struct input_event));
		if (ret != sizeof(struct input_event))
		{
			perror("read");
			close(fd);
			return -1;
		}
		
		printf("-------------------------n");
		printf("type: %hdn", ev.type);
		printf("code: %hdn", ev.code);
		printf("value: %dn", ev.value);
		printf("n");
	}
	
	close(fd);
	
	return 0;
}

 

最后

以上就是平常康乃馨为你收集整理的input子系统的全部内容,希望文章能够帮你解决input子系统所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部