04. Linux输入子系统

Linux Input 子系统

Input 子系统层次框架
输入(Input)子系统是分层架构的,总共分为 3 层,从上到下分别是:事件处理层(Event Handler)、输入子系统核心层(Input Core)、硬件驱动层(Input Driver)。
在这里插入图片描述

  1. 硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,比如你的设备是触摸输入设备,还是鼠标输入设备,还是键盘输入设备,这些不同的设备,自然有不同的硬件操作,驱动工程师往往只需要完成这层的代码编写。
  2. 输入子系统核心层是链接其他两层之间的纽带与桥梁,向下提供硬件驱动层的接口,向上提供事件处理层的接口。
  3. 事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
    各层之间通信的基本单位就是事件,任何一个输入设备的动作都可以抽象成一种事件,如键盘的按下,触摸屏的按下,鼠标的移动等。事件有三种属性:类型(type),编码(code),值(value), Input 子系统支持的所有事件都定义在 input.h中,包括所有支持的类型,所属类型支持的编码等。事件传送的方向是 硬件驱动层–>子系统核心–>事件处理层–>用户空间
    三个重要的结构体
  • 输入设备(input_dev)
struct input_dev {
	const char *name;
	/* 标识设备驱动特征,如总线类型、生产厂商、产品类型、版本 */
	struct input_id id;
	/* 表示能产生哪类事件 */
	unsigned long evbit[NBITS(EV_MAX)];
	/* 表示能产生哪些按键 */
	unsigned long keybit[NBITS(KEY_MAX)];
	/* 表示能产生哪些相对位移事件, x,y,滚轮 */
	unsigned long relbit[NBITS(REL_MAX)];
	/* 表示能产生哪些绝对位移事件, x,y */
	unsigned long absbit[NBITS(ABS_MAX)];
	struct device dev;
	/* 用来链接他所支持的 input_handle 结构,然后用
	* input_handle 找到里面的 input_handler
	*/
	struct list_head h_list;
	/* 链接到 input_handler_list,这个链表
	* 链接了所有注册到内核的事件处理器
	*/
	struct list_head node;
	...
}
  1. evbit[BITS_TO_LONGS(EV_CNT)]数组, 这个数组以位掩码的形式,代表了这个设备支持哪类事件,比如:
#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
#define EV_SND 			0x12
#define EV_REP 			0x14 //重复类
#define EV_FF 			0x15
#define EV_PWR 			0x16
#define EV_FF_STATUS 	0x17
#define EV_MAX 			0x1f
#define EV_CNT 			(EV_MAX+1)
  1. keybit[BITS_TO_LONGS(KEY_CNT)]数组,这个数组也是以位掩码的形式,代表这个设备支持哪些按键,比如:
#define KEY_ESC 		1
#define KEY_1 		2
#define KEY_2 		3
#define KEY_3 		4
#define KEY_TAB 		15
#define KEY_ENTER 	28
#define KEY_A 		30
#define KEY_B 		48
#define KEY_C 		46
……
  1. relbit[BITS_TO_LONGS(REL_CNT)]数组,这个数组也是以位掩码的形式,代表这个设备支持哪些相对位移事件,比如:
#define ABS_X 	0x00
#define ABS_Y	0x01
  • 事件处理器(input_handler)
struct input_handler {
	/* 当事件处理器接收到了来自 input 设备传来的
	* 事件时调用的处理函数,负责处理事件。
	*/
	void (*event)(struct input_handle *handle,	unsigned int type, unsigned int code, int value);
	/* 当一个 input 设备注册到内核的时候被调用, 将事件处理器与输入设备
	* 联系起来的函数, 也就是将 input_dev 和 input_handler 配对的函数。
	*/
	int (*connect)(struct input_handler *handler, struct input_dev *dev,	const struct input_device_id *id);
	/* 与 connect 相反 */
	void (*disconnect)(struct input_handle *handle);
	/* 文件操作集,因为事件处理器要完成读写功能 */
	const struct file_operations *fops;
	/* 事件处理器所支持的 input 设备 */
	const struct input_device_id *id_table;
	/* 链接他所支持的 input_handle 结构,然后用
	* input_handle 找到里面的 input_dev
	*/
	struct list_head h_list
	/* 链接到 input_handler_list,这个链表
	* 链接了所有注册到内核的事件处理器
	*/
	struct list_head node;
};
  • 事件沟通者(input_handle)
    之所以称 input_handle(注意了,不是事件处理器 input_handler)为事件沟通者,是因为它代表一个成功配对的 input_dev 和 input_handler。 主要成员有:
