IO内存 与 IO端口 >>Linux 设备驱动程序

啥时候要是写代码的时候像玩游戏一样开心就好了,我觉得那一天应该不会遥远,要做一个快乐的小二逼 哈哈哈;
懂得越多责任就越重大,喜欢责任重大,那就要让自己一天天的变强大。
如是说就得每天早上给自己一杯自己造的“鸡血”喝,热乎乎的比别人给的容易喝下去;不是吗?

[0x100] 内容概述

  1. 内存读写 checkpoint
  2. IO内存访问与操作
  3. IO端口访问与操作

[0x200] 访问硬件的不同方式

[0x210] 内存读写同步点–内存屏障

[0x211] 创建内存屏障

概念定义 :CPU对内存随机访问的一个同步点,必须执行该点之前的所有I/O 操作,才可以继续执行之后的指令;

#include <linux/compiler-gcc.h >
/*gcc编译器中的内存屏障的调用*/
#define barrier() __asm__ __volatile__("": : :"memory")
#include <asm-generic/barrier.h>
#define mb()    asm volatile ("": : :"memory")
#define rmb()   mb()
#define wmb()   asm volatile ("": : :"memory")
/*多处理器的的内存屏障,按照情况编译,其实都是一个*/
#ifdef CONFIG_SMP
#define smp_mb()        mb()
#define smp_rmb()       rmb()
#define smp_wmb()       wmb()
#else
#define smp_mb()        barrier()
#define smp_rmb()       barrier()
#define smp_wmb()       barrier()
#endif
#include <linux/compiler.h >
/*编译器的宏调用*/
#define barrier() __memory_barrier()

[0x212] 驱动中的实例

 priv->tx_curr_desc++;
        if (priv->tx_curr_desc == priv->tx_ring_size) {
                priv->tx_curr_desc = 0;
                len_stat |= DMADESC_WRAP_MASK;
        }
        priv->tx_desc_count--;
        /*这里有点类似于 等待更新了 DMA 降序打包标识,才能继续执行,类似暂定点同步过程*/
        wmb();
        desc->len_stat = len_stat;
        wmb();
         /*更新标识后,才能写入,暂定点同步过程*/
        /* kick tx dma */
        enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK,ENETDMA_CHANCFG_REG(priv->tx_chan));

[0x220] I/O 内存映射

  1. 概念定义 :通过将设备私有内存与寄存器映射到系统RAM,来达到处理器快速访问的目的;
  2. 操作方式 :跟操作虚拟内存一样

[0x221] 注册I/O 内存

#include <linux/ioport.h> 
/*注册设备寄存器或者设备内部存储到系统RAM,供CPU访问*/
struct resource *request_mem_region(unsigned long start,unsigned long len,char *name);

功能 :设备内部存储位置 映射到RAM为I/O内存空间,用于存储外部存储器的数据;
args1 :标识RAM分配为I/O内存的起始位置,可以使用platform 中 resource填充;
args2 :标识需要分配的RAM大小,字节为单位;
args3 :标识设备名称;
retval : 申请成功返回 struct resource指针,可用于填充可以填充platform ,申请失败 NULL;

[0x222] 转换虚拟地址

#include <asm/io.h>
/*使用物理地址标识的I/O内存,驱动程序是无法访问的,需要转换为虚拟内存才能正常使用*/
#define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE)
void __iomem *__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype)
{
        return arch_ioremap_caller(phys_addr, size, mtype,__builtin_return_address(0));
}

功能 :将注册的I/O内存,转换为虚拟内存地址,
args1 :标识RAM分配为I/O内存的起始位置;
args2 :标识需要分配的RAM大小,字节为单位;
retval :转换后可访问的虚拟内存地址,转换失败NULL;

[0x223] 操作I/O内存

#include <asm/io.h>
/*从虚拟映射IO内存地址,获取其中8位、16位、32位数据的方式,返回值对应地址中的数据 unsigned int,执行类型检查*/
#define ioread8(p)  ({ unsigned int __v = __raw_readb(p); __iormb(); __v; })
#define ioread16(p) ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __iormb(); __v; })
#define ioread32(p) ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __iormb(); __v; })

