使用平台设备的LED驱动

在驱动里面编写设备信息,没有实现设备和驱动的分离,一旦硬件变化,就必须修改驱动。
我们现在把LED驱动分解成平台设备和平台驱动
设备里面存放硬件信息,如果硬件有变化,只需要修改设备
驱动从设备中去获取硬件信息,硬件变化,驱动不用改变。
在这里插入图片描述

代码:向平台总线注册4个设备

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define FSLED_MAJOR 256
static void fsdev_release(struct device *dev)
{
}
//硬件信息用resource来表示,DEFINE_RES_MEM将后面的信息包装成一个struct resource
static struct resource led2_resource[]={
	[0]=DEFINE_RES_MEM(0x11000c40,4), //外设寄存器的地址,4个字节
//	[1]=DEFINE_RES_MEM(0x1100c44,4),
}
static struct resource led2_resource[]={
	[0]=DEFINE_RES_MEM(0x11000c20,4),
}
static struct resource led2_resource[]={
	[0]=DEFINE_RES_MEM(0x114001e0,4),
}
static struct resource led2_resource[]={
	[0]=DEFINE_RES_MEM(0x114001e0,4),
}
//寄存器的第几位
unsigned int led2pin = 7;
unsigned int led3pin = 0;
unsigned int led4pin = 4;
unsigned int led5pin = 5;
//开发板有4个LED,我们把每个灯都做成一个设备
//内核用struct platform_device的对象来表示一个平台设备,有4个LED灯,
struct platform_device fsled2={
	.name = "fsled",
	.id=2,
	.num_resources=ARRAY_SIZE(led2_resources), //资源个数
	.release=led2_release,
	.dev={
		//移除设备时调用
		.release=fsdev_release,
		.platform_data=&led2pin,
	}
};
struct platform_device fsled3={
	.name = "fsled",
	.id=3,
	.num_resources=ARRAY_SIZE(led3_resources), //资源个数
	.release=led3_release,
	.dev={
	//移除设备时调用
	.release=fsdev_release,
	.platform_data=&led3pin,
	}
};
struct platform_device fsled4={
	.name = "fsled",
	.id=4,
		.num_resources=ARRAY_SIZE(led4_resources), //资源个数
	.release=led4_release,
	.dev={
	//移除设备时调用
	.release=fsdev_release,
	.platform_data=&led4pin,
	}
};
struct platform_device fsled5={
	.name = "fsled",
	.id=5,
	.num_resources=ARRAY_SIZE(led5_resources), //资源个数
	.release=led5_release,
	.dev={
	//移除设备时调用
	.release=fsdev_release,
	.platform_data=&led5pin,
	}
};
//注册设备
static int __init fsdev_init(void)
{
	//向平台总线注册4个LED灯
	platform_device_register(&fsled2);
	platform_device_register(&fsled3);
	platform_device_register(&fsled4);
	platform_device_register(&fsled5);
	/*
		//使用先把4个LED包装称数组
		static struct platform_device *fsled_device[]={
			&fsled2,
			&fsled3,
			&fsled4,
			&fsled5,
		};
		//使用该函数一次性添加4个设备
		return platform_add_devices(fsled_devices,ARRAY_SIZE(fsled_devices));
	*/
}
//注销设备
static void __exit fsdev_exit(void)
{
		platform_device_unregister(&fsled2);
		platform_device_unregister(&fsled3);
		platform_device_unregister(&fsled4);
		platform_device_unregister(&fsled5);
}
module_init(fsdev_init);
module_init(fsdev_exit);
MODULE_LICENSE("GPL");

ARRAY_SIZE函数
在这里插入图片描述
设备树:内核启动后,会扫描设备树,生成各种设备
代码:平台驱动

