Transferred from: http://blog.csdn.net/skyflying2012/article/details/8672011
Copyright NOTICE: This article for Bo Master Kerneler hard original, without permission not reproduced.
Almost every peripheral is performed by a register on a read-write device, usually including a control register, a status register, and a data register, where the registers of the peripherals are often continually addressed. Depending on the CPU architecture, there are two ways that the CPU addresses the IO port:
(1) I/O mapping mode (i/o-mapped)
Typically, such as the X86 processor specifically implements a separate address space for peripherals, called "I/O address space" or "I/O port space," the CPU accesses address units in this space through specialized I/O directives such as X86 's in and out directives.
(2) Memory mapping mode (memory-mapped)
The CPU of a RISC instruction system (such as MIPS ARM PowerPC, etc.) usually implements only one physical address space, in which case the Physical address of the I/O port of the peripheral is mapped to the memory address space, and the peripheral I/O port becomes part of the memory. At this point, the CPU can access the peripheral I/O port as if it were accessing a memory unit without having to set up specialized peripheral I/O instructions.
However, the difference in hardware implementation is completely transparent to the software, and driver developers can treat the I/O port and peripheral memory as a "I/O Memory" resource in the memory-mapped way.
in general, when the system is running, the physical address of the peripheral's I/O memory resources is known and is determined by the hardware design. However, the CPU usually does not have a predefined virtual address range for the physical address of these known peripheral I/O memory resources, and the driver cannot access the I/O memory resources directly through the physical address, but must map them into the core virtual address space (via the page table) before the core virtual address ranges obtained from the mappings are Access these I/O memory resources through an inbound instruction. Linux declares the function Ioremap () in the IO.h header file to map the physical address of the I/O memory resource to the core virtual address space.
But to use I/O memory first to request , and then to map , use I/O port first to request , or call request , for I/O port request means let the kernel know you want to access this port , So the kernel knows that it will not let others access the port again . After all, the world is go round. The function that applies I/O port is request_region, and the function that requests I/O memory is request_mem_region,
from include/linux/ioport.h, 如下:
* Convenience shorthand with allocation */
#define Request_region (Start,n,name) __request_region (&ioport_resource, (start), (n), (name))
#define Request_mem_region (Start,n,name) __request_region (&iomem_resource, (start), (n), (name))
#define Rename_region (region, NewName) does {region]->name = (newname);} while (0)
extern struct resource * __request_region (struct resource *,
resource_size_t Start,
resource_size_t N, const char *name);
The key here is to parse the Request_mem_region function.
Linux把基于I/O映射方式的I/O端口和基于内存映射方式的I/O端口资源统称为“I/O区域”(I/O Region)。I/O Region仍然是一种I/O资源,因此它仍然可以用resource结构类型来描述。
Linux是以一种倒置的树形结构来管理每一类I/O资源(如:I/O端口、外设内存、DMA和IRQ)的。每一类I/O资源都对应有一颗倒置的资源树,树中的每一个节点都是一个resource结构,而树的根结点root则描述了该类资源的整个资源空间。
1. Structural body
1.1>struct resource Iomem_resource = {"PCI mem", 0x00000000, 0xFFFFFFFF, ioresource_mem};
1.2>struct Resource {
const char *name;
Unsigned long start, end;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
2. Calling functions
Request_mem_region (S1d_physical_reg_addr,s1d_physical_reg_size, "Epsonfb_rg")
#define Request_mem_region (Start,n,name) __request_region (&iomem_resource, (start), (n), (name))
__request_region checks if a continuous s1d_physical_reg_size byte size space after the starting physical address s1d_physical_reg_addr can be safely consumed
struct resource * __request_region (struct resource *parent, unsigned long start, unsigned long n, const char *name)
{
struct Resource *res = kmalloc (sizeof (*res), Gfp_kernel);
if (res) {
memset (res, 0, sizeof (*res));
Res->name = name;
Res->start = start;
Res->end = start + n-1;
Res->flags = Ioresource_busy;
Write_lock (&resource_lock);
for (;;) {
struct resource *conflict;
Conflict = __request_resource (parent, RES);
//sibling all cells under the parent, Detects if there are overlapping conflicts in the application section
if (!conflict)
//conflict=0; application succeeded, normally placed [start, end] to the appropriate location
Break
if (conflict! = parent) {
parent = conflict;
if (!) ( Conflict->flags & Ioresource_busy))
continue;
}
Kfree
(res);
A resource overlap conflict was detected and Kfree returned the memory requested by Kmalloc
res = NULL;
Break
}
Write_unlock (&resource_lock);
}
return res;
}
static struct resource * __request_resource (struct resource *root, struct resource *new)
{
unsigned long start = new->start;
unsigned long 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;
//root下的第一个链表元素*p.[child链表是以I/O资源物理地址从低到高的顺序排列的]
for (;;) {
tmp = *p;
if (!tmp || tmp->start > end) {
new->sibling = tmp;
*p = new;
//可以从root->child=null开始我们的分析考虑,此时tmp=null,那么第一个申请将以!tmp条件满足而进入
//这时root->child的值为new指针,new->sibling = tmp = null;当第二次申请发生时:如果tmp->start > end成立,
//那么,root->child的值为new指针,new->sibling = tmp;这样就链接上了,空间分布图如:
//child=[start,end]-->[tmp->start,tmp->end](1);
If the condition Tmp->start > End is not established, then only the!tmp condition enters
So, the value of Root->child is the same, tmp->sibling = new;new->sibling = TMP = NULL so it is linked, and the spatial distribution diagram is as follows:
Child=[child->start,child->end]-->[start,end] (2);
When the third application occurs: If start is between [Child->end,end] in (2), then Tmp->end < start will be established and then continue,
At this point tmp = (2) in [Start,end], because Tmp->start < end, so continue to execute p = &tmp->slibing = null,
Because Tmp->end > Start, resource Conflicts, returns the [Start,end] field in (2)
Two boundary value cases and an analysis of the median condition, you can know that the code implements an order list from the ground address to the high address.
Model diagram: Childe=[a,b]-->[c,d]-->[e,f], at this time there is a [x, y] need to insert, TMP as the sibling pointer to swim
TMP points to child=[a,b],
TMP points to [A, b] when tmp->start>y, the inserted link graph is:child=[x,y]-->[a,b]-->[c,d]-->[e,f]-->null; when tmp->end >=x when the conflict returns TMP
TMP points to [c,d] when tmp->start>y, the inserted link graph is:child=[a,b]-->[x,y]-->[c,d]-->[e,f]-->null; when tmp->end >=x when the conflict returns TMP
TMP points to [e,f] when tmp->start>y, the inserted link graph is:child=[a,b]-->[c,d]-->[x,y]-->[e,f]-->null; when tmp->end >=x when the conflict returns TMP
TMP points to null, after the inserted link graph is:child=[a,b]-->[c,d]-->[e,f]-->[x,y]-->null;
Successfully reached the detection conflict, the purpose of sequential links
New->parent = root;
return NULL;
}
p = &tmp->sibling;
if (Tmp->end < start)
Continue
return TMP;
}
}
其实说白了,request_mem_region函数并没有做实际性的映射工作,只是告诉内核要使用一块内存地址,声明占有,也方便内核管理这些资源。
重要的还是ioremap函数,ioremap主要是检查传入地址的合法性,建立页表(包括访问权限),完成物理地址到虚拟地址的转换。
void * IOREMAP (unsigned long phys_addr, unsigned long size, unsigned long flags);
The Iounmap function is used to cancel the mapping made by Ioremap () as follows:
void Iounmap (void * addr);
Both of these functions are implemented in the Mm/ioremap.c file.
After the physical address of the I/O memory resource is mapped to a core virtual address, we can theoretically read and write the I/O memory resources as read and write Ram. To ensure cross-platform portability of drivers, we should use specific functions in Linux to access I/O memory resources rather than through pointers to the core virtual addresses. As on the x86 platform, the functions of read/write I/O are as follows:
#define READB (Addr) (* (volatile unsigned char *) __io_virt (addr))
#define READW (Addr) (* (volatile unsigned short *) __io_virt (addr))
#define READL (Addr) (* (volatile unsigned int *) __io_virt (addr))
#define WRITEB (B,ADDR) (* (volatile unsigned char *) __io_virt (addr) = (b))
#define WRITEW (B,ADDR) (* (volatile unsigned short *) __io_virt (addr) = (b))
#define Writel (B,ADDR) (* (volatile unsigned int *) __io_virt (addr) = (b))
#define MEMSET_IO (A,b,c) memset (__io_virt (a), (b), (c))
#define MEMCPY_FROMIO (A,b,c) memcpy ((a), __io_virt (b), (c))
#define MEMCPY_TOIO (A,b,c) memcpy (__io_virt (a), (b), (c))
Finally, special emphasis is placed on the implementation of the MMAP function in the driver. Mapping a device with mmap means that a segment of the user's space is associated with the device's memory, which allows the program to read or write within the allocated address range, which is actually access to the device.
Understanding of Kernel Request_mem_region and ioremap "Turn"