Linux 驱动开发 四十八:Linux INPUT 子系统实验

一、input 子系统简介

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

比如按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可。为此 input 子系统分为 input 驱动层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点,input 子系统框架如图如下所示:
Linux 驱动开发 四十八:Linux INPUT 子系统实验
图中左边就是最底层的具体设备,比如按键、USB 键盘/鼠标等,中间部分属于Linux 内核空间,分为驱动层核心层事件层,最右边的就是用户空间,所有的输入设备以文件的形式供用户应用程序使用

编写驱动程序的时候只需要关注中间的驱动层、核心层和事件层,这三个层的分工如下:

  • 驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。
  • 核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。
  • 事件层:主要和用户空间进行交互。

二、input 驱动编写流程

input 核心层会向 Linux 内核注册一个字符设备,drivers/input/input.c 这个文件就是 input 输入子系统的核心层源码。部分源码内容如下:

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

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

static void __exit input_exit(void)
{
	input_proc_exit();
	unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0),
				 INPUT_MAX_CHAR_DEVICES);
	class_unregister(&input_class);
}

subsys_initcall(input_init);
module_exit(input_exit);

register_chrdev_region 函数作用为注册类。

#define INPUT_MAJOR		13
#define INPUT_MAX_CHAR_DEVICES		1024

err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
			     INPUT_MAX_CHAR_DEVICES, "input");

展开后,源码如下:
err = register_chrdev_region(MKDEV(13, 0),1024,"input");

注册一个 input 类,这样系统启动以后就会在/sys/class 目录下有一个 input 子目录,如下图所示:
Linux 驱动开发 四十八:Linux INPUT 子系统实验
input 子系统的所有设备主设备号都为 13,我们在使用 input 子系统处理输入设备的时候就不需要去注册字符设备了,我们只需要向系统注册一个 input_device 即可。

三、注册 input_dev

在使用 input 子系统的时候我们只需要注册一个 input设备即可,input_dev结构体表示 input 设备,此结构体定义在 include/linux/input.h 文件中,定义如下:

/**
 * struct input_dev - represents an input device 表示输入设备
 * @name: name of the device 设备名称
 * @phys: physical path to the device in the system hierarchy 设备在系统中的路径
 * @uniq: unique identification code for the device (if device has it) 设备的唯一标识码(如果设备有)
 * @id: id of the device (struct input_id) 设备Id (struct input Id)
 * @propbit: bitmap of device properties and quirks 设备属性位图
 * @evbit: bitmap of types of events supported by the device (EV_KEY,
 *	EV_REL, etc.) 设备支持的事件类型的位图
 * @keybit: bitmap of keys/buttons this device has 含有按键/按钮设备的位图
 * @relbit: bitmap of relative axes for the device 设备相对轴的位图
 * @absbit: bitmap of absolute axes for the device 设备的绝对轴位图
 * @mscbit: bitmap of miscellaneous events supported by the device 设备支持的各种事件的位图
 * @ledbit: bitmap of leds present on the device 设备上存在的 LED 的位图
 * @sndbit: bitmap of sound effects supported by the device 设备支持的声音效果位图
 * @ffbit: bitmap of force feedback effects supported by the device 设备支持的力反馈效果位图
 * @swbit: bitmap of switches present on the device 设备上显示的开关位图
 * @hint_events_per_packet: average number of events generated by the
 *	device in a packet (between EV_SYN/SYN_REPORT events). Used by
 *	event handlers to estimate size of the buffer needed to hold
 *	events.
 * 设备在数据包中生成的平均事件数(EV_SYN/SYN_REPORT 事件之间)。
 * 事件处理程序用于估计保存事件所需的缓冲区大小。
 * @keycodemax: size of keycode table 键码表的大小
 * @keycodesize: size of elements in keycode table 键码表中元素的大小
 * @keycode: map of scancodes to keycodes for this device 此设备的扫描码到键码的映射
 * @getkeycode: optional legacy method to retrieve current keymap. 用于检索当前键盘映射的可选旧方法
 * @setkeycode: optional method to alter current keymap, used to implement
 *	sparse keymaps. If not supplied default mechanism will be used.
 *	The method is being called while holding event_lock and thus must
 *	not sleep
 * 可选方法改变当前键映射,用于实现稀疏键映射。如果没有提供,将使用默认机制。
 * 在按住event_lock时调用该方法,因此不得休眠。
 * @ff: force feedback structure associated with the device if device
 *	supports force feedback effects 如果设备支持力反馈效果,则与设备关联的力反馈结构
 * @repeat_key: stores key code of the last key pressed; used to implement
 *	software autorepeat 存储最后一次按下的键码;用于实现软件自动重复
 * @timer: timer for software autorepeat 用于软件自动重复的计时器
 * @rep: current values for autorepeat parameters (delay, rate) 自动重复参数的当前值
 * @mt: pointer to multitouch state 指向多点触控状态
 * @absinfo: array of &struct input_absinfo elements holding information
 *	about absolute axes (current value, min, max, flat, fuzz,
 *	resolution) &struct input_absinfo元素数组,保存有关绝对轴的信息(当前值、最小值、最大值、平坦、模糊、分辨率)
 * @key: reflects current state of device's keys/buttons 反映设备按键/按钮的当前状态
 * @led: reflects current state of device's LEDs 反映设备 LED 的当前状态
 * @snd: reflects current state of sound effects 反映声音效果的当前状态
 * @sw: reflects current state of device's switches 反映设备交换机的当前状态
 * @open: this method is called when the very first user calls
 *	input_open_device(). The driver must prepare the device
 *	to start generating events (start polling thread,
 *	request an IRQ, submit URB, etc.)
 * 当第一个用户调用 input_open_device() 时调用此方法。
 * 驱动程序必须准备设备以开始生成事件(启动轮询线程、请求 IRQ、提交 URB 等)。
 * @close: this method is called when the very last user calls
 *	input_close_device(). 当最后一个用户调用 input_close_device() 时调用此方法。
 * @flush: purges the device. Most commonly used to get rid of force
 *	feedback effects loaded into the device when disconnecting
 *	from it 清除设备。最常用于摆脱与设备断开连接时加载到设备中的力反馈效应
 * @event: event handler for events sent _to_ the device, like EV_LED
 *	or EV_SND. The device is expected to carry out the requested
 *	action (turn on a LED, play sound, etc.) The call is protected
 *	by @event_lock and must not sleep
 * 发送到设备的事件的事件处理程序,如EV_LED或EV_SND。
 * 设备应执行请求的操作(打开 LED、播放声音等)。通话受@event_lock保护,不得休眠。
 * @grab: input handle that currently has the device grabbed (via
 *	EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
 *	recipient for all input events coming from the device
 * 当前已抓取设备的输入句柄(通过 EVIOCGRAB ioctl)。
 * 当一个句柄抓取一个设备时,它就成为该设备所有输入事件的唯一接收者。
 * @event_lock: this spinlock is is taken when input core receives
 *	and processes a new event for the device (in input_event()).
 *	Code that accesses and/or modifies parameters of a device
 *	(such as keymap or absmin, absmax, absfuzz, etc.) after device
 *	has been registered with input core must take this lock.
 * 当输入核接收到并处理设备的新事件时(在input_event()中),会获得这个自旋锁。
 * 在设备注册到输入核后,访问或修改设备参数(如keymap或absmin, absmax, absfuzz等)的代码必须使用该锁。
 * @mutex: serializes calls to open(), close() and flush() methods 序列化调用open()、close()和flush()方法
 * @users: stores number of users (input handlers) that opened this
 *	device. It is used by input_open_device() and input_close_device()
 *	to make sure that dev->open() is only called when the first
 *	user opens device and dev->close() is called when the very
 *	last user closes the device
 * 存储打开此设备的用户(输入处理程序)的数量。
 * 它由 input_open_device() 和 input_close_device() 使用,
 * 以确保仅当第一个用户打开设备时调用 dev->open(),并在最后一个用户关闭设备时调用 dev->close()
 * @going_away: marks devices that are in a middle of unregistering and
 *	causes input_open_device*() fail with -ENODEV.
 * 标记处于注销过程中的设备,并导致input_open_device*() 失败,并显示 -ENODEV。
 * @dev: driver model's view of this device 此设备的驱动模型视图
 * @h_list: list of input handles associated with the device. When
 *	accessing the list dev->mutex must be held
 * 与设备关联的输入句柄列表。访问列表时,必须持有 dev->mutex
 * @node: used to place the device onto input_dev_list 用于将设备放在input_dev_list
 * @num_vals: number of values queued in the current frame 当前帧中排队的值数
 * @max_vals: maximum number of values queued in a frame 帧中排队的最大值数
 * @vals: array of values queued in the current frame 在当前帧中排队的值数组
 * @devres_managed: indicates that devices is managed with devres framework
 *	and needs not be explicitly unregistered or freed.
 * 表示设备使用 devres 框架进行管理,不需要显式取消注册或释放。
 */
