linux platform分离机制是如何运作的呢

/**
 * 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_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.
 * @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.
 * @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.
 * @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
 * @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.
 */
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;
};

linux内核用bus_type结构体创建了platform_bus_type(见如下)

struct bus_type platform_bus_type = {
	.name		= "platform",  /*总线名称*/
	.dev_groups	= platform_dev_groups, /*platform总线上设备的默认属性*/
	.match		= platform_match,  /*匹配函数*/
	.uevent		= platform_uevent,  /*见下面解释*/
	.pm		= &platform_dev_pm_ops, /*总线电源管理操作接口*/
};
EXPORT_SYMBOL_GPL(platform_bus_type);

这里的uevent成员内核解释如下:
@uevent: Called when a device is added, removed, or a few other things
that generate uevents to add the environment variables.
当设备被添加,移除或者其他方面生成uevents的操作时候,被调用来改变环境变量?(个人翻译理解,仅作参考)
这里要重点关注其match()成员函数,正是此成员表明了platform_device和platform_driver之间如何匹配。

linux kernel 3.18.67版的匹配函数platform_match


/**
 * 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.
 */
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 */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	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);
}

linux kernel 2.6版的匹配函数platform_match()


/**
 * 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.
 */
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);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	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);
}

在Linux 2.6 的设备驱动模型中,关心总线、设备和驱动3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总线。基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为 platform_driver。

版权声明:本文为CSDN博主「zqixiao_09」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zqixiao_09/article/details/50865480
那么问题来了,platform设备驱动到底是怎么工作的呢 和 以及 如何来编写platform设备的驱动程序呢。

platform设备驱动软硬件分离机制原理

内核已经定义好了虚拟总线 BUS,是一个结构体,在这个总线上维护着两个链表, dev 链表和 drv 链表, dev 链表上每个节点描述者纯硬件信息, drv 链表上每个节点描述着纯软件信息,对应的结构体分别platform_device 和 platform_drivers 里面有个字段 name,分别描述硬件节点的名称和软件节点的名称, 内核自己调用 match 函数去遍历匹配,若匹配成功后,内核调用纯软件节点中的 probe 函数,把串口的硬件结点的首地址传递给 probe 函数。

两个重要的结构体(1)platform_device

由上图我们需要关注如下两个结构体,一个是硬件设备侧的结构体
struct platform_device

struct mfd_cell;

struct platform_device {
	const char	*name; /*硬件节点的名称,用于匹配*/
	int		id;      /*硬件节点的编号,如果 dev 链表上,仅有一个名称为 name
                     的硬件节点,id=-1 即可,如果 dev 链表上,有多个名称name(同名),通过 id 进行编号(0,1,2,...)*/
	bool		id_auto; /*kernel2.6里面没有这个成员 不知道干嘛的*/
	struct device	dev; /*(硬件模块必须包含该结构体?注释1)这里只
	                       提下他的一个成员release回调函数见下面介绍*/
	u32		num_resources; 
	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;
};

结构体platform_device成员 —(struct device)dev

介绍下结构体platform_device中的成员 结构体 (struct)device,内核源码里面他有很多的成员,看上去极其复杂,这里我暂时没有能力搞懂,我们通常关心里面的platform_data和release,前者是用来存储私有设备信息的,后者是供当这个设备的最后引用被删除时被内核回调,注意和rmmod没关系。顺带提一嘴我知道的一个关于它的一个成员release回调函数的小细节。

struct device {
  ................
  void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
  void    (*release)(struct device *dev);
  ................
};

@release: Callback to free the device after all references have gone away. This should be set by the allocator of the device (i.e. the bus driver that discovered the device).
上面的英文是linux内核的官方注解,我个人不是很理解,附上一个应用的小例子(为了去除警告)

............
//仅仅去除警告
static void led_release(struct device *dev) {}

//定义初始化描述LED的硬件信息对象
static struct platform_device led_dev = {
    .name = "vaccine", //用于匹配
    .id = -1,//硬件节点编号
    .resource = led_res,//装载resource描述的硬件信息
    .num_resources = ARRAY_SIZE(led_res),//硬件信息的个数
    .dev = {
        .release = led_release //仅仅是为了去除警告而已
    }
};
..........

无独有偶,我在CSDN上也看到一篇讲解LINUXplatform设备驱动开发实例解析的博文里面也是这样的单单只是初始化device结构体中的release回调函数
强烈推荐用心看看这一篇博文,博主写得很用心,一看就是下过不少气力的

在这里插入图片描述

结构体platform_device成员 —(struct)resource *resource

