按键、鼠标、键盘等设备都属于输入(input)设备。Linux内核有input子系统框架来处理事件。
目录标题
一、INPUT子系统简介
我们写驱动程序主要关注中间的驱动层、核心层和事件层。分工如下:
- 驱动层:输入设备的具体驱动程序。
- 核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层进行处理。
- 事件层:和用户空间交互。
二、驱动程序编写
在使用input子系统处理输入设备的时候不需要去注册字符设备。只需要向系统注册一个input_device即可:
1.input_dev说明
在使用input子系统的时候,只需要注册一个input设备。结构体定义在include/linux/input.h文件中:
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)];
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;
bool devres_managed;
};
其中evbit表示输入事件类型,定义如下:
/*
* Event types
*/
#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)
这次使用按键输入事件,所以选择EV_KEY,使用keybit。
按键值也定义在input.h按键值如下:
#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
......
开发板上的按键值KEY设置为KEY_0
2.申请&注销input_dev
struct input_dev *input_allocate_device(void)
//返回结构体地址
void input_free_device(struct input_dev *dev)
//输入结构体地址
3.初始化input_dev
需要初始化的内容主要为事件类(evbit)和事件值(keybit)。
4.向内核注册&注销
int input_register_device(struct input_dev *dev)
//返回值0注册成功。负值注册失败
void input_unregister_device(struct input_dev *dev)
三、注册示例
struct input_dev *inputdev;
static int __init xxx_init(void)
{
inputdev = input_allocate_device();
inputdev->name = "test_inputdev";
/*第一种*/
__set_bit(EV_KEY, inputdev->evbit);
__set_bit(EV_REP, inputdev->evbit);
__set_bit(KEY_0, inputdev->keybit);
/*第二种*/
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
keyinputdev.inputdev->keybit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0);
/*第三种*/
keyinputdev.inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0);
//注册
input_register_device(inputdev);
return 0;
}
static void __exit xxx_exit(void)
{
input_unregister_device(inputdev); //注销
input_free_device(inputdev); //删除
}
四、通知示例
1.函数
input设备都有输入功能,驱动程序需要告诉内核,输入了什么,因此有了上报事件。一般在按键中断里上报。
void input_event(struct input_dev *dev, //需要上报的input_dev
unsigned int typr, //上报事件 EV_KEY
unsigend int code, //时间码:注册的按键值KEY_0
int value) //事件值,1、0
//下面是专门的按键上报,推荐使用这个
static inline void input_report_key(struct input_dev *dev,
unsigned int code,
int value)
{
input_event(dev, EV_KEY, code, !!value);
}
上报完成后需要告诉内核,上报结束
void input_sync(struct input_dev *dev)
2.示例
void timer_function(unsigned long arg)
{
unsigned char value;
value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */
if(value == 0){ /* 按下按键 */
/* 上报按键值 */
input_report_key(inputdev, KEY_0, 1); /* 最后一个参数 1, 按下 */
input_sync(inputdev); /* 同步事件 */
}
}
五、应用层的接收
1.input_event
Linux内核告诉input_event这个结构体所有的输入事件。
struct input_event{
struct timeval time;
__u16 type;
__u16 vode;
__s32 value;
};
struct timeval{
__kernel_time_t tv_sec; //秒
__kernel_suseconds tv_usec; //微秒
}
2.示例
static struct input_event inputevent;
ina main (int argc, char *argv[])
{
fd = open(argv[1], O_RDWR);
err = reaf(fd, &inputevent, sizeof(inputevent));
switch(inputevent.type){
case EV_KEY:
...
}
}
六、驱动代码
见下章
七、编译测试
见下章