一、什么是input输入子系统?

1.1. Linux系统支持的输入设备繁多,例如键盘、鼠标、触摸屏、手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型、不同原理、不同的输入信息的输入设备的呢?其实就是通过input输入子系统这套软件体系来完成的。从整体上来说,input输入子系统分为3层:上层(输入事件驱动层)、中层(输入核心层)、下层(输入设备驱动层),如下图所示:

1.2. 图中Drivers对应的就是下层设备驱动层,对应各种各样不同的输入设备,Input Core对应的就是中层核心层,Handlers对应的就是上层输入事件驱动层,最右边的代表的是用户空间

1.3. 上层中的各个handler(Keyboard/Mouse/Joystick/Event)是属于平行关系。由于历史原因,一种设备可能连接到多个handler层中,由于event是后出的。所有event实现大一统,所有输入设备都可以连接到event handler中

1.4. 官方查看文档

文档1:kernel\Documentation\input\input.txt

文档2:kernel\Documentation\input\input-programming.txt

二. 输入子系统框架分析

2.1. 重要结构体

2.1.1.  input_dev结构体

a. 该结构体是所有输入设备的抽象,只要是输入设备都可以用此结构体描述

struct input_dev {
const char *name; // input设备的名字
const char *phys; //
const char *uniq; //
struct input_id id; // // 这些是用来表示该input设备能够上报的事件类型有哪些 是用位的方式来表示的
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;
int (*setkeycode)(struct input_dev *dev,
unsigned int scancode, unsigned int keycode);
int (*getkeycode)(struct input_dev *dev,
unsigned int scancode, unsigned int *keycode); struct ff_device *ff; unsigned int repeat_key;
struct timer_list timer; int sync; int abs[ABS_CNT];
int rep[REP_MAX + ]; unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)]; int absmax[ABS_CNT];
int absmin[ABS_CNT];
int absfuzz[ABS_CNT];
int absflat[ABS_CNT];
int absres[ABS_CNT]; int (*open)(struct input_dev *dev); // 设备的open函数
void (*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); // 上报事件 struct input_handle *grab; spinlock_t event_lock;
struct mutex mutex; unsigned int users;
bool going_away; struct device dev; // 内置的device结构体变量 struct list_head h_list; // 用来挂接input_dev 设备连接的所有handle 的一个链表头
struct list_head node; // 作为链表节点挂接到 input_dev_list 链表上 (input_dev_list链表是input核心层维护的一个用来挂接所有input设备的一个链表头)
};

2.1.2. input_handler结构体

struct input_handler {

    void *private;            //  私有数据

    void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);   //  handler用于向上层上报输入事件的函数
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev); // match 函数用来匹配handler 与 input_dev 设备
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); // 当handler 与 input_dev 匹配成功之后用来连接
void (*disconnect)(struct input_handle *handle); // 断开handler 与 input_dev 之间的连接
void (*start)(struct input_handle *handle); const struct file_operations *fops; // 一个file_operations 指针
int minor; // 该handler 的编号 (在input_table 数组中用来计算数组下标) input_table数组就是input子系统用来管理注册的handler的一个数据结构
const char *name; // handler的名字 const struct input_device_id *id_table; // 指向一个 input_device_id 类型的数组,用来进行与input设备匹配时用到的信息 struct list_head h_list; // 用来挂接handler 上连接的所有handle 的一个链表头
struct list_head node; // 作为一个链表节点挂接到 input_handler_list 链表上(input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头)
};

2.1.3. input_device_id结构体

struct input_device_id {

    kernel_ulong_t flags;    //  这个flag 表示我们的这个 input_device_id 是用来匹配下面的4个情况的哪一项
// flag == 1表示匹配总线 2表示匹配供应商 4表示匹配产品 8表示匹配版本
__u16 bustype;
__u16 vendor;
__u16 product;
__u16 version; kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + ];
kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + ];
kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + ];
kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + ];
kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + ];
kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + ];
kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + ];
kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + ];
kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + ]; kernel_ulong_t driver_info;
};