struct input_dev {
	const char *name;	/* 设备名称 */
	const char *phys;	/* 设备在系统中的路径 */
	const char *uniq;	/* 设备唯一id */
	struct input_id id;	/* input设备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)];	/* 支持led事件 */
	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];	/* 支持声音事件 */
	unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];		/* 支持受力事件 */
	unsigned long swbit[BITS_TO_LONGS(SW_CNT)];		/* 支持开关事件 */

	unsigned int hint_events_per_packet;	/* 平均事件数 */

	unsigned int keycodemax;	/* 支持最大按键数 */
	unsigned int keycodesize;	/* 每个键值字节数 */
	void *keycode;				/* 存储按键值的数组的首地址 */

	int (*setkeycode)(struct input_dev *dev,
			  const struct input_keymap_entry *ke,
			  unsigned int *old_keycode);
	int (*getkeycode)(struct input_dev *dev,
			  struct input_keymap_entry *ke);

	struct ff_device *ff;	/* 设备关联的反馈结构,如果设备支持 */

	unsigned int repeat_key;	/* 最近一次按键值,用于连击 */
	struct timer_list timer;	/* 自动连击计时器 */

	int rep[REP_CNT];		/* 自动连击参数 */

	struct input_mt *mt;	/* 多点触控区域 */

	struct input_absinfo *absinfo;	/* 存放绝对值坐标的相关参数数组 */

	unsigned long key[BITS_TO_LONGS(KEY_CNT)];	/* 反应设备当前的案件状态 */
	unsigned long led[BITS_TO_LONGS(LED_CNT)];	/* 反应设备当前的led状态 */
	unsigned long snd[BITS_TO_LONGS(SND_CNT)];	/* 反应设备当前的声音状态 */
	unsigned long sw[BITS_TO_LONGS(SW_CNT)];	/* 反应设备当前的开关状态 */

	int (*open)(struct input_dev *dev);		/* 第一次打开设备时调用,初始化设备用 */
	void (*close)(struct input_dev *dev);	/* 最后一个应用程序释放设备事件,关闭设备 */
	int (*flush)(struct input_dev *dev, struct file *file);	/* 用于处理传递设备的事件 */
    /* 事件处理函数,主要是接收用户下发的命令,如点亮led */
	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

	struct input_handle __rcu *grab;	/* 当前占有设备的input_handle */

	spinlock_t event_lock;	/* 事件锁 */
	struct mutex mutex;		/* 互斥体 */

	unsigned int users;		/* 打开该设备的用户数量 */
	bool going_away;		/* 标记正在销毁的设备 */

	struct device dev;		/* 一般设备 */

	struct list_head	h_list;	/* 设备所支持的input handle */
	struct list_head	node;	/* 用于将此input_dev连接到input_dev_list */

	unsigned int num_vals;		/* 当前帧中排队的值数 */
	unsigned int max_vals;		/* 队列最大的帧数 */
	struct input_value *vals;	/* 当前帧中排队的数组 */

	bool devres_managed;		/* 表示设备被devres 框架管理,不需要明确取消和释放 */
};

evbit 表示输入事件类型,可选的事件类型定义在 include/uapi/linux/input.h 文件中,事件类型如下:

/*
 * Event types
 */

#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		/* 压力状态事件 */
#define EV_MAX			0x1f
#define EV_CNT			(EV_MAX+1)

本章我们要使用到按键,那么就需要注册 EV_KEY事件,如果要使用连按功能的话还需要注册 EV_REP 事件。

本章要使用按键事件,因此要用到 keybit,keybit 就是按键事件使用的位图,Linux 内核定义了很多按键值,这些按键值定义在 include/uapi/linux/input.h 文件中,按键值如下:

/*
 * Keys and buttons 按键和按钮
 *
 * Most of the keys/buttons are modeled after USB HUT 1.12 大多数按键/按钮都以 USB HUT 1.12 为蓝本
 * (see http://www.usb.org/developers/hidpage).
 * Abbreviations in the comments: 评论中的缩略语:
 * AC - Application Control	应用程序控制
 * AL - Application Launch Button 应用程序启动按钮
 * SC - System Control 系统控制
 */

#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
#define KEY_LEFTBRACE		26
#define KEY_RIGHTBRACE		27
#define KEY_ENTER		28
#define KEY_LEFTCTRL		29
#define KEY_A			30
#define KEY_S			31
#define KEY_D			32
#define KEY_F			33
#define KEY_G			34
#define KEY_H			35
#define KEY_J			36
#define KEY_K			37
#define KEY_L			38
#define KEY_SEMICOLON		39
#define KEY_APOSTROPHE		40
#define KEY_GRAVE		41
#define KEY_LEFTSHIFT		42
#define KEY_BACKSLASH		43
#define KEY_Z			44
#define KEY_X			45
#define KEY_C			46
#define KEY_V			47
#define KEY_B			48
#define KEY_N			49
#define KEY_M			50
#define KEY_COMMA		51
#define KEY_DOT			52
#define KEY_SLASH		53
#define KEY_RIGHTSHIFT		54
#define KEY_KPASTERISK		55
#define KEY_LEFTALT		56
#define KEY_SPACE		57
#define KEY_CAPSLOCK		58
#define KEY_F1			59
#define KEY_F2			60
#define KEY_F3			61
#define KEY_F4			62
#define KEY_F5			63
#define KEY_F6			64
#define KEY_F7			65
#define KEY_F8			66
#define KEY_F9			67
#define KEY_F10			68
#define KEY_NUMLOCK		69
#define KEY_SCROLLLOCK		70
#define KEY_KP7			71
#define KEY_KP8			72
#define KEY_KP9			73
#define KEY_KPMINUS		74
#define KEY_KP4			75
#define KEY_KP5			76
#define KEY_KP6			77
#define KEY_KPPLUS		78
#define KEY_KP1			79
#define KEY_KP2			80
#define KEY_KP3			81
#define KEY_KP0			82
#define KEY_KPDOT		83

#define KEY_ZENKAKUHANKAKU	85
#define KEY_102ND		86
#define KEY_F11			87
#define KEY_F12			88
#define KEY_RO			89
#define KEY_KATAKANA		90
#define KEY_HIRAGANA		91
#define KEY_HENKAN		92
#define KEY_KATAKANAHIRAGANA	93
#define KEY_MUHENKAN		94
#define KEY_KPJPCOMMA		95
#define KEY_KPENTER		96
#define KEY_RIGHTCTRL		97
#define KEY_KPSLASH		98
#define KEY_SYSRQ		99
#define KEY_RIGHTALT		100
#define KEY_LINEFEED		101
#define KEY_HOME		102
#define KEY_UP			103
#define KEY_PAGEUP		104
#define KEY_LEFT		105
#define KEY_RIGHT		106
#define KEY_END			107
#define KEY_DOWN		108
#define KEY_PAGEDOWN		109
#define KEY_INSERT		110
#define KEY_DELETE		111
#define KEY_MACRO		112
#define KEY_MUTE		113
#define KEY_VOLUMEDOWN		114
#define KEY_VOLUMEUP		115
#define KEY_POWER		116	/* SC System Power Down */
#define KEY_KPEQUAL		117
#define KEY_KPPLUSMINUS		118
#define KEY_PAUSE		119
#define KEY_SCALE		120	/* AL Compiz Scale (Expose) */

#define KEY_KPCOMMA		121
#define KEY_HANGEUL		122
#define KEY_HANGUEL		KEY_HANGEUL
#define KEY_HANJA		123
#define KEY_YEN			124
#define KEY_LEFTMETA		125
#define KEY_RIGHTMETA		126
#define KEY_COMPOSE		127

#define KEY_STOP		128	/* AC Stop */
#define KEY_AGAIN		129
#define KEY_PROPS		130	/* AC Properties */
#define KEY_UNDO		131	/* AC Undo */
#define KEY_FRONT		132
#define KEY_COPY		133	/* AC Copy */
#define KEY_OPEN		134	/* AC Open */
#define KEY_PASTE		135	/* AC Paste */
#define KEY_FIND		136	/* AC Search */
#define KEY_CUT			137	/* AC Cut */
#define KEY_HELP		138	/* AL Integrated Help Center */
#define KEY_MENU		139	/* Menu (show menu) */
#define KEY_CALC		140	/* AL Calculator */
#define KEY_SETUP		141
#define KEY_SLEEP		142	/* SC System Sleep */
#define KEY_WAKEUP		143	/* System Wake Up */
#define KEY_FILE		144	/* AL Local Machine Browser */
#define KEY_SENDFILE		145
#define KEY_DELETEFILE		146
#define KEY_XFER		147
#define KEY_PROG1		148
#define KEY_PROG2		149
#define KEY_WWW			150	/* AL Internet Browser */
#define KEY_MSDOS		151
#define KEY_COFFEE		152	/* AL Terminal Lock/Screensaver */
#define KEY_SCREENLOCK		KEY_COFFEE
#define KEY_ROTATE_DISPLAY	153	/* Display orientation for e.g. tablets */
#define KEY_DIRECTION		KEY_ROTATE_DISPLAY
#define KEY_CYCLEWINDOWS	154
#define KEY_MAIL		155
#define KEY_BOOKMARKS		156	/* AC Bookmarks */
#define KEY_COMPUTER		157
#define KEY_BACK		158	/* AC Back */
#define KEY_FORWARD		159	/* AC Forward */
#define KEY_CLOSECD		160
#define KEY_EJECTCD		161
#define KEY_EJECTCLOSECD	162
#define KEY_NEXTSONG		163
#define KEY_PLAYPAUSE		164
#define KEY_PREVIOUSSONG	165
#define KEY_STOPCD		166
#define KEY_RECORD		167
#define KEY_REWIND		168
#define KEY_PHONE		169	/* Media Select Telephone */
#define KEY_ISO			170
#define KEY_CONFIG		171	/* AL Consumer Control Configuration */
#define KEY_HOMEPAGE		172	/* AC Home */
#define KEY_REFRESH		173	/* AC Refresh */
#define KEY_EXIT		174	/* AC Exit */
#define KEY_MOVE		175
#define KEY_EDIT		176
#define KEY_SCROLLUP		177
#define KEY_SCROLLDOWN		178
#define KEY_KPLEFTPAREN		179
#define KEY_KPRIGHTPAREN	180
#define KEY_NEW			181	/* AC New */
#define KEY_REDO		182	/* AC Redo/Repeat */

