linux驱动之struct input_dev结构体

 

目录

 1. struct input_dev结构体

 2. 输入设备实现的一般步骤

3. 函数原型及源码 

4. 中断和轮询方式示例

(1) 中断版本流程

(2) 轮询方式流程 

 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_eventinput_sync函数分别来报告按键和同步;

轮询设备需要内核支持:

Device Drivers ->

        Input device support ->

                <*> Polled input device skeleton

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天未及海宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值