/*从虚拟映射IO内存地址,写入8位、16位、32位数据的方式,执行类型检查*/
#define iowrite8(v,p)   ({ __iowmb(); (void)__raw_writeb(v, p); })
#define iowrite16(v,p)  ({ __iowmb(); (void)__raw_writew((__force __u16)cpu_to_le16(v), p); })
#define iowrite32(v,p)  ({ __iowmb(); (void)__raw_writel((__force __u32)cpu_to_le32(v), p); })
/*连续的读IO内存的方式,从paddr读取count大小到dst_buf中,没有返回值*/
#define ioread8_rep(paddr,dst_buf,count)      __raw_readsb(paddr,dst_buf,count)
#define ioread16_rep(paddr,dst_buf,count)     __raw_readsw(paddr,dst_buf,count)
#define ioread32_rep(paddr,dst_buf,count)     __raw_readsl(paddr,dst_buf,count)
/*连续的写IO内存的方式,将src_buf中的数据写入到paddr后count个字节,没有返回值*/
#define iowrite8_rep(paddr,src_buf,count)     __raw_writesb(paddr,src_buf,count)
#define iowrite16_rep(paddr,src_buf,count)    __raw_writesw(paddr,src_buf,count)
#define iowrite32_rep(paddr,src_buf,count)    __raw_writesl(paddr,src_buf,count)

[0x224] 销毁I/O内存

#include<asm/io.h>
/*先解除虚拟映射后,释放I/O内存*/
#define iounmap    __arm_iounmap
void __arm_iounmap(volatile void __iomem *io_addr)
{
        arch_iounmap(io_addr);
}
/*放弃当前注册的I/O内存*/
#include <linux/ioport.h>
#define release_mem_region(start,n)     __release_region(&iomem_resource, (start), (n))
void __release_region(struct resource *parent, resource_size_t start,resource_size_t n)

功能 :放弃当前注册的I/O内存
args1 :标识RAM分配为I/O内存的起始位置;
args2 :标识分配的RAM大小,字节为单位;

[0x230] IO端口相关函数

[0x231] 注册I/O 端口

#include <linux/ioport.h>  
/*1.检查IO接口的范围是否可用,如果不可用返回错误码*/
int check_region(unsigned long start,unsigned long n);
/*2.注册I/O端口*/
#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0)
struct resource * __request_region(struct resource * parent,resource_size_t start ,resource_size_t num 
                                                           ,const char *name, int flags);

功能 :驱动程序注册一个与对应设备通讯的IO端口;
args1:struct resource * 标识io端口的信息;
args2:端口起始地址;
args3:注册端口数量;
args4:需要注册IO端口设备的名称
args5:通常为0;
retval:返回填充的IO端口的结构体, 失败返回NULL;

[0x232] 操作I/O 端口

不注册之前原则上不应该操作对应IO端口
可以输出输入不同的位数的端口地址空间 8位 16位 32 位

#include <asm/io.h>
#define outb(v,p)       ({ __iowmb(); __raw_writeb(v,__io(p)); })
#define outw(v,p)       ({ __iowmb(); __raw_writew((__force __u16) cpu_to_le16(v),__io(p)); })
#define outl(v,p)       ({ __iowmb(); __raw_writel((__force __u32) cpu_to_le32(v),__io(p)); })

#define inb(p)  ({ __u8 __v = __raw_readb(__io(p)); __iormb(); __v; })
#define inw(p)  ({ __u16 __v = le16_to_cpu((__force __le16) __raw_readw(__io(p))); __iormb(); __v; })
#define inl(p)  ({ __u32 __v = le32_to_cpu((__force __le32) __raw_readl(__io(p))); __iormb(); __v; })

[0x233] 销毁I/O 端口

#include <linux/ioport.h> 
#define release_region(start,n) __release_region(&ioport_resource, (start), (n))
void __release_region(struct resource *parent, resource_size_t start,resource_size_t n)

功能 :驱动程序注销一个与对应设备通讯的IO端口;
args1:struct resource * 标识io端口的信息;
args2:端口号起始位置;
args3:注册端口数量;

[0x234] 像IO内存一样使用IO接口

#include <include/asm/io.h>
/*IO端口转化为IO内存,返回的是虚拟内存地址,这一步 IO端口就不是IO端口了,它是IO内存,所以销毁也是IO内存的方式来销毁*/
void __iomem *ioport_map(unsigned long port, unsigned int nr)
{
        return __io(port);
}

功能 :IO端口转化为IO内存;
args1:IO端口起始地址;
args2:注册端口数量;
retval : 返回为虚拟内存空间地址

扫描二维码关注公众号,回复: 4789926 查看本文章

猜你喜欢

转载自blog.csdn.net/god1992/article/details/85318477
今日推荐