#define KEY_F13			183
#define KEY_F14			184
#define KEY_F15			185
#define KEY_F16			186
#define KEY_F17			187
#define KEY_F18			188
#define KEY_F19			189
#define KEY_F20			190
#define KEY_F21			191
#define KEY_F22			192
#define KEY_F23			193
#define KEY_F24			194

#define KEY_PLAYCD		200
#define KEY_PAUSECD		201
#define KEY_PROG3		202
#define KEY_PROG4		203
#define KEY_DASHBOARD		204	/* AL Dashboard */
#define KEY_SUSPEND		205
#define KEY_CLOSE		206	/* AC Close */
#define KEY_PLAY		207
#define KEY_FASTFORWARD		208
#define KEY_BASSBOOST		209
#define KEY_PRINT		210	/* AC Print */
#define KEY_HP			211
#define KEY_CAMERA		212
#define KEY_SOUND		213
#define KEY_QUESTION		214
#define KEY_EMAIL		215
#define KEY_CHAT		216
#define KEY_SEARCH		217
#define KEY_CONNECT		218
#define KEY_FINANCE		219	/* AL Checkbook/Finance */
#define KEY_SPORT		220
#define KEY_SHOP		221
#define KEY_ALTERASE		222
#define KEY_CANCEL		223	/* AC Cancel */
#define KEY_BRIGHTNESSDOWN	224
#define KEY_BRIGHTNESSUP	225
#define KEY_MEDIA		226

#define KEY_SWITCHVIDEOMODE	227	/* Cycle between available video
					   outputs (Monitor/LCD/TV-out/etc) */
#define KEY_KBDILLUMTOGGLE	228
#define KEY_KBDILLUMDOWN	229
#define KEY_KBDILLUMUP		230

#define KEY_SEND		231	/* AC Send */
#define KEY_REPLY		232	/* AC Reply */
#define KEY_FORWARDMAIL		233	/* AC Forward Msg */
#define KEY_SAVE		234	/* AC Save */
#define KEY_DOCUMENTS		235

#define KEY_BATTERY		236

#define KEY_BLUETOOTH		237
#define KEY_WLAN		238
#define KEY_UWB			239

#define KEY_UNKNOWN		240

#define KEY_VIDEO_NEXT		241	/* drive next video source */
#define KEY_VIDEO_PREV		242	/* drive previous video source */
#define KEY_BRIGHTNESS_CYCLE	243	/* brightness up, after max is min */
#define KEY_BRIGHTNESS_AUTO	244	/* Set Auto Brightness: manual
					  brightness control is off,
					  rely on ambient */
#define KEY_BRIGHTNESS_ZERO	KEY_BRIGHTNESS_AUTO
#define KEY_DISPLAY_OFF		245	/* display device to off state */

#define KEY_WWAN		246	/* Wireless WAN (LTE, UMTS, GSM, etc.) */
#define KEY_WIMAX		KEY_WWAN
#define KEY_RFKILL		247	/* Key that controls all radios */

#define KEY_MICMUTE		248	/* Mute / unmute the microphone */

/* Code 255 is reserved for special needs of AT keyboard driver */

#define BTN_MISC		0x100
#define BTN_0			0x100
#define BTN_1			0x101
#define BTN_2			0x102
#define BTN_3			0x103
#define BTN_4			0x104
#define BTN_5			0x105
#define BTN_6			0x106
#define BTN_7			0x107
#define BTN_8			0x108
#define BTN_9			0x109

#define BTN_MOUSE		0x110
#define BTN_LEFT		0x110
#define BTN_RIGHT		0x111
#define BTN_MIDDLE		0x112
#define BTN_SIDE		0x113
#define BTN_EXTRA		0x114
#define BTN_FORWARD		0x115
#define BTN_BACK		0x116
#define BTN_TASK		0x117

#define BTN_JOYSTICK		0x120
#define BTN_TRIGGER		0x120
#define BTN_THUMB		0x121
#define BTN_THUMB2		0x122
#define BTN_TOP			0x123
#define BTN_TOP2		0x124
#define BTN_PINKIE		0x125
#define BTN_BASE		0x126
#define BTN_BASE2		0x127
#define BTN_BASE3		0x128
#define BTN_BASE4		0x129
#define BTN_BASE5		0x12a
#define BTN_BASE6		0x12b
#define BTN_DEAD		0x12f

#define BTN_GAMEPAD		0x130
#define BTN_SOUTH		0x130
#define BTN_A			BTN_SOUTH
#define BTN_EAST		0x131
#define BTN_B			BTN_EAST
#define BTN_C			0x132
#define BTN_NORTH		0x133
#define BTN_X			BTN_NORTH
#define BTN_WEST		0x134
#define BTN_Y			BTN_WEST
#define BTN_Z			0x135
#define BTN_TL			0x136
#define BTN_TR			0x137
#define BTN_TL2			0x138
#define BTN_TR2			0x139
#define BTN_SELECT		0x13a
#define BTN_START		0x13b
#define BTN_MODE		0x13c
#define BTN_THUMBL		0x13d
#define BTN_THUMBR		0x13e

#define BTN_DIGI		0x140
#define BTN_TOOL_PEN		0x140
#define BTN_TOOL_RUBBER		0x141
#define BTN_TOOL_BRUSH		0x142
#define BTN_TOOL_PENCIL		0x143
#define BTN_TOOL_AIRBRUSH	0x144
#define BTN_TOOL_FINGER		0x145
#define BTN_TOOL_MOUSE		0x146
#define BTN_TOOL_LENS		0x147
#define BTN_TOOL_QUINTTAP	0x148	/* Five fingers on trackpad */
#define BTN_TOUCH		0x14a
#define BTN_STYLUS		0x14b
#define BTN_STYLUS2		0x14c
#define BTN_TOOL_DOUBLETAP	0x14d
#define BTN_TOOL_TRIPLETAP	0x14e
#define BTN_TOOL_QUADTAP	0x14f	/* Four fingers on trackpad */

#define BTN_WHEEL		0x150
#define BTN_GEAR_DOWN		0x150
#define BTN_GEAR_UP		0x151

#define KEY_OK			0x160
#define KEY_SELECT		0x161
#define KEY_GOTO		0x162
#define KEY_CLEAR		0x163
#define KEY_POWER2		0x164
#define KEY_OPTION		0x165
#define KEY_INFO		0x166	/* AL OEM Features/Tips/Tutorial */
#define KEY_TIME		0x167
#define KEY_VENDOR		0x168
#define KEY_ARCHIVE		0x169
#define KEY_PROGRAM		0x16a	/* Media Select Program Guide */
#define KEY_CHANNEL		0x16b
#define KEY_FAVORITES		0x16c
#define KEY_EPG			0x16d
#define KEY_PVR			0x16e	/* Media Select Home */
#define KEY_MHP			0x16f
#define KEY_LANGUAGE		0x170
#define KEY_TITLE		0x171
#define KEY_SUBTITLE		0x172
#define KEY_ANGLE		0x173
#define KEY_ZOOM		0x174
#define KEY_MODE		0x175
#define KEY_KEYBOARD		0x176
#define KEY_SCREEN		0x177
#define KEY_PC			0x178	/* Media Select Computer */
#define KEY_TV			0x179	/* Media Select TV */
#define KEY_TV2			0x17a	/* Media Select Cable */
#define KEY_VCR			0x17b	/* Media Select VCR */
#define KEY_VCR2		0x17c	/* VCR Plus */
#define KEY_SAT			0x17d	/* Media Select Satellite */
#define KEY_SAT2		0x17e
#define KEY_CD			0x17f	/* Media Select CD */
#define KEY_TAPE		0x180	/* Media Select Tape */
#define KEY_RADIO		0x181
#define KEY_TUNER		0x182	/* Media Select Tuner */
#define KEY_PLAYER		0x183
#define KEY_TEXT		0x184
#define KEY_DVD			0x185	/* Media Select DVD */
#define KEY_AUX			0x186
#define KEY_MP3			0x187
#define KEY_AUDIO		0x188	/* AL Audio Browser */
#define KEY_VIDEO		0x189	/* AL Movie Browser */
#define KEY_DIRECTORY		0x18a
#define KEY_LIST		0x18b
#define KEY_MEMO		0x18c	/* Media Select Messages */
#define KEY_CALENDAR		0x18d
#define KEY_RED			0x18e
#define KEY_GREEN		0x18f
#define KEY_YELLOW		0x190
#define KEY_BLUE		0x191
#define KEY_CHANNELUP		0x192	/* Channel Increment */
#define KEY_CHANNELDOWN		0x193	/* Channel Decrement */
#define KEY_FIRST		0x194
#define KEY_LAST		0x195	/* Recall Last */
#define KEY_AB			0x196
#define KEY_NEXT		0x197
#define KEY_RESTART		0x198
#define KEY_SLOW		0x199
#define KEY_SHUFFLE		0x19a
#define KEY_BREAK		0x19b
#define KEY_PREVIOUS		0x19c
#define KEY_DIGITS		0x19d
#define KEY_TEEN		0x19e
#define KEY_TWEN		0x19f
#define KEY_VIDEOPHONE		0x1a0	/* Media Select Video Phone */
#define KEY_GAMES		0x1a1	/* Media Select Games */
#define KEY_ZOOMIN		0x1a2	/* AC Zoom In */
#define KEY_ZOOMOUT		0x1a3	/* AC Zoom Out */
#define KEY_ZOOMRESET		0x1a4	/* AC Zoom */
#define KEY_WORDPROCESSOR	0x1a5	/* AL Word Processor */
#define KEY_EDITOR		0x1a6	/* AL Text Editor */
#define KEY_SPREADSHEET		0x1a7	/* AL Spreadsheet */
#define KEY_GRAPHICSEDITOR	0x1a8	/* AL Graphics Editor */
#define KEY_PRESENTATION	0x1a9	/* AL Presentation App */
#define KEY_DATABASE		0x1aa	/* AL Database App */
#define KEY_NEWS		0x1ab	/* AL Newsreader */
#define KEY_VOICEMAIL		0x1ac	/* AL Voicemail */
#define KEY_ADDRESSBOOK		0x1ad	/* AL Contacts/Address Book */
#define KEY_MESSENGER		0x1ae	/* AL Instant Messaging */
#define KEY_DISPLAYTOGGLE	0x1af	/* Turn display (LCD) on and off */
#define KEY_BRIGHTNESS_TOGGLE	KEY_DISPLAYTOGGLE
#define KEY_SPELLCHECK		0x1b0   /* AL Spell Check */
#define KEY_LOGOFF		0x1b1   /* AL Logoff */

