linux驱动 关于资源resource

发布者:Joyful444Life最新更新时间:2025-02-08 来源: cnblogs关键字:linux驱动  resource  s5pv210 手机看文章 扫描二维码
随时随地手机看文章

 

接下来我们把后面的代码分析完。

4.parent->child = A        first = C2,我们这里不相等,当然我们这里多说一句,如果相等的情况就是我们这次插入的是和A范围相同的资源

此时要把A_作为parent的child,才能实现,后面完整的树的查找遍历。

if (parent->child == first) {
parent->child = new;
} else {
next = parent->child; /* 从A开始查找 */
while (next->sibling != first) /* 找到first C2 (我们这里第一步就找到了) */
next = next->sibling;
next->sibling = new; /* A->sibling = C2_ */
}
return NULL;


可以看到,这是一个很完美的树形结构,从root可以yi'c依次遍历所有的节点。

 

这里再对比我们之前的分析,做个小结:

1.__insert_resource函数第一次进来parent传的是root,会进入__request_resource,,对于root的child总线所有资源里最小的那个。(比如我们上面的A)

2.之后检查new的范围,和是否要插入的在root范围内,这一次因为root的start和end范围很大,只要参数不传错都是可以成功的,但对其它层可能就会不满足的,即返回传入的节点root。

3.之后对child所属的这一层(相同的parent为同一层,同层之间用sibling连接)从小到大查找是不是,如果小于本层的某个资源,且和本层的资源没重叠,则直接插入,否则退出,返回(如果是没找到,则返回的是本层的最大者的地址,否则如果是重叠了,则返回的是重叠的那个资源的地址)。

4.在__insert_resource里面判断,如果是成功插入,返回NULL,则退出。否则,检查参数,如果是要插入的和返回的有重叠,则break跳出循环,否则调到第2步。

5.之后检查new,如果和本层的某一个资源部分重叠,则不能插入,结束。如果本层查找结束,或小于本层的某一个资源,则跳出。

6.现在说明,new里面至少包含一个完整的资源,接下来就是把new替换掉new包含的部分资源,并把new包含的资源挂接到new下面,同时对挂接层的资源的parent都改为new本身。

7.最后,把new的同层的前一个节点的sibling指向new

 

这里需要注意的是,加入我们挂接了某个重复资源myled,则之前的资源就挂载它的child里面了

 

如果我们把myled资源释放掉,则挂载它下面的资源也会释放掉

 

 

一个资源下挂载多个

 

卸载后,都不见了

 

 

 

在这个的基础上

 

假设我们注册这段资源有部分重复的资源会怎么样

 

结果如下,会注册失败

 

 

最后,我对几个常用的资源注册函数进行分析

 

void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
void __iomem *devm_request_and_ioremap(struct device *dev,
struct resource *res);

void __iomem *devm_request_and_ioremap(struct device *dev,
struct resource *res)
{
void __iomem *dest_ptr;

dest_ptr = devm_ioremap_resource(dev, res);
if (IS_ERR(dest_ptr))
return NULL;

return dest_ptr;
}
 

 

