linux驱动 关于资源resource

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

在linux中有如下定义 

/*
* IO resources have these defined flags.
*/
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */

#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

#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */
#define IORESOURCE_READONLY 0x00004000
#define IORESOURCE_CACHEABLE 0x00008000
#define IORESOURCE_RANGELENGTH 0x00010000
#define IORESOURCE_SHADOWABLE 0x00020000

#define IORESOURCE_SIZEALIGN 0x00040000 /* size indicates alignment */
#define IORESOURCE_STARTALIGN 0x00080000 /* start field is alignment */

#define IORESOURCE_MEM_64 0x00100000
#define IORESOURCE_WINDOW 0x00200000 /* forwarded by bridge */
#define IORESOURCE_MUXED 0x00400000 /* Resource is software muxed */

#define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */
#define IORESOURCE_DISABLED 0x10000000
#define IORESOURCE_UNSET 0x20000000 /* No address assigned yet */
#define IORESOURCE_AUTO 0x40000000
#define IORESOURCE_BUSY 0x80000000 /* Driver has marked this resource busy */

/* PCI control bits. Shares IORESOURCE_BITS with above PCI ROM. */
#define IORESOURCE_PCI_FIXED (1<<4) /* Do not move resource */
其中最常用的就是这几种

#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
今天我们主要来分析前三种资源,即IO、MEM、REG

几乎每一种外设都是通过读写设备上的寄存器来进行的,外设的寄存器通常被连续地编址。根据CPU体系结构的不同,CPU对IO端口的编址方式有两种:

 (1)I/O映射方式(I/O-mapped)

  典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为'I/O地址空间'或者'I/O端口空间',CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元,IO地址空间,这个空间从kernel编程上来看,只能通过专门的接口函数才能访问.硬件层面上,cpu需要用特殊指令才能访问或需要用特殊访问方式才能访问,不能直接用指针来寻址,.在嵌入式中,基本上没有io address space。

  (2)内存映射方式(Memory-mapped)

  RISC指令系统的CPU(如MIPS ARM PowerPC等)通常只实现一个物理地址空间,像这种情况,外设的I/O端口的物理地址就被映射到内存地址空间中,外设I/O端口成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。

若果搜索内核代码,会发现,只有极少量的寄存器是通过 IORESOURCE_REG来表示的,绝大多数寄存器都是通过IORESOURCE_IO     或IORESOURCE_MEM来表示的。

我们的ARM平台对寄存的访问和内存一样,所以通常我们ARM中定义寄存器资源都是采用IORESOURCE_MEM来表示。

在内核中,形容资源,使用这样一个结构体来表示的,当然这个结构体也可以做成一个树的节点被链入一棵树中。

/*
* 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; /* 作为树的节点,链入树中 */
};
比如我们要使用一段寄存器作为资源传参给具体的驱动:

static struct resource xxxx = {
/* addr */
.start = 0x04014000,
.end = 0x04014003,
.flags = IORESOURCE_MEM,
}
一般来说,在系统运行时,外设的I/O资源的物理地址是已知的,有硬件决定,查看手册即可知道。但是CPU通常并没有为这些已知的外设I/O的物理地址分配虚拟地址,所以驱动程序并不能直接通过物理地址来访问I/O的地址资源,而必须将它们映射到核心虚拟地址空间(通过页表),然后才能根据映射所得到的核心虚拟地址范围,通过访问指令来访问这些I/O内存资源。linux中在io.h头文件中申明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心的虚拟地址空间。

一般我们,使用I/O内存首先要申请,然后才能映射,使用I/O端口首先要申请,或者叫请求,对于I/O端口的请求意思是让内核知道你要访问这个端口,这样内核知道了以后它就不会再让别人也访问这个端口了,不然两个用户同时访问一个硬件可能会有问题的。

这边先以平台总线中添加一段MEM资源为例,分析一下,资源的如何加入资源树。

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


/**
* device_initialize - init device structure.
* @dev: device.
*
* This prepares the device for use by other layers by initializing
* its fields.
* It is the first half of device_register(), if called by
* that function, though it can also be called separately, so one
* may use @dev's fields. In particular, get_device()/put_device()
* may be used for reference counting of @dev after calling this
* function.
*
* All fields in @dev must be initialized by the caller to 0, except
* for those explicitly set to some other value. The simplest
* approach is to use kzalloc() to allocate the structure containing
* @dev.
*
* NOTE: Use put_device() to give up your reference instead of freeing
* @dev directly once you have called this function.
*/
void device_initialize(struct device *dev)
{
/* 初始化dev信息 */
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
}

