天天看点

IO端口 和 IO 内存

                                                      IO端口和IO内存

一、概念

1.1什么是IO端口?

       芯片内核之外有诸多外设,如GPIO,I2C,USB,LCD等等,在S3C2440中,各种外设挂到了AHB总线或APB总线上,每种外设都是通过读写与其相关的寄存器进行控制。外设中寄存器的地址就称作IO端口。

IO端口 和 IO 内存

1.2什么是IO内存?

      将寄存器地址和设备内存地址映射到某个内存地址区段,这个内存区段就是IO内存。(有些外部设备有自己的内存,如视频采集卡,有16M的SDRAM作为缓冲区)

        IO内存类似于RAM,处理器可以通过总线而访问这些设备。软件不用理会寄存器和内存的差别(即他们的差别对软件是透明的)。

二、使用IO端口

驱动程序和设备很多时候是通过IO端口进行通信的。

2.1 IO端口分配:

      linux中,对端口进行操作之前,首先应该声明需要操作的端口。内核提供的注册操作端口的接口函数是:

      #include<linux/ioport.h>

      struct  resource *request_region(unsigned long first, unsigned long n, const  char *name);

     这个函数告诉内核,我们要使用起始于first的n个端口,name是设备名称。分配成功,返回非NULL值;失败,返回NULL。

      如果不再使用端口,可将端口释放:

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

所有端口的分配可从/proc/ioports中得到。

2.2 IO端口操作:

      linux中,对端口进行操作之前,首先应该声明需要操作的端口。内核提供的注册操作端口的接口函数是:

     asm/io.h

     unsigned   inb(unsigned port);

     unsigned   outb(unsigned char byte, unsigned port);

读写8位宽度(字节)端口。

      unsigned  inw(unsigned port);

      unsigned  outw(unsigned short word, unsigned port);

读写16位宽度(字节)端口。

      unsigned   inl(unsigned port);

      unsigned   outl(unsigned long word, unsigned port);

读写32位宽度(字节)端口。

三、使用IO内存

       ARM内存是通过页表访问的,内核必须首先使物理地址其对设备驱动程序可见,这意味着在IO操作之前先进行ioremap。LDD3不鼓励直接使用指向IO内存的指针,而是使用包装函数访问IO内存。原有有三:首先,使用包装函数在所有平台上都是安全的;另外。直接对指针指向的内存区域执行操作时,这些函数是经过优化的;最后,缺乏可读性和可移植性。

3.1 IO内存的分配和映射

      使用之前,必先分配IO内存区域,接口如下:

       linux/ioport.h

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

        函数从start开始分配len字节长的内存区域。分配成功,返回非NULL值;失败,返回NULL。分配的IO内存可从/proc/iomem中得到。

        如果不再使用分配的内存,可将端口释放:

        void    release_mem_region(unsigned long start, unsigned long len);

       获取另IO内存后,万不可直接应用指针访问。为了确保内核可以访问IO内存,必须通过ioremap函数建立映射。Ioremap专为IO内存区域分配虚拟地址。

         asm/io.h

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

         void    *iounmap(void *addr);

由ioremap返回的地址不应直接引用,而应该使用内核提供的accessor函数。

2.2 IO内存访问:

     linux中,对端口进行操作之前,首先应该声明需要操作的端口。内核提供的注册操作端口的接口函数是:

asm/io.h

      unsigned  int  ioread8(void *addr);

      unsigned  int  ioread16(void *addr);

      unsigned  int  ioread32(void *addr);

从IO内存读取值,addr是ioremap获得地址。

       void   iowrite8(u8 val, void *addr);

       void   iowrite16(u16 val, void *addr);

       void   iowrite32(u32 val, void *addr);

     给IO内存写值。

      还有一些老的接口,这里不做介绍。

四、像IO内存一样使用IO端口

      为了最小化IO内存和IO端口访问直接的表面区别,2.6内核采取另如下措施:在request_region分配IO端口之后,使用ioport_map函数进行操作:

        void*ioport_map(unsigned long port, unsigned int count);

       该函数重新映射了count个端口,看起来就像IO内存。驱动程序可在函数返回的地址之上使用ioread8等函数。

释放函数

        void ioport_unmap(void *addr);

四、内存屏障(memorybarrier)

      对IO寄存器的操作可能因为代码的编译器优化和硬件重新排序而导致错误。其解决办法是对硬件的特定顺序执行的操作之间设置内存屏障。

       #include<linux/kernel.h>

       void  barrier(void);

       这个函数通知编译器插入一个内存屏障,对硬件没有影响。

       #include<asm/system.h>

       void  rmb(void); //保证屏障之前的读操作一定会在后来读操作执行之前完成

       void  read_barrier_depends(void);

       void  wmb(void); //保证写操作不会乱序

       void  mb(void); //保证两者

五、ARM平台端口映射到内存,支持以上所有函数。

我根据LDD3做了led驱动实现 ,用了ioport,iomem和系统调用接口三种方法,存于百度网盘,led_iomem_ioport_sysinterface.rar,下载链接 :http://pan.baidu.com/s/1bnm1FsF

注:以上内容只是本人对LDD3的读书笔记。如有差错,欢迎指正!     



继续阅读