#define KEY_DOLLAR		0x1b2
#define KEY_EURO		0x1b3

#define KEY_FRAMEBACK		0x1b4	/* Consumer - transport controls */
#define KEY_FRAMEFORWARD	0x1b5
#define KEY_CONTEXT_MENU	0x1b6	/* GenDesc - system context menu */
#define KEY_MEDIA_REPEAT	0x1b7	/* Consumer - transport control */
#define KEY_10CHANNELSUP	0x1b8	/* 10 channels up (10+) */
#define KEY_10CHANNELSDOWN	0x1b9	/* 10 channels down (10-) */
#define KEY_IMAGES		0x1ba	/* AL Image Browser */

#define KEY_DEL_EOL		0x1c0
#define KEY_DEL_EOS		0x1c1
#define KEY_INS_LINE		0x1c2
#define KEY_DEL_LINE		0x1c3

#define KEY_FN			0x1d0
#define KEY_FN_ESC		0x1d1
#define KEY_FN_F1		0x1d2
#define KEY_FN_F2		0x1d3
#define KEY_FN_F3		0x1d4
#define KEY_FN_F4		0x1d5
#define KEY_FN_F5		0x1d6
#define KEY_FN_F6		0x1d7
#define KEY_FN_F7		0x1d8
#define KEY_FN_F8		0x1d9
#define KEY_FN_F9		0x1da
#define KEY_FN_F10		0x1db
#define KEY_FN_F11		0x1dc
#define KEY_FN_F12		0x1dd
#define KEY_FN_1		0x1de
#define KEY_FN_2		0x1df
#define KEY_FN_D		0x1e0
#define KEY_FN_E		0x1e1
#define KEY_FN_F		0x1e2
#define KEY_FN_S		0x1e3
#define KEY_FN_B		0x1e4

#define KEY_BRL_DOT1		0x1f1
#define KEY_BRL_DOT2		0x1f2
#define KEY_BRL_DOT3		0x1f3
#define KEY_BRL_DOT4		0x1f4
#define KEY_BRL_DOT5		0x1f5
#define KEY_BRL_DOT6		0x1f6
#define KEY_BRL_DOT7		0x1f7
#define KEY_BRL_DOT8		0x1f8
#define KEY_BRL_DOT9		0x1f9
#define KEY_BRL_DOT10		0x1fa

#define KEY_NUMERIC_0		0x200	/* used by phones, remote controls, */
#define KEY_NUMERIC_1		0x201	/* and other keypads */
#define KEY_NUMERIC_2		0x202
#define KEY_NUMERIC_3		0x203
#define KEY_NUMERIC_4		0x204
#define KEY_NUMERIC_5		0x205
#define KEY_NUMERIC_6		0x206
#define KEY_NUMERIC_7		0x207
#define KEY_NUMERIC_8		0x208
#define KEY_NUMERIC_9		0x209
#define KEY_NUMERIC_STAR	0x20a
#define KEY_NUMERIC_POUND	0x20b
#define KEY_NUMERIC_A		0x20c	/* Phone key A - HUT Telephony 0xb9 */
#define KEY_NUMERIC_B		0x20d
#define KEY_NUMERIC_C		0x20e
#define KEY_NUMERIC_D		0x20f

#define KEY_CAMERA_FOCUS	0x210
#define KEY_WPS_BUTTON		0x211	/* WiFi Protected Setup key */

#define KEY_TOUCHPAD_TOGGLE	0x212	/* Request switch touchpad on or off */
#define KEY_TOUCHPAD_ON		0x213
#define KEY_TOUCHPAD_OFF	0x214

#define KEY_CAMERA_ZOOMIN	0x215
#define KEY_CAMERA_ZOOMOUT	0x216
#define KEY_CAMERA_UP		0x217
#define KEY_CAMERA_DOWN		0x218
#define KEY_CAMERA_LEFT		0x219
#define KEY_CAMERA_RIGHT	0x21a

#define KEY_ATTENDANT_ON	0x21b
#define KEY_ATTENDANT_OFF	0x21c
#define KEY_ATTENDANT_TOGGLE	0x21d	/* Attendant call on or off */
#define KEY_LIGHTS_TOGGLE	0x21e	/* Reading light on or off */

#define BTN_DPAD_UP		0x220
#define BTN_DPAD_DOWN		0x221
#define BTN_DPAD_LEFT		0x222
#define BTN_DPAD_RIGHT		0x223

#define KEY_ALS_TOGGLE		0x230	/* Ambient light sensor */

#define KEY_BUTTONCONFIG		0x240	/* AL Button Configuration */
#define KEY_TASKMANAGER		0x241	/* AL Task/Project Manager */
#define KEY_JOURNAL		0x242	/* AL Log/Journal/Timecard */
#define KEY_CONTROLPANEL		0x243	/* AL Control Panel */
#define KEY_APPSELECT		0x244	/* AL Select Task/Application */
#define KEY_SCREENSAVER		0x245	/* AL Screen Saver */
#define KEY_VOICECOMMAND		0x246	/* Listening Voice Command */

#define KEY_BRIGHTNESS_MIN		0x250	/* Set Brightness to Minimum */
#define KEY_BRIGHTNESS_MAX		0x251	/* Set Brightness to Maximum */

#define KEY_KBDINPUTASSIST_PREV		0x260
#define KEY_KBDINPUTASSIST_NEXT		0x261
#define KEY_KBDINPUTASSIST_PREVGROUP		0x262
#define KEY_KBDINPUTASSIST_NEXTGROUP		0x263
#define KEY_KBDINPUTASSIST_ACCEPT		0x264
#define KEY_KBDINPUTASSIST_CANCEL		0x265

#define BTN_TRIGGER_HAPPY		0x2c0
#define BTN_TRIGGER_HAPPY1		0x2c0
#define BTN_TRIGGER_HAPPY2		0x2c1
#define BTN_TRIGGER_HAPPY3		0x2c2
#define BTN_TRIGGER_HAPPY4		0x2c3
#define BTN_TRIGGER_HAPPY5		0x2c4
#define BTN_TRIGGER_HAPPY6		0x2c5
#define BTN_TRIGGER_HAPPY7		0x2c6
#define BTN_TRIGGER_HAPPY8		0x2c7
#define BTN_TRIGGER_HAPPY9		0x2c8
#define BTN_TRIGGER_HAPPY10		0x2c9
#define BTN_TRIGGER_HAPPY11		0x2ca
#define BTN_TRIGGER_HAPPY12		0x2cb
#define BTN_TRIGGER_HAPPY13		0x2cc
#define BTN_TRIGGER_HAPPY14		0x2cd
#define BTN_TRIGGER_HAPPY15		0x2ce
#define BTN_TRIGGER_HAPPY16		0x2cf
#define BTN_TRIGGER_HAPPY17		0x2d0
#define BTN_TRIGGER_HAPPY18		0x2d1
#define BTN_TRIGGER_HAPPY19		0x2d2
#define BTN_TRIGGER_HAPPY20		0x2d3
#define BTN_TRIGGER_HAPPY21		0x2d4
#define BTN_TRIGGER_HAPPY22		0x2d5
#define BTN_TRIGGER_HAPPY23		0x2d6
#define BTN_TRIGGER_HAPPY24		0x2d7
#define BTN_TRIGGER_HAPPY25		0x2d8
#define BTN_TRIGGER_HAPPY26		0x2d9
#define BTN_TRIGGER_HAPPY27		0x2da
#define BTN_TRIGGER_HAPPY28		0x2db
#define BTN_TRIGGER_HAPPY29		0x2dc
#define BTN_TRIGGER_HAPPY30		0x2dd
#define BTN_TRIGGER_HAPPY31		0x2de
#define BTN_TRIGGER_HAPPY32		0x2df
#define BTN_TRIGGER_HAPPY33		0x2e0
#define BTN_TRIGGER_HAPPY34		0x2e1
#define BTN_TRIGGER_HAPPY35		0x2e2
#define BTN_TRIGGER_HAPPY36		0x2e3
#define BTN_TRIGGER_HAPPY37		0x2e4
#define BTN_TRIGGER_HAPPY38		0x2e5
#define BTN_TRIGGER_HAPPY39		0x2e6
#define BTN_TRIGGER_HAPPY40		0x2e7