/**
* arch_setup_pdev_archdata - Allow manipulation of archdata before its used
* @pdev: platform device
*
* This is called before platform_device_add() such that any pdev_archdata may
* be setup before the platform_notifier is called. So if a user needs to
* manipulate any relevant information in the pdev_archdata they can do:
*
* platform_device_alloc()
* ... manipulate ...
* platform_device_add()
*
* And if they don't care they can just call platform_device_register() and
* everything will just work out.
*/
void __weak arch_setup_pdev_archdata(struct platform_device *pdev)
{
/* 预留给特殊架构的 */
}
 

/**
* 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)
{
int i, 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: /* -1表示不需要设备标号 */
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_simple_get(&platform_devid_ida, 0, 0, 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) { /* 如果没设置资源的父节点,则检查是否是IORESOURCE_MEM或IORESOURCE_IO资源,如果说是,则把他们插入到这个资源所在的资源树中 */
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource; /* iomem资源树,信息 */
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource; /* ioport资源树,信息 */
}

/* 这里p必须存在,即如果自己没设置,默认也只添加 IO和MEM资源到资源树中 */
if (p && insert_resource(p, r)) {
dev_err(&pdev->dev, 'failed to claim resource %dn', i);
ret = -EBUSY;
goto failed;
}
}

pr_debug('Registering platform device '%s'. Parent at %sn',
dev_name(&pdev->dev), dev_name(pdev->dev.parent));

/* 增加设备,节点,sysfs信息,做匹配等 */
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;

failed:
if (pdev->id_auto) {
ida_simple_remove(&platform_devid_ida, pdev->id);
pdev->id = PLATFORM_DEVID_AUTO;
}

while (--i >= 0) {
struct resource *r = &pdev->resource[i];
if (r->parent)
release_resource(r);
}

err_out:
return ret;
}
 

这里我们先分析一下资源树的总信息,后面分析如何把资源加入资源树。(kernel/resource.c)

struct resource ioport_resource = {
.name = 'PCI IO',
.start = 0,
.end = IO_SPACE_LIMIT,
.flags = IORESOURCE_IO,
};

struct resource iomem_resource = {
.name = 'PCI mem',
.start = 0,
.end = -1,
.flags = IORESOURCE_MEM,
};
如果我们是32位的处理器们可以看到iomem资源,即PCI mem资源的大小为0~0xffff,ffff.

而ioport则是采用一个宏来表示的,搜索后发现

/*
* This is the limit of PC card/PCI/ISA IO space, which is by default
* 64K if we have PC card, PCI or ISA support. Otherwise, default to
* zero to prevent ISA/PCI drivers claiming IO space (and potentially
* oopsing.)
*
* Only set this larger if you really need inb() et.al. to operate over
* a larger address space. Note that SOC_COMMON ioremaps each sockets
* IO space area, and so inb() et.al. must be defined to operate as per
* readb() et.al. on such platforms.
*/
#ifndef IO_SPACE_LIMIT
#if defined(CONFIG_PCMCIA_SOC_COMMON) || defined(CONFIG_PCMCIA_SOC_COMMON_MODULE)
#define IO_SPACE_LIMIT ((resource_size_t)0xffffffff)
#elif defined(CONFIG_PCI) || defined(CONFIG_ISA) || defined(CONFIG_PCCARD)
#define IO_SPACE_LIMIT ((resource_size_t)0xffff)
#else
#define IO_SPACE_LIMIT ((resource_size_t)0)
#endif
#endif
如果定义了PCI设备其值才不为0,否则值为0。

即如果没PCI相关的宏,是不能使用IORESOURCE_IO定义资源的,否则肯定在注册资源树的时候就失败了。

 

下面我们看一下平台总线中,如果把一段MEM插入到资源树中。(先说一下,系统采用多叉树的策略插入资源的)

/**
* 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.
*/
int insert_resource(struct resource *parent, struct resource *new)
{
struct resource *conflict;

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

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

/* Return the conflict entry if you can't request it */
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
resource_size_t start = new->start;
resource_size_t end = new->end;
struct resource *tmp, **p;

if (end < start) /* 输入的参数范围有误 */
return root;
if (start < root->start) /* 输入的参数不再资源总范围内 */
return root;
if (end > root->end) /* 输入的参数不再资源总范围内 */
return root;
p = &root->child;
for (;;) {
tmp = *p;
if (!tmp || tmp->start > end) {
new->sibling = tmp;
*p = new;
new->parent = root;
return NULL;
}
p = &tmp->sibling;
if (tmp->end < start)
continue;
return tmp;
}
}
首先对re__insert_resource函数里面举例进行分析:

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

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

推荐阅读最新更新时间:2026-03-25 12:03

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