struct input_handle {
	/* 每个配对的事件处理器都会分配一个对应的设备结构,
	* 如 evdev 事件处理器的 evdev 结构,注意这个结构与
	* 设备驱动层的 input_dev 不同,初始化 handle 时,保存到这里。
	*/
	void *private;
	/* 指向 input_dev 结构体实例 */
	struct input_dev *dev;
	/* 指向 input_handler 结构体实例 */
	struct input_handler *handler;
	/* input_handle 通过 d_node 连接到了 input_dev 上的 h_list 链表上 */
	struct list_head d_node;
	/* input_handle 通过 h_node 连接到了 input_handler 的 h_list 链表 */
	struct list_head h_node;
};
  • 三个结构体之间的关系
    input_dev 是硬件驱动层,代表一个 input 设备。
    通过全局的 input_dev_list 链接在一起。设备注册的时候实现这个操作。
    input_handler 是事件处理层,代表一个事件处理器。
    通过全局的 input_handler_list 链接在一起。事件处理器注册的时候实现这个操作(事件处理器一般内核自带,一般不需要我们来写)
    input_handle 个人认为属于核心层,代表一个配对的 input 设备与 input 事件处理器。
    它没有一个全局的链表,它注册的时候将自己分别挂在了 input_dev 和input_handler 的 h_list 上了。通过 input_dev 和 input_handler 就可以找到input_handle 在设备注册和事件处理器, 注册的时候都要进行配对工作,配对后就会实现链接。通过 input_handle 也可以找到 input_dev 和input_handler。
    Input 子系统核心层
    子系统核心层的功能是:向下提供硬件驱动层的接口,向上提供事件处理层的接口。
    主要函数:
/* 向内核注册一个 input 设备 */
int input_register_device(struct input_dev *dev)
/* 向内核注册一个事件处理器 */
int input_register_handler(struct input_handler *handler)
/* 向内核注册一个 handle 结构 */
int input_register_handle(struct input_handle *handle)
  • input_register_device 的主要功能是:
    初始化一些默认的值,将自己的 device 结构添加到 linux 设备模型当中,将 input_dev 添加到 input_dev_list链表中, 对于 input_handler_list 链表的每一项,都调用 input_attach_handler,它根据 input_handler 的 id_table 判断能否支持这个输入设备。
  • input_register_handler的主要功能是:
    将 handler 放入 input_handler_list 链表,对于每个 input_dev,调用 input_attach_handler,它根据 input_handler 的 id_table 判断能否支持这个输入设备,如果支持,则调用 handler->connect 函数,建立“连接”
  • input_register_handle的主要功能是:
    把 handle 结构体通过 d_node 链表项链接到 input_dev 的 h_list;把 handle 结构体通过 h_node 链表项链接到input_handler 的 h_list。这样一来,就可以通过 input_dev 的 h_list 找到 handle,进而找到 handle里的 handler;或者通过 input_handler 的 h_list 找到 handle,进而找到 handle里的 input_dev。

输入子系统框架

  1. 初始化
  • 分配input_dev结构:input_allocate_device
  • 申明可能会上报的事件类型:set_bit
  • 如果上报的是按键,申明可能上报的按键编号:set_bit
  • 注册输入型设备:input_register_device
  1. 上报
  • 上报产生的事件:input_report_key
  • 告诉核心上报结束:input_sync

输入型按键驱动编写

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>  
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/cdev.h>  
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>

#include <asm/uaccess.h>
#include <asm/errno.h>
#include <asm/io.h>


struct input_dev *button_dev = NULL;

struct button_irq_desc {  
    int irq;        /* 中断号 */  
    int pin;        /* GPIO引脚 */  
    int button_val;    /* 按键初始值 */  
    char *name;     /* 名字 */  
};