/* We avoid low common keys in module aliases so they don't get huge. */
#define KEY_MIN_INTERESTING	KEY_MUTE
#define KEY_MAX			0x2ff
#define KEY_CNT			(KEY_MAX+1)

我们可以将开发板上的按键值设置为以上任意一个。

四、input 子系统 API

1、input_allocate_device

/* 申请 input_dev 结构体变量
 * 
 * 参数:无
 * 返回值:申请到的 input_dev
 */
struct input_dev __must_check *input_allocate_device(void)

2、input_free_device

/* 释放 input_dev 结构体变量
 *
 * 参数:需要释放的 input_dev
 * 返回值:无
 */
void input_free_device(struct input_dev *dev);

3、input_register_device

/* 向 Linux 内核注册 input_dev 结构体变量
 * 
 * 参数:需要向内核注册的 input_dev
 * 返回值:0:input_dev 注册成功;负值:input_dev 注册失败
 */
int __must_check input_register_device(struct input_dev *)

4、input_unregister_device

/* 向 Linux 内核注销 input_dev 结构体变量
 *
 * 参数:需要向内核注册的 input_dev
 * 返回值:无
 */
void input_unregister_device(struct input_dev *)

五、上报输入事件API

1、input_event

/* 上报指定的事件以及对应的值
 *
 * 参数:
 * dev:需要上报的 input_dev
 * type: 上报的事件类型,比如 EV_KEY
 * code:事件码,也就是我们注册的按键值,比如KEY_0、KEY_1 等等
 * value:事件值,比如 1 表示按键按下,0 表示按键松开
 * 返回值:无
 */
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

2、input_report_key

/* 上报按键所事件
 * 
 * 参数:
 * dev:需要上报的 input_dev
 * code:事件码,也就是我们注册的按键值,比如KEY_0、KEY_1 等等
 * value:事件值,比如 1 表示按键按下,0 表示按键松开
 * 返回值:无
 */
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
	input_event(dev, EV_KEY, code, !!value);
}

六、input_event 结构体

/*
 * The event structure itself 事件结构本身
 */

struct input_event {
	struct timeval time;	// 时间
	__u16 type;				// 事件类型
	__u16 code;				// 事件码
	__s32 value;			// 值
};
typedef long		__kernel_long_t;
typedef __kernel_long_t	__kernel_time_t;

typedef long		__kernel_long_t;
typedef __kernel_long_t		__kernel_suseconds_t;

struct timeval {
	__kernel_time_t		tv_sec;			/* 秒 */
	__kernel_suseconds_t	tv_usec;	/* 微秒 */
};

七、input 子系统驱动编写思路

1、在驱动加载函数调用 input_allocate_device 函数申请一个 input_dev 结构体变量。

2、初始化 input_dev 结构体变量的事件类型以及事件值。

3、调用input_register_device 函数向 Linux 系统注册前面初始化好的 input_dev 结构体变量。

4、驱动加载函数中开启按键中断,在中断服务函数中调用 input_event 函数上报事件。

5、在驱动卸载函数中调用 input_unregister_device 函数注销 input_dev 结构体变量。

6、调用 input_free_device 函数释放 input_dev 结构体变量。

7、在驱动卸载函数中释放中断

八、input 子系统驱动源码编写

input 子系统实验测试使用 key 进行完成。

1、搭建驱动框架

源码:

#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/input.h"

#define KEYINPUT_NAME       "keyinput"  /* 名字 */

/* keyinput设备结构体 */
struct keyinput_dev{
	struct input_dev *inputdev;		/* input结构体 */
};

struct keyinput_dev keyinputdev;	/* key input设备 */

int input_dev_init(void)
{
    int ret;

    printk("input dev init succed!\r\n");

    /* 1、申请 input_dev 结构体变量 */
    keyinputdev.inputdev = input_allocate_device();
    if(!keyinputdev.inputdev){
        printk(KERN_ERR "button.c: Not enough memory\n");
        ret = 1;
        goto err_input_allocate_device;
    }
    /* 2、初始化 input_dev 变量 */
    keyinputdev.inputdev->name = KEYINPUT_NAME;
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); /* 设置产生按键事件 */
    input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); /* 初始化input_dev,设置产生哪些按键 */

    /* 其他注册方式 */
#if 0
    /* 方式二 */
    /* 设置产生按键事件 */
	__set_bit(EV_KEY, keyinputdev.inputdev->evbit);
    /* 重复事件,比如按下去不放开,就会一直输出信息 */
	__set_bit(EV_REP, keyinputdev.inputdev->evbit);

	/* 初始化input_dev,设置产生哪些按键 */
	__set_bit(KEY_0, keyinputdev.inputdev->keybit);
#endif

#if 0
    /* 方式三 */
    /* 设置产生按键事件 */
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 
    /* 初始化input_dev,设置产生哪些按键 */
	keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
#endif

    /* 3、注册输入设备 */
    ret = input_register_device(keyinputdev.inputdev);
    if (ret) {
        printk("register input device failed!\r\n");
        ret = 2;
        goto err_input_register_device;
    }

    return 0;
err_input_register_device:
    input_free_device(keyinputdev.inputdev);
err_input_allocate_device:
    return ret;
}

int input_dev_exit(void)
{
    printk("input dev exit succed!\r\n");
    /* 2、注销输入设备 */
    input_unregister_device(keyinputdev.inputdev);
    /* 1、释放 input_dev 结构体变量 */
    input_free_device(keyinputdev.inputdev);

    return 0;
}

/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
    int ret;
    printk("newchrdev init succed!\r\n");
    ret = input_dev_init();
    if(ret){
        printk("input_dev_init failed!\r\n");
    }
    return ret;
}

/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
    printk("newchrdev exit succed!\r\n");
    input_dev_exit();
}

// 头文件 "linux/init.h"
module_init(newchrdev_init);
module_exit(newchrdev_exit);
MODULE_LICENSE("GPL");

编译:

onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ ls
key_input.c  Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/12_input modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
  CC [M]  /home/onlylove/linux/driver/linux_driver/12_input/key_input.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/onlylove/linux/driver/linux_driver/12_input/key_input.mod.o
  LD [M]  /home/onlylove/linux/driver/linux_driver/12_input/key_input.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ ls
key_input.c  key_input.ko  key_input.mod.c  key_input.mod.o  key_input.o  Makefile  modules.order  Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$

测试:

/ # ls
app           etc           linuxrc       root          tmp
bin           key_input.ko  mnt           sbin          usr
dev           lib           proc          sys
/ # ls /sys/class/input/
event0  input0  mice
/ #
/ # insmod key_input.ko
newchrdev init succed!
input dev init succed!
input: keyinput as /devices/virtual/input/input3
/ #
/ # ls /sys/class/input/
event0  event1  input0  input3  mice
/ # cd /sys/class/input/input3/
/sys/devices/virtual/input/input3 # ls
capabilities  modalias      power         uevent
event1        name          properties    uniq
id            phys          subsystem
/sys/devices/virtual/input/input3 # cat name
keyinput
/sys/devices/virtual/input/input3 # cd
/ #

2、添加 key 相关操作

设备树节点信息描述如下:

lq-key {
	compatible = "lq-key";
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_key>;
	key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
	interrupt-parent = <&gpio1>;			// 指定中断父节点
	interrupts = <18 IRQ_TYPE_EDGE_BOTH>;	// 指定 GPIO 引脚和触发类型(双边沿触发)
};

源码:

#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/input.h"
#include "linux/of.h"
#include "linux/of_gpio.h"
#include "linux/of_irq.h"
#include "linux/interrupt.h"

#define KEYINPUT_NAME       "keyinput"  /* 名字 */

/* keyinput设备结构体 */
struct keyinput_dev{
	struct input_dev *inputdev;             /* input结构体 */
    struct device_node	*nd;                /* 设备节点 */
    int gpio;                               /* gpio */
    char name[10];                          /* 名字 */
    int irqnum;                             /* 中断号 */
    irqreturn_t (*handler)(int, void *);    /* 中断服务函数 */
};

