linux之platform平台设备与驱动

总线,驱动,设备

在这里插入图片描述

要满足linux设备模型,就需要有对应总线(struct bus_type),驱动(struct device_driver),设备(struct device)。

具体的总线类型可看/sys/bus/下,常见的spi,i2c,usb等总线;

但是有的设备并没有对应的物理总线,如RTC,LED,按键等.为此内核专门开发了一种虚拟总线---platform总线;
用于连接一些没有物理总线或不支持热插拔的设备;

通常总线在内核中已经实现好了,我们只需要写对应总线驱动,有时还需要写相应的设备注册代码;
平台总线

在内核driver/base/platform.c下有platform总线定义;

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

内核自己实现好了platform总线,对应/sys/bus/platform;

当然,也可以自己使用struct bus_type实现定制总线,通过bus_register(&bus)注册;

struct bus_type
struct bus_type {
	const char		*name;//bus名字
	const char		*dev_name;//用于子系统枚举设备,如foo%u,dev->id
	struct device		*dev_root;//设备默认将其作为父对象
	const struct attribute_group **bus_groups;//bus默认属性
	const struct attribute_group **dev_groups;//dev默认属性
	const struct attribute_group **drv_groups;//drv默认属性

	//每当为该总线添加新设备或驱动程序时,被调用;匹配成功返回正值,否则返回0;
	int (*match)(struct device *dev, struct device_driver *drv);
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

	//当新设备或驱动程序添加到这个总线时被调用,并在其中回调驱动的probe来初始化匹配的设备;
	int (*probe)(struct device *dev);
	void (*sync_state)(struct device *dev);
	int (*remove)(struct device *dev);//当device从该总线移除时调用
	void (*shutdown)(struct device *dev);

	int (*online)(struct device *dev);
	int (*offline)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);//电源挂起调用
	int (*resume)(struct device *dev);//电源恢复调用

	int (*num_vf)(struct device *dev);

	int (*dma_configure)(struct device *dev);

	const struct dev_pm_ops *pm;

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;//驱动核心私有数据,仅仅驱动核能见;
	struct lock_class_key lock_key;

	bool need_parent_lock;
};

平台驱动

基本的设备驱动结构体:struct device_driver
struct device_driver {
	const char		*name;//设备驱动名
	struct bus_type		*bus;//这个驱动属于的设备的总线;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
	enum probe_type probe_type;

	const struct of_device_id	*of_match_table;//打开的固件表
	const struct acpi_device_id	*acpi_match_table;

	int (*probe) (struct device *dev);
	void (*sync_state)(struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;
	const struct attribute_group **dev_groups;

	const struct dev_pm_ops *pm;
	void (*coredump) (struct device *dev);

	struct driver_private *p;//驱动核私有数据
};
平台设备驱动:struct platform_driver
// include <linux/driver.h>
struct platform_driver {
	int (*probe)(struct platform_device *);//总线发现有匹配的平台设备时调用
	int (*remove)(struct platform_device *);//所驱动的平台设备被移除时或平台驱动注销时调用;
	void (*shutdown)(struct platform_device *);
	int (*suspend)(struct platform_device *, pm_message_t state);
	int (*resume)(struct platform_device *);
	struct device_driver driver;//内嵌struct device_dirver
	const struct platform_device_id *id_table;//平台驱动可以驱动的平台设备ID列表;
	bool prevent_deferred_probe;
};
向平台总线注册注销平台驱动;
int platform_driver_register(struct platform_driver *pdrv);
void platform_driver_unregister(struct platform_driver *pdrv);

平台设备

//#include <linux/platform_device.h>
struct platform_device {
	const char	*name;//平台设备名,在平台总线的match中可用于同平台驱动匹配;
	int		id;//设备的id号,用于区别同类型的不同平台设备.(pdev%d,id)
	bool		id_auto;
	struct device	dev;
	u64		platform_dma_mask;
	struct device_dma_parameters dma_parms;
	u32		num_resources;//平台设备使用的资源个数;
	struct resource	*resource;//平台设备的资源列表,指向资源的首地址;

