linux设备驱动(四)--利用linux设备模型实现按键驱动

之前学习了linux设备驱动模型,为加深理解,将之前的按键驱动程序改写成用linux设备模型的方法来实现

有了设备驱动模型,驱动的架构统一简洁,而且真正要深入学习驱动程序的,这个驱动模型的架构是一定要熟悉的,这期间参考了

http://blog.csdn.net/wulong117/article/details/7376509

这位仁兄的代码,在此表示感谢。

先简单回顾一下设备驱动模型,几个要素,总线,设备,驱动和类,这里的类我用的是input_dev,也就是input输入子系统

这里我的按键注册为平台设备platform_device

驱动注册为平台驱动platform_driver

他们都挂在总线platform上

这个platform总线是linux系统的一个虚拟总线,用于实现SOC片上外设的挂接,很方便,类似的还有很多其他的总线,比如iic,这些通过/sys目录可以查看

platform_device代码很简单,就是申明出设备使用的硬件资源,比如io地址,中断号,然后模块加载时注册设备

paltform_driver就是驱动程序啦,他从与之匹配的platform_device那里获得硬件资源的信息,然后进行相关操作,这里跟之前的驱动程序干的事差不多,详见代码注释

而这个匹配的工作就是由platform总线来做,platform总线已经再内核中实现好了,开机就已经加载好,就不用我们操心了

我们分别将platform_device和platform_driver加载到总线上,总线会跟据他们里面的名字进行匹配,匹配上了就执行platform_driver中的probe函数,这个函数就开始从

platform_device获取资源信息,然后各种初始化。

总体来看,比如我最后编译好的模块,设备模块是key_dev.ko,驱动模块是key_drv.ko,分别insmod这两个模块,顺序无所谓,反正只要总线上由设备或者驱动注册,总线都会进行扫描匹配工作的,insmod完了,如果名称一样他们就匹配上了,驱动程序就正常工作了。

这里我的按键操作,沿用了上一篇中的input子系统输入的方法,操作简便。

下面是代码。

先看设备模块

#include<linux/module.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/errno.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<asm/io.h>
#include<asm/system.h>
#include<asm/uaccess.h>
#include<mach/regs-gpio.h>
#include<linux/interrupt.h>
#include<linux/irq.h>
#include<linux/slab.h>
#include<linux/sched.h>
#include<linux/wait.h>
#include<mach/gpio-fns.h>
#include<linux/ioport.h>
#include<linux/platform_device.h>
//GPF的端口地址
#define GPF_PA	(0x56000050)
//平台设备
struct platform_device *key_dev;
//定义设备资源,这里两项,io地址资源和中断号资源
struct resource key_resource[]={
	[0]={
		.start=GPF_PA,
		.end=GPF_PA+8,
		.flags=IORESOURCE_MEM,
	},
	[1]={
		.start=IRQ_EINT0,
		.end=IRQ_EINT0,
		.flags=IORESOURCE_IRQ,
	},
};

static int __init s3c2440_key_init(void)
{
	int ret;
	//注册平台设备,第一个参数为设备名,这是与平台驱动driver进行匹配的依据,-1是id,自动分配,不care
	key_dev=platform_device_alloc("platform_key",-1);
	//为平台设备添加资源
	platform_device_add_resources(key_dev,key_resource,2);
	//添加设备
	ret=platform_device_add(key_dev);
	if(ret)
		platform_device_put(key_dev);
	printk(KERN_NOTICE "platform device added\n");
	return ret;
}
static void __exit s3c2440_key_exit(void)
{
	platform_device_unregister(key_dev);
}
MODULE_AUTHOR("weicz");
MODULE_LICENSE("Dual BSD/GPL");
module_init(s3c2440_key_init);
module_exit(s3c2440_key_exit);
再看驱动模块

