Chinaunix首页 | 论坛 | 博客
  • 博客访问: 66822
  • 博文数量: 21
  • 博客积分: 290
  • 博客等级: 二等列兵
  • 技术积分: 286
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-30 16:16
文章分类
文章存档

2019年(3)

2017年(1)

2012年(17)

我的朋友

分类: LINUX

2012-04-09 11:56:27

    

字符驱动之八IO访问(硬件操作)

                          2012-04-09

     在进行IO访问之前,得先区分IO ports IO mem 还有 IO 空间和内存空间。以下有些概念和总结来自别人的博客,觉得写得不错,就在这里跟大家共享一下,如果你觉得对你一种不尊敬,请您告诉我,我立即删除有关内容。

1CPUi386架构的情况

i386系列的处理中,内存和外部IO是独立编址,也是独立寻址的MEM的内存空间是32位可以寻址到4GIO空间是16位可以寻址到64K

Linux内核中,访问外设上的IO Port必须通过IO Port的寻址方式。而访问IO Mem就比较罗嗦,外部MEM不能和主存一样访问,虽然大小上不相上下,可是外部MEM是没有在系统中注册的。访问外部IO MEM必须通过remap映射到内核的MEM空间后才能访问。

为了达到接口的同一性,内核提供了IO PortIO Mem的映射函数。映射后IO Port就可以看作是IO Mem,按照IO Mem的访问方式即可。

2CPUARM PPC架构的情况

在这一类的嵌入式处理器中,IO Port的寻址方式是采用内存映射,也就是IO bus就是Mem bus。系统的寻址能力如果是32位,IO PortMem(包括IO Mem)可以达到4G

访问这类IO Port时,我们也可以用IO Port专用寻址方式。至于在对IO Port寻址时,内核是具体如何完成的,这个在内核移植时就已经完成。在这种架构的处理器中,仍然保持对IO Port的支持,完全是i386架构遗留下来的问题,在此不多讨论。而访问IO Mem的方式和i386一致。

注意:linux内核给我提供了完全对IO PortIO Mem的支持,然而具体去看看driver目录下的驱动程序,很少按照这个规范去组织IO PortIO Mem资源。对这二者访问最关键问题就是地址的定位,在C语言中,使用volatile 就可以实现。很多的代码访问IO Port中的寄存器时,就使用volatile关键字,虽然功能可以实现,我们还是不推荐使用。就像最简单的延时莫过于while,可是在多任务的系统中是坚决避免的!

3

(1)内存空间:内存地址寻址范围,32位操作系统内存空间为232次幂,即4G

(2)IO空间:X86特有的一个空间,与内存空间彼此独立的地址空间,32X8664KIO空间。 

(3)IO端口:当寄存器或内存位于IO空间时,称为IO端口。一般寄存器也俗称I/O端口,或者说I/O ports,这个I/O端口可以被映射在Memory Space,也可以被映射在I/O Space

(4)IO内存:当寄存器或内存位于内存空间时,称为IO内存

 4 、操作方法:

CPU对外设端口物理地址的编址方式有两种:一种是IO映射方式,另一种是内存映射方式,而具体采用哪一种则取决于CPU的体系结构。Linux将基于IO映射方式的和内存映射方式的IO端口统称为IO区域(IO region)。

1)所有的读写指令所赋的地址必须都是虚拟地址,你有两种选择:使用内核已 经定义好的地址,如 S3C2440_GPJCON等等,这些都是内核定义好的虚拟地址,有兴趣的可以看源码。还有一种方法就是使用自己用ioremap映射的虚拟地址。绝对不能使用实际的物理地址,否则会因为内核无法处理地址而出现oops

2)在使用I/O指令时,可以不使用request_regionrequest_mem_region,而直  接使用outbioread等指令。因为request的功能只是告诉内核端口被谁占用了,如再次request,内核会制止。

3)在使用I/O指令时,所赋的地址数据有时必须通过强制类型转换为 unsigned  long ,不然会有警告(具体原因请看-内核的数据类型) 。虽然你的程序可能  也可以使用,但是最好还是不要有警告为妙。

4)在include\asm-arm\arch-s3c2410\hardware.h中定义了很多io口的操作函数 ,有需要可以在驱动中直接使用。【这些来自gzprogramming.blog.chinaunix.net 的博客】

【使用方法之一操作内存】

[NOTE]数据大部分都是无符号长整型

1、申请 【12主要将硬件配置和驱动分开,最大限度的提高驱动的使用效率】

2、映射

3、访问

4、释放==>申请之后得释放、映射之后也要解除,有先后顺序

【使用方法之二 IO PORT

1、申请

2、访问

3、释放

【在访问IO之前一般都会进行屏障处理,以防被优化】

头文件:

#include

#include

[数据结构]