struct keyinput_dev keyinputdev;	/* key input设备 */

/* @description		: 中断服务函数,开启定时器,延时10ms,
 *				  	  定时器用于按键消抖。
 * @param - irq 	: 中断号 
 * @param - dev_id	: 设备结构。
 * @return 			: 中断执行结果
 */
static irqreturn_t key_handler(int irq, void *dev_id)
{
    printk("key0_handler\r\n");

    return IRQ_RETVAL(IRQ_HANDLED);
}

int key_gpio_init(void)
{
    int ret = 0;

    printk("key gpio init succed!\r\n");

    /* 1、查找设备节点 */
    keyinputdev.nd = of_find_node_by_path("/lq-key");
	if (keyinputdev.nd== NULL){
		printk("key node not find!\r\n");
		return -1;
	}

    /* 2、从设备树中获取 GPIO 配置信息 */
    keyinputdev.gpio = of_get_named_gpio(keyinputdev.nd,"key-gpio",0);
    if(keyinputdev.gpio < 0){
        printk("can't get key-gpio\r\n");
        return -2;
    }

    /* 3、申请一个 GPIO */
    memcpy(keyinputdev.name,"KEY",sizeof("KEY"));
    gpio_request(keyinputdev.gpio,keyinputdev.name);

    /* 4、设置 GPIO 为输入 */
    gpio_direction_input(keyinputdev.gpio);

    /* 5、获取中断号 */
    keyinputdev.irqnum = irq_of_parse_and_map(keyinputdev.nd,0);
    printk("key:gpio=%d, irqnum=%d\r\n",keyinputdev.gpio, keyinputdev.irqnum);

    /* 6、申请中断 */
    keyinputdev.handler = key_handler;
    ret = request_irq(keyinputdev.irqnum,keyinputdev.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,keyinputdev.name,&keyinputdev);
    if(ret < 0){
        printk("can't request irq!\r\n");
        return -3;
    }

    return 0;
}

int key_gpio_exit(void)
{
    printk("key gpio exit succed!\r\n");
    free_irq(keyinputdev.irqnum,&keyinputdev);
    return 0;
}

int input_dev_init(void)
{
    int ret;

    printk("input dev init succed!\r\n");

    /* 1、申请 input_dev 结构体变量 */
    keyinputdev.inputdev = input_allocate_device();
    if(!keyinputdev.inputdev){
        printk(KERN_ERR "button.c: Not enough memory\n");
        ret = 1;
        goto err_input_allocate_device;
    }
    /* 2、初始化 input_dev 变量 */
    keyinputdev.inputdev->name = KEYINPUT_NAME;
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); /* 设置产生按键事件 */
    input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); /* 初始化input_dev,设置产生哪些按键 */

    /* 其他注册方式 */
#if 0
    /* 方式二 */
    /* 设置产生按键事件 */
	__set_bit(EV_KEY, keyinputdev.inputdev->evbit);
    /* 重复事件,比如按下去不放开,就会一直输出信息 */
	__set_bit(EV_REP, keyinputdev.inputdev->evbit);

	/* 初始化input_dev,设置产生哪些按键 */
	__set_bit(KEY_0, keyinputdev.inputdev->keybit);
#endif

#if 0
    /* 方式三 */
    /* 设置产生按键事件 */
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 
    /* 初始化input_dev,设置产生哪些按键 */
	keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
#endif

    /* 3、注册输入设备 */
    ret = input_register_device(keyinputdev.inputdev);
    if (ret) {
        printk("register input device failed!\r\n");
        ret = 2;
        goto err_input_register_device;
    }

    return 0;
err_input_register_device:
    input_free_device(keyinputdev.inputdev);
err_input_allocate_device:
    return ret;
}

int input_dev_exit(void)
{
    printk("input dev exit succed!\r\n");
    /* 2、注销输入设备 */
    input_unregister_device(keyinputdev.inputdev);
    /* 1、释放 input_dev 结构体变量 */
    input_free_device(keyinputdev.inputdev);

    return 0;
}

/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
    int ret;
    printk("newchrdev init succed!\r\n");
    ret = input_dev_init();
    if(ret){
        printk("input_dev_init failed!\r\n");
        ret = 1;
        goto err_input_dev_init;
    }
    ret = key_gpio_init();
    if(ret){
        printk("key_gpio_init failed!\r\n");
        ret = 2;
        goto err_key_gpio_init;
    }
    return 0;
err_key_gpio_init:
    input_dev_exit();
err_input_dev_init:
    return ret;
}

/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
    printk("newchrdev exit succed!\r\n");
    key_gpio_exit();
    input_dev_exit();
}

// 头文件 "linux/init.h"
module_init(newchrdev_init);
module_exit(newchrdev_exit);
MODULE_LICENSE("GPL");

编译:

onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ ls
key_input.c  Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/12_input modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
  CC [M]  /home/onlylove/linux/driver/linux_driver/12_input/key_input.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/onlylove/linux/driver/linux_driver/12_input/key_input.mod.o
  LD [M]  /home/onlylove/linux/driver/linux_driver/12_input/key_input.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ ls
key_input.c  key_input.ko  key_input.mod.c  key_input.mod.o  key_input.o  Makefile  modules.order  Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$

测试:

/ # ls
app           etc           linuxrc       root          tmp
bin           key_input.ko  mnt           sbin          usr
dev           lib           proc          sys
/ # ls /sys/class/input/
event0  input0  mice
/ #
/ # ls /dev/input/
event0  mice
/ #
/ # lsmod
Module                  Size  Used by    Not tainted
/ #
/ # insmod key_input.ko
newchrdev init succed!
input dev init succed!
input: keyinput as /devices/virtual/input/input3
key gpio init succed!
key:gpio=18, irqnum=49
/ #
/ # lsmod
Module                  Size  Used by    Tainted: G
key_input               2311  0
/ #
/ # ls /sys/class/input/
event0  event1  input0  input3  mice
/ # ls /dev/input/
event0  event1  mice
/ #
/ # key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler

/ #

3、添加定时器消抖

源码:

#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/input.h"
#include "linux/of.h"
#include "linux/of_gpio.h"
#include "linux/of_irq.h"
#include "linux/interrupt.h"

#define KEYINPUT_NAME       "keyinput"  /* 名字 */

/* keyinput设备结构体 */
struct keyinput_dev{
	struct input_dev *inputdev;             /* input结构体 */
    struct device_node	*nd;                /* 设备节点 */
    int gpio;                               /* gpio */
    char name[10];                          /* 名字 */
    int irqnum;                             /* 中断号 */
    irqreturn_t (*handler)(int, void *);    /* 中断服务函数 */
    struct timer_list key_timer;            /* 中断定时器,用于按键消抖 */
};

struct keyinput_dev keyinputdev;	/* key input设备 */

void timer_function(unsigned long arg)
{
    printk("timer_function\r\n");
}

void key_timer_init(void)
{
    printk("key timer init succed!\r\n");
    init_timer(&keyinputdev.key_timer);
    keyinputdev.key_timer.function = timer_function;
}

void key_timer_exit(void)
{
    printk("key timer exit succed!\r\n");
    del_timer(&keyinputdev.key_timer);
}

/* @description		: 中断服务函数,开启定时器,延时10ms,
 *				  	  定时器用于按键消抖。
 * @param - irq 	: 中断号 
 * @param - dev_id	: 设备结构。
 * @return 			: 中断执行结果
 */
static irqreturn_t key_handler(int irq, void *dev_id)
{
    printk("key0_handler\r\n");
    
    /* 开启定时器,并延时 2 ms */
    keyinputdev.key_timer.data = (unsigned long)(&keyinputdev.key_timer);
    mod_timer(&keyinputdev.key_timer,jiffies + msecs_to_jiffies(2000));

    return IRQ_RETVAL(IRQ_HANDLED);
}

int key_gpio_init(void)
{
    int ret = 0;

    printk("key gpio init succed!\r\n");

    /* 1、查找设备节点 */
    keyinputdev.nd = of_find_node_by_path("/lq-key");
	if (keyinputdev.nd== NULL){
		printk("key node not find!\r\n");
		return -1;
	}

    /* 2、从设备树中获取 GPIO 配置信息 */
    keyinputdev.gpio = of_get_named_gpio(keyinputdev.nd,"key-gpio",0);
    if(keyinputdev.gpio < 0){
        printk("can't get key-gpio\r\n");
        return -2;
    }

    /* 3、申请一个 GPIO */
    memcpy(keyinputdev.name,"KEY",sizeof("KEY"));
    gpio_request(keyinputdev.gpio,keyinputdev.name);

    /* 4、设置 GPIO 为输入 */
    gpio_direction_input(keyinputdev.gpio);

    /* 5、获取中断号 */
    keyinputdev.irqnum = irq_of_parse_and_map(keyinputdev.nd,0);
    printk("key:gpio=%d, irqnum=%d\r\n",keyinputdev.gpio, keyinputdev.irqnum);

    /* 6、申请中断 */
    keyinputdev.handler = key_handler;
    ret = request_irq(keyinputdev.irqnum,keyinputdev.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,keyinputdev.name,&keyinputdev);
    if(ret < 0){
        printk("can't request irq!\r\n");
        return -3;
    }
    
    /* 7、初始化定时器(用于按键消抖) */
    key_timer_init();

    return 0;
}

