Linux网络驱动之Fixed-Link(26)

接前一篇文章:Linux网络驱动之Fixed-Link(25)

三、深入了解

上一回继续解析platform_device_add函数,没有讲完,本回继续往下讲解。为了便于理解和回顾,再次贴出其源码,在drivers/base/platform.c中,如下:

cpp 复制代码
/**
 * platform_device_add - add a platform device to device hierarchy
 * @pdev: platform device we're adding
 *
 * This is part 2 of platform_device_register(), though may be called
 * separately _iff_ pdev was allocated by platform_device_alloc().
 */
int platform_device_add(struct platform_device *pdev)
{
	u32 i;
	int ret;
 
	if (!pdev)
		return -EINVAL;
 
	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;
 
	pdev->dev.bus = &platform_bus_type;
 
	switch (pdev->id) {
	default:
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
		break;
	case PLATFORM_DEVID_NONE:
		dev_set_name(&pdev->dev, "%s", pdev->name);
		break;
	case PLATFORM_DEVID_AUTO:
		/*
		 * Automatically allocated device ID. We mark it as such so
		 * that we remember it must be freed, and we append a suffix
		 * to avoid namespace collision with explicit IDs.
		 */
		ret = ida_alloc(&platform_devid_ida, GFP_KERNEL);
		if (ret < 0)
			goto err_out;
		pdev->id = ret;
		pdev->id_auto = true;
		dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
		break;
	}
 
	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];
 
		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);
 
		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}
 
		if (p) {
			ret = insert_resource(p, r);
			if (ret) {
				dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);
				goto failed;
			}
		}
	}
 
	pr_debug("Registering platform device '%s'. Parent at %s\n",
		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));
 
	ret = device_add(&pdev->dev);
	if (ret == 0)
		return ret;
 
 failed:
	if (pdev->id_auto) {
		ida_free(&platform_devid_ida, pdev->id);
		pdev->id = PLATFORM_DEVID_AUTO;
	}
 
	while (i--) {
		struct resource *r = &pdev->resource[i];
		if (r->parent)
			release_resource(r);
	}
 
 err_out:
	return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);

接下来来到了for循环,代码片段如下:

cpp 复制代码
    for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];
 
		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);
 
		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}
 
		if (p) {
			ret = insert_resource(p, r);
			if (ret) {
				dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);
				goto failed;
			}
		}
	}

这段代码虽然不多,但其中包含了相当多的知识点。首先来看struct platform_device的定义,在kernel/linux-5.10-origin/include/linux/platform_device.h中,如下:

cpp 复制代码
struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u64		platform_dma_mask;
	struct device_dma_parameters dma_parms;
	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;
};

重点关注其中以下成员:

cpp 复制代码
	u32		num_resources;
	struct resource	*resource;

分别代表了资源的个数和资源数组。for循环也就是以num_resources为最大值进行循环的,也就是遍历resource中的每一个成员。

首先让struct resource *r指向&pdev->resource[i]。如果r->name为NULL,则将dev_name(&pdev-dev)赋给r->name。pdev->dev的名字就是在上边赋值的,为"%s.%d"(例如"led-gpio.0")或者"%s"(例如rtc)。

再来看struct resource的定义,在kernel/linux-5.10-origin/include/linux/ioport.h中,如下:

cpp 复制代码
/*
 * Resources are tree-like, allowing
 * nesting etc..
 */
struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	unsigned long desc;
	struct resource *parent, *sibling, *child;
};

resource_type是一个内联函数,在kernel/linux-5.10-origin/include/linux/ioport.h中,代码如下:

cpp 复制代码
static inline unsigned long resource_type(const struct resource *res)
{
	return res->flags & IORESOURCE_TYPE_BITS;
}

IORESOURCE_TYPE_BITS是一个宏,其定义也在include/linux/ioport.h中,如下:

cpp 复制代码
#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */

IORESOURCE_MEM和IORESOURCE_IO的定义就在下边,如下:

cpp 复制代码
#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
#define IORESOURCE_IO		0x00000100	/* PCI/ISA I/O ports */
#define IORESOURCE_MEM		0x00000200
#define IORESOURCE_REG		0x00000300	/* Register offsets */
#define IORESOURCE_IRQ		0x00000400
#define IORESOURCE_DMA		0x00000800
#define IORESOURCE_BUS		0x00001000

如果r即pdev->resource[i]的资源类型为IORESOURCE_MEM,则将p即r->parent设置为&iomem_resource;如果r的资源类型为IORESOURCE_IO,则将p设置为&ioport_resource。代码片段如下:

cpp 复制代码
        p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}