static struct button_irq_desc button_irqs [] = {  
    {IRQ_EINT(16), S5PV210_GPH2(0), KEY_A, "S1"},           /* S1 */  
    {IRQ_EINT(17), S5PV210_GPH2(1), KEY_B, "S2"},           /* S2 */  
    {IRQ_EINT(18), S5PV210_GPH2(2), KEY_C, "S3"},           /* S3 */  
    {IRQ_EINT(19), S5PV210_GPH2(3), KEY_L, "S4"},           /* S4 */  
      
    {IRQ_EINT(24), S5PV210_GPH3(0), KEY_S, "S5"},           /* S5 */  
    {IRQ_EINT(25), S5PV210_GPH3(1), KEY_ENTER, "S6"},       /* S6 */  
    {IRQ_EINT(26), S5PV210_GPH3(2), KEY_LEFTSHIFT, "S7"},   /* S7 */  
    {IRQ_EINT(27), S5PV210_GPH3(3), KEY_DELETE, "S8"},      /* S8 */  
};

static volatile int ev_press;

static struct button_irq_desc *irq_pd = NULL;

struct work_struct *button_work = NULL;

struct timer_list button_timer;

static void button_timer_function(unsigned long data)
{
    unsigned int pinval = 0;
    ev_press = 1;
    
    /* 获取产生中断的引脚值 */
    pinval = gpio_get_value(irq_pd->pin);

    if(pinval == 1)
    {
        input_report_key(button_dev, irq_pd->button_val, 1);
    }
    else
    {
        input_event(button_dev, EV_KEY, irq_pd->button_val, 0);
    }

    /* 上报同步事件 */
    input_sync(button_dev);
}


static void button_work_fun(struct work_struct *work)
{
    /* 启动定时器 */
    mod_timer(&button_timer, (jiffies + HZ/10));
}

irqreturn_t button_interrupt(int irq, void *dev_id)
{
    /* 检查是否产生中断(共享中断才用)*/
    /* 清除中断标志*/
    ev_press = 1;
    
    /* 中断下半部: 将产生中断的中断号传递到工作函数,提交工作(到内核默认的工作队列)*/
    irq_pd = (struct button_irq_desc *)dev_id;
    schedule_work(button_work);
    return IRQ_RETVAL(IRQ_HANDLED);
}

/* 驱动程序的入口函数 */
static int __init button_init(void)
{
    int i   = 0;
    int err = 0;

    /* 输入子系统初始化 */
    /* 分配input_dev结构 */
    button_dev = input_allocate_device();

    /* 申明可能会上报的事件类型 */
    set_bit(EV_KEY,        button_dev->evbit);
    set_bit(KEY_A,         button_dev->keybit);
    set_bit(KEY_B,         button_dev->keybit);
    set_bit(KEY_C,         button_dev->keybit);
    set_bit(KEY_L,         button_dev->keybit);
    set_bit(KEY_S,         button_dev->keybit);
    set_bit(KEY_ENTER,     button_dev->keybit);
    set_bit(KEY_LEFTSHIFT, button_dev->keybit);
    set_bit(KEY_DELETE,    button_dev->keybit);
    /* 注册输入型设备 */
    err = input_register_device(button_dev);
    if(err)
    {
        return -EBUSY;
    }
    /* 使用 request_irq 函数注册中断 */
    for(i = 0; i < sizeof(button_irqs) / sizeof(button_irqs[0]); i++)
    {
        err = request_irq(button_irqs[i].irq, button_interrupt, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                          button_irqs[i].name, (void *)&button_irqs[i]);
    }
    if(err)
    {
        i--;
        while(i--)
        {
            disable_irq(button_irqs[i].irq);
            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
        }
        return -EBUSY;
    }

    /* 创建工作 */
    button_work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
    INIT_WORK(button_work, button_work_fun);

    /* 初始化定时器 */
    init_timer(&button_timer);
    button_timer.function = button_timer_function;

    /* 注册定时器 */
    add_timer(&button_timer);
	return 0;
}

/* 驱动程序的出口函数 */
static void __exit button_exit(void)
{
    int i = 0;
    
    /* 注销输入型设备*/
	input_unregister_device(button_dev);
    /* 注销中断 */  
    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++)  
    {  
        free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);  
    }  
    
}

/* 用于修饰入口/出口函数,换句话说,相当于
* 告诉内核驱动程序的入口/出口函数在哪里
*/
module_init(button_init);
module_exit(button_exit);

/* 该驱动支持的协议 */
MODULE_LICENSE("GPL");

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章