input子系统

本文介绍了Linux Input子系统的原理及实现方法,详细解释了input_dev结构体及其成员,并通过一个按键驱动实例展示了如何使用Input子系统。适用于希望深入了解Linux输入设备驱动的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、简介

1.1 概述

为输入设备(按键、键盘、触摸屏)的驱动规范完成上报输入信息任务的子系统。input是对字符设备驱动的另一种封装。
input子系统在驱动中不是必须的,它的存在只是规范了上报输入信息这一任务,减少驱动与应用开发工程师的沟通成本。
input子系统是输入设备驱动一个标准,一个约定俗成的规范,几乎所有输入设备驱动都是使用input来上报输入信息的

在Linux中,Input子系统由三大部分组成,它们是Input子系统核心层、Input子系统事件处理层和Input子系统设备驱动层。在通常情况下,Input子系统核心层和Input子系统事件处理层都已经实现了,而作为驱动开发者,我们仅仅只需要完成Input子系统设备驱动层。

1.2 编写流程

1、定义一个input_dev结构体
2、申请input_dev内存空间并初始化
3、填充input_dev部分成员
4、向core注册一个input_dev
5、获取键码并上报按键(一般在中断里)
注:input是对字符设备驱动的封装,它在底层实现了file_operations这一套机制而不用我们去填充了,只需按照以上流程即可完成驱动

1.3 主要结构体
//linux/input.h
struct input_dev{
    const char *name;       //设备名
    const char *phys;       //设备在系统中路径
    const char *uniq;
    struct input_id id;     //用于匹配input hander参数
    unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
    //设备所支持事件类型,主要有EV_SYNC,EV_KEY,EV_REL,EV_ABS等                
    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_slot *mt;
    int mtsize;
    int slot;
    int trkid;
    struct input_absinfo *absinfo;
    //按键对应的键值
    unsigned long key[BITS_TO_LONGS(KEY_CNT)];                                 
    //LED对应的指示灯状态
    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);
    //事件处理函数,主要是接收用户下发的命令,如点亮led;
    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;
    bool sync;
    struct device dev;
    //设备所支持的input handle; 
    struct list_headh_list;                    
    struct list_headnode;
};

evbit的值主要有

EV_SYN 同步事件
EV_KEY 按键事件
EV_REL 相对坐标事件
EV_ABS 绝对坐标事件

keybit的值主要有

#define     KEY_Q                   16
#define     KEY_W                   17
#define     KEY_E                   18
#define     KEY_R                   19
#define     KEY_T                   20
1.3.2 input_dev的赋值

1.3.2.1 直接赋值

evbit[0]= BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY);
keybit[BIT_WORD(KEY_D)] |= BIT_MASK(KEY_D);

1.3.2.2 函数赋值

set_bit(nr,addr);
参数nr:要置1的那个位;
参数addr:数组首地址

set_bit( EV_SYN , evbit);
set_bit( EV_KEY , evbit);

set_bit( KEY_D , keybit);
1.4 主要方法
//申请、初始化input_dev
struct input_dev *input_allocate_device(void);
//注册input_dev
int input_register_device(struct input_dev *dev);
//注销input_dev
void input_unregister_device(struct input_dev *dev);
//上报
input_report_key(struct input_dev *dev, unsigned int code, int value);
code: 键码(填充结构体时已经注册支持)
value:按下1 或 抬起0
//同步
input_sync(struct input_dev *dev);

2、demo 基于iTop4412

不完整的demo 主要介绍input使用流程

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <asm/atomic.h>

#include <linux/input.h>

#define DEMO_DEBUG
#ifdef  DEMO_DEBUG
#define dem_dbg(fmt, arg...)        printk(KERN_WARNING fmt, ##arg)
#else
#define dem_dbg(fmt, arg...)        printk(KERN_DEBUG fmt, ##arg)
#endif

#define KEY_CODE    KEY_LEFT

static struct input_dev *input_key;
static int open_flag = 0;
static int dev_id = 123;
static int irq;

