目录
1. struct input_dev结构体
struct input_dev {
const char *name;//输入设备的名字
const char *phys;//在系统层次结构中设备的物理路径;
const char *uniq;
struct input_id id;//输入设备id,包含总线类型,制造商id,产品id和版本号等;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];//设备属性和怪异位图
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//设备支持的事件类型;EV_KEY, EV_REL;
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)];//设备存在的led位图
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;
unsigned int keycodemax;//按键编码表的大小;
unsigned int keycodesize;//按键编码表中每个编码的大小
void *keycode;//按键编码表
//打开设备
int (*open)(struct input_dev *dev);
//关闭设备
void (*close)(struct input_dev *dev);
...
struct device dev;//设备的驱动程序模型视图
struct list_head h_list;
struct list_head node;
};
evbit:设备能够报告的事件类型; 常见取值如下:
#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 表设备支持长按键检测;
2. 输入设备实现的一般步骤
(a) 使用input_allocate_device()函数分配一个struct input_dev对象;
(b) 初始化输入设备对象,如名字,输入设备id,能报告的事件类型和编码表相关内容等;
(c) 使用 input_register_device(struct input_dev *)注册输入设备;
(d) 在输入设备产生事件时使用input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)函数报告事件,type为设备能够报告的事件类型,如上取值;
(e) 使用input_sync同步事件;
(f) 不需要时使用input_unregister_device(struct input_dev *)注销; 使用input_free_device(struct input_dev *dev)释放内存;
3. 函数原型及源码
(1) struct input_dev *input_allocate_device(void);
作用:动态分配一个struct input_dev结构对象,返回对象的地址,NULL表示失败;
struct input_dev *input_allocate_device(void)
{
struct input_dev *dev;
//分配内存
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
//分配成功,对成员进行初始化;
dev->dev.type = &input_dev_type;
dev->dev.class = &input_class;
device_initialize(&dev->dev);
mutex_init(&dev->mutex);
spin_lock_init(&dev->event_lock);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}
对应的释放接口为 void input_free_device(struct input_dev *dev);
(2) int input_register_device(struct input_dev *dev)
作用: 向Linux输入核注册输入设备;
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
/* 每个输入设备生成EV_SYN/SYN_REPORT事件 */
__set_bit(EV_SYN, dev->evbit);
/* KEY_RESERVED为编号为0的键, 不支持发送到用户空间。*/
__clear_bit(KEY_RESERVED, dev->keybit);
...
//填充dev其他功能
error = device_add(&dev->dev);
if (error)
return error;
...
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
对应的注销接口为void input_unregister_device(struct input_dev *dev);
(3) void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value);
作用:报告一个事件,dev为报告事件的输入设备,type为事件类型,code为编码,value为具体值;
void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{
unsigned long flags;
//测试dev输入设备是否支持type类型
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
(4) void input_sync(struct input_dev *dev);
作用:同步事件;
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
4. 中断和轮询方式示例
(1) 中断版本流程
//申请中断号
request_irq(irq, btn_drv_irq, IRQF_TRIGGER_FALLING | IRQF_SHARED, buttons_desc[i].name, (void*)&buttons_desc[i]);
//分配输入设备
btn_dev = input_allocate_device
set_bit(EV_KEY, btn_dev->evbit); //支持按键
set_bit(EV_REP, btn_dev->evbit); //支持长按
//将需要支持的按键加入
for(i = 0; i < ARRAY_SIZE(buttons_desc); i++){
set_bit(buttons_desc[i].key_code, btn_dev->keybit);
}
//注册input_device
ret = input_register_device(btn_dev);
//定时器
init_timer(&btn_timer);
btn_timer.function = btn_timer_func;
add_timer(&btn_timer);
中断回调函数btn_drv_irq在按键按下时自动调用, 然后通过定时器消除按键消抖; 在btn_timer_func定时器函数中通过:
input_event(btn_dev, EV_KEY, KEY_A, 0); 报告按键A 0=释放; 1=按下;
input_sync(btn_dev); 同步事件
(2) 轮询方式流程
轮询方式用到struct input_polled_dev结构体;
#include <linux/input-polldev.h>
struct input_polled_dev {
void *private; //私有驱动数据
void (*open)(struct input_polled_dev *dev); //打开输入设备
void (*close)(struct input_polled_dev *dev);//关闭数据设备
void (*poll)(struct input_polled_dev *dev);//输入设备轮询函数
unsigned int poll_interval; /* msec */ //轮询间隔 ms为单位
unsigned int poll_interval_max; /* msec */ //轮询间隔最大值
unsigned int poll_interval_min; /* msec */ //轮询间隔最小值
struct input_dev *input; //需要被轮询检查的输入设备
/* private: */
struct delayed_work work;
};
流程为:
//分配struct input_polled_dev
polldev = input_allocate_polled_device();
polldev->private = xx;
//指定轮询回调函数
polldev.poll = demo_poll;
// 填充struct input_dev
polldev.input.xxx
//注册
input_register_polled_device(polldev);
然后就可以在demo_poll函数中判断处理并调用:input_event和input_sync函数分别来报告按键和同步;
轮询设备需要内核支持:
Device Drivers ->
Input device support ->
<*> Polled input device skeleton