一、输入子系统简介
输入子系统就是Linux内核专门针对输入设备创建的框架驱动。
输入子系统的主设备号都为13,我们使用input子系统处理输入设备的时候就不需要注册字符设备了。只需要定义input_device结构体即可。定义在linux/input.h中。
看结构体可知大概功能,按键的事件,按键,相对位置,绝对位置,其他,led,音频,前馈状态,开关。每一项的具体功能可以进一步追踪宏条件。比如EV_CNT都有哪些事件。
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)];
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)];
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);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
struct input_handle __rcu *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
bool going_away;
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;
};
二、输入子系统的编程
1.初始化输入子系统
1.定义输入子系统的input_dev成员。
2.申请输入设备input_allocate_device。
3.设置事件值及事件类型。
4.注销驱动的时候要释放掉设备input_unregister_device。
5.删除输入设备input_free_device。
2.上报事件
上报事件的API函数input_event.设备,事件类型,按键码,事件值
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
//基于input_event的不同类型而创建的函数
static inline void input_report_key(struct input_dev *dev,unsigned int code, int value)
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value);
static inline void input_mt_sync(struct input_dev *dev);
//同步上报事件
static inline void input_sync(struct input_dev *dev);
3.驱动程序
#include<linux/module.h>
#include<linux/init.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/slab.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/io.h>
#include<linux/ioctl.h>
#include<linux/platform_device.h>
#include<linux/irq.h>
#include<linux/of_irq.h>
#include<linux/interrupt.h>
#include<linux/input.h>
#include<linux/device.h>
#include<linux/jiffies.h>
#include<asm/uaccess.h>
#define INPUT_CNT 1
#define INPUT_MINOR 11
#define INPUT_NAME "inputkey"
#define KEYVALUE 0x01
#define INVAIL 0xff
#define input_OPEN _IOW(0xEF , 1 , int)
#define input_CLOSE _IOW(0xEF , 2 , int)
/*中断IO描述结构体*/
struct irq_keydesc{
int gpio; /*gpio号*/
int irqnum; /*中断号*/
char name[10]; /*名字*/
unsigned char value; /*中断按键值*/
irqreturn_t (*handler)(int , void*); /*中断服务函数*/
};
/*设备结构体*/
struct input_desc{
dev_t devid;
int major;
int minor;
struct class *class;
struct device *device;
struct cdev cdev;
struct device_node *nd;
struct timer_list timer;
struct irq_keydesc irq[1];
struct input_dev *inputdev;
int currentnum;
};
struct input_desc input;
/*
中断处理函数
*/
static irqreturn_t key_irq_handler(int irq , void *dev_id){
struct input_desc *dev =(struct input_desc *)dev_id;
dev->currentnum = 0;
dev->timer.data = (volatile long)dev_id;
mod_timer(&dev->timer , jiffies + msecs_to_jiffies(10));
return IRQ_RETVAL(IRQ_HANDLED);
}
/*
定时器处理函数
*/
void timer_handler(unsigned long arg){
unsigned char value;
unsigned char num;
struct input_desc *dev = (struct input_desc *)arg;
num = dev->currentnum;
value = gpio_get_value(dev->irq[0].gpio);
if(value == 1){
input_report_key(dev->inputdev , dev->irq[0].value , 1);//上报按键KEY_0
input_sync(dev->inputdev); //同步上报
}else{
input_report_key(dev->inputdev , dev->irq[0].value , 0);//上报按键KEY_0
input_sync(dev->inputdev); //同步上报
}
}
/*
驱动加载模块
*/
static int __init input_init(void){
int ret = 0;
printk("----------input_init----------\r\n");
/* 1.找结点 */
input.nd = of_find_node_by_path("/key");
if(input.nd == NULL){
goto fail_findnd;
}
/* 2.申请IO */
input.irq[0].gpio = of_get_named_gpio(input.nd , "key-gpios" , 0);
if(input.irq[0].gpio < 0){
goto fail_gpio;
}
gpio_request(input.irq[0].gpio , "KEY");
gpio_direction_input(input.irq[0].gpio);
input.irq[0].irqnum = irq_of_parse_and_map(input.nd , 0);
/* 4.申请中断 */
input.irq[0].handler = key_irq_handler;
input.irq[0].value = KEY_0;
input.irq[0].name[0] = "KEY_0";
ret = request_irq(input.irq[0].irqnum , input.irq[0].handler ,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
input.irq[0].name , &input);
if(ret < 0){
goto fail_request;
}
/* 5.初始化定时器 */
init_timer(&input.timer);
input.timer.function = timer_handler;
/* 6.申请输入子系统 */
input.inputdev = input_allocate_device();
input.inputdev->name = INPUT_NAME;
__set_bit(EV_KEY , input.inputdev->evbit);
__set_bit(EV_REP , input.inputdev->evbit);
__set_bit(KEY_0 , input.inputdev->keybit);
/* 7.注册输入设备 */
ret = input_register_device(input.inputdev);
if(ret < 0){
goto fail_register;
}
return 0;
fail_register:
printk("fail_register\r\n");
del_timer_sync(&input.timer);
free_irq(input.irq[0].irqnum ,&input);
fail_request:
printk("fail_request\r\n");
fail_gpio:
printk("fail_gpio\r\n");
fail_findnd:
printk("fail_findnd\r\n");
return -EINVAL;
}
/*
驱动注销模块
*/
static void __exit input_exit(void){
printk("----------input_exit----------\r\n");
del_timer_sync(&input.timer);
free_irq(input.irq[0].irqnum ,&input);
/*释放输入设备*/
input_unregister_device(input.inputdev);
input_free_device(input.inputdev);
}
module_init(input_init);
module_exit(input_exit);
MODULE_LICENSE("GPL");
4.应用程序
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/input.h>
static struct input_event inputevent;
/*
要求命令 ./led_app <read/write>
*/
int main(int argc, char *argv[])
{
int key_fd = 0 , ret = 0 ;
unsigned int cmd;
unsigned char arg;
unsigned char str[100];
key_fd = open("/dev/input/event2" , O_RDWR);
if(key_fd < 0){
printf("打开KEY设备结点失败\n");
return -1;
}
while(1){
ret = read(key_fd , &inputevent , sizeof(inputevent));
if(ret > 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;
}
}
}
close(key_fd);
return 0;
}
三、测试
在注册input子系统以后,在debian中发现/dev/input中产生一个event2的设备文件。
定义的副设备号为11,打印按键值为11
key 11 press
key 11 release
也可以使用hexdump指令进行测试,不用app程序。
每当按下一次按钮就会产生数据
/* 编号 */ /* tv_sec */ /* tv_usec */ /* type */ /* code */ /* value */
0000000 0c41 0000 d7cd 000c 0001 000b 0001 0000
0000010 0c41 0000 d7cd 000c 0000 0000 0000 0000
0000020 0c42 0000 54bb 0000 0001 000b 0000 0000
0000030 0c42 0000 54bb 0000 0000 0000 0000 0000