继续往下学习第九章的内容。从这一章开始才接触到了一些实际的硬件设备,硬件是我的强项啊,呵呵。加油!
一、I/O端口和I/O内存
在学习这部分之前,需要先明确一个概念:side effect。明确这个概念有助于理解内存操作和IO寄存器操作的不同。LDD3中将这个词翻译成了“边际效应”,对此我持保留意见,至少我当时学习的时候就被这个词搞得一头雾水(我本身是搞硬件的,自认为对寄存器还是很了解的,这里完全被这个词搞晕,我勒个去以为是什么高级的东东)。LDD2中将此翻译成“副作用”,这至少比“边际效应”更容易理解一些,但是“副作用”本身多指一件事物不好的方面的影响,不太符合国人的习惯。所以这里我想把它叫做“侧面影响”或者“连带效应”(一家之言,也未必准确),相对于对寄存器值本身的变化的“正面影响”或“表面效应”。 这里我姑且叫他“连带效应”吧。不同于普通内存没有连带效应,对寄存器的操作所产生的连带效应可能是多方面的。一个例子是:对于S3C2440的时钟控制寄存器CLKCON的操作,不仅仅是变化了寄存器本身的值(表面效应),更重要的是每一位都控制了相应外设的时钟使能(连带效应)。另外一个例子:同样对于S3C2440,CLKSLOW寄存器中MPLL_OFF位置1(表面效应)可以Turn off PLL(连带效应),但同时必须要SLOW_BIT位置1才可以实现(表面效应),此时对SLOW_BIT位的操作限制了对MPLL_OFF位操作的结果(连带效应)。这是在同一寄存器不同位的情况,同样的,在不同寄存器之间也会有这样的影响,对一个寄存器的操作能否去的预期的效果取决于另一个寄存器的状态。可以看出,连带效应才是我们进行寄存器操作的目的。这样我们就容易理解为什么对寄存器的操作顺序如此重要。
编译器工作时往往会对程序进行优化,这种优化可能会改变内存或者寄存器的访问顺序,如果发生这样的情况,IO操作就会出现问题。所以Linux提供了4个宏来确保操作的执行顺序。我们可以在需要严格执行顺序的地方插入这些宏。具体如下:
- #include <asm/system.h>
- #include <linux/kernel.h>
- void barrier(void);
- void rmb(void);
- void read_barrier_depends(void);
- void wmb(void);
- void mb(void);
- void smp_rmb(void);
- void smp_read_barrier_depends(void);
- void smp_wmb(void);
- void smp_mb(void);
关于这些宏的具体应用参考本博客转载的一篇文章“内核同步机制-优化屏障和内存屏障”。
二、使用I/O端口
驱动程序中使用一些函数来进行I/O端口的分配和操作。具体如下(linux 2.6.32):
分配相关的函数:
- #include <linux/ioport.h>
- #define request_region(start,n,name)__request_region(&ioport_resource, (start), (n), (name), 0)
- #define release_region(start,n)__release_region(&ioport_resource, (start), (n))
- extern struct resource * __request_region(struct resource *,
- resource_size_t start,
- resource_size_t n,
- const char *name, int flags);
- extern void __release_region(struct resource *, resource_size_t, resource_size_t);
下面详细看一下__request_region的实现,定义在kernel/resource.c中。
- /**
- * __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)
- {
- struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); /* 首先,调用kmalloc()函数在SLAB分配器缓存中分配并清零一个resource结构*/
- if (!res)
- return NULL;
- /*初始化所分配的resource结构*/
- res->name = name;
- res->start = start;
- res->end = start + n - 1;
- res->flags = IORESOURCE_BUSY;
- res->flags |= flags;
- write_lock(&resource_lock); /*写者获得读写锁*/
- for (;;) {
- struct resource *conflict;
- conflict = __request_resource(parent, res); /*首先,调用__request_resource函数进行资源分配,成功则返回NULL*/
- if (!conflict)
- break; /*如果分配成功,直接退出*/
- if (conflict != parent) { /*如果分配不成功,则判断冲突资源节点是不是父资源节点parent,如果不是,则试图在当前冲突的资源节点中进行分配*/
- parent = conflict;
- if (!(conflict->flags & IORESOURCE_BUSY))
- continue;
- }
- /* Uhhuh, that didn't work out.. */
- /*如果冲突节点就是父节点,则分配失败。kfree掉所有分配的resource结构,设置res指针为NULL,然后break*/
- kfree(res);
- res = NULL;
- break;
- }
- write_unlock(&resource_lock); /*写者释放读写锁*/
- return res; /*返回所分配的resource结构指针*/
- }
下面看一下__release_region的实现:
- /**
- * __release_region - release a previously reserved resource region
- * @parent: parent resource descriptor
- * @start: resource start address
- * @n: resource region size
- *
- * The described resource region must match a currently busy region.
- */
- void __release_region(struct resource *parent, resource_size_t start,
- resource_size_t n)
- {
- struct resource **p;
- resource_size_t end;
- p = &parent->child;
- end = start + n - 1;
- write_lock(&resource_lock);
- for (;;) {
- struct resource *res = *p; /*res指向当前被扫描的子资源节点*/
- if (!res)/*res为NULL说明整个child链表扫描完了,退出for*/
- break;
- if (res->start <= start && res->end >= end) {
- if (!(res->flags & IORESOURCE_BUSY)) {
- p = &res->child;
- continue;
- }
- if (res->start != start || res->end != end)
- break;
- *p = res->sibling;
- write_unlock(&resource_lock);
- kfree(res);
- return;
- }
- p = &res->sibling;
- }
- write_unlock(&resource_lock);
- printk(KERN_WARNING "Trying to free nonexistent resource "/**
- * __release_region - release a previously reserved resource region
- * @parent: parent resource descriptor
- * @start: resource start address
- * @n: resource region size
- *
- * The described resource region must match a currently busy region.
- */
- void __release_region(struct resource *parent, resource_size_t start,
- resource_size_t n)
- {
- struct resource **p;
- resource_size_t end;
- p = &parent->child;
- end = start + n - 1;
- write_lock(&resource_lock);
- for (;;) {
- struct resource *res = *p;
- if (!res)
- break;
- if (res->start <= start && res->end >= end) {
- if (!(res->flags & IORESOURCE_BUSY)) {
- p = &res->child;
- continue;
- }
- if (res->start != start || res->end != end)
- break;
- *p = res->sibling;
- write_unlock(&resource_lock);
- kfree(res);
- return;
- }
- p = &res->sibling;
- }
- write_unlock(&resource_lock);
- printk(KERN_WARNING "Trying to free nonexistent resource "
- "<%016llx-%016llx>/n", (unsigned long long)start,
- (unsigned long long)end);
- }
操作I/O端口的函数:
- /*读写8位端口*/
- unsigned inb(unsigned port);
- void outb(unsigned char byte, unsigned port);
- /*读写16位端口*/
- unsigned inw(unsigned port);
- void outw(unsigned short word, unsigned port);
- /*读写32位端口*/
- unsigned inl(unsigned port);
- void outl(unsigned longword, unsigned port);