Linux的Input子系统

概述

Linux中的input子系统是为各种不同的输入设备设计的,从物理角度看,它要支持不同类型的设备,从用户的角度看,希望它能屏蔽不同的设备差异,向用户提供一个统一的接口和操作方法,这就是input子系统要解决的问题。

输入子系统原理框图:
在这里插入图片描述

  • 驱动层
    负责物理设备的管理,不同的物理设备,有具体不同的硬件操作方法。
  • 核心层
    对驱动层和事件处理层进行管理,将两者匹配后建立通讯通道。
  • 事件处理层
    为应用程序获取输入信息,将输入事件封装成标准的形式提供给应用层,同时接受应用层的控制。

用户和驱动层接口

#include <linux/input.h>
struct input_event 
{
	struct timeval time;
	__u16 type;  //事件类型
	__u16 code;  //事件代码
	__s32 value; //状态
};

//以下是节选一部分事件类型宏定义
/*
 * Event types 事件类型
 */
#define EV_SYN			0x00  //同步信号,表示一次事件完成
#define EV_KEY			0x01  //表示此次事件是按键(键盘按键,鼠标按键等)
#define EV_REL			0x02  //相对值,比如鼠标
#define EV_ABS			0x03  //绝对值,比如触摸屏
#define EV_MSC			0x04  //键盘


一、 input 核心层分析


input核心层是作为一个模块被注册到系统中的,在系统启动的时候被系统自动注册的。下面是代码分析,代码在 driver/input/input.c 中。

1、 input 模块的注册:

注册输入类设备input,在目录/sys下建立了input目录,以后所有的输入类设备都出现在这个目录中。下面是 input_init 函数的原型。

static int __init input_init(void)
{
    err = class_register(&input_class); //设备类的注册,建立目录/sys/input
    err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备
}

函数 class_register(&input_class)

这个函数是input类的注册,下面是参数分析。

  • 参数 input_class
struct class input_class = 
{
	.name		= "input",  /* 这就是创建的目录:/sys/input */
	.devnode	= input_devnode,
};

函数 register_chrdev(INPUT_MAJOR, “input”, &input_fops)

这个函数是字符设备的注册,输入类设备作为字符设备,且统一使用主设备号是13,不同的输入类设备使用次设备号区分,下面是参数分析。

  • 参数 input_fops 字符设备操作函数
static const struct file_operations input_fops = 
{
	.owner = THIS_MODULE,
	.open = input_open_file,
};

这里的 input_open_file 不是最终的操作硬件的函数,在这个函数中通过一个表寻找到那个具体的函数(硬件操作中实现的那个函数),然后去执行,如下所示。

static int input_open_file(struct inode *inode, struct file *file)
{
    ......
    
   /* No load-on-demand here? */
	handler = input_table[iminor(inode) >> 5];
	if (handler)
		new_fops = fops_get(handler->fops);//根据input_table表,得到具体的操作函数

    ......
    
    err = new_fops->open(inode, file);//然后才打开操作硬件的函数

}

####################################################################################

2、设备驱动层接口

– 初始化函数 input_allocate_device

给具体的设备分配内存,同时初始化成员。这里的初始化是通用性的内容的设置,详细的特有的设备的数据初始化在后面的函数中进行设置。

struct input_dev *input_allocate_device(void)
{
	struct input_dev *dev;
	dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
	if (dev) 
	{
		dev->dev.type = &input_dev_type;
		dev->dev.class = &input_class;
		device_initialize(&dev->dev);
		mutex_init(&dev->mutex);
		spin_lock_init(&dev->event_lock);
		INIT_LIST_HEAD(&dev->h_list);
		INIT_LIST_HEAD(&dev->node);
		__module_get(THIS_MODULE);
	}
	return dev;
}
  • input_dev_type结构体