int key_gpio_exit(void)
{
    printk("key gpio exit succed!\r\n");
    /* 2、删除定时器 */
    key_timer_exit();
    /* 1、释放中断号 */
    free_irq(keyinputdev.irqnum,&keyinputdev);
    return 0;
}

int input_dev_init(void)
{
    int ret;

    printk("input dev init succed!\r\n");

    /* 1、申请 input_dev 结构体变量 */
    keyinputdev.inputdev = input_allocate_device();
    if(!keyinputdev.inputdev){
        printk(KERN_ERR "button.c: Not enough memory\n");
        ret = 1;
        goto err_input_allocate_device;
    }
    /* 2、初始化 input_dev 变量 */
    keyinputdev.inputdev->name = KEYINPUT_NAME;
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); /* 设置产生按键事件 */
    input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); /* 初始化input_dev,设置产生哪些按键 */

    /* 其他注册方式 */
#if 0
    /* 方式二 */
    /* 设置产生按键事件 */
	__set_bit(EV_KEY, keyinputdev.inputdev->evbit);
    /* 重复事件,比如按下去不放开,就会一直输出信息 */
	__set_bit(EV_REP, keyinputdev.inputdev->evbit);

	/* 初始化input_dev,设置产生哪些按键 */
	__set_bit(KEY_0, keyinputdev.inputdev->keybit);
#endif

#if 0
    /* 方式三 */
    /* 设置产生按键事件 */
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 
    /* 初始化input_dev,设置产生哪些按键 */
	keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
#endif

    /* 3、注册输入设备 */
    ret = input_register_device(keyinputdev.inputdev);
    if (ret) {
        printk("register input device failed!\r\n");
        ret = 2;
        goto err_input_register_device;
    }

    return 0;
err_input_register_device:
    input_free_device(keyinputdev.inputdev);
err_input_allocate_device:
    return ret;
}

int input_dev_exit(void)
{
    printk("input dev exit succed!\r\n");
    /* 2、注销输入设备 */
    input_unregister_device(keyinputdev.inputdev);
    /* 1、释放 input_dev 结构体变量 */
    input_free_device(keyinputdev.inputdev);

    return 0;
}

/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
    int ret;
    printk("newchrdev init succed!\r\n");
    ret = input_dev_init();
    if(ret){
        printk("input_dev_init failed!\r\n");
        ret = 1;
        goto err_input_dev_init;
    }
    ret = key_gpio_init();
    if(ret){
        printk("key_gpio_init failed!\r\n");
        ret = 2;
        goto err_key_gpio_init;
    }
    return 0;
err_key_gpio_init:
    input_dev_exit();
err_input_dev_init:
    return ret;
}

/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
    printk("newchrdev exit succed!\r\n");
    key_gpio_exit();
    input_dev_exit();
}

// 头文件 "linux/init.h"
module_init(newchrdev_init);
module_exit(newchrdev_exit);
MODULE_LICENSE("GPL");

编译:

onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ ls
key_input.c  Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/12_input modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
  CC [M]  /home/onlylove/linux/driver/linux_driver/12_input/key_input.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/onlylove/linux/driver/linux_driver/12_input/key_input.mod.o
  LD [M]  /home/onlylove/linux/driver/linux_driver/12_input/key_input.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ ls
key_input.c  key_input.ko  key_input.mod.c  key_input.mod.o  key_input.o  Makefile  modules.order  Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$

测试:

/ # ls
app           etc           linuxrc       root          tmp
bin           key_input.ko  mnt           sbin          usr
dev           lib           proc          sys
/ # ls /sys/class/input/
event0  input0  mice
/ # ls /dev/input/
event0  mice
/ # lsmod
Module                  Size  Used by    Not tainted
/ # insmod key_input.ko
newchrdev init succed!
input dev init succed!
input: keyinput as /devices/virtual/input/input3
key gpio init succed!
key:gpio=18, irqnum=49
key timer init succed!
/ # ls /sys/class/input/
event0  event1  input0  input3  mice
/ # ls /dev/input/
event0  event1  mice
/ # key0_handler
key0_handler
timer_function
key0_handler
key0_handler
timer_function
key0_handler
key0_handler
key0_handler
key0_handler
timer_function
key0_handler
key0_handler
key0_handler
timer_function
key0_handler
key0_handler
timer_function
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
timer_function
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
timer_function

/ #

4、添加 input 子系统源码

思路:在定时器处理函数中上报按键事件。
源码:

#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/input.h"
#include "linux/of.h"
#include "linux/of_gpio.h"
#include "linux/of_irq.h"
#include "linux/interrupt.h"

#define KEYINPUT_NAME       "keyinput"  /* 名字 */

/* keyinput设备结构体 */
struct keyinput_dev{
	struct input_dev *inputdev;             /* input结构体 */
    struct device_node	*nd;                /* 设备节点 */
    int gpio;                               /* gpio */
    char name[10];                          /* 名字 */
    int irqnum;                             /* 中断号 */
    irqreturn_t (*handler)(int, void *);    /* 中断服务函数 */
    struct timer_list key_timer;            /* 中断定时器,用于按键消抖 */
};

struct keyinput_dev keyinputdev;	/* key input设备 */

void timer_function(unsigned long arg)
{
    unsigned char value;

//    printk("timer_function\r\n");

    value = gpio_get_value(keyinputdev.gpio);  /* 读取IO值 */
    if(value == 0){                         /* 按下按键 */
		//input_event(keyinputdev.inputdev, EV_KEY, KEY_0, 1);
		input_report_key(keyinputdev.inputdev, KEY_0, 1); /* 最后一个参数表示按下还是松开,1为按下,0为松开 */
		input_sync(keyinputdev.inputdev);
    }else{                                  /* 按键松开 */
        //input_event(keyinputdev.inputdev, EV_KEY, KEY_0, 0);
		input_report_key(keyinputdev.inputdev, KEY_0, 0);
		input_sync(keyinputdev.inputdev);
    }
}

void key_timer_init(void)
{
    printk("key timer init succed!\r\n");
    init_timer(&keyinputdev.key_timer);
    keyinputdev.key_timer.function = timer_function;
}

void key_timer_exit(void)
{
    printk("key timer exit succed!\r\n");
    del_timer(&keyinputdev.key_timer);
}

/* @description		: 中断服务函数,开启定时器,延时10ms,
 *				  	  定时器用于按键消抖。
 * @param - irq 	: 中断号 
 * @param - dev_id	: 设备结构。
 * @return 			: 中断执行结果
 */
static irqreturn_t key_handler(int irq, void *dev_id)
{
//    printk("key0_handler\r\n");
    
    /* 开启定时器 */
    keyinputdev.key_timer.data = (unsigned long)(&keyinputdev.key_timer);
    mod_timer(&keyinputdev.key_timer,jiffies + msecs_to_jiffies(10));

    return IRQ_RETVAL(IRQ_HANDLED);
}

int key_gpio_init(void)
{
    int ret = 0;

    printk("key gpio init succed!\r\n");

    /* 1、查找设备节点 */
    keyinputdev.nd = of_find_node_by_path("/lq-key");
	if (keyinputdev.nd== NULL){
		printk("key node not find!\r\n");
		return -1;
	}

    /* 2、从设备树中获取 GPIO 配置信息 */
    keyinputdev.gpio = of_get_named_gpio(keyinputdev.nd,"key-gpio",0);
    if(keyinputdev.gpio < 0){
        printk("can't get key-gpio\r\n");
        return -2;
    }

    /* 3、申请一个 GPIO */
    memcpy(keyinputdev.name,"KEY",sizeof("KEY"));
    gpio_request(keyinputdev.gpio,keyinputdev.name);

    /* 4、设置 GPIO 为输入 */
    gpio_direction_input(keyinputdev.gpio);

    /* 5、获取中断号 */
    keyinputdev.irqnum = irq_of_parse_and_map(keyinputdev.nd,0);
    printk("key:gpio=%d, irqnum=%d\r\n",keyinputdev.gpio, keyinputdev.irqnum);

    /* 6、申请中断 */
    keyinputdev.handler = key_handler;
    ret = request_irq(keyinputdev.irqnum,keyinputdev.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,keyinputdev.name,&keyinputdev);
    if(ret < 0){
        printk("can't request irq!\r\n");
        return -3;
    }
    
    /* 7、初始化定时器(用于按键消抖) */
    key_timer_init();

    return 0;
}

int key_gpio_exit(void)
{
    printk("key gpio exit succed!\r\n");
    /* 2、删除定时器 */
    key_timer_exit();
    /* 1、释放中断号 */
    free_irq(keyinputdev.irqnum,&keyinputdev);
    return 0;
}

int input_dev_init(void)
{
    int ret;

    printk("input dev init succed!\r\n");

    /* 1、申请 input_dev 结构体变量 */
    keyinputdev.inputdev = input_allocate_device();
    if(!keyinputdev.inputdev){
        printk(KERN_ERR "button.c: Not enough memory\n");
        ret = 1;
        goto err_input_allocate_device;
    }
    /* 2、初始化 input_dev 变量 */
    keyinputdev.inputdev->name = KEYINPUT_NAME;
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); /* 设置产生按键事件 */
    input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); /* 初始化input_dev,设置产生哪些按键 */

    /* 其他注册方式 */
