-
iomem
—
I/O
映
射方式的
I/O
端口和内存映射方式的
I/O
端口
Linux
将基于
< br>I/O
映射方式的
I/O
端口和
基于内存映射方式的
I/O
端口资源统称为
“
I/O
区域
”
< br>(
I/O
Region
)
。
I/O
Region
仍然是一种
I/O
< br>资源,因此它仍然可以用
resource
结构类型来描
述。下面我们就来看看
Linux
是如何管理
< br>I/O
Region
的。
<
/p>
3
.
3
.
1 I/O Region
的分配
在函数
__request_resource()
的基础上,
Linux
实现了用于分配
I/O
区域的函数
__request_region()
,如下
:
1 struct
resource * __request_region(struct resource
*parent,
2
unsigned long
start, unsigned long n, const char *name)
3
{
4
struct resource *res =
kmalloc(sizeof(*res),
GFP
_KERNE
L);
5
6
if
(res) {
7
memset(res, 0, sizeof(*res));
8
res->name = name;
9
res->start =
start;
10
res->end = start + n - 1;
11
res->
flags =
IORE
SOURCE
_BUSY;
12
13
write_lock(&
resource_lock);
14
15
for (;;) {
16
struct resource *conflict;
17
18
conflict = __request_resource(parent,
res);
19
if (!conflict)
20
break;
21
22
if (conflict != parent) {
23
parent = conflict;
24
if
(!(conflict->
flags &
IORESOURCE
_BUSY))
25
continue;
26
}
27
28
/* Uhhuh, that didn't w
ork
out.. */
29
kfree(res);
30
res = NULL;
31
break;
32
}
33
w
rite_unlock
(&resource_lock);
34
}
35
return res;
36
}
37
NOTE
:
①
首先,
调用
kmalloc
()函数在
SLA
B
分配器缓存中分配一个
resource
结构。
②
然后,相应的根据参数来填充
p>
resource
结构。注意!
flags
成员被初始化为
IORE
SOURCE
_BUSY
。
③
p>
接下来,用一个
for
循环开始进行资源分
配,循环体的步骤如下:
l
首先,
调用
__request_resource()
函数进行资源
分配。
如果返回
NULL
,
说明分配成功,
因此就执行
break
语句推出
for
循环,返回所分配的
resource
结构的指针,函数成功地结束。
l <
/p>
如果
__request_resource()
函数分配不成功,则进一步判断所返回的冲突资源节点是否就是父资源节点
pa
rent
。如果不是,则将分配行为下降一个层次,即试图在当前冲突的资源节点中进行
分配(只有在冲突的资源节点没
有设置
IORE
SOURCE
_BUSY
的情况下才可以)
,于是让
parent
指针等于
conflict
,并在
co
nflict->
flags&IORE
SOURCE
_BUSY
为
0
的情况
下执行
continue
语句继续
f
or
循环。
l <
/p>
否则如果相冲突的资源节点就是父节点
parent
,或者相冲突资源节点设置了
IORE
SOURCE
_BUSY
标
志位,
< br>则宣告分配失败。
于是调用
k
f
ree
()
函数释放所分配的
res
ource
结构,
并将
res
指针置为
NULL
,
最
后用
break
语句推出
for
循环。
④
最后,
返回所分配的
resource
结构的指针。
< br>
3
.
3
p>
.
2 I/O
Region
的释放
函数
__
release_region()
实现在一个父资源节点
pa
rent
中释放给定范围的
I/O Region
。
实际上该函数的实
现思想与
__release_resource()
相类似。其源代码如下:
1 void __release_region(struct
resource *parent,
2
unsigned long start,
unsigned long n)
3
{
4
struct resource **p;
5
unsigned long end;
6
7
p =
&parent->child;
8
end = start + n - 1;
9
10
for (;;) {
11
struct resource *res = *p;
12
13
if (!res)
14
break;
15
if
(res->
start <= start && res->end >=
end) {
16
if (!(res->
flags &
IORE
SOURCE
_BUSY)) {
17
p
= &res->child;
18
continue;
19
}
20
if
(res->
start != start' 'res->end != end)
21
break;
22
*p = res->sibling;
23
kfree(res);
24
return;
25
}
26
p =
&res->sibling;
27
}
28
printk(Trying to free
nonexi
s
tent resource
<
%08lx-%08lx>
29
, start, end);
30
}
31
类似地,该函数也是通过一个
for
循环来遍历父资源
parent
的
p>
child
链表。为此,它让指针
res<
/p>
指向当前
正被扫描的子资源节点,指针
p
指向前一个子资源节点的
sibling
成员变量,
p
的初始值为指向
par
ent->child
。
For
循环<
/p>
体的步骤如下:
①
让
p>
res
指针指向当前被扫描的子资源节点(
res
=
*p
)
。
p>
②
如果
res
指针
为
NULL
,说明已经扫描完整个
ch
ild
链表,所以退出
for
循环。<
/p>
p>
③
如果
res
指针
不为
NULL
,则继续看看所指定的
I
/O
区域范围是否完全包含在当前资源节点中,也即看
看
[start,start+n-1]
是否包含在
res->[start,end]
中。如果不属于,则让
p
指向当前资源节点的
sibling
成员,然后继续
for
循环。如果属于,则执行下列步骤:
p>
l
先看看当前资源节点是否设置了
I
ORESOURCE
_BUSY
标志位。如果没有设置该标志位
,则说明该资
源节点下面可能还会有子节点,
因此将扫描过程下
降一个层次,
于是修改
p
指针,
使它指向
res->child
,
然后执行
continue
语句继续
for
循环。
l <
/p>
如果设置了
IORESOURCE
_BU
SY
标志位。则一定要确保当前资源节点就是所指定的
I/O<
/p>
区域,然后
将当前资源节点从其父资源的
child
链表中去除。这可以通过让前一个兄弟资源节点的
s
ibling
指针指向当前资源节点的
下一个兄弟资源节点来实
现(即让
*p=res->
sibling
)
,最后调用
k
free
()函数释放当前资源节点的
resource
结构。然后
函数就可以成功返回了。
3
p>
.
3
.
3
检查指定的
I/O
Region
是否已被占用
函数
__
check_region()
检查指定的
I/O
Region
是否已被占用。其源代码如下:
1 int __check_region(struct resource
*parent, unsigned long start, unsigned long n)
2
{
3
struct resource * res;
4
5
res =
__request_region(parent, start, n, check-region);
6
if
(!res)
7
return -E
BUSY;
8
9
release_resource(res);
10
kfree(res);
11
return 0;
12
}
13
该函数
的实现与
__check_resource()
的实现思想类
似。首先,它通过调用
__request_region()
函数试图在
父资源
parent
中分配指定的
I/O R
egion
。如果分配不成功,将返回
NULL
,因此此时函数返回错误值-
E
BUSY
表示所
指定的
I/O Region
已被占用。如果
res
指针不为空则说明所指定的
p>
I/O Region
没有被占用。于是调用
__release_resource()
函数将刚刚分配的资源释放掉
(实际上是将
res
结构从
parent
的
child
链表去除)
,
然后调用
kfree
()
函数释放
res
p>
结构所占用的内存。
最后,
返回
0
值表示指定的
I/O
Region
没有被占用。
中国网管论坛
3
.
4
管理
I/O
端口资源
我们都
知道,采用
I/O
映射方式的
X86<
/p>
处理器为外设实现了一个单独的地址空间,也即
“
I/O
空间
”
(
I/O
S
pace
)或称
为
“
I/O
端口空间
< br>”
,其大小是
64KB
(
0x0000
-
0xffff
)
。
Linux
在其所支持的
所有平台上都实现了
“
I/O
端口空<
/p>
间
”
这一概念。
由于
I/
O
空间非常小,因此即使外设总线有一个单独的
I/O
端口空间,
却也不是所有的外设都将其
I/O<
/p>
端
口(指寄存器)映射到
“
I/O
端口空间
”
中。比如
,大多数
P
CI
卡都通过内存映射方式
来将其
I/O
端口或外设内存映射到
C
P
U
的
RAM
物理地址空间中。而老式的
ISA
卡
通常将其
I/O
端口映射到
I/O
p>
端口空间中。
Linux
是基于
< br>“
I/O Region
”
这一
概念来实现对
I/O
端口资源(
I/O
-
mapped
或
Memory
-
mapped
)的管
理的。
p>
p>
3
.
4
.
1
资源根节点的定义
Lin
ux
在
kernel/Resource.c
< br>文件中定义了全局变量
ioport_resource
和
iomem_resource
,来分别描述基
于
I/O
映射方式的整个
I
/O
端口空间和基于内存映射方式的
I/O
内存资源空间
(包括
I/O
端口和
外设内存)
。
其定义如下:
1
struct resource ioport_resource =
2
{
P
CI IO, 0x0000,
IO_SP
ACE
_LIMIT
,
IORE
SOURCE
_IO };
3
struct resource iomem_resource =
4
{
P
CI mem, 0x00000000, 0xffffffff,
IORESOURCE
_MEM };
p>
其中,宏
IO_SP
ACE
_LIMIT
表示整个
I/O
空间的大小,对于
X86
平台而言,它是
0xffff
(定义在
include/asm-i386/
io.h
头文件中)
。显然,
I/O<
/p>
内存空间的大小是
4GB
。