Linux驱动学习之input子系统

  • 简介

  1. input 子系统就是管理输入的子系统,和pinctrl、gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。
  2. 按键、鼠标、键盘、触摸屏等都属于输入设备,linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。
  3. 输入设备本质上还是字符设备,只是在此基础上套上了input框架,用户只需要负责上报输入事件,比如按键值、坐标等信息。
  4. 对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可
  5. 为此input子系统分为 input驱动层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点

input子系统框架

                                         Linux驱动学习之input子系统_第1张图片

编写input驱动我们只关心内核层,在内核层注册input子系统,然后进行事件上报就可以了。

  • input驱动实现 

  • 向内核注册input设备 

input子系统的所有设备主设备号都为 13,我们在使用input子系统处理输入设备的时候就不需要去注册字符设备了,我们只需要向系统注册一个input_device即可 

*****************input_dev****注册流程

  • 定义input_dev结构体
struct input_dev * input_dev;
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)];    //led灯
	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;                    //设备关联的反馈结构,如果设备支持

	struct input_dev_poller *poller;           

	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)];    //反应设备当前的led状态
	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);//事件处理函数,主要是接收用户下发的命令,如点亮led 

	struct input_handle __rcu *grab;  //当前占有设备的input_handle

	spinlock_t event_lock;   //事件锁
	struct mutex mutex;     //互斥锁

	unsigned int users;      //打开该设备的用户数量(input_handle)
	bool going_away;        //标记正在销毁的设备

	struct device dev;        //一般设备

	struct list_head	h_list;    //设备所支持的input handle
	struct list_head	node;        //用于将此input_dev连接到input_dev_list

	unsigned int num_vals;        //当前帧中排队的值数
	unsigned int max_vals;        //队列最大的帧数
	struct input_value *vals;        //当前帧中排队的数组

	bool devres_managed;            //表示设备被devres 框架管理,不需要明确取消和释放

	ktime_t timestamp[INPUT_CLK_MAX];
}

  •  初始化input_dev结构体
input_dev=input_allocate_device();

 input_allocate_device()函数,给input_dev分配空间,并作了部分初始化。

  • 添加监测设备的信息
    set_bit(EV_KEY, input_dev->evbit);
    set_bit(EV_REP, input_dev->evbit);
    set_bit(KEY_1,input_dev->keybit);

事件类型

define EV_SYN            0x00    /* 同步事件     */ 
define EV_KEY            0x01    /* 按键事件     */ 
define EV_REL            0x02    /* 相对坐标事件   */ 
define EV_ABS            0x03    /* 绝对坐标事件   */ 
define EV_MSC            0x04    /* 杂项(其他)事件   */ 
define EV_SW             0x05    /* 开关事件     */ 
define EV_LED            0x11    /* LED       */ 
define EV_SND            0x12    /* sound(声音)   */ 
define EV_REP            0x14    /* 重复事件     */ 
define EV_FF             0x15    /* 压力事件     */ 
define EV_PWR            0x16    /* 电源事件     */ 
define EV_FF_STATUS      0x17    /* 压力状态事件   */ 

支持以下按键(部分) 

#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
#define KEY_6           7
#define KEY_7           8
#define KEY_8           9
#define KEY_9           10
#define KEY_0           11

向核心结构体里添加了按键,并且可连续触发,按键code为1。

  • 注册input设备
ret=input_register_device(input_dev);

参数为定义的input_dev结构体

返回值:成功0/失败非0

  • 上传事件
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)

参数1:核心结构体,

参数2:事件类型,

参数3:事件编码,

参数4:事件值 

无返回值

  • 同步事件

此操作必须有,否则缓冲区不满,返回不了当前操作。

 void input_sync(struct input_dev *dev)

参数:核心结构体

无返回值

此函数也是调用了input_event(); 

Linux驱动学习之input子系统_第2张图片

代码实列

#include "linux/input.h"
#include "linux/gpio.h"
#include "linux/input-event-codes.h"
#include "linux/interrupt.h"
#include "linux/irq.h"
#include "linux/jiffies.h"
#include "linux/module.h"
#include "linux/of_gpio.h"
#include "linux/platform_device.h"
#include "linux/timer.h"
#include "linux/types.h"

int pin;
int irq;
struct input_dev * input_dev;
struct timer_list timer;
uint8_t value;
irqreturn_t fun_callback(int irq, void *arg)
{
     value=gpio_get_value(pin);
    mod_timer(&timer,jiffies+msecs_to_jiffies(10));
    return 0;
}
void func(struct timer_list *tl)
{
  
    if(value)
    {
        input_event(input_dev,EV_KEY,KEY_1,1);
    }
    else
    {
        input_event(input_dev,EV_KEY,KEY_1,0);
    }
    input_sync(input_dev);
}
static int probe(struct platform_device *pd)
{
    pin=of_get_named_gpio(pd->dev.of_node,"key_pin",0);
    printk("pin=%d\r\n",pin);
    gpio_request(pin,"key_pim");
    gpio_direction_input(pin);
    irq= platform_get_irq(pd,0);
    printk("irq=%d\r\n",irq);
    int ret=  devm_request_irq(&pd->dev, irq,fun_callback,IRQ_TYPE_EDGE_BOTH,"key_irq",NULL);
    if(ret)
    {

    }
    input_dev=input_allocate_device();
    set_bit(EV_KEY, input_dev->evbit);
    set_bit(EV_REP, input_dev->evbit);
    set_bit(KEY_1,input_dev->keybit);
    ret=input_register_device(input_dev);
    if(ret)
    {

    }
    timer_setup(&timer,func, 0);
    return 0;
}
struct of_device_id match={
    .compatible="key",

};
struct platform_driver drv={
    .driver = {
        .name = "key",
        .of_match_table=&match,
    },
    .probe=probe,
};
int __init input_init(void)
{
     
    return platform_driver_register(&drv);
}
void __exit input_exit(void)
{

}
module_init(input_init);
module_exit(input_exit);
MODULE_LICENSE("GPL");

你可能感兴趣的:(Linux驱动学习,学习)