介绍下结构体platform_device中的成员 结构体 (struct resource)*resource,内核提供了struct resource来对这些资源进行封装。这些资源即是所谓的设备信息,主要分为两种:硬件信息、软件信息,硬件信息主要包括xxx控制器在xx地址上,xx设备占用了xx中断号,即地址资源,中断资源等。

定义在 //include/linux/ioport.h

struct resource {
	resource_size_t start;  //硬件资源起始地址   
	resource_size_t end;   //硬件资源结束地址
	const char *name;      
	unsigned long flags;   //区分是资源什么类型的
	struct resource *parent, *sibling, *child;
};
 
#define IORESOURCE_MEM        0x00000200
#define IORESOURCE_IRQ        0x00000400   
  • start表示资源开始的位置,如果是IO地址资源,就是起始物理地址,如果是中断资源,就是中断号;

  • end表示资源结束的位置,如果是IO地址地址,就是映射的最后一个物理地址,如果是中断资源,就不用填;

  • name就是这个资源的名字。

  • flags表示资源类型,提取函数在寻找资源的时候会对比自己传入的参数和这个成员,理论上只要和可以随便写,但是合格的工程师应该使用内核提供的宏,这些宏也在"ioport.h"中进行了定义,比如IORESOURCE_MEM表示这个资源是地址资源,IORESOURCE_IRQ表示这个资源是中断资源…。

有了这几个属性,就可以完整的描述一个资源,但如果每个资源都需要单独管理而不是组成某种数据结构,显然是一种非常愚蠢的做法,所以内核的resource结构还提供了三个指针:parent,sibling,child(24),分别用来表示资源的父资源,兄弟资源,子资源,这样内核就可以使用树结构来高效的管理大量的系统资源,linux内核有两种树结构:iomem_resource,ioport_resource,进行板级开发的时候,通常将主板上的ROM资源放入iomem_resource树的一个节点,而将系统固有的I/O资源挂到ioport_resource树上。【此处拷贝优秀博文《Linux Platform驱动模型(一) _设备信息》】

下面是一个小例子,分别用两种写法表示了地址资源和中断资源,强烈推荐使用DEFINE_RES_XXX的版本

//IO地址资源,自己填充resource结构体+flags宏
static struct resource led_res = {
        .start = 0xC001C000, //起始地址
        .end = 0xC001C000 + 0x24 - 1, //结束地址
        .flags = IORESOURCE_MEM //地址类信息
};

我们换另一种内核定义的宏来实现上面的装载IO地址资源的resource结构体

static struct resource led_res = DEFINE_RES_MEM(0xC001C000, 0x24);

是不是看上去简单多了,然后在特意到linux 内核中的include/linux/ioport.h文件中查看是如何实现的。
static struct resource led_res = DEFINE_RES_MEM(0xC001C000, 0x24)
= DEFINE_RES_MEM_NAMED(0xC001C000, 0x24, NULL) = DEFINE_RES_NAMED(0xC001C000, 0x24, NULL, IORESOURCE_MEM)
= {
.start = 0xC001C000,
.end = 0xC001C000 + 0x24 -1,
.name = NULL,
.flags = IORESOURCE_MEM
}

我这里把上述层层嵌套的宏的最后一个宏 DEFINE_RES_NAMED,也是最关键的一步的内核定义粘贴如下,供大家了解,有兴趣建议去看内核源码

/* helpers to define resources */
#define DEFINE_RES_NAMED(_start, _size, _name, _flags)			\
	{								\
		.start = (_start),					\
		.end = (_start) + (_size) - 1,				\
		.name = (_name),					\
		.flags = (_flags),					\
	}

这里我强调一点是 .end = start + size -1 ; 不要忘记减一(为什么要减1仔细想想估计大家都能想明白)
接下来我们看看如何装载中断资源的

//中断资源,自己填充resource结构体+flags宏
struct resource res = {    
        .start    = 10,
        .flags    = IORESOURCE_IRQ,
};

我们换另一种内核定义的宏来实现上面的装载中断资源的resource结构体

//中断资源,使用内核提供的定义宏
struct resource res = DEFINE_RES_IRQ(10);

我同样一层一层地将这个宏还原如下:
struct resource res = DEFINE_RES_IRQ(10) = DEFINE_RES_IRQ_NAMED(10, NULL) = DEFINE_RES_NAMED(10, 1, NULL, IORESOURCE_IRQ) = {
.start = 10,
.end = 10, (很多都是省略掉的)
.name = NULL,
.flags = IORESOURCE_IRQ,
}

通过这两个实例,我们对platform_device结构体中的重要成员struct resource *resource 有了一定的了解,在使用它装载硬件资源信息时,我们不难发现如果使用内核已经封装好的宏,会十分简便,当然前提是要理解宏的定义。
硬件侧的platform_device结构体的介绍就告一段落了,接下来我们来看看跟这个结构体相配套的两个函数。

