十、Linux驱动之输入子系统使用

linux驱动 专栏收录该内容
23 篇文章 5 订阅

九、Linux驱动之输入子系统分析中我们分析了输入子系统的框架,接下来我以按键模拟键盘的方式来编写输入子系统代码。

1. 分析设备

本人使用的是JZ2440v3开发板,该开发板CPU使用的是S3C2440A,按键与CPU连接如下:




    可以看到4个按键分别连接到2440的GPF0、GPF2、GPG3、GPG11引脚上面,4个按键接上拉电阻,可知按键按下时引脚输入低电平,按键松开时引脚输入高电平。
    再定位到S3C2440A芯片手册:






从上面资料可知信息:
    1. 想要控制开发板上面的4个按键,需要控制2440的GPF0、GPF2、GPG3、GPG11三个引脚为Input模式,所以要配置GPFCON寄存器的位[5:4]=00,[1:0]=00,配置GPGCON寄存器的位[23:22]=00,[7:6]=00。
    2. 想要控制4个按键还需要控制GPFDAT、GPGDAT寄存器对应位。
    3. GPFCON寄存器的物理地址为0x56000050,GPFDAT寄存器的物理地址为0x56000054。GPGCON寄存器的物理地址为0x56000060,GPGDAT寄存器的物理地址为0x56000064。

2. 编写代码

驱动程序button_input.c代码如下:

/* 参考drivers\input\keyboard\gpio_keys.c */

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>

struct pin_desc{
	int irq;
	char *name;
	unsigned int pin;
	unsigned int key_val;
};

struct pin_desc pins_desc[4] = {
	{IRQ_EINT0,  "S2", S3C2410_GPF0,   KEY_L},
	{IRQ_EINT2,  "S3", S3C2410_GPF2,   KEY_S},
	{IRQ_EINT11, "S4", S3C2410_GPG3,   KEY_ENTER},
	{IRQ_EINT19, "S5",  S3C2410_GPG11, KEY_LEFTSHIFT},
};

static struct input_dev *buttons_dev;
static struct pin_desc *irq_pd;
static struct timer_list buttons_timer;

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	/* 10ms后启动定时器 */
	irq_pd = (struct pin_desc *)dev_id;
	mod_timer(&buttons_timer, jiffies+HZ/100);
	return IRQ_RETVAL(IRQ_HANDLED);
}

static void buttons_timer_function(unsigned long data)
{
	struct pin_desc * pindesc = irq_pd;
	unsigned int pinval;

	if (!pindesc)
		return;
	
	pinval = s3c2410_gpio_getpin(pindesc->pin);

	if (pinval)
	{
		/* 松开 : 最后一个参数: 0-松开, 1-按下 */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);    //上报事件
		input_sync(buttons_dev);     //报告完毕,同步事件
	}
	else
	{
		/* 按下 */
		input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);    //上报事件
		input_sync(buttons_dev);     //报告完毕,同步事件
	}
}

static int buttons_init(void)
{
	int i,j;
	int err;

	/* 1. 分配一个input_dev结构体 */
	buttons_dev = input_allocate_device();

	/* 2. 设置 */
	/* 2.1 能产生哪类事件 */
	set_bit(EV_KEY, buttons_dev->evbit);    //能产生按键类事件
	set_bit(EV_REP, buttons_dev->evbit);    //能产生重复事件
	
	/* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
	set_bit(KEY_L, buttons_dev->keybit);
	set_bit(KEY_S, buttons_dev->keybit);
	set_bit(KEY_ENTER, buttons_dev->keybit);
	set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);

	/* 3. 注册 */
	input_register_device(buttons_dev);
	
	/* 4. 硬件相关的操作 */
	init_timer(&buttons_timer);
	buttons_timer.function = buttons_timer_function;
	add_timer(&buttons_timer);
	
	for (i = 0; i < 4; i++)
	{
		err = request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
		if(err < 0)
		{
			for(j = 0; j < i; j++)
				free_irq(pins_desc[j].irq, &pins_desc[j]);
		}
			
	}
	return err;
}

static void buttons_exit(void)
{
	int i;
	for (i = 0; i < 4; i++)
	{
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}

	del_timer(&buttons_timer);
	input_unregister_device(buttons_dev);
	input_free_device(buttons_dev);	
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_AUTHOR("LVZHENHAI");
MODULE_LICENSE("GPL");

Makefile代码如下:

KERN_DIR = /work/system/linux-2.6.22.6    //内核目录

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= button_input.o

3. 测试

内核:linux-2.6.22.6
编译器:arm-linux-gcc-3.4.5
环境:ubuntu9.10

在开发板目录上执行:
    ls /dev/event*

可以看到已经存在一个input设备。将button_input.c、Makefile三个文件放入网络文件系统内,在ubuntu该目录下执行:
    make
再次在开发板目录上执行:
    insmod button_input.ko
    ls /dev/event*

新出现的/dev/event1设备便是我们创建的input模拟键盘设备了。下面有两种方法测试该驱动。
方法一:
在开发板目录上执行:
    cat /dev/tty1   
此命令表示显示输出tty1。按下开发板的S2、S3,开发板lLCD上就能看到“ls”被打印出来了。然后按下S4,此时串口终端结果如下:

按键虚拟键盘键值正确被系统接收到了,说明我们的驱动成功了。
PS:使用命令:exec 0</dev/tty1 (将标准输入改为tty1)
方法二:
在开发板目录上执行:
    hexdump /dev/event1
然后按下S2键串口终端输出如下:

其中2、3列表示秒,4、5列表示微妙,6列表示类,7列表示code,8、9列表示value。

4. 程序说明

    在buttons_init初始化函数中分配一个input_dev结构体,设置并注册它。当按键按下或松开时,中断发生,调用中断函数buttons_irq(),该函数里在按键事件10ms后调用buttons_timer_function定时器函数,在该函数里面针对中断获取到的不同键值调用input_event函数上报。可见我们的程序里并没有创建设备节点那一套操作,是由系统替我们完成的。

5. 知识点

5.1 编写输入子系统驱动我们需要做什么?

    (1) 分配一个input_dev结构体。
    (2) 设置input_dev结构体(1.能产生哪类事件,2.能产生这类操作里的哪些事件)。
    (3) 注册这个input_dev结构体。
    (4) 获得事件时调用input_event上报事件和input_sync同步事件(表示上报结束)。

5.2 input设备支持的事件类型

EV_SYN 0x00  //同步事件
EV_KEY 0x01  //按键事件 EV_REL 0x02 相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)
EV_ABS 0x03  //绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)
EV_MSC 0x04  //其它
EV_SW 0x05  //开关
EV_LED 0x11  //按键/设备灯
EV_SND 0x12  //声音/警报
EV_REP 0x14  //重复
EV_FF 0x15  //力反馈
EV_PWR 0x16  //电源
EV_FF_STATUS 0x17  //力反馈状态
EV_MAX 0x1f  //事件类型最大个数和提供位掩码支持

  • 0
    点赞
  • 0
    评论
  • 6
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页

打赏作者

墨、白

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值