资讯详情

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

一、platform 整理基本概念

Linux 系统要考虑到驱动的,所以提出了驱动这样的。因此提出总线负责驱动和设备的管理。系统中有许多物理总线:I2c、SPI、USB等等,有些设备没有物理总线,所以引入是为了统一驱动架构

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

1、bus_type

/** * struct bus_type - The bus type of the device 设备总线类型 * * @name: The name of the bus. * @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id). 用于子系统枚举设备。 * @dev_root: Default device to use as the parent. 默认设备用作父设备。 * @dev_root: Default device to use as the parent. 默认设备用作父设备。 * @dev_attrs: Default attributes of the devices on the bus. 总线设备的默认属性。 * @bus_groups: Default attributes of the bus. 总线的默认属性。 * @dev_groups: Default attributes of the devices on the bus. 总线设备的默认属性。 * @drv_groups: Default attributes of the device drivers on the bus. 总线设备驱动程序的默认属性。 * @match: Called, perhaps multiple times, whenever a new device or driver * is added for this bus. It should return a nonzero value if the * given device can be handled by the given driver. * 在为总线添加新设备或驱动程序时,可多次调用。如果给定的设备可以由给定的驱动程序处理,则应返回非零值。 * @uevent: Called when a device is added, removed, or a few other things * that generate uevents to add the environment variables. * 添加、删除设备或生成 uevents 调用添加环境变量的其他内容。 * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. * 将新设备或驱动器添加到总线时,调用特定驱动器的检测,初始化匹配设备。 * @remove: Called when a device removed from this bus. 调用设备从总线移除时。 * @remove: Called when a device removed from this bus. 调用设备从总线移除时。 * @shutdown: Called at shut-down time to quiesce the device. 关机时调用,停止设备。 * * @online: Called to put the device back online (after offlining it). 调用使设备重新上线(脱机后)。 * @offline: Called to put the device offline for hot-removal. May fail. 调用热拆除设备离线。 * * @suspend: Called when a device on this bus wants to go to sleep mode. * 当总线上的设备想要进入睡眠模式时。 * @resume: Called to bring a device on this bus out of sleep mode. * 从睡眠模式中唤醒总线上的设备。 * @pm: Power management operations of this bus, callback the specific * device driver's pm-ops. 本总线的电源管理操作,特定设备驱动程序的回调 pm-ops。 * @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU * driver implementations to a bus and allow the driver to do * bus-specific setup * 此总线的 IOMMU 特定操作,用于将 IOMMU 将驱动程序附加到总线,并允许驱动程序在总线上执行特定的设置。 * @p: The private data of the driver core, only the driver core can * touch this. 只有驱动核心才能使用驱动核心的私有数据。 * @lock_key: Lock class key for use by the lock validator 类键用于锁验证器 * * A bus is a channel between the processor and one or more devices. For the * purposes of the device model, all devices are connected via a bus, even if * it is an internal, virtual, "platform" bus. Buses can plug into each other. * A USB controller is usually a PCI device, for example. The device model * represents the actual connections between buses and the devices they control. * A bus is represented by the bus_type structure. It contains the name, the * default attributes, the bus' methods, PM operations, and the driver core's * private data. * 总线是处理器和一个或多个设备之间的通道。 * 就设备型号而言,即使是内部虚拟的,所有设备都通过总线连接"platform"总线。 * 就设备型号而言,即使是内部虚拟的,所有设备都通过总线连接"platform"总线。总线可以相互插入。 * 例如,USB 通常是控制器 PCI 设备。 * 设备模型表示总线与控制设备之间的实际连接。 * 总线由bus_type结构表示。它包含名称、默认属性和总线的方法PM 私有数据是操作和驱动程序的核心。 */ struct bus_type { 
          const char  *name; /* 总线名字 */  const char  *dev_name;  struct device  *dev_root;  struct device_attribute *dev_attrs; /* use dev_groups instead */  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 中,函数内容如下所示:

/** * platform_match - bind platform device to platform driver. 将平台设备绑定到平台驱动程序。 * @dev: device. * @drv: driver. * * Platform device IDs are assumed to be encoded like this: * "<name><instance>", where <name> is a short description of the type of * device, like "pci" or "floppy", and <instance> is the enumerated * instance of the device, like '0' or '42'. Driver IDs are simply * "<name>". So, extract the <name> from the platform_device structure, * and compare it against the name of the driver. Return whether they match * or not. * 假定平台设备 ID 的编码方式如下: * "<name><instance>", 其中name是设备类型的简短描述,如pci或floppy,而instance是设备的枚举实例,如'0'或'42'。 * 驱动程序 ID 只是简单"<name>"。 * 因此,<name>从platform_device结构中提取 ,并将其与驱动程序的名称进行比较。 * 无论它们是否匹配,都返回。 */
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);

	/* When driver_override is set, only bind to the matching driver */
    /* 当设置驱动程序覆盖时,只绑定到匹配的驱动程序 */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first 首先尝试 OF 样式匹配(设备树方式) */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match 然后尝试 ACPI 样式匹配 */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table 然后尝试与 id 表匹配 */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match 回退到驱动程序名称匹配 */
	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;	/* id_table 表(是一个数组) */
	bool prevent_deferred_probe;
};
/** * struct device_driver - The basic device driver structure 基本设备驱动程序结构 * @name: Name of the device driver. 设备驱动程序的名称。 * @bus: The bus which the device of this driver belongs to. 此驱动程序的设备所属的总线。 * @owner: The module owner. 模块所有者。 * @mod_name: Used for built-in modules. 用于内置模块。 * @suppress_bind_attrs: Disables bind/unbind via sysfs. 通过 sysfs 禁用绑定/取消绑定。 * @of_match_table: The open firmware table. 打开的固件表。 * @acpi_match_table: The ACPI match table. ACPI 匹配表。 * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. 调用该函数可以查询特定设备是否存在,该驱动是否可以使用该设备,以及将该驱动绑定到特定设备。 * @remove: Called when the device is removed from the system to * unbind a device from this driver. 当设备从系统中移除时调用,以从此驱动程序中解绑定设备。 * @shutdown: Called at shut-down time to quiesce the device. 在关闭时间调用,以停止设备。 * @suspend: Called to put the device to sleep mode. Usually to a * low power state. 调用将设备置于睡眠模式。通常为低功率状态。 * @resume: Called to bring a device from sleep mode. 调用以使设备退出睡眠模式。 * @groups: Default attributes that get created by the driver core * automatically. 由驱动程序核心自动创建的默认属性。 * @pm: Power management operations of the device which matched * this driver. 与此驱动程序匹配的设备的电源管理操作。 * @p: Driver core's private data, no one other than the driver * core can touch this. 驱动核心的私有数据,除了驱动核心没有人可以碰它。 * * The device driver-model tracks all of the drivers known to the system. * The main reason for this tracking is to enable the driver core to match * up drivers with new devices. Once drivers are known objects within the * system, however, a number of other things become possible. Device drivers * can export information and configuration variables that are independent * of any specific device. * 设备驱动程序模型跟踪系统已知的所有驱动程序。 * 这种跟踪的主要原因是使驱动程序核心能够将驱动程序与新设备匹配起来。 * 然而,一旦驱动程序成为系统中的已知对象,许多其他事情就成为可能。 * 设备驱动程序可以导出独立于任何特定设备的信息和配置变量。 */
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 */

	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;	/* 表示 resource 大小 */
	struct resource	*resource; /* 表示资源,也就是设备信息,比如外设寄存器等 */

	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;
};
/* * Resources are tree-like, allowing * nesting etc.. */
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