#if 0
    /* 方式二 */
    /* 设置产生按键事件 */
	__set_bit(EV_KEY, keyinputdev.inputdev->evbit);
    /* 重复事件,比如按下去不放开,就会一直输出信息 */
	__set_bit(EV_REP, keyinputdev.inputdev->evbit);

	/* 初始化input_dev,设置产生哪些按键 */
	__set_bit(KEY_0, keyinputdev.inputdev->keybit);
#endif

#if 0
    /* 方式三 */
    /* 设置产生按键事件 */
    keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); 
    /* 初始化input_dev,设置产生哪些按键 */
	keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
#endif

    /* 3、注册输入设备 */
    ret = input_register_device(keyinputdev.inputdev);
    if (ret) {
        printk("register input device failed!\r\n");
        ret = 2;
        goto err_input_register_device;
    }

    return 0;
err_input_register_device:
    input_free_device(keyinputdev.inputdev);
err_input_allocate_device:
    return ret;
}

int input_dev_exit(void)
{
    printk("input dev exit succed!\r\n");
    /* 2、注销输入设备 */
    input_unregister_device(keyinputdev.inputdev);
    /* 1、释放 input_dev 结构体变量 */
    input_free_device(keyinputdev.inputdev);

    return 0;
}

/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
    int ret;
    printk("newchrdev init succed!\r\n");
    ret = input_dev_init();
    if(ret){
        printk("input_dev_init failed!\r\n");
        ret = 1;
        goto err_input_dev_init;
    }
    ret = key_gpio_init();
    if(ret){
        printk("key_gpio_init failed!\r\n");
        ret = 2;
        goto err_key_gpio_init;
    }
    return 0;
err_key_gpio_init:
    input_dev_exit();
err_input_dev_init:
    return ret;
}

/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
    printk("newchrdev exit succed!\r\n");
    key_gpio_exit();
    input_dev_exit();
}

// 头文件 "linux/init.h"
module_init(newchrdev_init);
module_exit(newchrdev_exit);
MODULE_LICENSE("GPL");

编译:

onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ ls
key_input.c  Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/12_input modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
  CC [M]  /home/onlylove/linux/driver/linux_driver/12_input/key_input.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/onlylove/linux/driver/linux_driver/12_input/key_input.mod.o
  LD [M]  /home/onlylove/linux/driver/linux_driver/12_input/key_input.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$ ls
key_input.c  key_input.ko  key_input.mod.c  key_input.mod.o  key_input.o  Makefile  modules.order  Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/12_input$

测试:

/ # ls
app           etc           linuxrc       root          tmp
bin           key_input.ko  mnt           sbin          usr
dev           lib           proc          sys
/ # ls /sys/class/input/
event0  input0  mice
/ # ls /dev/input/
event0  mice
/ # lsmod
Module                  Size  Used by    Not tainted
/ #
/ # insmod key_input.ko
newchrdev init succed!
input dev init succed!
input: keyinput as /devices/virtual/input/input3
key gpio init succed!
key:gpio=18, irqnum=49
key timer init succed!
/ #
/ # rmmod key_input.ko
newchrdev exit succed!
key gpio exit succed!
key timer exit succed!
input dev exit succed!
random: nonblocking pool is initialized
/ #
/ # insmod key_input.ko
newchrdev init succed!
input dev init succed!
input: keyinput as /devices/virtual/input/input4
key gpio init succed!
key:gpio=18, irqnum=49
key timer init succed!
/ #
/ # ls /sys/class/input/
event0  event1  input0  input4  mice
/ # ls /dev/input/
event0  event1  mice
/ #
/ # hexdump /dev/input/event1
0000000 1c0c 0000 cee9 0000 0001 000b 0001 0000
0000010 1c0c 0000 cee9 0000 0000 0000 0000 0000
0000020 1c0c 0000 4003 0003 0001 000b 0000 0000
0000030 1c0c 0000 4003 0003 0000 0000 0000 0000
0000040 1c0d 0000 32a0 0000 0001 000b 0001 0000
0000050 1c0d 0000 32a0 0000 0000 0000 0000 0000
0000060 1c0d 0000 7c94 0002 0001 000b 0000 0000
0000070 1c0d 0000 7c94 0002 0000 0000 0000 0000
0000080 1c0d 0000 8ea9 000c 0001 000b 0001 0000
0000090 1c0d 0000 8ea9 000c 0000 0000 0000 0000
00000a0 1c0d 0000 3c5f 000e 0001 000b 0000 0000
00000b0 1c0d 0000 3c5f 000e 0000 0000 0000 0000
00000c0 1c0e 0000 a449 000b 0001 000b 0001 0000
00000d0 1c0e 0000 a449 000b 0000 0000 0000 0000
00000e0 1c0e 0000 c721 000d 0001 000b 0000 0000
00000f0 1c0e 0000 c721 000d 0000 0000 0000 0000
0000100 1c0f 0000 acb4 0007 0001 000b 0001 0000
0000110 1c0f 0000 acb4 0007 0000 0000 0000 0000
0000120 1c0f 0000 92cb 000a 0001 000b 0000 0000
0000130 1c0f 0000 92cb 000a 0000 0000 0000 0000
0000140 1c10 0000 7c89 0002 0001 000b 0001 0000
0000150 1c10 0000 7c89 0002 0000 0000 0000 0000
0000160 1c10 0000 3ba2 0005 0001 000b 0000 0000
0000170 1c10 0000 3ba2 0005 0000 0000 0000 0000
0000180 1c10 0000 1546 000e 0001 000b 0001 0000
0000190 1c10 0000 1546 000e 0000 0000 0000 0000
00001a0 1c11 0000 43fe 0001 0001 000b 0000 0000
00001b0 1c11 0000 43fe 0001 0000 0000 0000 0000
00001c0 1c11 0000 e0f0 000a 0001 000b 0001 0000
00001d0 1c11 0000 e0f0 000a 0000 0000 0000 0000
00001e0 1c11 0000 03cc 000d 0001 000b 0000 0000
00001f0 1c11 0000 03cc 000d 0000 0000 0000 0000
0000200 1c12 0000 fef8 0005 0001 000b 0001 0000
0000210 1c12 0000 fef8 0005 0000 0000 0000 0000
0000220 1c12 0000 21cb 0008 0001 000b 0000 0000
0000230 1c12 0000 21cb 0008 0000 0000 0000 0000
^C
/ #

Linux 驱动开发 四十八:Linux INPUT 子系统实验

九、app程序

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


static struct input_event inputevent;

int main(int argc, char *argv[])
{
    int fd;
    int err = 0;

    if(argc != 2){
        printf("Error Usage!\r\n");
        return -1;
    }

    fd = open(argv[1], O_RDWR);
    while (1) {
        err = read(fd, &inputevent, sizeof(inputevent));
        if (err > 0) { /* 读取数据成功 */
            switch (inputevent.type) {
                case EV_KEY:
                    if (inputevent.code < BTN_MISC) { /* 键盘键值 */
                        printf("key %d %s\r\n", inputevent.code,inputevent.value ? "press" : "release");
                    }else{
                        printf("button %d %s\r\n", inputevent.code,inputevent.value ? "press" : "release");
                    }
                    break;
                default:
                    break;
            }
        }else{
            printf("读取数据失败\r\n");
        }
    }

    return 0;
}

编译:

arm-linux-gnueabihf-gcc key_input_app.c -o app

十、测试

/ # ls
app           etc           linuxrc       root          tmp
bin           key_input.ko  mnt           sbin          usr
dev           lib           proc          sys
/ # ls /sys/class/input/
event0  input0  mice
/ # ls /dev/input/
event0  mice
/ # lsmod
Module                  Size  Used by    Not tainted
/ # insmod key_input.ko
newchrdev init succed!
input dev init succed!
input: keyinput as /devices/virtual/input/input3
key gpio init succed!
key:gpio=18, irqnum=49
key timer init succed!
/ #
/ # lsmod
Module                  Size  Used by    Tainted: G
key_input               2836  0
/ #
/ # rmmod key_input.ko
newchrdev exit succed!
key gpio exit succed!
key timer exit succed!
input dev exit succed!
/ #
/ # lsmod
Module                  Size  Used by    Tainted: G
/ #
/ # insmod key_input.ko
newchrdev init succed!
input dev init succed!
input: keyinput as /devices/virtual/input/input4
key gpio init succed!
key:gpio=18, irqnum=49
key timer init succed!
/ #
/ # lsmod
Module                  Size  Used by    Tainted: G
key_input               2836  0
/ #
/ # ls /sys/class/input/
event0  event1  input0  input4  mice
/ #
/ # ls /dev/input/
event0  event1  mice
/ #
/ # ./app /dev/input/event1
key 11 press
key 11 release
key 11 press
key 11 release
key 11 press
key 11 release
key 11 press
key 11 release
key 11 press
key 11 release
key 11 press
key 11 release
key 11 press
key 11 release
key 11 press
key 11 release
^C
/ #

通过以上测试,可以确定按键功能正常。

上一篇:html入门


下一篇:关于System Verilog task参数传递问题的解决