static struct device_type input_dev_type = 
{
    .groups        = input_dev_attr_groups, //设备属性的初始化
    .release       = input_dev_release,     //设备释放函数
    .uevent        = input_dev_uevent,      //设备所支持的事件
#ifdef CONFIG_PM
    .pm            = &input_dev_pm_ops,     //电源管理
#endif
};

  • input_class 结构体

同上面使用的是同一个结构体

struct class input_class = 
{
	.name		= "input",  /* 这就是创建的目录:/sys/input */
	.devnode	= input_devnode,
};
– 设置函数 input_set_capability

对设备的具体功能进行设置,需要向应用层上报具体的信息设置。有多少种设备能力就调用多少次函数进行设置。

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
	switch (type) {
	case EV_KEY:
		__set_bit(code, dev->keybit);
		break;
	case EV_REL:
		__set_bit(code, dev->relbit);
		break;
	case EV_ABS:
		__set_bit(code, dev->absbit);
		break;
	case EV_MSC:
		__set_bit(code, dev->mscbit);
		break;
	case EV_SW:
		__set_bit(code, dev->swbit);
		break;
	case EV_LED:
		__set_bit(code, dev->ledbit);
		break;
	case EV_SND:
		__set_bit(code, dev->sndbit);
		break;
	case EV_FF:
		__set_bit(code, dev->ffbit);
		break;
	case EV_PWR:
		/* do nothing */
		break;
	default:
		printk(KERN_ERR
			"input_set_capability: unknown type %u (code %u)\n",
			type, code);
		dump_stack();
		return;
	}
	__set_bit(type, dev->evbit);
}
– 注册输入设备

输入设备的注册,设备添加到设备链表中,找到和当前设备匹配的那个事件响应函数handler,进行挂接。

int input_register_device(struct input_dev *dev)
{
   ......
   dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);///sys/input目录下具体设备文件名字
   error = device_add(&dev->dev);//设备添加
   ......
   //链表管理:输入设备的主设备号13,所以所有的输入设备是通过一个链表管理的。
   list_add_tail(&dev->node, &input_dev_list);//添加设备到input设备的管理链表中去
   //这里注册了很多输入设备的事件处理函数。遍历链表,匹配硬件对应的具体的事件类型handler,即事件处理层。
   list_for_each_entry(handler, &input_handler_list, node)//这是宏定义for
		input_attach_handler(dev, handler);//匹配和连接对应的handler,对应具体的设备文件

}

设备和事件处理层handler的匹配和连接函数 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
    ......
    error = handler->connect(handler, dev, id);//连接
    ......
}




二、事件驱动层分析

– 注册 handler

/driver/input/evdev.c 这个文件包括了几乎所有设备的响应函数(鼠标、键盘、触摸屏等)。它作为一个模块被注册到系统中的。


事件响应函数注册

通用事件驱动注册(系统启动时被当做模块注册),在/driver/input/ evdev.c 文件中

module_init(evdev_init); //注册模块

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

结构体 input_handler 原型

struct input_handler 
{
	void *private;//事件处理程序的私有数据
	void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); //函数指针,处理事件
	bool (*match)(struct input_handler *handler, struct input_dev *dev);//进行匹配的函数
	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;//事件驱动处理程序的文件操作集合(字符设备驱动函数集合)
	int minor;
	const char *name;
	const struct input_device_id *id_table;//输入设备ID表,通过这个表匹配它的设备
	struct list_head	h_list;
	struct list_head	node;
};

下面对结构体 input_handler 原型进行分析:


  • event事件响应函数。加工数据,把硬件层传递上来的数据进行封装,给应用层使用。即内核数据拷贝到应用层,等待应用读取。
  • connect 当驱动和事件匹配上了,就使用本函数进行连接。
  • match 函数在这里没有用,在核心层已经有这个函数了。
  • *file_operations fops 应用层读写等函数对应的底层读写实际操作的函数集合。是字符设备原理。
  • *input_device_id id_table 系统以及注册了的所有响应设备的集合。
  • h_list node 链表,挂接device 和handler