/* 注册驱动程序 * * drv:要注册的 platform 驱动 * 返回值:负数,失败;0,成功。 */
int platform_driver_register(struct platform_driver *drv);

2、platform_driver_unregister

/* 注销驱动程序 * * drv:要注销的 platform 驱动 * 返回值:无 */
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"

/* * @description : flatform驱动的probe函数,当驱动与设备匹配以后此函数就会执行 * @param - dev : platform设备 * @return : 0,成功;其他负值,失败 */
static int led_probe(struct platform_device *dev)
{ 
        
	printk("led probe!\r\n");
    return 0;
}

/* * @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行 * @param - dev : platform设备 * @return : 0,成功;其他负值,失败 */
static int led_remove(struct platform_device *dev)
{ 
        
	printk("led remove!\r\n");
    return 0;
}

/* platform驱动结构体 */
static struct platform_driver led_driver = { 
        
	.driver		= { 
        
		.name	= "imx6ul-led",			/* 驱动名字,用于和设备匹配 */
	},
	.probe		= led_probe,
	.remove		= led_remove,
};

/* * @description : 驱动模块加载函数 * @param : 无 * @return : 无 */
static int __init led_driver_init(void)
{ 
        
    return platform_driver_register(&led_driver);
}

/* * @description : 驱动模块卸载函数 * @param : 无 * @return : 无 */
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

/* 注册设备 * * drv:要注册的 platform 设备 * 返回值:负数,失败;0,成功 */
int platform_device_register(struct platform_device *pdev)

2、platform_device_unregister

/* 注销设备 * * drv:要注销的 platform 设备 * 返回值:无 */
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"

/* * 设备资源信息,也就是LED0所使用的所有寄存器 */
static struct resource led_resources[] = { 
        

};

/* @description : 释放flatform设备模块的时候此函数会执行 * @param - dev : 要释放的设备 * @return : 无 */
static void	led_release(struct device *dev)
{ 
        
	printk("led device released!\r\n");	
}

/* * platform设备结构体 */
static struct platform_device led_device = { 
        
	.name = "imx6ul-led",
	.id = -1,
	.dev = { 
        
		.release = &led_release,
	},
	.num_resources = ARRAY_SIZE(led_resources),
	.resource = led_resources,
};

/* * @description : 设备模块加载 * @param : 无 * @return : 无 */
static int __init led_device_init(void)
{ 
        
    return platform_device_register(&led_device);
}

/* * @description : 设备模块注销 * @param : 无 * @return : 无 */
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 接口,供上层应用使用。

标签: 插接式连接器mfd001插接式连接器mfd014

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台