结构体platform_device相关配套函数—platform_device_register();

  • platform_device_register(); // *注册:把指定设备添加到内核中平台总线的设备列表,等待匹配,该函数写在加载模块的函数里面

/**
 * platform_device_register - add a platform-level device
 * @pdev: platform device we're adding
 */
int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);
	arch_setup_pdev_archdata(pdev);
	return platform_device_add(pdev);
}

结构体platform_device相关配套函数—platform_device_unregister();

  • platform_device_unregister(); //注销:把指定设备从设备列表中删除,该函数写在加载模块的函数里面
/**
 * platform_device_unregister - unregister a platform-level device
 * @pdev: platform device we're unregistering
 *
 * Unregistration is done in 2 steps. First we release all resources
 * and remove it from the subsystem, then we drop reference count by
 * calling platform_device_put().
 */
void platform_device_unregister(struct platform_device *pdev)
{
	platform_device_del(pdev);
	platform_device_put(pdev);
}

好了,到此,硬件侧相关的结构体和函数介绍的差不多了,我们写一个硬件侧的例子。

linux platform软硬件分离机制的驱动硬件侧代码led_dev.c

/*头文件*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
/*使用struct resource来定义初始化led灯的硬件信息对象*/
struct resource led_res[] = {
		{
		.start = 0xC001C000,
		.end = 0xC001C000 + 0x24 -1 ,
		.flags = IORESOURCE_MEM
		},
		{
		......
		}
};
//仅仅去除警告
static void led_release(struct device *dev) {}

/*使用platform_device定义初始化led相关的硬件节点对象*/
static struct platform_device led_dev = {
		.name = "vaccine_leds",
		.id = -1,
		.resource = led_res,
		.num_resources = ARRAY_SIZE(led_res),
		.dev = {
				.release = led_release
		}
};

/*入口函数:即是模块加载函数*/
static int led_dev_init(void){
/*注册:把指定设备添加到内核中平台总线的设备列表,等待匹配*/
	platform_device_register(&led_dev);
	return 0;
}
/*出口函数,即是模块卸载函数*/
static int led_dev_exit(void){
/*注销:把指定设备从设备列表中删除,该函数写在加载模块的函数里面*/
	platform_device_unregister(&led_dev);
	return 0;
}
/*各种GPL规则的修饰*/
moudle_init(led_dev_init);
moudle_exit(led_dev_init);
MOUDLE_LENCENSE("GPL");

两个重要的结构体(2)platform-driver

另一个需要我们关注的结构体是驱动软件侧的struct 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 (*suspend_late)(struct platform_device *, pm_message_t state);
    int (*resume_early)(struct platform_device *);
    int (*resume)(struct platform_device *);
    struct device_driver driver; 
};

这里面我们注意关心一个结构体成员 (struct device_driver)driver 和两个函数指针 proberemove。实际上就是这两个函数指针所指的probe函数和remove函数。

结构体platform_driver成员 — struct device_driver driver

  • struct device_driver driver,看下内核里面对结构体device_driver的定义
/**
 - 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.
 - @of_match_table: The open firmware table.
 - @acpi_match_table: The ACPI match table.
 - @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.
 -  25. 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;
};

内核里对device_driver的定义很复杂,我没有能力搞懂所有的成员,这里我们先需要关注其中的字段(成员)name,该成员用于与硬件侧的name进行匹配,以便相关驱动能够找到对应的硬件信息。

结构体platform_driver成员 — probe函数(指针)

  • int (*probe)(struct platform_device *);
    probe函数:当硬件节点与软件节点匹配成功之后(内核的工作),内核就会调用此函数,(也就是说probe函数被内核调用,就意味着硬件和软件节点匹配成功),也就是说这个函数接口是内核提供给用户的,给开发者的?
    probe函数一般做如下工作:
  1. 获取匹配成功的纯硬件信息,比如寄存器物理地址 中断号等,
  2. 处理获取的纯硬件信息,比如地址映射,申请资源,注册与初始化
  3. 注册混杂设备或是字符设备

结构体platform_driver成员 — remove函数(指针)

  • int (*remove)(struct platform_device *);
    remove函数:从 dev 链表删除硬件节点或者从 drv 链表删除软件节点,不管删除哪一个,都代表这个驱动不再完整。此时内核就会调用 remove 函数
    remove 函数和 probe 函数是一对死对头!

以上platform_driver结构体中我们需要关注的三个成员(字段)的简单介绍就告一段落。下面我们来看看跟platform_driver相关的两个配套函数:platform_driver_register()platform_driver_unregister()

结构体platform_driver相关配套函数—platform_driver_register();

  • platform_driver_register()函数以我现在的水平很难用语言说明白它是不是我们所学的linux内核正宗定义的函数。不考虑细节的话,该函数可以写为如下
int platform_driver_register(struct platform_driver *drv){};

而实际上它是内核使用define 宏定义的函数,在这个定义的过程中省略了参数struct module *owner (因为将该参数默认为THIS_MOUDLE),而这么做的目的内核的注释为“use a macro to avoid include chaining to get THIS_MODULE”,翻译过来是“使用宏避免包含链接以获取此模块”这句不知所云的话,如下贴出来供大家看一看细节。

/*
 * use a macro to avoid include chaining to get THIS_MODULE
 */
