Linux INPUT 子系统

1.按键、鼠标、键盘、触摸屏等都属于输入(input)设备, Linux 内核为此专门做了一个叫做 input
子系统的框架
来处理输入事件。

2.输入设备本质上还是字符设备,只是在此基础上套上了 input 框架,用户只需要负责上报输入事件,比如按键值、坐标等信息, input 核心层负责处理这些事件。

3.input 就是输入的意思,因此 input 子系统就是管理输入的子系统,和 pinctrl、 gpio 子系统
一样,都是 Linux 内核针对某一类设备而创建的框架。

4.input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层,最终给用户空间提供可访问的设备节点。

左边就是最底层的具体设备,比如按键、 USB 键盘/鼠标等;

中间部分属于Linux 内核空间,分为驱动层、核心层和事件层;

最右边的就是用户空间,所有的输入设备以文件的形式供用户应用程序使用。

input 子系统用到了我们前面讲解的驱动分层模型,我们编写驱动程序的时候只需要关注中间的驱动层、核心层和事件层,这三个层的分工如下:
驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
事件层:主要和用户空间进行交互。

5.input_dev 注册过程:

①、使用 input_allocate_device 函数申请一个 input_dev。
②、初始化 input_dev 的事件类型以及事件值。
③、使用 input_register_device 函数向 Linux 系统注册前面初始化好的 input_dev。
④、卸载input驱动的时候需要先使用input_unregister_device函数注销掉注册的input_dev,
然后使用 input_free_device 函数释放掉前面申请的 input_dev。
 

      input_dev 注册流程
 struct input_dev *inputdev; /* input 结构体变量 */

/* 驱动入口函数 */
static int __init xxx_init(void)
{
	......
	inputdev = input_allocate_device(); /* 申请 input_dev */
	inputdev->name = "test_inputdev";   /* 设置 input_dev 名字 */

	/*********第一种设置事件和事件值的方法***********/
	__set_bit(EV_KEY, inputdev->evbit); /* 设置产生按键事件 */
	__set_bit(EV_REP, inputdev->evbit); /* 重复事件 */
	__set_bit(KEY_0, inputdev->keybit); /*设置产生哪些按键值 */
	/************************************************/

	/*********第二种设置事件和事件值的方法***********/
	keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | T_MASK(EV_REP);
	keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= T_MASK(KEY_0);
	/************************************************/

	/*********第三种设置事件和事件值的方法***********/
	keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | T_MASK(EV_REP);
	input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
	/************************************************/

	/* 注册 input_dev */
	input_register_device(inputdev);
	......
	return 0;
}

/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
	input_unregister_device(inputdev); /* 注销 input_dev */
	input_free_device(inputdev); /* 删除 input_dev */
}

6.上报输入事件

      事件上报参考代码
/* 用于按键消抖的定时器服务函数 */
void timer_function(unsigned long arg)
{
	unsigned char value;

	value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */
	if(value == 0)  /* 按下按键 */
	{ 
		/* 上报按键值 
			dev:需要上报的 input_dev。
			type: 上报的事件类型,比如 EV_KEY。
			code: 事件码,也就是我们注册的按键值,比如 KEY_0、 KEY_1 等等。
			value:事件值,比如 1 表示按键按下, 0 表示按键松开。
	     */
		input_report_key(inputdev, KEY_0, 1); /* 最后一个参数 1, 按下 */
		input_sync(inputdev); /* 同步事件 */
	} 
	else  /* 按键松开 */
	{ 
		input_report_key(inputdev, KEY_0, 0); /* 最后一个参数 0, 松开 */
		input_sync(inputdev); /* 同步事件 */
	}
}

7.驱动测试:

按下按键就会有数据输出:

 

 EV_KEY 事件值为 1, EV_SYN 事件值为0。因此第 1 行表示 EV_KEY 事件(按键事件),第 2 行表示 EV_SYN 事件(同步事件)。

code 为事件编码,也就是按键号,查看示例代码 58.1.2.4 可以, KEY_0 这个按键编号为 11,对应的十六进制为 0xb,因此第1 行表示 KEY_0 这个按键事件。

最后的 value 就是按键值,为 1 表示按下,为 0 的话表示松开。

 

8.应用层:

获取按键输入信息,那么必须借助于 input_event 结构体。所有的输入设备最终都是按照 input_event 结构体呈现给用户的,用户应用程序可以通过 input_event 来获取到具体的输入事件或相关的值。

input_event 结构体
struct input_event {
	struct timeval time;
	__u16 type;
	__u16 code;
	__s32 value;
};

time:时间,也就是此事件发生的时间。
type: 事件类型,比如 EV_KEY,表示此次事件为按键事件,此成员变量为 16 位。
code: 事件码,比如在 EV_KEY 事件中 code 就表示具体的按键码,如: KEY_0、 KEY_1等等这些按键。此成员变量为 16 位。
value: 值,比如 EV_KEY 事件中 value 就是按键值,表示按键有没有被按下,如果为 1 的话说明按键按下,如果为 0 的话说明按键没有被按下或者按键松开了。

按键驱动对应的文件就是/dev/input/eventX,(X=0,1,2,3。。。),应用程序读取/dev/input/event1来得到按键信息,也就是按键有没有被按下。

通过/dev/input/event1读到的信息是input_event结构体形式的。

使用read函数读取输入设备文件,也就是/dev/input/eventX,读取到的数据按照 input_event 结构体组织起来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值