#include<linux/module.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/errno.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<asm/io.h>
#include<asm/system.h>
#include<asm/uaccess.h>
#include<mach/regs-gpio.h>
#include<linux/interrupt.h>
#include<linux/irq.h>
#include<linux/slab.h>
#include<linux/sched.h>
#include<linux/wait.h>
#include<mach/gpio-fns.h>
#include<linux/ioport.h>
#include<linux/platform_device.h>
#include<asm/bitops.h>
#include<linux/input.h>
#define DEVICE_NAME	"plat_key_driver"
//#define GPFCON (0x56000050)
//#define GPFDAT (0x56000054)
//#define GPFCFG	(0x0002)
//地址映射后的io虚拟地址,这里用到了GPIOF,只用GPFCON和GPFDAT两个寄存器
//GPFCON 16bit GPFDAT 8bit
void __iomem *gpfcon;
void __iomem *gpfdat;
//定义输入设备,使用输入子系统
struct input_dev *key_input_dev;
//定义中断资源,这里只做了一个按键,这个结构显得有点多余,为的方便扩展
struct irq_des
{
	int key_irq;
	char *name;
};
struct irq_des key_irqs={
	.name="KEY0",
};
//中断处理函数
static irqreturn_t s3c2440_eint_key(int irq,void *dev_id)
{
	int value;
	//读出端口状态,并且只关心做后一个bit,对应我的按键key0
	value=ioread8(gpfdat);
	value=value & 0x01;
	//讲述据上报给输入子系统
	input_report_key(key_input_dev,KEY_0,value);
	//printk(KERN_NOTICE "value=%d\n",value);
	input_sync(key_input_dev);
	return IRQ_HANDLED;
}
//打开函数,主要工作就是申请中断
static int s3c2440_key_open(struct input_dev *dev)
{
	set_irq_type(key_irqs.key_irq,IRQF_TRIGGER_RISING);
	if(request_irq(key_irqs.key_irq,s3c2440_eint_key,IRQF_SAMPLE_RANDOM,key_irqs.name,NULL))
		return -1;
	printk(KERN_NOTICE "key_open\n");
	return 0;

}
static void s3c2440_key_release(struct input_dev *dev)
{
	disable_irq_nosync(key_irqs.key_irq);
	free_irq(key_irqs.key_irq,NULL);
}
//probe函数,这是driver的核心内容
//当总线匹配到driver和device时,调用driver的probe函数
//这里主要做的工作就是从对应的device的resource中获得device的硬件资源
//比如io地址,做地址映射,同时为对应的按键申请输入子系统的相关数据结构
static int key_probe(struct platform_device *dev)
{
	int ret=0;
	int value;
	struct resource *plat_resource;
	struct platform_device *pdev=dev;
	struct input_dev *input_dev;
	
	printk(KERN_NOTICE "platform driver match platform device\n");
	//get resource[0]
	//num代表的不是resource数组的数组号
	//而是具有相同资源类型的序号(从0开始的序号)
	//在本例中,因为IORESOURCE_IRQ类型的资源只有一个
	//故platform_get_irq()函数的第二个参数是0,而不是resource数组的序号1
	//获取io地址资源
	plat_resource=platform_get_resource(pdev,IORESOURCE_MEM,0);
	//申请iomem资源
	request_mem_region(plat_resource->start,resource_size(plat_resource),"platform key io_res");
	//地址映射,每个地址4个字节
	//GPFCON
	gpfcon=ioremap(plat_resource->start,0x00000004);
	//GPFDAT
	gpfdat=ioremap(plat_resource->start+4,0x00000004);
	//由于我的板子网卡用到了GPF的管脚,为防止配置中破坏其他管教配置
	//先读出原有配置,再对按键对应的GPF0进行单独配置,也就是最低两位配置位10,代表中断模式,具体的参见手册
	value=ioread16(gpfcon);
	value=(value | (0x1<<1))&(0xfffe);
	//写回配置
	iowrite16(value,gpfcon);
	//获取中断号资源
	plat_resource=platform_get_resource(pdev,IORESOURCE_IRQ,0);
	//向全局的数据结构中填写中断号,这个值再open函数调用时进行中断申请
	key_irqs.key_irq=plat_resource->start;
	//申请input子系统资源
	input_dev=input_allocate_device();

	key_input_dev=input_dev;
	//input子系统的按键配置
	set_bit(EV_KEY,key_input_dev->evbit);
	set_bit(KEY_0,key_input_dev->keybit);
	key_input_dev->name="mykeys";
	key_input_dev->dev.init_name="input_key";
	key_input_dev->open=s3c2440_key_open;
	key_input_dev->close=s3c2440_key_release;

	ret=input_register_device(key_input_dev);
	if(ret)
		return -1;
	printk(KERN_NOTICE "registered\n");	
	return ret;
}
static int key_remove(struct platform_device *dev)
{
	printk(KERN_NOTICE "platform device removed\n");
	input_unregister_device(key_input_dev);
	return 0;
}
struct platform_driver key_drv={
	.probe=key_probe,
	.remove=key_remove,
	.driver={
		.owner=THIS_MODULE,
		//这个名字要和对应的device中的名字一致,这是匹配dirver和device的依据
		.name="platform_key",
	},
};


static int __init s3c2440_key_init(void)
{
	int ret;
	//注册驱动程序
	ret=platform_driver_register(&key_drv);
	return ret;	
}
static void __exit s3c2440_key_exit(void)
{
	platform_driver_unregister(&key_drv);
}
MODULE_AUTHOR("weicz");
MODULE_LICENSE("Dual BSD/GPL");
module_init(s3c2440_key_init);
module_exit(s3c2440_key_exit);

测试代码就是上一篇按键input子系统的代码,不用改动,makefile也一样,改个名字就行

看看/sys/bus/platform/devices和/sys/bus/platform/driver目录下都会多出来platform_key

/sys/class/input目录下会有input_key

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值