输入(input)子系统基础概念
统一管理外部输入设备
- 按键
- 键盘
- 鼠标
- 触摸屏
…
用户空间接口
- /dev/input/event0/1/2/…
- /dev/input/mouse0/1/2/…
- /dev/input/sj0/1/2/…
- …
分层模型
-
核心层
- 创建input设备类
- 根据输入设备种类、分发事件到不同事件处理器
-
事件处理层
提供具体设备的操作接口,为输入设备(input_dev)创建具体设备文件
-
通用事件处理器(drivers/input/evdev.c)
-
鼠标事件处理器(drivers/input/mousedev.c)
-
摇杆事件处理器(drivers/input/joydev.c)
-
…
-
创建input设备类
input_init()函数
drivers/input/input.c
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 = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),
INPUT_MAX_CHAR_DEVICES, "input");
if (err) {
pr_err("unable to register char major %d", INPUT_MAJOR);
goto fail2;
}
return 0;
fail2: input_proc_exit();
fail1: class_unregister(&input_class);
return err;
}
input_class定义
drivers/input/input.c
struct class input_class = {
.name = "input",
.devnode = input_devnode,
};
INPUT_MAJOR定义
include/uapi/linux/major.h
#define INPUT_MAJOR 13
INPUT_MAX_CHAR_DEVICES定义
drivers/input/input.c
#define INPUT_MAX_CHAR_DEVICES 1024
input_dev结构体
include/linux/input.h
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
...
int (*open)(struct input_dev *dev);
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 device dev;
struct list_head h_list;
struct list_head node;
unsigned int num_vals;
unsigned int max_vals;
struct input_value *vals;
bool devres_managed;
};
-
name:输入设备名
-
id:输入设备与事件处理器的匹配信息
-
evbit:指定支持的事件类型
- 同步事件、按键事件、坐标事件…
-
keybit:指定支持的按键值类型
- key1、key2…
-
relbit:指定支持相对坐标类型
- x轴、y轴、z轴、滑轮…
-
absbit:指定支持绝对坐标类型
- x轴、y轴、z轴、滑轮…
事件类型
include/linux/input.h
#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02
#define EV_ABS 0x03
...
#define EV_CNT (EV_MAX+1)
按键值类型
include/linux/input.h
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
#define KEY_3 4
#define KEY_4 5
#define KEY_5 6
...
注册/销毁输入设备
input_allocate_device()函数
drivers/input/input.c
分配并初步初始化 input_dev 结构体变量
struct input_dev *input_allocate_device(void)
input_register_device()函数
drivers/input/input.c
向系统注册输入设备
int input_register_device(struct input_dev *dev)
input_unregister_device()函数
drivers/input/input.c
向系统注释输入设备
void input_unregister_device(struct input_dev *dev)
input_free_device()函数
drivers/input/input.c
释放 input_dev 结构体变量
void input_free_device(struct input_dev *dev)
上报输入事件
input_event()函数
drivers/input/input.c
通用事件上报接口(上报给用户空间)
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
参数:
-
dev:需要上报信息的输入设备
-
type:上报的具体输入事件类型
-
按键输入类型:EV_KEY
-
坐标输入类型:EV_REL、EV_ABS
-
特殊类型:EV_SYN
- 同步事件:通知用户空间的程序接收消息
-
…
-
-
code:记录输入事件类型中的具体事件
-
键盘发生按键输入类型事件时,记录键盘那个值被按下
-
…
-
-
value:具体事件的对应值
-
按键按下,value值为1;按键松开,value值为0
-
…
-
返回值:无
input_report_key()函数
include/linux/input.h
按键事件上报接口
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value){ input_event(dev, EV_KEY, code, !!value);}
input_report_rel()函数
include/linux/input.h
相对坐标事件上报接口
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value){ input_event(dev, EV_REL, code, value);}
input_report_abs()函数
include/linux/input.h
绝对坐标事件上报接口
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value){ input_event(dev, EV_ABS, code, value);}
input_sync()函数
include/linux/input.h
同步事件上报接口
static inline void input_sync(struct input_dev *dev){ input_event(dev, EV_SYN, SYN_REPORT, 0);}
输入(input)子系统实验
通用输入设备(evdev.c)识别
- /dev/input/event0
- /dev/input/event1
- …
查看输入设备名和event对应关系
cat /proc/bus/input/devices
BIT_MASK宏
include/asm-generic/bitops/non-atomic.h
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
input_set_capability()函数
drivers/input/input.c
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
switch (type) {
case EV_KEY:
__set_bit(code, dev->keybit);
break;
case EV_REL:
__set_bit(code, dev->relbit);
break;
case EV_ABS:
input_alloc_absinfo(dev);
if (!dev->absinfo)
return;
__set_bit(code, dev->absbit);
break;
...
}
输入(input)子系统实验源码:button_irq.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/input.h>
#include <asm/uaccess.h>
struct device_node *button_device_node = NULL; //定义按键设备节点结构体
unsigned button_GPIO_number = 0; //保存button使用的GPIO引脚编号
u32 interrupt_number = 0; // button 引脚中断编号
/*input 子系统相关内容*/
#define BUTTON_NAME "input_button"
struct input_dev *button_input_dev = NULL; //定义按键对应的输入子系统结构体
/*按键中断处理函数*/
static irqreturn_t button_irq_hander(int irq, void *dev_id)
{
int button_satus = 0;
/*读取按键引脚的电平,根据读取得到的结果输入按键状态*/
button_satus = gpio_get_value(button_GPIO_number);
if(0 == button_satus)
{
input_report_key(button_input_dev, KEY_1, 0);
input_sync(button_input_dev);
}
else
{
input_report_key(button_input_dev, KEY_1, 1);
input_sync(button_input_dev);
}
return IRQ_HANDLED;
}
/*
*驱动初始化函数
*/
static int __init button_driver_init(void)
{
int error;
printk(KERN_ERR"button_driver_init \n");
/*获取按键 设备树节点*/
button_device_node = of_find_node_by_path("/button_interrupt");
if (NULL == button_device_node)
{
printk(KERN_ERR "of_find_node_by_path error!");
return -1;
}
/*获取按键使用的GPIO*/
button_GPIO_number = of_get_named_gpio(button_device_node, "button_gpio", 0);
if (0 == button_GPIO_number)
{
printk(KERN_ERR"of_get_named_gpio error");
return -1;
}
/*申请GPIO , 记得释放*/
error = gpio_request(button_GPIO_number, "button_gpio");
if (error < 0)
{
printk(KERN_ERR "gpio_request error");
gpio_free(button_GPIO_number);
return -1;
}
error = gpio_direction_input(button_GPIO_number); //设置引脚为输入模式
/*获取中断号*/
interrupt_number = irq_of_parse_and_map(button_device_node, 0);
printk(KERN_ERR"\n interrupt_number = %d \n", interrupt_number);
/*申请中断, 记得释放*/
error = request_irq(interrupt_number, button_irq_hander, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_interrupt", NULL);
if (error != 0)
{
printk(KERN_ERR "request_irq error:%d\r\n",error);
gpio_free(button_GPIO_number);
free_irq(interrupt_number, NULL);
return -1;
}
/*申请输入子系统结构体*/
button_input_dev = input_allocate_device();
if (NULL == button_input_dev)
{
printk(KERN_ERR "input_allocate_device error");
return -1;
}
button_input_dev->name = BUTTON_NAME;
/*设置要使用的输入事件类型*/
button_input_dev->evbit[0] = BIT_MASK(EV_KEY);
/*标记设备能够触发的具体事件代号*/
input_set_capability(button_input_dev, EV_KEY, KEY_1);
/*注册输入设备*/
error = input_register_device(button_input_dev);
if (0 != error)
{
printk(KERN_ERR "input_register_device error:%d",error);
gpio_free(button_GPIO_number);
free_irq(interrupt_number, NULL);
input_unregister_device(button_input_dev);
return -1;
}
return 0;
}
/*
*驱动注销函数
*/
static void __exit button_driver_exit(void)
{
printk(KERN_ERR "button_driver_exit\n");
/*释放申请的引脚,和中断*/
gpio_free(button_GPIO_number);
free_irq(interrupt_number, NULL);
/*释放输入子系统相关内容*/
input_unregister_device(button_input_dev);
input_free_device(button_input_dev);
}
module_init(button_driver_init);
module_exit(button_driver_exit);
MODULE_LICENSE("GPL");
输入(input)子系统实验源码:app.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/input.h>
#include <asm/uaccess.h>
struct device_node *button_device_node = NULL; //定义按键设备节点结构体
unsigned button_GPIO_number = 0; //保存button使用的GPIO引脚编号
u32 interrupt_number = 0; // button 引脚中断编号
/*input 子系统相关内容*/
#define BUTTON_NAME "input_button"
struct input_dev *button_input_dev = NULL; //定义按键对应的输入子系统结构体
/*按键中断处理函数*/
static irqreturn_t button_irq_hander(int irq, void *dev_id)
{
int button_satus = 0;
/*读取按键引脚的电平,根据读取得到的结果输入按键状态*/
button_satus = gpio_get_value(button_GPIO_number);
if(0 == button_satus)
{
input_report_key(button_input_dev, KEY_1, 0);
input_sync(button_input_dev);
}
else
{
input_report_key(button_input_dev, KEY_1, 1);
input_sync(button_input_dev);
}
return IRQ_HANDLED;
}
/*
*驱动初始化函数
*/
static int __init button_driver_init(void)
{
int error;
printk(KERN_ERR"button_driver_init \n");
/*获取按键 设备树节点*/
button_device_node = of_find_node_by_path("/button_interrupt");
if (NULL == button_device_node)
{
printk(KERN_ERR "of_find_node_by_path error!");
return -1;
}
/*获取按键使用的GPIO*/
button_GPIO_number = of_get_named_gpio(button_device_node, "button_gpio", 0);
if (0 == button_GPIO_number)
{
printk(KERN_ERR"of_get_named_gpio error");
return -1;
}
/*申请GPIO , 记得释放*/
error = gpio_request(button_GPIO_number, "button_gpio");
if (error < 0)
{
printk(KERN_ERR "gpio_request error");
gpio_free(button_GPIO_number);
return -1;
}
error = gpio_direction_input(button_GPIO_number); //设置引脚为输入模式
/*获取中断号*/
interrupt_number = irq_of_parse_and_map(button_device_node, 0);
printk(KERN_ERR"\n interrupt_number = %d \n", interrupt_number);
/*申请中断, 记得释放*/
error = request_irq(interrupt_number, button_irq_hander, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "button_interrupt", NULL);
if (error != 0)
{
printk(KERN_ERR "request_irq error:%d\r\n",error);
gpio_free(button_GPIO_number);
free_irq(interrupt_number, NULL);
return -1;
}
/*申请输入子系统结构体*/
button_input_dev = input_allocate_device();
if (NULL == button_input_dev)
{
printk(KERN_ERR "input_allocate_device error");
return -1;
}
button_input_dev->name = BUTTON_NAME;
/*设置要使用的输入事件类型*/
button_input_dev->evbit[0] = BIT_MASK(EV_KEY);
/*标记设备能够触发的具体事件代号*/
input_set_capability(button_input_dev, EV_KEY, KEY_1);
/*注册输入设备*/
error = input_register_device(button_input_dev);
if (0 != error)
{
printk(KERN_ERR "input_register_device error:%d",error);
gpio_free(button_GPIO_number);
free_irq(interrupt_number, NULL);
input_unregister_device(button_input_dev);
return -1;
}
return 0;
}
/*
*驱动注销函数
*/
static void __exit button_driver_exit(void)
{
printk(KERN_ERR "button_driver_exit\n");
/*释放申请的引脚,和中断*/
gpio_free(button_GPIO_number);
free_irq(interrupt_number, NULL);
/*释放输入子系统相关内容*/
input_unregister_device(button_input_dev);
input_free_device(button_input_dev);
}
module_init(button_driver_init);
module_exit(button_driver_exit);
MODULE_LICENSE("GPL");