//中断回调
static irqreturn_t eint9_interrupt(int irq, void *dev_id) {
    int gpio_val = 0;
    printk("%s(%d)\n", __FUNCTION__, __LINE__);

    /*延时去抖的改进,两次获取电平,如果是抖动直接返回*/
    gpio_val = gpio_get_value(EXYNOS4_GPX1(1));
    msleep(50);
    if(gpio_val != gpio_get_value(EXYNOS4_GPX1(1)))
        return IRQ_HANDLED;

    if(gpio_val == 0){  //汇报按下事件
        input_report_key(input_key, KEY_CODE, 1);

    }else{  //汇报抬起事件
        input_report_key(input_key, KEY_CODE, 0);
    }
    //汇报同步事件
    input_sync(input_key);

    return IRQ_HANDLED;
}

static int key_open(struct input_dev *dev)
{
    dem_dbg("==> %s\n", __FUNCTION__);

    if(open_flag++ == 0)
        enable_irq(IRQ_EINT(19));       //打开中断
    return 0;
}

static void key_close(struct input_dev *dev)
{
    dem_dbg("==> %s\n", __FUNCTION__);

    if(open_flag-- == 0)
        disable_irq(IRQ_EINT(19));  
}

static int key_probe(struct platform_device *pdev)
{
    int retval;
    struct resource *res = NULL;

    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (res == NULL) {
        dem_dbg("==> no memory resource specified\n");
        return PTR_ERR(res);
    }

    irq = res->start;

    //申请中断
    retval = request_irq(irq, eint9_interrupt,
            IRQ_TYPE_EDGE_FALLING /*IRQF_TRIGGER_FALLING*/, "eint9", (void *)&dev_id);
    if (retval < 0) {
        printk("Request IRQ %d failed, %d\n", IRQ_EINT(9), retval);
        goto exit;
    }

    disable_irq(irq);   //中断关闭

    //1 申请input_dev结构体变量空间
    input_key = input_allocate_device();
    if (!input_key) {
        dem_dbg("==> allocate input device failed!\n");
        retval = -ENOMEM;
        goto input_alloc_err;
    }

    //2 input_dev结构体变量初始化
    input_key->name = "key";            //查看cat /proc/bus/input/devices
    input_key->phys = "inputkey";
    input_key->id.bustype = BUS_HOST;
    input_key->id.vendor = 0x0001;
    input_key->id.product = 0x0001;
    input_key->id.version = 0x0100;

    //指定input设备所支持的事件:按键事件、同步事件、连续长按键事件
    input_key->evbit[0] = BIT(EV_KEY) | BIT(EV_SYN) | BIT(EV_REP);

    //指定input设备支持汇报的键值: KEY_CODE宏指定的KEY_LEFT
    set_bit(KEY_CODE, input_key->keybit);

    input_key->open =   key_open;
    input_key->close = key_close;

    //3 input_dev结构体的注册
    retval = input_register_device(input_key);
    if (retval) {
        dem_dbg("==> Failed to register input device!\n");
        goto input_regs_err;
    }
    return 0;


input_regs_err:
    input_free_device(input_key);

input_alloc_err:
    free_irq(irq, (void *)&dev_id);

exit:
    return retval;
}

static int key_remove(struct platform_device *dev)
{
    dem_dbg("==> in module exit function\n");

    free_irq(irq, (void *)&dev_id);

    //4 input_dev结构体的注销和释放
    input_unregister_device(input_key);
    input_free_device(input_key);

    return 0;
}

static struct platform_driver key_driver = {
    .probe = key_probe,
    .remove = key_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "demo_input",
    },
};

static int __init demo_init(void)
{   
    dem_dbg("==>demo_init\n");

    return platform_driver_register(&key_driver);
}

static void __exit demo_exit(void)
{
    dem_dbg("==>demo_exit\n");

    platform_driver_unregister(&key_driver);

}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("Dual BSD/GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值