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

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

三、深入了解

上一回继续解析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);

上一回在解析platform_device_add函数的时候,讲解到了以下代码片段:

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;
		}
 
		if (p) {
			ret = insert_resource(p, r);
			if (ret) {
				dev_err(&pdev->dev, "failed to claim resource %d: %pR\n", i, r);
				goto failed;
			}
		}

引出了3个函数:insert_resource()以及其下的insert_resource_conflict()和__insert_resource()。本回开始,对于这3个函数一一进行解析。

(1)insert_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函数是Linux内核资源管理核心函数 ,作用是:向内核的全局资源树(resource tree)中注册一段资源(如IOMEM、IOPORT、IRQ、DMA等),防止资源冲突、重复使用。

参数说明:

  • struct resource *parent:父资源结点(根资源)。
  • struct resouce *new:要插入的新资源。

常用root根资源(内核预定义):

  • &iomem_resource // 物理地址资源(最常用:寄存器、DDR、外设地址)
  • &ioport_resource // IO端口资源
  • &dma_resource // DMA资源
  • &irq_resource // 中断资源

参见上一回定义:

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;
};

(2)insert_resource_conflict函数

insert_resource_conflict函数也在kernel/linux-5.10-origin/kernel/resource.c中(就在insert_resource函数上边),代码如下:

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函数就在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;
}

对于__insert_resource函数的解析,请看下回。

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