linux学习之input子系统

什么是input子系统?

按键、鼠标、键盘、触摸屏等都属于输入(input)设备, Linux 内核为此专门做了一个叫做 input子系统的框架来处理输入事件。输入设备本质上还是字符设备,只是在此基础上套上了 input 框架,用户只需要负责上报输入事件,比如按键值、坐标等信息, input 核心层负责处理这些事件。

input 就是输入的意思,因此 input 子系统就是管理输入的子系统,和 pinctrl 和 gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可。为此 input 子系统分为 input 驱动层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点。

input驱动编写流程

首先,input 核心层会向 Linux 内核drivers/input/input.c 这个文件注册一个字符设备,
在这里插入图片描述
这个时候,在我们用户板卡加载了dtb镜像后,就可以在/sys/class 目录下有一个 input 子目录
在这里插入图片描述
input 子系统的所有设备主设备号都为 13,我们在使用 input 子系统处理输入设备的时候就不需要去注册字符设备了,我们只需要向系统注册一个 input_device 即可。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/input.h>


#define KEYINPUT_CNT     1
#define KEYINPUT_NAME    "keyinputdev"
#define KEY_NUM            1
#define KEY0VALUE          0X01
#define INVAKEY            0XFF


/* key结构体 */
struct irq_keydesc{
    int gpio;               /* io编号 */
    int irqnum;             /* 中断号 */
    int value;    /* 键值 */
    char name[10];          /* 名字 */
    irqreturn_t (*handler) (int, void *);  /* 中断处理函数 */
};


/* keyinputdev设备结构体 */
struct keyinput_dev{

    struct device_node *nd;
    struct irq_keydesc irqkey[KEY_NUM];
    struct timer_list timer; 

    struct input_dev *inputdev;

};

struct keyinput_dev keyinputdev; /* irq设备 */

/* 按键中断处理函数 */
static irqreturn_t key0_handler(int irq, void *dev_id)
{
    struct keyinput_dev *dev = dev_id;

    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) {
    int value = 0;
    struct keyinput_dev *dev = (struct keyinput_dev*)arg;

    value = gpio_get_value(dev->irqkey[0].gpio);
    if(value == 0)   {          /* 按下 */
        input_event(dev->inputdev,EV_KEY,KEY_0,1);
        input_sync(dev->inputdev);
    } else if(value == 1) {     /* 释放 */
        input_event(dev->inputdev,EV_KEY,KEY_0,0);
        input_sync(dev->inputdev);
    }
}


/* 按键初始化 */
static int keyio_init(struct keyinput_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-gpios", 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_to_irq(dev->irqkey[i].gpio); /* 获取中断号 */
#if 0
        dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd, i);
#endif

    }

    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value  = KEY_0;

    /* 2,按键中断初始化 */
    for(i = 0; i < KEY_NUM; i++) {
        ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, 
                         IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, 
                         dev->irqkey[i].name, &keyinputdev);
        if(ret) {
            printk("irq %d request failed!\r\n", dev->irqkey[i].irqnum);
            goto fail_irq;
        }
    }

    /* 3、初始化定时器 */
    init_timer(&keyinputdev.timer);
    keyinputdev.timer.function = 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 keyinput_init(void)
{

    int ret = 0;

    /* 初始化IO */
    ret = keyio_init(&keyinputdev);
    if(ret < 0) {
        goto fail_keyinit;
    }

    //注册inputdev
    keyinputdev.inputdev = input_allocate_device();
    if(keyinputdev.inputdev == NULL){
        ret = -EINVAL;
        goto fail_keyinit;
    }

    keyinputdev.inputdev->name = KEYINPUT_NAME;
    __set_bit(EV_KEY,keyinputdev.inputdev->evbit);  //按键事件
    __set_bit(EV_REP,keyinputdev.inputdev->evbit);  //重复事件
    __set_bit(KEY_0,keyinputdev.inputdev->keybit);  //按键值

    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 keyinput_exit(void)
{
    int i = 0;
    /*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);

    /*注销input*/
    input_unregister_device(keyinputdev.inputdev);
    input_free_device(keyinputdev.inputdev);
}

module_init(keyinput_init);
module_exit(keyinput_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

该代码实现的功能是通过input子系统讲外部按键的信息上报处理,key节点需要自己创建或者使能linux自带的按键驱动程序
配置menui的路径如下:
-> Device Drivers
-> Input device support
-> Generic input layer (needed for keyboard, mouse, …) (INPUT [=y])
-> Keyboards (INPUT_KEYBOARD [=y])
->GPIO Buttons

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值