2.2. input框架中重要函数

2.2.1、核心模块注册input_init

a. 函数位于drivers\input\input.c

b. class_register

c. input_proc_init

d. register_chrdev

static int __init input_init(void)
{
int err; input_init_abs_bypass(); err = class_register(&input_class); // 创建设备类 /sys/class/input
if (err) {
printk(KERN_ERR "input: unable to register input_dev class\n");
return err;
} err = input_proc_init(); // proc文件系统相关的初始化
if (err)
goto fail1; err = register_chrdev(INPUT_MAJOR, "input", &input_fops); // 注册字符设备驱动 主设备号13 input_fops 中只实现了open函数,所以他的原理其实和misc其实是一样的
if (err) {
printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
goto fail2;
} return ; fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}

2.2.2. 核心层提供给设备驱动层的接口函数

2.2.2.1. input设备驱动框架留给设备驱动层的接口函数主要有3个:

a. input_allocate_device。分配一块input_dev结构体类型大小的内存

struct input_dev *input_allocate_device(void)
{
struct input_dev *dev; // 定义一个 input_dev 指针 dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); // 申请分配内存
if (dev) {
dev->dev.type = &input_dev_type; // 确定input设备的 设备类型 input_dev_type
dev->dev.class = &input_class; // 确定input设备所属的设备类 class
device_initialize(&dev->dev); // input设备的初始化
mutex_init(&dev->mutex); // 互斥锁初始化
spin_lock_init(&dev->event_lock); // 自旋锁初始化
INIT_LIST_HEAD(&dev->h_list); // input_dev -> h_list 链表初始化
INIT_LIST_HEAD(&dev->node); // input_dev -> node 链表初始化 __module_get(THIS_MODULE);
} return dev;
}

b. input_set_capability。设置输入设备可以上报哪些输入事件

函数原型:input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)

参数:dev就是设备的input_dev结构体变量

type表示设备可以上报的事件类型

code表示上报这类事件中的那个事件

注意:input_set_capability函数一次只能设置一个具体事件,如果设备可以上报多个事件,则需要重复调用这个函数来进行设置,

例如:  input_set_capability(dev, EV_KEY, KEY_Q); // 至于函数内部是怎么设置的,将会在后面进行分析。

input_set_capability(dev, EV_KEY, KEY_W);

input_set_capability(dev, EV_KEY, KEY_E);

c. input_register_device。向input核心层注册设备

int input_register_device(struct input_dev *dev)      //  注册input输入设备
{
static atomic_t input_no = ATOMIC_INIT();
struct input_handler *handler; // 定义一个 input_handler 结构体指针
const char *path;
int error; /* Every input device generates EV_SYN/SYN_REPORT events. */
__set_bit(EV_SYN, dev->evbit); // 每一个input输入设备都会发生这个事件 /* KEY_RESERVED is not supposed to be transmitted to userspace. */
__clear_bit(KEY_RESERVED, dev->keybit); // 清除KEY_RESERVED 事件对应的bit位,也就是不传输这种类型的事件 /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev); // 确保input_dev中的用来记录事件的变量中没有提到的位掩码是干净的。 /*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = ;
dev->rep[REP_PERIOD] = ;
} if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode; dev_set_name(&dev->dev, "input%ld", // 设置input设备对象的名字 input+数字
(unsigned long) atomic_inc_return(&input_no) - ); error = device_add(&dev->dev); // 添加设备 例如: /sys/devices/virtual/input/input0
if (error)
return error; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); // 获取input设备对象所在的路径 /sys/devices/virtual/input/input_xxx
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path); error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
} list_add_tail(&dev->node, &input_dev_list); // 链表挂接: 将 input_dev->node 作为节点挂接到 input_dev_list 链表上 list_for_each_entry(handler, &input_handler_list, node) // 遍历input_handler_list 链表上的所有handler
input_attach_handler(dev, handler); // 将handler与input设备进行匹配 input_wakeup_procfs_readers(); // 更新proc 文件系统 mutex_unlock(&input_mutex); return ;
}

d. input_attach_handler函数:

input_attach_handler就是input_register_device函数中用来对下层的设备驱动和上层的handler进行匹配的一个函数,只有匹配成功之后就会调用上层handler中的connect函数

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
const struct input_device_id *id; // 定义一个input_device_id 的指针
int error; id = input_match_device(handler, dev); // 通过这个函数进行handler与input设备的匹配工作
if (!id)
return -ENODEV; error = handler->connect(handler, dev, id); // 匹配成功则调用 handler 中的 connect 函数进行连接
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->dev.kobj), error); return error;
} static const struct input_device_id *input_match_device(struct input_handler *handler,
struct input_dev *dev)
{
const struct input_device_id *id; // 定义一个 input_device_id 指针
int i; for (id = handler->id_table; id->flags || id->driver_info; id++) { // 依次遍历handler->id_table 所指向的input_device_id 数组中的各个元素
// 依次进行下面的匹配过程
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) // 匹配总线
if (id->bustype != dev->id.bustype)
continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) // 匹配供应商
if (id->vendor != dev->id.vendor)
continue; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) // 匹配产品
if (id->product != dev->id.product)
continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) // 匹配版本
if (id->version != dev->id.version)
continue; // 下面的这些是匹配我们上传的事件是否属实
MATCH_BIT(evbit, EV_MAX);
MATCH_BIT(keybit, KEY_MAX);
MATCH_BIT(relbit, REL_MAX);
MATCH_BIT(absbit, ABS_MAX);
MATCH_BIT(mscbit, MSC_MAX);
MATCH_BIT(ledbit, LED_MAX);
MATCH_BIT(sndbit, SND_MAX);
MATCH_BIT(ffbit, FF_MAX);
MATCH_BIT(swbit, SW_MAX); if (!handler->match || handler->match(handler, dev))
return id; // 如果数组中的某个匹配成功了就返回他的地址
} return NULL;
}

三. 驱动开发者开发流程

3.1. 内核提供的函数

3.1.1. input_allocate_device

3.1.2. input_register_device

3.1.3. input_register_device

3.2. 开发代码

3.2.1. button_device.c

