驱动:10.2 input子系统

 1)按键键值标准化的问题
 2)按键键值缓冲区的问题
    它本质上应该是一个循环缓冲队列
    用户进程对该缓冲区执行读操作
    中断处理过程要对该缓冲区执行写操作
    它属于共享资源,如何解决竞态?
    建议尝试编程
 3) 按键按住不放
    按下保存了键值后 开启一个新的定时器
    该定时器隔一段时间就向按键缓冲区存储一个键值 ...
    当释放按键动作产生时停止该定时器
    建议尝试

1.什么是input子系统

它是内核中的一部分代码

Linux中随着支持的硬件设备越来越多
发现有一类设备,只有输入没有输出 例如:键盘 鼠标 触摸屏游戏摇杆 。。。
完成其驱动程序时,都涉及到 设备号 cdev注册 设备文件的创建 需要实现阻塞、非阻塞方式访问
键值玄幻缓冲队列的实现
按住不放的逻辑

在内核中将以上逻辑硬件无关的部分由内核实现
驱动程序开发人员开发输入设备硬件驱动程序时,
可以复用该部分代码,只需要关注硬件相关代码逻辑实现
从而简化了输入设备驱动编程工作

2.如何使用input子系统

核心数据结构

struct input_dev{
	const char *name;
	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
	unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
	...
}

使用步骤:

1)分配一个input_dev变量空间
	struct input_dev *input_allocate_device(void)
2)设置input_dev变量
	两个变量
		dev->evbit
			该设备支持那些事件类型
		dev->keybit
			该设备会报告的按键编码
	一个函数
	void __set_bit(int nr, volatile unsigned long *addr)
		nr,键值,代表要操作的 *addr 的第 nr 位
		addr,evbit/keybit,代表要操作的状态位
3)注册input_dev变量
	int input_register_device(struct input_dev *dev)
4)硬件操作
	注册中断
	延时去抖
	判断是哪个按键触发的 是按下触发还是释放触发
5)报告事件  //保存键值  唤醒睡眠进程
	void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value)
		dev, 哪个设备报告事件
		type,报告的事件类型 (必须是设备支持的)
		code, 当事件类型为EV_KEY时,code指的就是按键编码值 (必须是设备支持的) 
		value, 当事件类型为EV_KEY时,1:按下动作 0:释放动作 2:按住不放
6)注销input_dev变量
	void input_unregister_device(struct input_dev *dev)
7)释放input_dev变量空间
	void input_free_devie(struct input_dev *dev);

示例代码:
vi btn_drv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <mach/platform.h>

MODULE_LICENSE("GPL");

struct input_dev *btn_input;
typedef struct btn_desc
{
    unsigned short code;//按键编码值
    char *name;
    int irq; 
    int gpio;
}btn_desc_t;
btn_desc_t buttons[]={
    {KEY_UP, "up", IRQ_GPIO_A_START+28, PAD_GPIO_A+28},
    {KEY_RIGHT, "right", IRQ_GPIO_B_START+9, PAD_GPIO_B+9},
};

struct timer_list btn_timer;

irqreturn_t btn_isr(int irq, void *dev)
{
    /*传递按键描述信息*/
    btn_timer.data = (unsigned long)dev;
    /*启动延时去抖*/
    mod_timer(&btn_timer, jiffies+HZ/100);

    return IRQ_HANDLED;
}
void btn_timer_func(unsigned long data)
{
    int stat = 0;
    /*获取哪个按键触发的中断*/
    btn_desc_t *pdata = (btn_desc_t *)data;
    /*判断是按下触发还是释放触发*/
    stat = gpio_get_value(pdata->gpio);
    /*5报告事件*/
    /*描述了完整的按键信息*/
    input_event(btn_input, EV_KEY, pdata->code, !stat);
    /*报告完毕*/
    input_event(btn_input, EV_SYN, 0, 0);
}

int __init btn_drv_init(void)
{
    int i = 0;
    int ret = 0;
    /*1 申请input_dev变量空间*/
    btn_input = input_allocate_device();
    /*2 设置input_dev变量*/
    btn_input->name = "mybuttons";
    //段错误
    //strcpy(btn_input->name, "mybuttons");
    /*2.1 设置该设备支持哪些事件类型*/
    __set_bit(EV_SYN, btn_input->evbit);
    __set_bit(EV_KEY, btn_input->evbit);
    __set_bit(EV_REP, btn_input->evbit);
    /*2.2 设置EV_KEY事件会报告的按键编码*/
    for(; i<ARRAY_SIZE(buttons); i++)
    {
        __set_bit(buttons[i].code, btn_input->keybit);
    }
    /*3 注册input_dev变量*/
    ret = input_register_device(btn_input);
    /*4 硬件操作*/
    for(i=0; i<ARRAY_SIZE(buttons); i++)
    {
        ret = request_irq(buttons[i].irq, btn_isr, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,buttons[i].name, buttons+i);
    }
    init_timer(&btn_timer);
    btn_timer.function = btn_timer_func;
    return 0;
}
void __exit btn_drv_exit(void)
{
    int i = 0;
    del_timer(&btn_timer);
    for(; i<ARRAY_SIZE(buttons); i++)
    {
        free_irq(buttons[i].irq,  buttons+i);
    }
    /*6注销input_dev变量*/
    input_unregister_device(btn_input);
    /*7释放input_dev变量对应的内存空间*/
    input_free_device(btn_input);
}
module_init(btn_drv_init);
module_exit(btn_drv_exit);

vi test.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
int main(void)
{
    struct input_event key;
    int fd = open("/dev/input/event5", O_RDONLY);
    while(1)
    {
        read(fd, &key, sizeof(key));
        if(key.type == EV_KEY)
        {
            printf("type=%d code=%d value=%d\n", key.type,key.code, key.value);
        }
        //sleep(1);
    }
    close(fd);
    return 0;
}

intput 子系统支持的事件类型

			#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	声音事件
			#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)

安装模块后,设备文件的确定方式:

 方式一:
        ls /dev/input/event*
        insmod btn_drv.ko
        ls /dev/input/event*
        多出来的eventX设备文件就是我们要找的
 方式二:
        insmod btn_drv.ko
        cat /proc/bus/input/devices
        通过分析该输出信息判断对应的设备文件时eventX

可以通过hexdump来读取按键信息

hexdump /dev/input/event5,按下按键输出信息如下所示
行号      秒        微秒   type code   value
0000000 90e0 54a4 5db4 0008 0001 0067 0001 0000
0000010 90e0 54a4 5dba 0008 0000 0000 0000 0000
0000020 90e0 54a4 de50 000a 0001 0067 0000 0000
0000030 90e0 54a4 de54 000a 0000 0000 0000 0000
0000040 90e2 54a4 6d51 0008 0001 006a 0001 0000
0000050 90e2 54a4 6d55 0008 0000 0000 0000 0000
0000060 90e2 54a4 bf10 000a 0001 006a 0000 0000
0000070 90e2 54a4 bf13 000a 0000 0000 0000 0000

3、input子系统的实现

input子系统的核心模块:drivers/input/input.c
	input_init(void){
		//主设备号为13的设备对应的操作函数集合input_fops
		err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
	}
ls /dev/input/event5  -l // 13 69
open("/dev/input/event5", ...)
--------------------------------------------
sys_open(){
	input_fops.open()//input_open_file函数

在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值