初始化后的为 input_handler  evdev.c对应的驱动响应函数定义的结构体,通用响应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,//空,可匹配所有的设备
};

函数 evdev_connect粗略分析

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
    struct evdev *evdev;
    
    //从这个表中找出一个空的位置
	for (minor = 0; minor < EVDEV_MINORS; minor++)
		if (!evdev_table[minor])
			break;
	if (minor == EVDEV_MINORS) //如果minor已经等于32了,说明没有空间再容纳这个设备了
	{
		printk(KERN_ERR "evdev: no more free evdev devices\n");
		return -ENFILE;
	}
    evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//申请空间,后面对handle管理项目进行初始化。
    dev_set_name(&evdev->dev, "event%d", minor);//对设备文件的命名 event0 event1这样的命名。
   
    //下面是设备的注册,它放到这里了
    ......
    device_initialize(&evdev->dev);
    ......
    error = device_add(&evdev->dev);
    ......
}

到这里,我们一直没有发现设备的注册,实际上,device的注册就再这个函数中。原型的设备注册函数是:

int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}

可见本文中的设备注册被拆分开来了。


初始化后的 **file_operations ** 函数实例 evdev_fops

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

#######################################################################

对事件处理handler的管理 input_register_handle

这个函数在handler注册的时候被调用。(上面的evdev_handler 结构体成员函数中的evdev_connect调用input_register_handle)。handle管理着所有被注册到系统的handler。

int input_register_handle(struct input_handle *handle)
{
	struct input_handler *handler = handle->handler;
	struct input_dev *dev = handle->dev;
	int error;
	/*
	 * We take dev->mutex here to prevent race with
	 * input_release_device().
	 */
	error = mutex_lock_interruptible(&dev->mutex);
	if (error)
		return error;
	/*
	 * Filters go to the head of the list, normal handlers
	 * to the tail.
	 */
	if (handler->filter)
		list_add_rcu(&handle->d_node, &dev->h_list);
	else
		list_add_tail_rcu(&handle->d_node, &dev->h_list);
	mutex_unlock(&dev->mutex);
	/*
	 * Since we are supposed to be called from ->connect()
	 * which is mutually exclusive with ->disconnect()
	 * we can't be racing with input_unregister_handle()
	 * and so separate lock is not needed here.
	 */
	list_add_tail_rcu(&handle->h_node, &handler->h_list);

	if (handler->start)
		handler->start(handle);

	return 0;
}
struct input_handle 
{
	void *private;
	int open;
	const char *name;
	struct input_dev *dev; //设备,和下面的响应函数挂接
	struct input_handler *handler; //响应函数
	struct list_head	d_node;
	struct list_head	h_node;
};

input_handler evdev_handler 中的evdev_event分析

static void evdev_event(struct input_handle *handle,	unsigned int type, unsigned int code, int value)
{
	struct evdev *evdev = handle->private;
	struct evdev_client *client;
	struct input_event event;
	struct timespec ts;

    //下面是对struct input_event 结构体进行数据的封装。 struct input_event 是报告给应用的数据包格式。
   
	ktime_get_ts(&ts);
	event.time.tv_sec = ts.tv_sec;
	event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
	event.type = type;
	event.code = code;
	event.value = value;
	
    client = rcu_dereference(evdev->grab);
	if (client)
		evdev_pass_event(client, &event);//数据信息只通知给一个handler,因为只匹配了一个handler。
	else //下面是一个设备匹配了两个handler,就通知多个。
		list_for_each_entry_rcu(client, &evdev->client_list, node)
			evdev_pass_event(client, &event);//这里异步通知等方法通知应用层

	rcu_read_unlock();
	wake_up_interruptible(&evdev->wait);
}

struct input_event 原型:

struct input_event 
{
	struct timeval time;
	__u16 type;
	__u16 code;
	__s32 value;
};
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值