一. 概述
1. 輸入子系統的概念
輸入子系統是對分散的、多種不同類別的輸入設備(鍵盤、鼠標、觸摸屏、加速計、跟蹤球、操縱桿等)進行統一處理的驅動程序。
2. 輸入子系統的優點
抽象底層形態各異的硬件(鼠標,鍵盤,觸摸屏,遊戲杆等)輸入設備,爲上層提供了統一的操作接口。
二. 輸入子系統的分層結構
1.三層結構
事件驅動層:負責和應用程序的接口。
核心層: 提供事件驅動層和設備驅動層所需的函數接口
設備驅動層:負責和底層設備驅動通信
2.層次框架圖
三. 設備驅動
1. 設備驅動的數據結構
對於系統的每個輸入設備硬件,在輸入子系統都要實現一個設備驅動。每個設備驅動都由input_dev的數據結構描述,部分成員定義如:
struct input_dev{
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned longevbit[BITS_TO_LONGS(EV_CNT)];
unsigned longkeybit[BITS_TO_LONGS(KEY_CNT)];
}
name: 該成員的設備驅動名字。但與對於的設備文件名無任何關係。
id : 該成員的輸入設備身份。
evbit :該成員會產生的輸入事件類型。事件類型定義如下:
1. #define EV_SYN 0x00 /*表示設備支持所有的事件*/
2. #define EV_KEY 0x01 /*鍵盤或者按鍵,表示一個鍵碼*/
3. #define EV_REL 0x02 /*鼠標設備,表示一個相對的光標位置結果*/
4. #define EV_ABS 0x03 /*手寫板產生的值,其是一個絕對整數值*/
5. #define EV_MSC 0x04 /*其他類型*/
6. #define EV_LED 0x11 /*LED燈設備*/
7. #define EV_SND 0x12 /*蜂鳴器,輸入聲音*/
8. #define EV_REP 0x14 /*允許重複按鍵類型*/
9. #define EV_PWR 0x16 /*電源管理事件*/
把指定的位置1,可以使用set_bit()函數:
set_bit( EV_KEY, input_dev->evbit);
keybit : 當設備驅動可以產生按鍵事件時,keybit成員表示設備驅動支持按鍵的鍵值。鍵值的定義:
#define KEY_RESERVED 0
#define KEY_ESC 1
#define KEY_1 2
#define KEY_2 3
......
2. 註冊/註銷設備驅動
當一個input_dev結構體被初始化完成後,就可以調用input_register_device()函數註冊到輸入子系統:
int input_register_device(struct input_dev *);
註銷:
void input_unregister_device(struct input_dev *);
3. 報告按鍵值
void input_report_key(struct input_dev*dev,unsigned int code,int value);
value : 1爲按下,0爲提起;
4. 提交同步事件,防止數據混亂
void input_sync(struct input_dev *dev);
四. 驅動實現
1. 驅動函數event_drv.c
/*
* event_drv.c
*
* Program: Key input event
*
* Author: Lin Xubin
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/input.h>
#include <linux/interrupt.h>
struct input_dev *key_event_dev;
typedef struct
{
int irq;
char *devname;
unsigned int gpio_pin;
unsigned int key_val;
}KeyEvent_TypeDef;
KeyEvent_TypeDef KeyEvent[4]={
{IRQ_EINT0, "S2", S3C2410_GPF0, KEY_A},
{IRQ_EINT2, "S3", S3C2410_GPF2, KEY_B},
{IRQ_EINT11, "S4", S3C2410_GPG3, KEY_C},
{IRQ_EINT19, "S5", S3C2410_GPG11, KEY_D},
};
static irqreturn_t key_drv_irq(int irq,void *dev_id)
{
KeyEvent_TypeDef *KeyPin = (KeyEvent_TypeDef *)dev_id;
static int PinState;
PinState = s3c2410_gpio_getpin(KeyPin->gpio_pin);
if(PinState)
{
input_event(key_event_dev, EV_KEY, KeyPin->key_val, 1);
input_sync(key_event_dev);
}
else
{
input_event(key_event_dev, EV_KEY, KeyPin->key_val, 0);
input_sync(key_event_dev);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
int key_drv_init(void)
{
int i;
key_event_dev = input_allocate_device(); //分配input_dev結構體
set_bit(EV_KEY, key_event_dev->evbit); //按鍵事件
for(i=0; i<4; i++)
{
set_bit(KeyEvent[i].key_val , key_event_dev->keybit);
}
input_register_device(key_event_dev); //註冊input事件
for(i=0; i<4; i++)
{
request_irq(KeyEvent[i].irq , key_drv_irq, IRQT_BOTHEDGE, KeyEvent[i].devname, &KeyEvent[i]);
}
return 0;
}
void key_drv_exit(void)
{
int i;
for(i=0; i<4; i++)
{
free_irq(KeyEvent[i].irq, &KeyEvent[i]);
}
input_unregister_device(key_event_dev);
input_free_device(key_event_dev);
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
2. 應用測試程序 event_drv_test.c
/*
* event_drv_test.c
*
* Author: Lin Xubin
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <unistd.h>
#include <linux/input.h>
int main(char argc, char *argv[])
{
int fd,val;
struct input_event key_event_val;
fd = open("/dev/event1", O_RDWR);
if(fd < 0)
{
printf("open device failed\n");
return 0;
}
while(1)
{
val = read(fd,&key_event_val, sizeof(struct input_event));
if( val < 0)
{
printf("read input err\n");
return 0;
}
switch(val)
printf("%d,%d,%d\n", key_event_val.type, key_event_val.code, key_event_val.value);
}
}