linux的input子系统(二)

咱们接着上一篇的内容继续啊,如果你看的莫名其妙那就看看《linux的input子系统(一)》吧,首先我把上一篇提到的按键驱动的源代码附上吧,咱们以源代码为线索一一述说嘛。

#include<linux/input.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/delay.h>
#include<asm/irq.h>
#include<linux/irq.h>
#include<asm/io.h>
#include<linux/interrupt.h>
#include<asm/uaccess.h>
#include<mach/hardware.h>
#include<linux/platform_device.h>

#include<mach/map.h>
#include<mach/gpio.h>
#include<mach/regs-clock.h>
#include<mach/regs-gpio.h>

struct button_desc {
	int gpio;
	int number;
	char *name;
	struct input_dev *button_dev;//定义一个输入设备
};

static struct button_desc buttons[] = {
	{ S5PV210_GPH2(0), 0, "KEY0" },
};

static irqreturn_t button_interrupt(int irq, void *dummy)
{
	input_report_key(buttons[0].button_dev, BTN_0, gpio_get_value(buttons[0].gpio) & 1);//在中断里把按键的值提交到核心层,要注意中断是由于按键的按下或者释放而产生的
	input_sync(buttons[0].button_dev);//这个函数是做一个同步的作用,当然在这里其实没什么作用,不过像在触摸屏里面就有用了
	return IRQ_HANDLED;
}

static int __init button_init(void)
{
	int error;
	int irq;

	irq = gpio_to_irq(buttons[0].gpio);
	if(request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, buttons[0].name, (void *)&buttons[0])){
		printk(KERN_ERR "button.c: Can't allocate irq %d\n", irq);
		return -EBUSY;
	}
	buttons[0].button_dev = input_allocate_device();//分配一个input设备,当然里面做了一些必要的处理
	if(!buttons[0].button_dev){
		printk(KERN_ERR "button.c: Not enough memory\n");
		error = -ENOMEM;
		goto err_free_irq;
	}

	buttons[0].button_dev->evbit[0] = BIT_MASK(EV_KEY);//设备支持的事件类型
	buttons[0].button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);//事件EV_KEY支持的事件码

	buttons[0].button_dev->name = "KEY0";
	buttons[0].button_dev->phys = "s5pv210/input0";
	error = input_register_device(buttons[0].button_dev);//通过这个函数就把输入设备注册到了input的核心层
	if(error){
		printk(KERN_ERR "button.c:Failed to register device\n");
		goto err_free_dev;
	}

	return 0;

err_free_dev:
	input_free_device(buttons[0].button_dev);//当注册失败时,就调用这个函数释放输入设备
err_free_irq:
	free_irq(irq, button_interrupt);
	return error; 
}

static void __exit button_exit(void)
{
	int irq;

	irq = gpio_to_irq(buttons[0].gpio);
	input_unregister_device(buttons[0].button_dev);//卸载input的输入设备
	free_irq(irq, (void *)&buttons[0]);
}

module_init(button_init);
module_exit(button_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("yingdong xie");
这个程序是按 linux-3.0.8/Documentation/input/input-programming.txt文档中讲的为模板在smart210上的实现,可能写的很是粗糙,有许多地方需要改进,但基本的功能还是完成的,看到上面的注释部分了吧,这是编写设备的input驱动常用到的一些函数,也是咱们要讲的重点。

看驱动嘛,肯定是要从button_init开始了。在button_init中第一个看到的注释部分input_allocate_device()这个函数,从名字看就是就是分配一个input设备,没错,不过在它里面还做了一些input设备的初始化工作,这个在以后的文章中详细介绍,因为咱们这里是讨论怎么写设备驱动,所以知道是干什么的就行了,不是这里的重点。接下来的红色部分标识为设备支持的事件和具体事件支持的事件嘛,这里简单的说一下什么事件,什么是事件码!linux中定义了许多事件,包括按键事件、相对坐标事件、绝对坐标事件等,比如说咱们写的按键驱动就用按键事件了,如果要写鼠标驱动,那就应该还需要相对坐标事件了,那事件就可以理解为:具体设备产生的要让input子系统处理的数据类型,因为对鼠标来说,它就是用相对坐标来表示移动的,而按键的话其实就是按下和释放嘛。正是因为每种设备要处理的数据不一样,linux才抽象了许多事件,这样便于管理,这也就是为什么“事件处理层中”对应着:evdev.c、tsdev.c、mousedev.c等好多文件,在这些文件中做了对具体事件的处理。下一个问题,什么是事件码?事件码定义了事件的精确类型,一类事件包括了好多用于产生事件的事件码。也就是说,事件是一个集合,而事件码就是这个集合里面的具体的元素了。就比如说这里的BTN_0代表了smart210上面的一按键一样。关于这部分可以参考linux-3.0.8/Documentation/input/event-codes.txt文档。这里注意:一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件码。下面是linux-3.0.8所支持的事件类

#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)
再接下来的注释部分就是注册input设备驱动的部分了,下面是注册和注销函数:

int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
其实在注册函数里对input设备做了进一步的初始化,并且通过把input设备与事件处理层里的具体事件处理驱动做匹配,匹配的依据就是上面初始化里面的input设备定义的支持的事件以及事件码,当然可能还有什么总线类型啊,id号等。当然通过注册函数,告诉了input子系统产生了一个input设备。

最后要说的注释部分就是input_report_key()了,这个函数就是当事件产生时,给子系统报告的函数。下面是常用的报告函数:

用于报告EV_KEY、EV_REL、EV_ABS等事件的函数有:
void input_report_key(struct input_dev *dev, unsigned int code, int value)
    void input_report_rel(struct input_dev *dev, unsigned int code, int value)
    void input_report_abs(struct input_dev *dev, unsigned int code, int value)
    如果你觉得麻烦,你也可以只记住1个函数(因为上述函数都是通过它实现的)
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
写一个input的设备驱动的一些主要的函数就讲完了,现在再总体上梳理一下写input设备驱动的过程:a.用input_dev定义一个input设备;b.用input_allocate_device()给定义的input设备分配内存;c.初始化input的特征,比如是按键类型;d.把input设备注册到子系统。

现在驱动写完了,可能大家想具体测试一下,好吧,那咱们下一篇从应用层的角度来看input子系统,同时也写一个简单的测试程序看看咱们写的input设备驱动程序,好吧,伙伴们,下一篇见!


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值