//定义一个字符设备,内嵌一个cdev
struct fsled_dev
{
	//根据设备的具体情况,我们增加结构体成员
	unsigned int __iomem *con;  //存放映射后的地址
	unsigned int __iomem *dat;
	unsigned int pin;
	struct cdev cdev;
}
static int fsled_open(struct inode*inode,struct file*filp)
{
	struct fsled_dev *fsled=container_of(inode->cdev,struct fsled_dev,cdev);
	filp->private_data=fsled;
	return 0;
}
static int fsled_release(struct inode*inode,struct file*filp)
{
	
}
struct file_operation fsled_ops=
{
	.owner = THIS_MODULE,
	.open = fsled_open,
	.release=fsled_release,
	.unlocked_ioctl=fs_ioctl,
};
//当设备和驱动匹配成功时,自动调用该函数,参数是一个指向匹配设备的指针
static int fsled_probe(struct platform_device *pdev)
{
	//1.申请注册设备号
	int ret;
	dev_t dev; //本例中有4设备,会被调用4次
	struct fsled_dev *fsled;  //定义一个字符设备
	//硬件信息用struct resource来表示
	struct resource *res;
	//获取设备中的管脚信息,这是非标准的硬件信息
	unsigned int pin = *(unsigned int)(pdev->dev.platform_data)
	dev=MKDEV(FSLED_MAJOR,pdev->id); //设备结构体中有id
	ret=register_chrdev_region(dev,1,"fsled");
	if(ret)
	{
		goto reg_err;
		fsled = kzalloc(sizeof(struct fsled_dev),GFP_KERNEL);
		if(!fsled)
		{
			ret = -ENOMEM;
			goto mem_err;
		}
		cdev_init(&fsled->cdev,&fsled_ops);
		fsled->cdev.owner=THIS_MODULE;
		ret=cdev_add(&fsled->cdev,dev,1);
		//下面开始从pdev设备中获取硬件信息,res为获取的硬件地址
		res = platform_get_resource(pdev,IORESOURCE_MEM,0);
		if(!res)
		{
			ret = -ENOENT;
			goto res_err;
		}
		//物理--->虚拟  0x11000c40  4
		fsled->con=ioremap(res->start,resource_size(res));
		if(!fsled->con)
		{
			ret = -EBUSY;
			goto map_err;
		}
		//另一个寄存器的地址
		fsled->dat=fsled->con+1;
		fsled->pin = pin;
		/*************硬件信息已经获取完毕******************/
		/******fsled->con,fsled->dat,fsled-pin*************/
		//接下来实施控制LED灯让GPIO输出功能,熄灭LED
		write((readl(fsled->con)&~(0xf<<4*fsled->pin))|(0x1<<4*fsled->pin),fsled->con);
		write(readl(fsled->dat)&~(0x1<<fsled->pin),fsled->dat);
		//为了以后方便的得到fsled这个指针,我们把它存放在pdev中一个成员里,方便以后获取。
		platform_set_drvdata(pdev,fsled);
		return 0;
	res_err:
		cdev_del(&fsled->cdev);
	add_err:
		kfree(fsled);
	mem_err:
		unregister_chrdev_region(dev,1);
	reg_err:
		return ret;				
	}
}
//参数是指向被移除的设备的指针
static int fsled_remove(struct platform_device *pdev)
{
	//本函数也是被调用4次
	//从pdev中恢复我们之前存入的fsled
	struct fsled_dev* fsled = platform_get_drvdata(pdev);
	ioumap(fsled->con); //解除映射
	cdev_del(&fsled->cdev);  //删除设备
	kfree(fsled);//释放资源
	unregister_chrdev_region(cdev,1);
	return 0;
	
}
//内核中用platform_driver的对象来表示平台驱动
struct platform_driver fsled_drv={
 	.driver={
 		//名字要注意,要和设备匹配
 		.name="fsled",
 		.owner=THIS_MODULE,
 	},
 	//当匹配成功,会自动调用probe函数
 	.probe=fsled_probe,
 	.remove=fsled_remove,
 };
 //平台设备的加载与卸载函数通常只注册和注销驱动
//设备号,设备等放到probe函数中去做。
//用该函数替换之前的加载,卸载函数
module_platform_driver(fsled_drv);
/*
static int __init fsled_init(void)
{
	//注册平台驱动
	platform_driver_register(&fsled_drv);
	return 0;
}
static void__exit fsled_exit(void)
{
	//注销平台驱动
	platform_driver_unregister(&fsled);
}

module_init(fsled_init);
module_exit(fsled_exit);
*/
MODULE_LICENSE("GPL");

由于加载函数、卸载函数基本上做固定的事情、
总线要根据名字匹配设备和驱动
平台驱动的加载和卸载函数通常只注册和注销平台驱动
以前的字符设备驱动程序的加载函数要做申请设备号,添加cdev都放在了probe中,相应的注销设备号,cdev_del放到remove中去。
在这里插入图片描述
kmalloc和kzalloc都是内核中用来动态分配内存的函数,kzalloc会将分配的内存清0,这两个函数分配内存时,一般都使用GFP_KERNEL的方式;释放空间kfree();
硬件信息需要从设备中获取。
标准硬件信息用硬件资源来表示,platform_get_resource来获得信息

struct resource结构体

struct resource
{
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent,*sibing,*chind;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

<( ̄︶ ̄)Okay.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值