/**
* devm_ioremap_resource() - check, request region, and ioremap resource
* @dev: generic device to handle the resource for
* @res: resource to be handled
*
* Checks that a resource is a valid memory region, requests the memory region
* and ioremaps it either as cacheable or as non-cacheable memory depending on
* the resource's flags. All operations are managed and will be undone on
* driver detach.
*
* Returns a pointer to the remapped memory or an ERR_PTR() encoded error code
* on failure. Usage example:
*
* res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
* base = devm_ioremap_resource(&pdev->dev, res);
* if (IS_ERR(base))
* return PTR_ERR(base);
*/
void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res)
{
resource_size_t size;
const char *name;
void __iomem *dest_ptr;

BUG_ON(!dev);

/* 通常我们arm上ioremap的都是mem资源的,不是则退出 */
if (!res || resource_type(res) != IORESOURCE_MEM) {
dev_err(dev, 'invalid resourcen');
return IOMEM_ERR_PTR(-EINVAL);
}

size = resource_size(res); /* 得到资源大小 */
name = res->name ?: dev_name(dev); /* 资源如果申请时没起名,就和设备名一样 */

if (!devm_request_mem_region(dev, res->start, size, name)) { /* 请求资源 */
dev_err(dev, 'can't request region for resource %pRn', res);
return IOMEM_ERR_PTR(-EBUSY);
}

/* ioremap映射物理地址到虚拟地址 */
if (res->flags & IORESOURCE_CACHEABLE)
dest_ptr = devm_ioremap(dev, res->start, size);
else
dest_ptr = devm_ioremap_nocache(dev, res->start, size);

if (!dest_ptr) {
dev_err(dev, 'ioremap failed for resource %pRn', res);
devm_release_mem_region(dev, res->start, size);
dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
}

return dest_ptr;
}


/* IO资源的大小 */
static inline resource_size_t resource_size(const struct resource *res)
{
return res->end - res->start + 1;
}
 

#define devm_request_mem_region(dev,start,n,name)
__devm_request_region(dev, &iomem_resource, (start), (n), (name))

/*
* Managed region resource
*/
struct region_devres {
struct resource *parent;
resource_size_t start;
resource_size_t n;
};


struct resource * __devm_request_region(struct device *dev,
struct resource *parent, resource_size_t start,
resource_size_t n, const char *name)
{
struct region_devres *dr = NULL;
struct resource *res;

/* 申请空间,管理资源 */
dr = devres_alloc(devm_region_release, sizeof(struct region_devres),
GFP_KERNEL);
if (!dr)
return NULL;

/* 初始化 */
dr->parent = parent;
dr->start = start;
dr->n = n;

/* 申请资源 */
res = __request_region(parent, start, n, name, 0);
if (res)
devres_add(dev, dr); /* 把申请到的资源,加入到该设备中 */
else
devres_free(dr);

return res;
}



static DECLARE_WAIT_QUEUE_HEAD(muxed_resource_wait);

/**
* __request_region - create a new busy resource region
* @parent: parent resource descriptor
* @start: resource start address
* @n: resource region size
* @name: reserving caller's ID string
* @flags: IO resource flags
*/
struct resource * __request_region(struct resource *parent,
resource_size_t start, resource_size_t n,
const char *name, int flags)
{
DECLARE_WAITQUEUE(wait, current);
struct resource *res = alloc_resource(GFP_KERNEL);

if (!res)
return NULL;

/* 填充资源信息 */
res->name = name;
res->start = start;
res->end = start + n - 1;
res->flags = resource_type(parent);
res->flags |= IORESOURCE_BUSY | flags; /* 注意这里默认加了busy标志 */

write_lock(&resource_lock);

for (;;) {
struct resource *conflict;

/* 插入资源(我们之前已经分析过了,插入成功则返回NULL) */
conflict = __request_resource(parent, res);
if (!conflict)
break;
if (conflict != parent) { /* 这次的范围属于申请的范围内,继续查找如果这段资源忙的话,则等待忙完 */
if (!(conflict->flags & IORESOURCE_BUSY)) {
parent = conflict;
continue;
}
}
/* 如果这个资源是多个设备轮换使用的话,把这个设备加入等待队列,等资源可用再被唤醒 */
if (conflict->flags & flags & IORESOURCE_MUXED) {
add_wait_queue(&muxed_resource_wait, &wait);
write_unlock(&resource_lock);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule(); /* 这边直接调度走 */
remove_wait_queue(&muxed_resource_wait, &wait); /* 唤醒说明资源到为了,这里删除掉从等待队列 */
write_lock(&resource_lock);
continue;
}
/* Uhhuh, that didn't work out.. */
free_resource(res);
res = NULL; /* 范围NULL说明没申请到 */
break;
}
write_unlock(&resource_lock);
return res; /* 返回申请到的资源地址 */
}

把该资源加入到资源设备链表中,注销设备时就会释放掉该资源,以及占用的空间。

void devres_add(struct device *dev, void *res)
{
struct devres *dr = container_of(res, struct devres, data);
unsigned long flags;

spin_lock_irqsave(&dev->devres_lock, flags);
add_dr(dev, &dr->node);
spin_unlock_irqrestore(&dev->devres_lock, flags);
}

static void add_dr(struct device *dev, struct devres_node *node)
{
devres_log(dev, node, 'ADD');
BUG_ON(!list_empty(&node->entry));
list_add_tail(&node->entry, &dev->devres_head);
}
这理我们再看一下,申请资源时已经绑定了释放函数,到时候会卸载设备时,采用回调方式的形式,释放掉资源的。

struct resource * __devm_request_region(struct device *dev,
struct resource *parent, resource_size_t start,
resource_size_t n, const char *name)
{
struct region_devres *dr = NULL;
struct resource *res;

dr = devres_alloc(devm_region_release, sizeof(struct region_devres),
GFP_KERNEL);
if (!dr)
return NULL;

dr->parent = parent;
dr->start = start;
dr->n = n;

res = __request_region(parent, start, n, name, 0);
if (res)
devres_add(dev, dr);
else
devres_free(dr);

return res;

}


[1] [2] [3]
关键字:linux驱动  resource  s5pv210 引用地址:linux驱动 关于资源resource

上一篇:代码示例_中断
下一篇:uboot常用的函数

推荐阅读最新更新时间:2026-03-19 05:57

S5PV210之温度传感器DS18B20 linux3.0.8驱动
一 Ds18b20功能描述  FEATURES(特点) 1) Unique 1-WireTM interface requires only oneport pin for communication 独特的单总线接口,使用一个引脚实现双线通线 2) Multidrop capability simplifies distributed temperature sensing applications 多分功能简化分布式温度传感器的应用 3) Requires no external components 不需要外部器件 4) Can be powered from data line. Power supply range is
[单片机]
linux驱动开发之九鼎板载蜂鸣器驱动测试
字符设备驱动用的fileopretion结构体。 1、板载蜂鸣器的驱动测试 我手里有一个BSP,九鼎的Bsp,里面有蜂鸣器的驱动,我们先测试一下好不好用。我们拿到一个BSP时,如果要做或移植蜂鸣器的驱动,首先要确定下这个内核中究竟有没有蜂鸣器的驱动,我们可以用sourceInsight将内核放进去,搜索buzzer这个文件,看有没有,如果不行,也可以在内核中输入make menuconfig,利用这个配置界面来搜索buzzer英文,看不能找到相应的信息,从而也会知道这个设备在哪个路径下,通过对九鼎的内核进行make menuconfig后,搜索buzzer后,知道buzzer的驱动在/driver/char/buzzer/目录
[单片机]
linux驱动学习笔记---实现中断下半部以及驱动编写规范(七)
中断下半部: tasklet : struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); //下半部要执行的代码 unsigned long data; // 传递给func的参数 }; 1, 初始化tasklet tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data) 2, 在中断上半部
[单片机]
linux驱动(五)内核中静态映射表的建立
1:我们在linux内核中都是开启mmu的所以都是用的虚拟地址,需要建立VA到PA的映射表; 我们内核中映射表在arch/arm/mach-s5pv210/mach-smdkc110.c文件中 建立映射的函数是,smdkc110_map_io建立映射表 smdkc110_map_io 这个函数调用s5p_init_io函数真正     s5p_init_io         iotable_init     s3c24xx_init_clocks     s5pv210_gpiolib_init     s3c24xx_init_uarts smdkc110_map_io 这个函数调用s5p_init_io函数,s5p_init
[单片机]
<font color='red'>linux</font><font color='red'>驱动</font>(五)内核中静态映射表的建立
linux网卡驱动移植
这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层。DM9000A将MAC和PHY做到一起,也可以像IIS设备那样,SOC内有IIS的控制器,而声卡UDA1341放在片外。网卡当然也有这种设计,它是把PHY的下层MAC放入SOC内,片外的是PHY,当然我暂时还没见过这种的。DM9000A的输入是并行的总线,可以和CPU直接IO。而IIS那种需要通过:CPU CORE BUS- I2S控制器- 外设。通过对比,应该了解DM9000A怎样进行IO了吧。其中PHY receiver中的AUTO-MDIX控制网卡的自适应,也就是说它能自
[单片机]
Linux驱动入门(二)操作硬件
Linux驱动入门(二)操作硬件 一、通用做法 ioremap iounmap 寄存器读写 二、gpiolib gpio.h gpiolib.c 三星平台提供的gpio-cfg 三、总结 一、通用做法 玩过单片机的朋友应该懂得如何操作寄存器 举个例子,例如现在想往寄存器地址0xFF223440写数据,在单片机编程中的做法如下 volatile unsigned int *reg = (unsigned int*)0xFF223440; *reg = val; 在Linux中做法也是类似,只不过Linux不可以直接访问寄存器地址,而要经过映射后才能访问,下面开始介绍 ioremap 映射地址 /* * cookie:表示要映射的地址
[单片机]
Samsung_tiny4412(驱动笔记01)----linux 3.5,U-Boot,Busybox,SD卡启动环境搭建
/*********************************************************************************** * * linux 3.5,U-Boot,Busybox,SD卡启动环境搭建 * * 声明: * 1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会 * 不对齐,从而影响阅读. * 2. 以下所有的shell命令都是在root权限下运行的; * 3. minicom(U-Boot)指的是用minicom连接开发板做为U-Boot的终端; * 4. 文中在需
[单片机]
Linux驱动:LCD驱动测试
(1) 进入内核源码目录中,make menuconfig - Device Drivers - Graphics support - Support for frame buffer devices 重新编译内核 make uImage ,然后make modules,将driver/video/下的 fb.ko、cfbfillrect.ko、cfbimgblt.ko、cfbcopyarea.ko拷贝到210的根文件中,分别 insmod 安装到内核中。 (2) 安装lcd.ko驱动模块 (3) 应用层读写 frame buffer 测试程序框架 #include unistd.h #include stdl
[单片机]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved