Linux 驱动开发 四十八:Linux INPUT 子系统实验
2022/2/8 7:12:49
本文主要是介绍Linux 驱动开发 四十八:Linux INPUT 子系统实验,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
一、input 子系统简介
input 就是输入的意思,因此 input 子系统就是管理输入的子系统,是 Linux 内核针对某一类设备而创建的框架。
比如按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可。为此 input 子系统分为 input 驱动层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点,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 子目录,如下图所示:
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 / #
九、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 / #
通过以上测试,可以确定按键功能正常。
这篇关于Linux 驱动开发 四十八:Linux INPUT 子系统实验的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法