#include <linux/init.h>            // __init   __exit
#include <linux/module.h> // module_init module_exit
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h> #include <mach/gpio.h>
#include <linux/leds.h>
#include <asm/string.h> #include <linux/platform_device.h> #include <linux/input.h>
#include "button_device_driver.h" #define BUTTON_GPIO_CONFIG 0xFF /* KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT
* LEFT -> EINT2 -> GPH0_2
* DOWN -> EINT3 -> GPH0_3
* UP -> KP_COL0 -> GPH2_0
* RIGHT -> KP_COL1 -> GPH2_1
* MENU -> KP_COL3 -> GPH2_3 (KEY_A)
* BACK -> KP_COL2 -> GPH2_2 (KEY_B)
*/ void s5pv210_button_release(struct device *dev); struct gpioIRQ button_irq[] =
{
{
"button_Left_IRQ",
BUTTON_LEFT_IRQ,
IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
},
{
"button_down_IRQ",
BUTTON_DOWN_IRQ,
IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,
}
};
static struct s5pv210_button_platdata x210_button_pdata[] = {
[]={
.name = "button_Left",
.gpio = S5PV210_GPH0(),
.gpio_cfg = BUTTON_GPIO_CONFIG,
.button_code= KEY_LEFT,
.button_irq = &button_irq[],
},
[]={
.name = "button_down",
.gpio = S5PV210_GPH0(),
.gpio_cfg = BUTTON_GPIO_CONFIG,
.button_code= KEY_DOWN,
.button_irq = &button_irq[],
}
}; static struct platform_device s5pv210_device_button = {
.name = "s5pv210-button",
.id = -,
.dev = {
.platform_data = &x210_button_pdata,
.release = s5pv210_button_release,
},
};
void s5pv210_button_release(struct device *dev)
{
printk(KERN_WARNING "s5pv210_button_release successful \n");
} static int __init s5pv210_button_init(void)
{
int ret = -;
printk(KERN_INFO "device.c : s5pv210_button_init successful \n");
ret = platform_device_register(&s5pv210_device_button);
if (ret < )
{
printk(KERN_WARNING "platform_add_devices fail \n");
}
return ret;
} static void __exit s5pv210_button_exit(void)
{
printk(KERN_INFO "device.c : s5pv210_button_exit successful \n"); platform_device_unregister(&s5pv210_device_button);
} module_init(s5pv210_button_init);
module_exit(s5pv210_button_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("musk"); // 描述模块的作者
MODULE_DESCRIPTION("x210 button device"); // 描述模块的介绍信息
MODULE_ALIAS("button_device"); // 描述模块的别名信息
3.2.. button_driver.c #include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/io.h> #include <mach/irqs.h> #include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h> #include "button_device_driver.h"
#include <linux/kernel.h>
/*
* POWER -> EINT1 -> GPH0_1
* LEFT -> EINT2 -> GPH0_2
* DOWN -> EINT3 -> GPH0_3
* UP -> KP_COL0 -> GPH2_0
* RIGHT -> KP_COL1 -> GPH2_1
* MENU -> KP_COL3 -> GPH2_3 (KEY_A)
* BACK -> KP_COL2 -> GPH2_2 (KEY_B)
*/ static struct input_dev *button_dev; static irqreturn_t button_interrupt(int irq, void *dummy)
{ struct platform_device *pdev = (struct platform_device *)dummy;
struct s5pv210_button_platdata *pdata = pdev->dev.platform_data;
if(dummy == NULL)
{
printk("\n button_interrupt fail;irq = %d\n",irq);
return -;
}
printk("\n irq = %d\n",irq);
if(irq == pdata[].button_irq->irq)
{
s3c_gpio_cfgpin(pdata[].gpio, S3C_GPIO_SFN(0x00));
input_report_key(button_dev, pdata[].button_code,!gpio_get_value(pdata[].gpio));
s3c_gpio_cfgpin(pdata[].gpio, S3C_GPIO_SFN(0x0f));
}
if(irq == pdata[].button_irq->irq)
{
s3c_gpio_cfgpin(pdata[].gpio, S3C_GPIO_SFN(0x00));
input_report_key(button_dev, pdata[].button_code,!gpio_get_value(pdata[].gpio));
s3c_gpio_cfgpin(pdata[].gpio, S3C_GPIO_SFN(0x0f));
}
input_sync(button_dev); return IRQ_HANDLED;
} static int s5pv210_button_probe(struct platform_device *pdev)
{
int error,ret;
struct s5pv210_button_platdata *pdata = pdev->dev.platform_data;
printk(KERN_INFO "button_driver.c: s5pv210_button_probe successful\n");
if (request_irq(pdata[].button_irq->irq, button_interrupt, pdata[].button_irq->irq_trigger, pdata[].button_irq->name, pdev))
{
printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %d\n", pdata[].button_irq->irq);
return -EBUSY;
}
ret = gpio_request(pdata[].gpio, pdata[].name);
if(ret)
printk("button-driver: request %s fail", pdata[].name);
s3c_gpio_cfgpin(pdata[].gpio, S3C_GPIO_SFN(pdata[].gpio_cfg)); if (request_irq(pdata[].button_irq->irq, button_interrupt, pdata[].button_irq->irq_trigger, pdata[].button_irq->name, pdev))
{
printk(KERN_ERR "key-s5pv210.c: Can't allocate irq %d\n", pdata[].button_irq->irq);
return -EBUSY;
}
ret = gpio_request(pdata[].gpio, pdata[].name);
if(ret)
printk(KERN_ERR "button-driver: request %s fail", pdata[].name);
s3c_gpio_cfgpin(pdata[].gpio, S3C_GPIO_SFN(pdata[].gpio_cfg)); button_dev = input_allocate_device();
if (!button_dev)
{
printk(KERN_ERR "button.c: Not enough memory\n");
error = -ENOMEM;
goto err_free_irq;
} set_bit(EV_KEY, button_dev->evbit); set_bit(pdata[].button_code, button_dev->keybit);
set_bit(pdata[].button_code, button_dev->keybit); error = input_register_device(button_dev);
if (error)
{
printk(KERN_ERR "button_driver.c: Failed to register device\n");
goto err_free_dev;
}
return ; err_free_dev:
input_free_device(button_dev); err_free_irq:
free_irq(pdata[].button_irq->irq, pdev);
free_irq(pdata[].button_irq->irq, pdev);
gpio_free(pdata[].gpio);
gpio_free(pdata[].gpio); return error;
} static int s5pv210_button_remove(struct platform_device *pdev)
{
struct s5pv210_button_platdata *pdata = pdev->dev.platform_data;
printk(KERN_INFO "button_driver.c: s5pv210_button_remove successful\n"); free_irq(IRQ_EINT2, pdev);
free_irq(IRQ_EINT3, pdev);
gpio_free(pdata[].gpio);
gpio_free(pdata[].gpio);
input_unregister_device(button_dev); return ;
} static struct platform_driver s5pv210_button_driver = {
.probe = s5pv210_button_probe,
.remove = s5pv210_button_remove,
.driver = {
.name = "s5pv210-button",
.owner = THIS_MODULE,
}
}; static int __init s5pv210_button_init(void)
{
return platform_driver_register(&s5pv210_button_driver);
}
static void __exit s5pv210_button_exit(void)
{
platform_driver_unregister(&s5pv210_button_driver);
} module_init(s5pv210_button_init);
module_exit(s5pv210_button_exit); MODULE_AUTHOR("musk");
MODULE_DESCRIPTION("s5pv210 button driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("button-s5pv210");
3.2.. button_device_driver.h #ifndef __BUTTON_DEVICE_DRIVER_H
#define __BUTTON_DEVICE_DRIVER_H #define BUTTON_LEFT_IRQ IRQ_EINT2
#define BUTTON_DOWN_IRQ IRQ_EINT3 #include <mach/irqs.h>
#include <linux/interrupt.h> struct gpioIRQ {
char *name;
unsigned int irq;
unsigned long irq_trigger; };
struct s5pv210_button_platdata {
char *name; unsigned int gpio;
unsigned int gpio_cfg;
unsigned int button_code;
struct gpioIRQ *button_irq;
}; #endif /* __ASM_ARCH_LEDSGPIO_H */

索引文献:https://www.cnblogs.com/deng-tao/p/6094049.html

最新文章

  1. Xcode7.1环境下上架iOS App到AppStore 流程③(Part 三)
  2. 用xutils3.0进行下载
  3. Android 网络图片查看器
  4. TCP/IP协议(一)
  5. JS实现星级评价
  6. Vue.js学习 Item5 -- 计算属性computed与$watch
  7. jQuery之渐变切换
  8. 1523. K-inversions(K逆序对)
  9. stm32 时钟配置&mdash;&mdash;外部时钟倍频、内部时钟倍频 【worldsing笔记】
  10. JUnit-4.11使用报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing错误
  11. 网页调用外部APP
  12. HDOJ 1326 Box of Bricks(简单题)
  13. 数塔,杭电oj-2048
  14. 利用 :before :after伪类实现鼠标悬浮动画效果
  15. 【POJ1743】Musical Theme(后缀数组)
  16. linux下压缩解压缩命令
  17. Windows 的命令行安装Scoop程序管理工具
  18. JAVA 根据设置的概率生成随机数
  19. Linux中cp直接覆盖不提示的方法
  20. console.log等不能打印全部数据/信息

热门文章

  1. Flater-Provide狀態管理
  2. spring security基本知识(四) WebSecurity
  3. 运行biggan demo
  4. Mike的农场
  5. 举个例子去理解vuex(状态管理),通俗理解vuex原理,通过vue例子类比
  6. Java——static
  7. c++复习——临考前的女娲补天 &gt;=.&lt;
  8. IIS新建项目
  9. Cmdow-一个win32窗口管理命令行工具
  10. [BZOJ3262]:陌上花开(CDQ分治)