Linux驱动---输入子系统

1.概述

1.1 什么叫做输入子系统

        简单来说,输入子系统就是统一各种各样的输入设备的系统;

        常见的输入设备比如: 键盘、触摸屏、按键、鼠标。

1.2 为什么要引入输入子系统

        每个人写驱动代码都有自己的风格和习惯,导致代码会有一定的差异,举个简单的例子:

同样的人写一个驱动,当按键按下时,有人给应用层返回1,有些人就会返回0;

        输入子系统就是为了统一这个输入。当按键按下去之后 ,驱动层不直接给应用层返回一个信息,而是告诉输入子系统,由输入子系统做了统一之后再告诉应用层。

2.相关API

2.1 向内核注册一个输入子系统

函数的头文件: <linux/input.h>

函数的原型:int input_register_device(struct input_dev *dev)

函数的参数:struct input_dev *dev:输入子系统的核心的结构体

函数的返回值:

        成功返回 0     

        失败返回  非零

2.2 输入子系统的核心结构体的注册

这个结构体不允许自己定义,需要通过内核注册;

函数的头文件:<linux/input.h>

函数的原型:struct input_dev *input_allocate_device(void)

函数的参数:无

函数的返回值:

        成功返回 输入子系统的核心结构体的指针

        失败返回 NULL

下边是输入子系统的核心结构体,可以看到里面包含的内容非常多,所以需要向内核注册。

struct input_dev {

        const char *name;   //设备名(sys目录下的设备)

        const char *phys;   //sys目录下的设备路径  不需要用户填充

        const char *uniq;  //设备的唯一识别码

        struct input_id id;  //用于匹配事件处理层handler(事件处理者)

        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;

        unsigned int keycodemax;  //支持的按键值的个数

        unsigned int keycodesize;  //每个键值的字节数

        void *keycode;           //存放按键值的数组首地址

        int (*setkeycode)(struct input_dev *dev,

        const struct input_keymap_entry *ke,

        unsigned int *old_keycode);  //修改键值的函数  可选

        int (*getkeycode)(struct input_dev *dev,

        struct input_keymap_entry *ke); //获取扫描码的键值

        struct ff_device *ff;

        unsigned int repeat_key;  //最近一次键值,用于连击

        struct timer_list timer;  //自动连击计时器

        int sync;   //最后一次同步后没有新的事件置1

        int abs[REP_CNT];  //当前各个坐标的值

        int rep[REP_CNT];  //自动连击的参数

        struct input_mt_slot *mt;

        int mtsize;

        int slot;

        int trkid;

        struct input_absinfo *absinfo;

        unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反映当前按键状态的位图

        unsigned long led[BITS_TO_LONGS(LED_CNT)]; //反映当前 led 状态的位图

        unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反映当前 beep 状态的位图

        unsigned long sw[BITS_TO_LONGS(SW_CNT)];

        int (*open)(struct input_dev *dev);//打开函数,可以不实现,如果有就调用

        void (*close)(struct input_dev *dev);//关闭函数,可以不实现,如果有就调用。

        int (*flush)(struct input_dev *dev, struct file *file);//断开连接时清空上报上去数据

        int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); //回调函数,可选

        struct input_handle __rcu *grab;

        spinlock_t event_lock;

        struct mutex mutex;

        unsigned int users;

        bool going_away;

        bool sync;

        struct device dev;

        struct list_head h_list;//handle 链表

        struct list_head node;//input_dev 链表

};

evbit:支持的事件的类型

        set_bit(EV_KEY,evbit);    //表示设置事件为按键事件

        set_bit(EV_REP,evbit);    //表示设置的事件为重复事件

输入子系统支持的事件的宏定义:

        #define EV_SYN  0x00   //同步事件

        #define EV_KEY  0x01   //按键事件  使用的比较多

        #define EV_REL  0x02   //相对事件

        #define EV_ABS  33      //绝对事件

        #define EV_MSC  0x04  //其他事件

        #define EV_SW  0x05   //开关事件

        #define EV_LED  0x11   //按键LED灯事件

        #define EV_SND 0x12   //按键声音事件

        #define EV_REP 0x14   //重复事件   比较常用的

        #define EV_FF  0x15  //力反馈事件

        #define EV_PWR 0x16 /*电源事件*/

        #define EV_FF_STATUS 0x17 /*受压状态*/

keybit:输入子系统支持的按键

        设置按键支持第一个按键:set_bit(KEY_1,keybit);

2.3 输入子系统事件的上报

函数的头文件:<linux/input.h>

函数的原型:

void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)

函数的参数:

        struct input_dev *dev:输入子系统的核心结构体

        unsigned int type:上报的事件的类型(EV_KEY  按键事件)

        unsigned int code, :按键   也就是是哪个按键(KEY_1 KEY_2)

        int value:按键的值

假如是按键事件:

        0:表示按键松开

        1:表示按键按下

函数的返回值:无

++++++++++++++++++++++++++++++++++++++++++++++++++++++++

例如上报一个按键事件:

函数的头文件:<linux/input.h>

函数的原型:void input_report_key(struct input_dev *dev, unsigned int code, int value)

函数的参数:

        struct input_dev *dev:输入子系统的核心结构体

        unsigned int code:哪个按键

int value:

        0表示按键松开

        1表示按键按下

函数的返回:无

++++++++++++++++++++++++++++++++++++++++++++++++++++++++

2.4 报告同步事件

函数的头文件: <linux/input.h>

函数的原型:void input_sync(struct input_dev *dev);

函数的参数:struct input_dev *dev:输入子系统的核心的结构体

函数的返回值:无

3.下边是练习的一个例子,仅供参考

内核层:

//内核层代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/timer.h>

struct input_dev *myinput=NULL;
int my_irq;       //中断号
struct timer_list mytime;

//定时器1回调函数
void my_fun(unsigned long data)
{
	int val=0;
	val=gpio_get_value(EXYNOS4_GPX3(2));
	if(val==0)
	{
		//printk("按键按下了,事件上报1");
		input_report_key(myinput,KEY_1,1);
	}
	else if(val==1)
	{
		//printk("按键松开了,事件上报0");
		input_report_key(myinput,KEY_1,0);
	}
	//报告同步事件
	input_sync(myinput);
	
	//printk("我被调用了\n");
}

irqreturn_t func(int num, void *arg)
{	
	//定时器1
	mod_timer(&mytime, jiffies+msecs_to_jiffies(10)); //延时10ms
	
	
	return 0;
}

static int __init test_init(void)
{
	int ret,ret1;

	//定时器1
	mytime.expires=jiffies+msecs_to_jiffies(20); //20ms
	mytime.function=my_fun;
	init_timer(&mytime);        //初始化定时器
	add_timer(&mytime);         //向内核注册一个定时器  并运行
	
	
	//1:中断号转换
	my_irq=gpio_to_irq(EXYNOS4_GPX3(2)); //key1
	//2:使能中断号
	enable_irq(my_irq);
	//3:注册中断
	ret1=request_irq(my_irq,func,IRQ_TYPE_EDGE_BOTH, "mykey", NULL);

	
	//向内核注册一个输入子系统
	myinput=input_allocate_device();//输入子系统的核心结构体的注册
	myinput->name="key";
	set_bit(EV_KEY,myinput->evbit);//表示设置事件为按键事件
	set_bit(EV_REP,myinput->evbit);//表示设置的事件为重复事件
	set_bit(KEY_1,myinput->keybit);//设置按键支持第一个按键
	
	ret=input_register_device(myinput);
	return 0;
}

static void __exit test_exit(void)
{
	//取消注册
	input_unregister_device(myinput); 
}

module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");

应用层:

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

struct input_event mydata;
int main()
{
	int fd;
	fd=open("/dev/input/event3",O_RDWR);
	while(1)
	{
		read(fd,&mydata,sizeof(mydata));
		if(mydata.code == 2)
		{
			if(mydata.value == 1)
			{
				printf("应用层:key1按下了\n");
			}
			else if(mydata.value == 0)
			{
				printf("应用层:key1松开了\n");
			}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程小白菜123

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

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

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

打赏作者

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

抵扣说明:

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

余额充值