#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)
/**
 * __platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 * @owner: owning module/driver
 */
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__platform_driver_register);

/**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	BUG_ON(!drv->bus->p);

	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	other = driver_find(drv->name, drv->bus);
	if (other) {
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

	ret = bus_add_driver(drv);
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);

	return ret;
}
EXPORT_SYMBOL_GPL(driver_register);

其实在内核的__platform_driver_register()函数中,我又继续追看了其中的代码,但是太复杂了,看得我头痛欲裂,对函数platform_driver_register()的介绍就到此为止,其功能是向内核 drv 链表添加软件节点对象,内核会帮你遍历 dev 链表,帮你匹配,如果匹配成功,帮你调用 probe 函数,帮你把匹配成功的硬件节点传递给 probe 函数

结构体platform_driver相关配套函数—platform_driver_unregister();

  • platform_driver_unregister()函数跟上面的platform_driver_register()函数刚好是反着的功能,即从内核 drv 链表删除软件节点,内核会调用软件节点的 remove 函数

/**
 * platform_driver_unregister - unregister a driver for platform-level devices
 * @drv: platform driver structure
 */
void platform_driver_unregister(struct platform_driver *drv)
{
	driver_unregister(&drv->driver);
}
EXPORT_SYMBOL_GPL(platform_driver_unregister);

/**
 * driver_unregister - remove driver from system.
 * @drv: driver.
 *
 * Again, we pass off most of the work to the bus-level call.
 */
void driver_unregister(struct device_driver *drv)
{
	if (!drv || !drv->p) {
		WARN(1, "Unexpected driver unregister!\n");
		return;
	}
	driver_remove_groups(drv, drv->groups);
	bus_remove_driver(drv);
}

好了,到此,软件侧相关的结构体和函数介绍的差不多了,我们写一个软件侧的例子。

linux platform软硬件分离机制的驱动软件侧代码led_drv.c

/*头文件*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/io.h>
#include <linux/uaccess.h>
/*定义操作led灯的函数接口*/
static long led_ioctl(struct file *file, \
					unsigned int cmd, unsigned long arg){
					/*自己写如何操作led等的逻辑*/
					
}
/*定义初始化硬件操作接口对象*/
static struct file_operations led_fops = {
		.owner = THIS_MOUDLE,
		.unlocked_ioctl = led_ioctl,
};
/*定义初始化混杂设备对象*/
struct miscdevice led_misc = {
		.minor = MISC_DYNAMIC_MINOR,
		.name = "myled",
		.fops = &led_fops
};
/*led_probe*/
static int led_probe(struct platform_device *pdev){
    	 /*通过形参pdev指针获取匹配成功的纯硬件信息/
			/*处理获取的纯硬件信息*/
			/*注册混杂设备对象*/
			misc_register(&led_misc);
			return 0;
}
/*led_remove*/
static int led_remove(struct platform_device *pdev){

	/*卸载混杂设备对象*/
	misc_deregister(&led_misc);
	/*其他操作*/
	....
	return 0;
}
/*使用platform_driver定义初始化软件节点对象*/
static struct platform_driver led_driver = {
		.probe = led_probe,
		.remove = led_remove,
		.driver.name = "vaccine_leds", //用于与硬件节点匹配
}
/*模块入口函数*/
static int led_drv_init(void){
		/*向内核drv链表添加注册软件节点led_driver*/
		platform_driver_register(&led_driver);
		return 0;
}
/*模块出口函数*/
static int led_drv_exit(void){
		/*从内核drv链表删除软件节点led_driver*/
		platform_driver_unregister(&led_driver);
		return 0;
}
/*相关修饰和GPL规则*/
moudle_init(led_drv_init);
moudle_exit(led_drv_exit);
MOUDLE_LENCENSE("GPL");

猜你喜欢

转载自blog.csdn.net/weixin_43326587/article/details/106668790