Linux 驱动开发 四十三:platform 设备驱动实验(一)

Linux 驱动开发 四十三:platform 设备驱动实验(一),第1张

Linux 驱动开发 四十三:platform 设备驱动实验(一) 一、platform 基本概念整理

Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路。因此提出驱动、总线和设备的驱动架构,总线负责管理驱动和设备。系统中有很多的物理总线:I2c、SPI、USB等,有部分设备不存在物理总线,为了驱动架构统一,因此引入 platform 虚拟总线。

platform 虚拟总线用 platform_driver 结构体表示驱动,用 platform_device 结构体描述设备。对于所有设备都使用 bus_type 结构体类型。

1、bus_type
struct bus_type {
	const char		*name;	
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	
	const struct attribute_group **bus_groups;	
	const struct attribute_group **dev_groups;	
	const struct attribute_group **drv_groups;	

	int (*match)(struct device *dev, struct device_driver *drv);
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	int (*remove)(struct device *dev);
	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);

	const struct dev_pm_ops *pm;

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};

platform总线是 bus_type 的一个具体实例,定义在文件 drivers/base/platform.c,platform总线定义如下:

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

platform_bus_type 就是 platform平台总线,其中 platform_match 就是匹配函数

platform_match 函数定义在文件 drivers/base/platform.c 中,函数内容如下所示:

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	
    
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	
	if (of_driver_match_device(dev, drv))
		return 1;

	
	if (acpi_driver_match_device(dev, drv))
		return 1;

	
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	
	return (strcmp(pdev->name, drv->name) == 0);
}

platform_match 函数讲述驱动和设备的匹配有四种方法:

1、OF 类型的匹配,也就是设备树采用的匹配方式,of_driver_match_device 函数定义在文件 include/linux/of_device.h 中。

2、ACPI 匹配方式。

3、id_table 匹配,每个 platform_driver 结构体有一个 id_table 成员变量。

4、直接比较驱动和设备的 name 字段,看看是不是相等,如果相等的话就匹配成功。

2、platform_driver
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;
	const struct platform_device_id *id_table;	
	bool prevent_deferred_probe;
};
struct device_driver {
	const char		*name;
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	

	bool suppress_bind_attrs;	

	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;

	int (*probe) (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 dev_pm_ops *pm;

	struct driver_private *p;
};
3、platform_device
struct platform_device {
	const char	*name;	
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;	
	struct resource	*resource; 

	const struct platform_device_id	*id_entry;
	char *driver_override; 

	
	struct mfd_cell *mfd_cell;

	
	struct pdev_archdata	archdata;
};
struct resource {
	resource_size_t start;	
	resource_size_t end;	
	const char *name;		
	unsigned long flags;	
	struct resource *parent, *sibling, *child;
};
二、platform 驱动相关 API 1、platform_driver_register
int platform_driver_register(struct platform_driver *drv);
2、platform_driver_unregister
void platform_driver_unregister(struct platform_driver *drv)
三、platform 驱动源码编写思路

1、定义 platform 驱动结构体。

2、在 platform 驱动结构体中指定 probe 函数(驱动和设备匹配成功后执行)和 remove 函数(设备注销或驱动注销)。

3、在驱动模块加载函数中调用 platform_driver_register 函数。

4、在驱动模块卸载函数中调用 platform_driver_unregister 函数。

5、probe 函数完成驱动初始化。

#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"


static int led_probe(struct platform_device *dev)
{
	printk("led probe!rn");
    return 0;
}


static int led_remove(struct platform_device *dev)
{
	printk("led remove!rn");
    return 0;
}


static struct platform_driver led_driver = {
	.driver		= {
		.name	= "imx6ul-led",			
	},
	.probe		= led_probe,
	.remove		= led_remove,
};


static int __init led_driver_init(void)
{
    return platform_driver_register(&led_driver);
}


static void __exit led_driver_exit(void)
{
    platform_driver_unregister(&led_driver);
}

module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
四、platform 设备相关 API 1、platform_device_register
int platform_device_register(struct platform_device *pdev)
2、platform_device_unregister
void platform_device_unregister(struct platform_device *pdev)
五、platform 设备源码编写思路

1、定义 platform 设备结构体(包括设备资源信息)。

2、在设备模块加载函数中调用 platform_device_register 函数。

3、在设备模块注销函数中调用 leddevice_exit 函数。

#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"


static struct resource led_resources[] = {

};


static void	led_release(struct device *dev)
{
	printk("led device released!rn");	
}


static struct platform_device led_device = {
	.name = "imx6ul-led",
	.id = -1,
	.dev = {
		.release = &led_release,
	},
	.num_resources = ARRAY_SIZE(led_resources),
	.resource = led_resources,
};


static int __init led_device_init(void)
{
    return platform_device_register(&led_device);
}


static void __exit led_device_exit(void)
{
    platform_device_unregister(&led_device);
}

module_init(led_device_init);
module_exit(led_device_exit);
MODULE_LICENSE("GPL");
六、platform 总结

platform 平台包括驱动、总线和设备 3 部分。总线负责管理驱动和设备,这部分有内核进行维护。设备主要描述硬件相关信息,和设备树作用相同,因此有设备树可以不需要这部分。驱动主要用于实现对硬件的 *** 作,向应用层提供一些 API 接口,供上层应用使用。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5720725.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存