一、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      1024err = 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 videooutputs (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: manualbrightness 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.oBuilding modules, stage 2.MODPOST 1 modulesCC      /home/onlylove/linux/driver/linux_driver/12_input/key_input.mod.oLD [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.oBuilding modules, stage 2.MODPOST 1 modulesCC      /home/onlylove/linux/driver/linux_driver/12_input/key_input.mod.oLD [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.oBuilding modules, stage 2.MODPOST 1 modulesCC      /home/onlylove/linux/driver/linux_driver/12_input/key_input.mod.oLD [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.oBuilding modules, stage 2.MODPOST 1 modulesCC      /home/onlylove/linux/driver/linux_driver/12_input/key_input.mod.oLD [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 子系统实验相关推荐

  1. Linux驱动开发(十八)---网络(网卡)驱动学习

    前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...

  2. Linux驱动开发(十)---树莓派输入子系统学习(红外接收)

    前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...

  3. Linux 驱动开发 四十四:platform 设备驱动实验(二)

    驱动测试通过操作 led 完成. 一.原理图 二.无设备树源码实现 无设备树时候通过 platform_device.name 和 platform_driver.driver.name 进行匹配. ...

  4. Linux驱动开发(十八):I2C驱动

    简介 I2C是我们在单片机开发时时常会用到的通讯接口,用来与一些字符型设备进行通信,比如:陀螺仪.温度传感器等等,同样的在Linux下I2C驱动也是十分重要的.有了操作系统的加持,我们不用像在32上那 ...

  5. Linux 驱动开发 四十六:Linux MISC驱动实验

    misc 的意思是混合.杂项的,因此MISC 驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动. MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 p ...

  6. Linux 驱动开发 六十:《input.txt》翻译

    文档路径:linux-imx-4.1.15\Documentation\input\input.txt. 一.介绍 这是一个驱动进程集合,旨在支持 Linux 下的所有输入设备.虽然它目前仅用于 US ...

  7. Linux 驱动开发 二十八:读写锁

    参考博客:Linux 内核同步(三):读-写自旋锁(rwlock)_StephenZhou-CSDN博客_linux rwlock 使用 spinlock 保护临界区时,多个读之间无法并发,只能被 s ...

  8. 从零开始之驱动发开、linux驱动(四十、Linux common clock framework(5)_使用举例)

    前面三节分析的3.16.57内核中,三星的驱动中,恰好S5PV210的CPU没有使用Linux common clock framework来处理clk.所以,本文采用4.9.92的内核来分析S5PV ...

  9. Linux驱动开发(十五)---如何使用内核现有驱动(显示屏)

    前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...

最新文章

  1. python办公自动化excel_简直出神入化,教你用Python控制Excel实现自动化办公
  2. 在 Windows 7 中安装上网认证客户端
  3. 工程搭建:搭建子工程之搭建实体类模块和lombok插件
  4. linux ap程序,ubuntu(linux)无线网卡开启/关闭wifi(AP)
  5. javafx html5_JavaFX技巧5:可观察
  6. html中水印影响文字复制,HTML中文字水印的清除方法_HTML教程
  7. 解决mysql导入数据量很大导致失败及查找my.ini 位置(my.ini)在哪
  8. JS-DOM-节点-获取元素-文档结构-元素的属性
  9. golang导入包的理解
  10. OLS和GWR模型部分参数解释
  11. Verilog HDL的简单应用之74ls138功能的实现
  12. pause、who、whos命令,Matlab中存储实数的形式总结,format两个命令
  13. pyqt5-事件机制
  14. 如何用牛顿迭代法求平方根
  15. html前端的几种加密/解密方式
  16. EV4加密转换MP4
  17. php google地图,google地图 标记地图代码
  18. VR全景拍摄时我们应该注意哪些技巧?
  19. R语言|散点图 ———R语言数据可视化系列(一)
  20. PHP获取本机真实IP

热门文章

  1. ibm websphere_IBM WebSphere开发者技术期刊,使用Tivoli Access Manager和WebSphere Portal配置单点登录
  2. (跟我一起来学区块链(1.5))之 区块链的架构
  3. 不说话的“狼人杀”游戏
  4. 21开运网的每日通胜 出行参考
  5. 目录下的多个文件夹里的内容合并到一个文件夹
  6. android自定义view 模仿win10进度条
  7. 电脑问题处理篇8:解决电脑启动时间过长问题
  8. Embedded software development engineer self-introduction
  9. 钉钉、2号人事部等苦心经营的SaaS入口,或许多数人悟错了道!
  10. xx省驾驶员信用评分模型