linux设备驱动开发之input

input子系统是管理系统输入的子系统,比如鼠标、键盘、触摸屏等等。不同设备所代表的输入信息不同,对于驱动开发人员而言,不需要关心应用层的事情,通过input子系统按照既定规则上报输入事件即可。为此 input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层,最终给用户空间提供可访问的设备节点。在这里插入图片描述
系统定义主设备号头文件,input子系统主设备号13.
include/upai/linux/major.h

input核心层通过向linux系统注册字符设备。如下代码所示,注册一个input类,系统启动后,会在/sys/class/中生成一个input目录。
driver/input/input.c

struct class input_class = {
	.name		= "input",
	.devnode	= input_devnode,
};

static int __init input_init(void)
{
	int err;

	err = class_register(&input_class);
	if (err) {
		pr_err("unable to register input_dev class\n");
		return err;
	}

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

	err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
				     INPUT_MAX_CHAR_DEVICES, "input");
	if (err) {
		pr_err("unable to register char major %d", INPUT_MAJOR);
		goto fail2;
	}

	return 0;

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

input_dev 表示input设备,使用input设备通常只需要注册一个input_dev即可。
include/linux/input.h

struct input_dev {
	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;

	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];

	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
	unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
	unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
	unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
	unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
	unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
	unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
	.....
};

输入事件类型定义,根据需求事件功能注册相应事件。
include/uapi/linux/input.h

#define EV_SYN 0x00 /* 同步事件 */
#define EV_KEY 0x01 /* 按键事件 */
#define EV_REL 0x02 /* 相对坐标事件 */
#define EV_ABS 0x03 /* 绝对坐标事件 */
#define EV_MSC 0x04 /* 杂项(其他)事件 */
#define EV_SW 0x05 /* 开关事件 */
#define EV_LED 0x11 /* LED */
#define EV_SND 0x12 /* sound(声音) */
#define EV_REP 0x14 /* 重复事件 */
#define EV_FF 0x15 /* 压力事件 */
#define EV_PWR 0x16 /* 电源事件 */
#define EV_FF_STATUS 0x17 /* 压力状态事件 */

事件值
include/uapi/linux/input.h

#define KEY_RESERVED		0
#define KEY_ESC			1
#define KEY_1			2
#define KEY_2			3
#define KEY_3			4
#define KEY_4			5
#define KEY_5			6
#define KEY_6			7
#define KEY_7			8
#define KEY_8			9
#define KEY_9			10
#define KEY_0			11
#define KEY_MINUS		12
#define KEY_EQUAL		13
#define KEY_BACKSPACE		14
#define KEY_TAB			15
#define KEY_Q			16
#define KEY_W			17
#define KEY_E			18
#define KEY_R			19
#define KEY_T			20
#define KEY_Y			21
#define KEY_U			22
#define KEY_I			23
#define KEY_O			24
#define KEY_P			25
.....
.....

在编写input设备驱动,一般先申请一个input_dev结构体变量,通过如下函数申请与释放:

struct input_dev *input_allocate_device(void)
void input_free_device(struct input_dev *dev)

申请到input_dev,需要初始化设备内容,主要内容包括事件类型evbit和事件值keybit,通过如下函数注册与注销:

int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)

input设备注册流程:

  1. 使用 input_allocate_device 函数申请一个 input_dev。
  2. 初始化 input_dev 的事件类型以及事件值。
  3. 使用 input_unregister_device 函数向 Linux 系统注册前面初始化好的 input_dev。
  4. 卸载input驱动的时候需要先使用input_unregister_device函数注销掉注册的input_dev,然后使用input_free_device 函数释放掉前面申请的 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) |
BIT_MASK(EV_REP);
keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |=
BIT_MASK(KEY_0);
/************************************************/
/*********第三种设置事件和事件值的方法***********/
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) |
BIT_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 */
}

设备注册完毕后,还无法使用input设备。需要获取具体的输入值或输入事件,上报到linux内核。比如按键抖动,中断将按键值上报到linux内核,linux才能获取到正确的输入值。不同事件类型,上报函数不一样。

/**
 * input_event() - report new input event
 * @dev: device that generated the event
 * @type: type of the event(事件类型)
 * @code: event code(事件值)
 * @value: value of the event
 **/
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)

input_event原理上可以上报任何函数,danlinux相继封装了针对不同事件类型的上报函数,其本质上还是input_event.

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);
}

static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_REL, code, value);
}

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_ABS, code, value);
}

static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_FF_STATUS, code, value);
}

static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_SW, code, !!value);
}

上报结束后需要告诉linux上报结束,使用如下函数

static inline void input_sync(struct input_dev *dev)
{
	input_event(dev, EV_SYN, SYN_REPORT, 0);
}

用户层如何处理上报信息?
linux定义了一个上报事件结构体,用户层只需要处理这个结构体。
include/uapi/linux/input.h

struct input_event {
	struct timeval time;
	__u16 type;	// 事件类型
	__u16 code;	// 事件值
	__s32 value;
};

input系统按键案例(来源于正点原子):
input 驱动:
https://github.com/Jiongyu/linux_driver_example/blob/master/20_input/keyinput.c
input 应用层测试:
https://github.com/Jiongyu/linux_driver_example/blob/master/20_input/keyinputApp.c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值