struct resource

{

  resource_size_t start;

  resource_size_t end;

  const char *name;

  unsigned long flags;

  struct resource *parent, *sibling, *child;

};

IO内存操作方法】

/* 第一步申请IO内存

 * @brief       申请IO内存

 * @param[in]   start   ??物理地址还是虚拟地址呢<测试过貌似无区别è在于IO映射与IO申请分开>

 * @param[in]   len     从起始地址开始的长度

 * @param[in]   name    一般为设备名称

 * @return          成功返回一个非NULL指针,否则返回NULL

 */

[NOTE]I/O 内存分配情况都 /proc/iomem 

struct resource *request_mem_region(unsigned long start, unsigned long len,  char *name);

 

/*第二步:phy to vir 的映射

 * @brief    在访问I/O内存之前,必须进行物理地址到虚拟地址的映射

 * @param[in]   phys_addr   物理地址的起始地址

 * @param[in]   size    从起始地址开始的长度

 * @return      返回内存的虚拟地址,为IO访问做好准备

 */

[NOTE]要强制类型转换(unsigned long)

void *ioremap(unsigned long phys_addr, unsigned long size)

[明确与硬件有关]

void *ioremap_nocache(unsigned long phys_addr, unsigned long size);

/*第三步:访问IO内存

 * @brief    访问I/O内存

 * @param[in] addr   物理地址到虚拟地址的映射的虚拟地址

 * @return     返回内存的实际值  

 */一般在访问IO内存之前都会进行屏蔽处理

I/O 内存读,使用下列之一:

unsigned ioread8(void *vir_addr)

unsigned ioread16(void *vir_addr)

unsigned ioread32(void *vir_addr)

I/O内存, 使用下列之一:

void iowrite8(u8 value, void *vir_addr)

void iowrite16(u16 value, void *vir_addr)

void iowrite32(u32 value, void *vir_addr)

//访问一系列

void ioread8_rep(void *addr, void *buf, unsigned long count);

void ioread16_rep(void *addr, void *buf, unsigned long count);

void ioread32_rep(void *addr, void *buf, unsigned long count);

void iowrite8_rep(void *addr, const void *buf, unsigned long count);

void iowrite16_rep(void *addr, const void *buf, unsigned long count);

void iowrite32_rep(void *addr, const void *buf, unsigned long count);

/*第四步:释放IO内存

 * @brief             释放IO内存

 * @param[in]       phys_addr   物理地址的起始地址

 * @param[in]   len     从起始地址开始的长度

 * @return          no

 */

(1)申请IO资源要释放

void release_mem_region(unsigned long start, unsigned long len)

(2)映射之后的资源也要释放

void iounmap((void *)vir_addr);

 

 

 

 

 

IO端口操作方法】

/*第一步申请IO端口

 * @brief             申请IO端口

 * @param[in]   first  采用内核定义好的虚拟地址

 * @param[in]   len     从起始地址开始的长度

 * @param[in]   name    一般为设备名称

 * @return          成功返回一个非NULL指针,否则返回NULL

 */

[NOTE]I/O 内存分配情况都 /proc/ioport

[NOTE]first必须采用虚拟地址,而非物理地址,其实S3C2440_GPJCON是内核定义好的虚拟地址。

struct resource *request_region(unsigned long first, unsigned

long n,   const char *name)                               

/*第二步:访问IO内存

 * @brief    访问I/O内存

 * @param[in] port  内核帮忙写好的虚拟地址如S3C2440_GPJCON

 * @return         

 */

[NOTE]所赋的地址数据有时必须通过强制类型转换为 unsigned long

I/O 端口读,使用下列之一:

unsigned inb(unsigned port);

unsigned inw(unsigned port);

unsigned inl(unsigned port);

 

I/O 端口,使用下列之一:

void outb(unsigned char byte, unsigned port);

void outw(unsigned short word, unsigned port);

void outl(unsigned longword, unsigned port);                                  

/* @brief 第三步:释放IO端口  */

void release_region(unsigned long start, unsigned long n);  

 

5、小结

 

其实,IO端口访问也是一种IO内存访问,原因在于IO端口访问是内核在BSP(版及相关的时候)帮我们做了物理地址到虚拟地址的映射,从而我就可以直接使用,看起来貌似是实际地址,实际上不是,是虚拟地址,大家可以自己去S3C2440_GPJCON最终展开就是内存的虚拟地址。

阅读(5399) | 评论(2) | 转发(3) |
给主人留下些什么吧!~~

cyycyh2012-04-10 13:28:20

十七岁的回忆: 64位处理器要称霸未来了吧~.....
是啊 时代在变化,科技在进步啊! 嘻嘻

十七岁的回忆2012-04-10 02:01:38

64位处理器要称霸未来了吧~