1. 程式人生 > >IO記憶體 與 IO埠 >>Linux 裝置驅動程式

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 : 返回為虛擬記憶體空間地址