	//用于平台驱动匹配的ID,在平台总线的match函数中,首先尝试匹配该ID,如果不成功,在尝试用name匹配;
	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

平台设备最主要的就是struct resource,它是实现设备与驱动分量的关键;

//#include <linux/ioport.h>
struct resource {
	//资源的开始,对于IO内存来说是起始的内存地址;对于中断资源来说就是起始中断号;对于DMA来说就是起始的DMA通道号;
	resource_size_t start;
	resource_size_t end;//资源的结束
	const char *name;
	
	//资源的标志;IORESOURCE_MEM资源的类型时内存资源; IORESOURCE_IRQ:中断资源  IORESOURCE_DMA:DMA通道资源;
	unsigned long flags;
	unsigned long desc;
	struct resource *parent, *sibling, *child;
};
向平台总线注册或注销平台设备:
int platform_add_devices(struct platform_device **devs, int num);//一次可以注册num个
int platform_device_register(struct platform_device *pdev);//只能注册一个
void platform_device_unregister(struct platform_device *pdev);

关键:当平台总线发现有和平台设备匹配的驱动时,就会调用平台驱动内的probe函数,并传递匹配的平台设备结构地址;
平台驱动就可以获得设备资源;

获取资源的函数:
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);
resource_size_t resource_size(const struct resource *res);
定义资源
struct resource res[] = {
	[0] = {
		.start = 0x10000000,//物理地址 开始
		.end   = 0x10000000+100,//结束
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = 0x100,//中断号 中断号有限 只能start即可
		.flags = IORESOURCE_IRQ,
	},
	//用宏定义
	[2] = DEFINE_RES_MEM(0x20000000,0x100),
	[3] = DEFINE_RES_IRQ(0x200),
};
示例

平台驱动;

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>

static int pdrv_probe(struct platform_device *dev)
{
	printk("-----pdrv_probe----\n");

	return 0;
}

static int pdrv_remove(struct platform_device *dev)
{
	printk("-----pdrv_remove----\n");

	return 0;
}

static int drv_suspend(struct device *dev)
{
	printk("-----drv_suspend----\n");

	return 0;
}

static int drv_resume(struct device *dev)
{
	printk("-----drv_resume----\n");

	return 0;
}
	
static struct dev_pm_ops drv_pm_opt = {
	.suspend = drv_suspend,
	.resume  = drv_resume,
};

static struct platform_driver pltdrv = {
	.driver = {
		.name = "pltdev",
		.owner = THIS_MODULE,
		.pm = &drv_pm_opt,
	},
	.probe = pdrv_probe,
	.remove = pdrv_remove,
};

static __init int pltdrv_init(void)
{
	platform_driver_register(&pltdrv);

	return 0;
}

static void __exit pltdrv_exit(void)
{
	platform_driver_unregister(&pltdrv);
}

module_init(pltdrv_init);
module_exit(pltdrv_exit);

MODULE_LICENSE("GPL");

平台设备;

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>

static void pdev_release(struct device *dev)
{
}

static struct platform_device pltdev0 = {
	.name = "pltdev",
	.id   = 0,
	.num_resources = 0,
	.resource = NULL,
	.dev = {
		.release = pdev_release,
	},
};

static struct platform_device pltdev1 = {
	.name = "pltdev",
	.id   = 1,
	.num_resources = 0,
	.resource = NULL,
	.dev = {
		.release = pdev_release,
	},
};

static __init int pltdev_init(void)
{
	platform_device_register(&pltdev0);
	platform_device_register(&pltdev1);

	return 0;
}

static void __exit pltdev_exit(void)
{
	platform_device_unregister(&pltdev0);
	platform_device_unregister(&pltdev1);
}

module_init(pltdev_init);
module_exit(pltdev_exit);

MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天未及海宽

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

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

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

打赏作者

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

抵扣说明:

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

余额充值