iomem_resource和ioport_resource的初始化在kernel/linux-5.10-origin/kernel/resource.c中,代码如下:

cpp 复制代码
struct resource ioport_resource = {
	.name	= "PCI IO",
	.start	= 0,
	.end	= IO_SPACE_LIMIT,
	.flags	= IORESOURCE_IO,
};
EXPORT_SYMBOL(ioport_resource);

struct resource iomem_resource = {
	.name	= "PCI mem",
	.start	= 0,
	.end	= -1,
	.flags	= IORESOURCE_MEM,
};
EXPORT_SYMBOL(iomem_resource);

insert_resource函数也在kernel/linux-5.10-origin/kernel/resource.c中,代码如下:

cpp 复制代码
/**
 * insert_resource - Inserts a resource in the resource tree
 * @parent: parent of the new resource
 * @new: new resource to insert
 *
 * Returns 0 on success, -EBUSY if the resource can't be inserted.
 *
 * This function is intended for producers of resources, such as FW modules
 * and bus drivers.
 */
int insert_resource(struct resource *parent, struct resource *new)
{
	struct resource *conflict;

	conflict = insert_resource_conflict(parent, new);
	return conflict ? -EBUSY : 0;
}
EXPORT_SYMBOL_GPL(insert_resource);

insert_resource_conflict函数就在上边,代码如下:

cpp 复制代码
/**
 * insert_resource_conflict - Inserts resource in the resource tree
 * @parent: parent of the new resource
 * @new: new resource to insert
 *
 * Returns 0 on success, conflict resource if the resource can't be inserted.
 *
 * This function is equivalent to request_resource_conflict when no conflict
 * happens. If a conflict happens, and the conflicting resources
 * entirely fit within the range of the new resource, then the new
 * resource is inserted and the conflicting resources become children of
 * the new resource.
 *
 * This function is intended for producers of resources, such as FW modules
 * and bus drivers.
 */
struct resource *insert_resource_conflict(struct resource *parent, struct resource *new)
{
	struct resource *conflict;

	write_lock(&resource_lock);
	conflict = __insert_resource(parent, new);
	write_unlock(&resource_lock);
	return conflict;
}

__insert_resource函数就在insert_resource_conflict函数的上边,代码如下:

cpp 复制代码
/*
 * Insert a resource into the resource tree. If successful, return NULL,
 * otherwise return the conflicting resource (compare to __request_resource())
 */
static struct resource * __insert_resource(struct resource *parent, struct resource *new)
{
	struct resource *first, *next;

	for (;; parent = first) {
		first = __request_resource(parent, new);
		if (!first)
			return first;

		if (first == parent)
			return first;
		if (WARN_ON(first == new))	/* duplicated insertion */
			return first;

		if ((first->start > new->start) || (first->end < new->end))
			break;
		if ((first->start == new->start) && (first->end == new->end))
			break;
	}

	for (next = first; ; next = next->sibling) {
		/* Partial overlap? Bad, and unfixable */
		if (next->start < new->start || next->end > new->end)
			return next;
		if (!next->sibling)
			break;
		if (next->sibling->start > new->end)
			break;
	}

	new->parent = parent;
	new->sibling = next->sibling;
	new->child = first;

	next->sibling = NULL;
	for (next = first; next; next = next->sibling)
		next->parent = new;

	if (parent->child == first) {
		parent->child = new;
	} else {
		next = parent->child;
		while (next->sibling != first)
			next = next->sibling;
		next->sibling = new;
	}
	return NULL;
}

对于这三个函数的解析,请看下回。

相关推荐
蓝天居士1 个月前
Linux网络驱动之Fixed-Link(18)
网卡·设备驱动
蓝天居士2 个月前
RS485在Linux内核(驱动)及全志T113平台上的实现(7)
串口·rs485·设备驱动
蓝天居士2 个月前
RS485在Linux内核(驱动)及全志T113平台上的实现(4)
串口·rs485·设备驱动
蓝天居士2 个月前
RS485在Linux内核(驱动)及全志T113平台上的实现(5)
串口·rs485·设备驱动
蓝天居士2 个月前
Linux网络驱动之Fixed-Link(7)
网卡·设备驱动
蓝天居士2 个月前
Linux网络驱动之Fixed-Link(8)
网卡·设备驱动
蓝天居士2 个月前
RTL8367RB芯片介绍(17)
网卡·设备驱动·芯片资料
蓝天居士3 个月前
RTL8367RB芯片介绍(8)
网卡·设备驱动·芯片资料
蓝天居士3 个月前
RTL8367RB芯片介绍(6)
网卡·设备驱动·芯片资料