[国嵌攻略][138][输入子系统模型解析]

为什么需要输入子系统

在Linux系统中按键属于输入型设备,同样的把按键换成鼠标、键盘等输入型设备,它们的注册和操作方法都是类似的,不同的是在中断处理中对硬件的操作。输入子系统就是把和输入设备有共性的部分提取出来,而把不同的部分让程序员来实现。

 

输入子系统模型

1.input device drivers,需要程序员来实现

2.input core,属于系统内核实现

3.input event drivers,属于系统内核实现

例如:

当按键被按下时,会产生一个事件,然后事件由input device drivers上报给input core,由input core处理后把事件交给input event drivers。input event drivers需要实现字符驱动和对应的系统调用接口。上层应用从input event drivers实现的系统调用接口中读取数据。input device drivers实现硬件相关的工作,input event drivers实现硬件无关的工作,这样就可以简化代码实现。

 

输入型设备驱动

输入设备事件:

EV_RST reset   EV_KEY 按键   EV_LED LED   EV_REL 相对坐标   EV_ABS 绝对坐标

EV_MSC 其他   EV_SND 声音   EV_REP repreat   EV_FF 力反馈

 

当事件类型为EV_KEY时,还需要声明按键类型:

BTN_LEFT:鼠标左键   BTN_RIGHT:鼠标右键   BTN_0:数字0键   BTN_1:数字1键

 

初始化

1.分配input_dev结构,使用input_allocate_device

2.声明可能会上报的事件类型,使用set_bit

3.如果上报的是按键,声明可能上报的键值

4.注册输入型设备,使用input_register_device

 

上报

1.上报产生的事件,使用input_report_key

2.告诉核心上报结束,使用input_sync

 

注册输入型设备时,会自动创建设备文件,在/dev/目录下的event%d文件。

 

keydev.c

/********************************************************************
*头文件
*********************************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/sched.h>

/********************************************************************
*宏定义
*********************************************************************/
#define GPGCON 0x56000060   //按键控制寄存器地址
#define GPGDAT 0x56000064   //按键数据寄存器地址

/********************************************************************
*全局变量
*********************************************************************/
unsigned int *keycon;       //按键控制指针
unsigned int *keydat;       //按键数据指针

struct work_struct *work;   //按键工作
struct timer_list timer;    //按键定时

struct input_dev *keydev;   //按键输入设备

/********************************************************************
*定时处理
*********************************************************************/
//设备定时
void key_timer(unsigned long data){
    //读取按键状态
    unsigned short datTmp;
    int key1, key2;
    
    datTmp = readw(keydat);   //获取GPGDAT值
    key1 = datTmp & (1<<0);   //获取key1电平
    key2 = datTmp & (1<<3);   //获取key2电平
    
    //判断按键状态
    if(key1 == 0){
        input_report_key(keydev, KEY_1, 1);   //上报按键事件
        printk("key1 down!\n");
    }
    
    if(key2 == 0){
        input_report_key(keydev, KEY_2, 1);   //上报按键事件
        printk("key2 down!\n");
    }
    
    //上报事件完成
    input_sync(keydev);
}

/********************************************************************
*中断处理
*********************************************************************/
//设备中断下部
void key_work(struct work_struct *work){
    //启动定时设备
    mod_timer(&timer, jiffies + HZ/100);   //定时10ms,jiffies表示系统当前嘀嗒数,1HZ = 1s = 1000jiffies
}

//设备中断上部
irqreturn_t key_irq(int irq, void *dev_id){
    //处理硬件相关
    
    //提交按键工作
    schedule_work(work);
    
    return 0;
}

/********************************************************************
*模块安装
*********************************************************************/
//注册硬件
void handware_init(void){
    //初始化按键控制寄存器
    unsigned int conTmp;
    
    keycon = ioremap(GPGCON, 4);   //虚拟地址映射
    
    conTmp = readl(keycon);             //获取GPGCON值
    conTmp &= ~((0x3<<6) | (0x3<<0));   //GPB3[7:6]:00,GPG0[1:0]:00
    conTmp |=  ((0x2<<6) | (0x2<<0));   //GPB3[7:6]:EINT[11],GPG0[1:0]:EINT[8]
    writel(conTmp, keycon);             //设置GPGCON值
    
    //初始化按键状态寄存器
    keydat = ioremap(GPGDAT, 2);   //虚拟地址映射
}

//安装模块
static int ikey_init(void){
    //分配输入设备
    keydev = input_allocate_device();
    
    //声明按键事件
    set_bit(EV_KEY, keydev->evbit);
    
    //声明按键编号
    set_bit(KEY_1, keydev->keybit);
    set_bit(KEY_2, keydev->keybit);
    
    //注册输入设备
    input_register_device(keydev);

    //注册硬件设备
    handware_init();
        
    //注册中断处理
    request_irq(IRQ_EINT8, key_irq, IRQF_TRIGGER_FALLING, "ikey", (void *)1);    //下降沿触发,IRQ_EINT8定义在irqs.h文件中
    request_irq(IRQ_EINT11, key_irq, IRQF_TRIGGER_FALLING, "ikey", (void *)2);
    
    //注册工作队列
    work = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
    INIT_WORK(work, key_work);
    
    //注册定时设备
    init_timer(&timer);           //初始化定时器
    timer.function = key_timer;   //添加定时函数
    add_timer(&timer);            //添加定时设备
    
    return 0;
}

//卸载模块
static void ikey_exit(void){
    //注销输入设备
    input_unregister_device(keydev);
    
    //注销中断处理
    free_irq(IRQ_EINT8, (void *)1);
    free_irq(IRQ_EINT11, (void *)2);
}

/********************************************************************
*模块声明
*********************************************************************/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("D");
MODULE_DESCRIPTION("");
MODULE_VERSION("v1.0");

module_init(ikey_init);
module_exit(ikey_exit);

 

keyapp.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/input.h>

int main(int argc, char **argv){
    //打开设备
    int fd;
    
    fd = open("/dev/event1", O_RDWR);
    
    while(1){
        //读取设备
        struct input_event keyevt;
        
        read(fd, &keyevt, sizeof(keyevt));
        
        //显示数据
        if(keyevt.type == EV_KEY){
            printf("Event type:%d code:%d value:%d\n", keyevt.type, keyevt.code - 1, keyevt.value);
        }
        
        if(keyevt.type == EV_SYN){
            printf("Sync event\n");
        }
    }
    
    //关闭设备
    close(fd);
    
    return 0;
}

 

posted @ 2016-03-12 20:07  盛夏夜  阅读(484)  评论(0编辑  收藏  举报