input 就是输入的意思,因此 input 子系统就是管理输入的子系统,和 pinctrl、gpio 子系统
一样,都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等
等这些都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息,
鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,对于驱动编写者而言不需要去关心
应用层的事情,我们只需要按照要求上报这些输入事件即可。
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define keyinputdev_CNT 1 /* 设备号个数 */
#define keyinputdev_NAKE "ketinput" /* 名字 */
#define KEY_NUM 1//KEY按键数量
#define KEY0VALUE 0X01 /* KEY0按键值 */
#define INVAKEY 0XFF /* 无效的按键值 */
//key结构体
struct irq_keydesc{
int gpio; //io编号
int irqnum; //中断号
unsigned char value; //键值
char name[10]; //名字
irqreturn_t (*handler) (int, void *) //中断处理函数
};
/* keyinputdev设备结构体 */
struct keyinputdev_dev{
struct devide_node *nd; //设备节点
struct irq_keydesc irqkey[KEY_NUM]; //描述按键数组
struct timer_list timer; //定义一个定时器
struct input_dev *inputdev; //输入设备 已经在内核里面的文件定义好
};
struct keyinputdev_dev keyinputdev; /* keyinputdev设备 */
//中断处理函数
static irqreturn_t key0_handler(int irq, void *dev_id)
{
struct keyinputdev_dev *dev = (struct keyinputdev_dev *)dev_id;
/*value = gpio_get_value(dev->irqkey[0].gpio);
if(value == 0) {//按下
printk("KEY0 Push!\r\n");
}else if(value == 1){ //释放
printk("KEY0 release!\r\n");
}*/
dev->timer.data = (volatile long)dev_id;
mod_timer(&dev->timer,jiffies + msecs_to_jiffies(20)); //20ms定时,消抖
return IRQ_HANDLED;
}
//定时器消抖函数
static void timer_func(unsigned long arg){ //20ms结束后此函数执行
int value = 0;
struct keyinputdev_dev *dev = (struct keyinputdev_dev*)arg;
//printk("timer_func\r\n");
value = gpio_get_value(dev->irqkey[0].gpio); //读取io值
if(value == 0) {//按下
//上报按键值
input_event(dev->inputdev, EV_KEY, KEY_0, 1);/* 最后一个参数表示按下还是松开,1为按下,0为松开 */
//input_report_key(dev->inputdev, keydesc->value, 1);/* 最后一个参数表示按下还是松开,1为按下,0为松开 */
input_sync(dev->inputdev); //告诉 Linux 内核 input 子系统上报结束
}else if(value == 1){ //释放
//上报按键值
input_event(dev->inputdev, EV_KEY, KEY_0, 1);/* 最后一个参数表示按下还是松开,1为按下,0为松开 */
//input_report_key(dev->inputdev, keydesc->value, 0);
input_sync(dev->inputdev);
}
}
//按键初始化
static int keyio_init(struct keyinputdev_dev *dev)
{
int ret = 0;
int i = 0;
//1.按键初始化
dev->nd = of_find_node_by_path("/key");
if(dev->nd == NULL){
ret = -EINVAL;
goto fail_nd;
}
for(i=0; i<KEY_NUM; i++){
dev->irqkey[i].gpio = of_get_named_gpio(dev->nd,"key-gpio",i);
}
for(i=0;i<KEY_NUM;i++){
memset(dev->irqkey[i].name,0,sizeof(dev->irqkey[i].name));
sprintf(dev->irqkey[i].name,"KEY%d",i); //名字
gpio_request(dev->irqkey[i].gpio,dev->irqkey[i].name);
gpio_direction_input(dev->irqkey[i].gpio);
// dev->irqkey[i].irqnum = gpio_toirq(dev->irqkey[i].gpio);//获取中断号
dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd,i); //获取中断号
}
//2.按键中断初始化
/* 申请中断 */
dev->irqkey[0].handler = key0_handler;
dev->irqkey[0].value = KEY_0;
for(i=0;i<KEY_NUM;i++){
ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler,
IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, dev->irqkey[i].name, &keyinputdev);
if(ret){
printk("irq %d request faild!\r\n",dev->irqkey[i].irqnum);
goto fail_irq;
}
}
//3.初始化定时器
init_timer(&keyinputdev.timer);;
keyinputdev.timer.function = timer_func;
keyinputdev.timer.data = (unsigned long)&keyinputdev; //传递给timer_func函数的一个参数
return 0;
fail_irq:
for(i=0;i<KEY_NUM;i++){
gpio_free(dev->irqkey[i].gpio);
}
fail_nd:
return ret;
}
static int __init keyinputdev_init(void)
{
int ret = 0;
//1.初始化IO
ret = keyio_init(&keyinputdev);
if(ret < 0){
printk("keyio_init fail\r\n");
}
//2.申请input_dev输入设备
keyinputdev.inputdev = input_allocate_device();
if(keyinputdev.inputdev == NULL){
ret = -EINVAL;
goto fail_keyinit;
}
keyinputdev.inputdev->name = keyinputdev_NAKE;
__set_bit(EV_KEY,keyinputdev.inputdev->evbit); //按键事件,evbit 表示输入事件类型
__set_bit(EV_REP,keyinputdev.inputdev->evbit); //重复事件
__set_bit(KEY_0,keyinputdev.inputdev->keybit); //keybit是按键事件使用的位图
//注册input_dev输入设备
ret = input_register_device(keyinputdev.inputdev);
if (ret) {
goto fail_input_register;
}
return 0;
fail_input_register:
input_free_device(keyinputdev.inputdev);
fail_keyinit:
return ret;
}
static void __exit keyinputdev_exit(void)
{
int i;
//1.释放中断
for(i=0;i<KEY_NUM;i++){
free_irq(keyinputdev.irqkey[i].irqnum,&keyinputdev);
}
//2.释放io
for(i=0;i<KEY_NUM;i++){
gpio_free(keyinputdev.irqkey[i].gpio);
}
//3.删除定时器
del_timer_sync(&keyinputdev.timer);
//4.注销input_dev
input_unregister_device(keyinputdev.inputdev);
input_free_device(keyinputdev.inputdev);
}
module_init(keyinputdev_init);
module_exit(keyinputdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("deng");
因为是Linux 内核针对某一类设备而创建的框架,所以不需要我自己去创建设备
应用程序为:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
#include <linux/input.h>
//input_event结构体变量
static struct input_event inputevent;
int main(int argc, char *argv[])
{
int fd,err;
int ret = 0;
char *filename;
// unsigned char data;
if (argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("Can't open file %s\r\n", filename);
return -1;
}
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;
/* 其他类型的事件,自行处理 */
case EV_REL:
break;
case EV_ABS:
break;
case EV_MSC:
break;
case EV_SW:
break;
}
} else {
printf("读取数据失败\r\n");
}
}
close(fd);
return ret;
}
执行结果为: