1. 程式人生 > >驅動移植過程中DMA內存相關接口替換

驅動移植過程中DMA內存相關接口替換

linux sylixos dma

1. 相關概念介紹及移植簡介
1.1 物理地址與總線地址
1)物理地址是與CPU相關的。在CPU的地址信號線上產生的就是物理地址,在程序指令中的的虛擬地址經過段映射和頁面映射後,就生成了物理地址,這個物理地址被放到CPU的地址線上。
2)總線地址,顧名思義,是與總線相關的,外設使用的就是總線地址。
在x86平臺下,外設的I/O地址是獨立的,即有專門的指令訪問外設I/O,I/O地址就是所謂的“總線地址”。而“物理地址”就是RAM地址。
在ARM平臺下,I/O和RAM統一編址,即“總線地址”就是“物理地址”。
Linux系統無論是在內核還是用戶空間,都是直接使用“虛擬地址”訪問內存或I/O空間,因此要訪問外設I/O,必須將I/O地址轉換成“虛擬地址”才能夠進行訪問。

MMU啟動前程序中的地址為“物理地址”,和硬件手冊中規定的地址一致。MMU啟動後程序中的地址為“虛擬地址”,“虛擬地址”和“物理地址”之間的關系參照MMU地址映射表。
1.2 移植介紹
在移植Linux驅動的過程中,會遇到很多非POSIX接口,這些接口是跟Linux系統相關的,而在SylixOS中並未提供兼容接口,因此在替換過程中,需要結合SylixOS本身提供的一些機制實現一套兼容接口,在替換過程中為了保持與linux接口的兼容性,將不改變函數的原型,而只是將內部實現替換成SylixOS接口實現。
本篇將介紹在移植Linux驅動過程中有關DMA內存操作的相關接口的替換方案,註意,本文檔提供的替換方案僅適用於“物理地址”和“總線地址”相同的硬件平臺。
2. DMA內存相關接口介紹及替換
在移植過程中主要遇到的和DMA內存相關操作的接口如表 2-1所示。
表 2-1 Linux DMA內存相關操作接口

系統接口接口功能
dma_alloc_coherent分配一片DMA一致性的內存區域
dma_free_coherent釋放一片DMA一致性內存
dma_pool_create創建一片DMA內存池
dma_pool_alloc從DMA內存池中分配一塊DMA內存
dma_pool_free釋放DMA內存到DMA內存池中
dma_pool_destroy銷毀DMA內存池

2.1 一致性內存相關接口介紹及替換
2.1.1 分配一致性內存


1)Linux接口介紹
Linux內核提供相應接口用於分配一個DMA一致性的內存區域。

void *dma_alloc_coherent(struct device  *dev, 
                          size_t         size, 
                          dma_addr_t    *handle, 
                          gfp_t          gfp)

函數dma_alloc_coherent原型分析:
此函數成功時返回分配的緩沖區地址,失敗時返回NULL;
參數dev為設備結構,SylixOS沒有提供該結構,因此在實際替換中,對該參數進行了修改;
參數size為分配的DMA內存大小;
參數handle為分配的DMA內存的地址;
參數gfp為分配內存標誌。
2)SylixOS接口替換
在SylixOS內核中提供分配DMA內存的接口,且DMA內存是一致性內存,因此為保持兼容,dma_alloc_coherent接口在SylixOS中的實現如程序清單 2-1所示。
程序清單 2-1 dma_alloc_coherent接口實現

void *dma_alloc_coherent(void *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
{
    *handle = (dma_addr_t )API_VmmDmaAllocAlign(size, size);
    if(0 == *handle) {
        *handle = ~0;
        return NULL;
    }
    return (void *)(*handle);
}

主要調用了API_VmmDmaAllocAlign分配DMA物理內存並將DMA地址返回。與Linux中的不同點在於該替換接口返回的是DMA內存地址,而Linux返回的是handle所對應的虛擬地址,提供給用戶使用。
2.1.2 釋放一致性內存
1)Linux接口介紹
Linux內核提供相應接口用於釋放DMA一致性的內存區域。

void dma_free_coherent(struct device    *dev, 
                        size_t           size, 
                        void            *cpu_addr, 
                        dma_addr_t       handle)

函數dma_free_coherent原型分析:
參數dev為設備結構,SylixOS沒有提供該結構,因此在實際替換中,對該參數進行了修改;
參數size為分配的DMA內存大小;
參數cpu_addr為待釋放的緩沖區地址;
參數handle為DMA地址的值。
2)SylixOS接口替換
在SylixOS中提供釋放DMA內存的接口,因此為了兼容,釋放DMA內存實現如程序清單 2-2所示。
程序清單 2-2 dma_free_coherent接口實現

void dma_free_coherent(void *dev, size_t size, void *cpu_addr, dma_addr_t handle)
{
    API_VmmDmaFree ((void *)handle);
}

由於在SylixOS中DMA內存的物理地址和虛擬地址是一一對應的,因此cpu_addr和handle在數值上是相同的。
2.2 DMA內存池相關接口介紹及替換
2.2.1 創建DMA內存池
1)Linux接口介紹
Linux內核提供相應接口用於創建DMA內存池。

struct dma_pool *dma_pool_create(const char     *name, 
                                  struct device *dev,
                                  size_t         size, 
                                  size_t         align, 
                                  size_t         boundary)

函數dma_pool_create原型分析:
此函數成功時返回DMA池的結構指針,由於SylixOS沒有提供該結構,因此在替換過程中接口返回值做了修改。失敗時返回NULL;
參數name為DMA池的名字;
參數dev為設備結構,SylixOS沒有提供該結構,因此在實際替換中,對該參數進行了修改;
參數size為從該DMA池中分配的緩沖區的大小;
參數align為從該池分配時所遵循的對齊原則;
參數boundary表示從該DMA池返回的內存不能越過2的boundary次方的邊界。
2)SylixOS接口替換
SylixOS提供定長內存管理機制,因此創建DMA內存池接口實現如程序清單 2-3所示。
程序清單 2-3 dma_pool_create接口實現

void *dma_pool_create(LW_OBJECT_HANDLE  *ulId, void *dev,
                 size_t size, size_t align, size_t boundary)
{
    PVOID pucDMAPool = NULL;
    pucDMAPool = API_VmmDmaAllocAlign(size, align);
    if (pucDMAPool == NULL) {
        return NULL;
    }    
    *ulId = Lw_Partition_Create("my_partition",
                                 pucDMAPool,
                                 size *2,
                                 4096/64,
                                 LW_OPTION_OBJECT_GLOBAL,
                                 LW_NULL);
    return pucDMAPool;
}

主要實現過程是通過調用API_VmmDmaAllocAlign分配一片DMA內存,調用Lw_Partition_Create對該DMA內存進行管理。此接口成功返回DMA內存池的地址,失敗返回NULL。
2.2.2 從DMA內存池獲取內存塊
1)Linux接口介紹
Linux內核提供相應接口用於從DMA內存池中獲取內存塊。

void *dma_pool_alloc(struct dma_pool    *pool, 
                      gfp_t                  mem_flags,
                      dma_addr_t        *handle)

函數dma_pool_alloc原型分析:
此函數成功時返回從DMA池中獲取的內存塊的地址,失敗時返回NULL;
參數pool為創建DMA池時返回的結構體指針,由於SylixOS沒有提供該結構,因此在替換過程中接口返回值做了修改;
參數mem_flags為分配內存標誌;
參數handle為獲取的內存塊的DMA地址。
2)SylixOS接口替換
SylixOS提供從創建的定長內存中獲取內存塊,因此從DMA內存池中獲取內存塊的實現如程序清單 2-4所示。
程序清單 2-4 dma_pool_alloc接口實現

void *dma_pool_alloc(LW_OBJECT_HANDLE  ulId, gfp_t mem_flags,
             dma_addr_t *handle)
{
    void *alloc;
    alloc = Lw_Partition_Allocate(ulId);

    /*
     * 由於內存是直接從dma內存中分配,因此,物理地址和虛擬地址一樣,
     * 不需要調用API_VmmVirtualToPhysical((addr_t)alloc, handle);進行轉換。
     */
    *handle = (dma_addr_t)alloc;

    return alloc;
}

主要調用定長內存分配接口從定長內存中獲取內存塊,其中ulId是創建內存池成功時產生的句柄,通過該句柄可以從指定的內存池中獲取內存塊。
2.2.3 釋放內存塊到內存池中
1)Linux接口介紹
Linux內核提供相應接口用於釋放內存塊到DMA內存池中。

void dma_pool_free(struct dma_pool *pool, 
                    void           *vaddr, 
                    dma_addr_t      dma)

函數dma_pool_free原型分析:
參數pool為創建DMA池時返回的結構體指針,由於SylixOS沒有提供該結構,因此在替換過程中接口返回值做了修改;
參數vaddr為從DMA池中獲取的內存塊的地址;
參數dma為DMA池中獲取的內存塊的地址對應的DMA地址。
2)SylixOS接口替換
通過調用定長內存釋放函數可實現將分配的內存返還到定長內存中,具體實現如程序清單 2-5所示。
程序清單 2-5 dma_pool_free接口實現

void dma_pool_free(LW_OBJECT_HANDLE  ulId, void *vaddr, dma_addr_t dma)
{
    Lw_Partition_Free(ulId, vaddr);
}

2.2.4 銷毀DMA內存池
1)Linux接口介紹
Linux內核提供相應接口用於銷毀DMA內存池。

void dma_pool_destroy(struct dma_pool *pool)

函數dma_pool_free原型分析:
參數pool為創建DMA池時返回的結構體指針,由於SylixOS沒有提供該結構,因此在替換過程中接口返回值做了修改;
2)SylixOS接口替換
通過調用刪除定長內存函數即可銷毀DMA內存池,具體實現如程序清單 2-6所示。
程序清單 2-6 dma_pool_destroy接口實現

void dma_pool_destroy(LW_OBJECT_HANDLE   *pulId)
{
    Lw_Partition_Delete (pulId);
}

3. 總結
在移植過程中,經常會遇到平臺相關的接口,雖然SylixOS沒有直接提供相應的接口進行替換,但是如果理解該接口的實現目的,那麽就可以通過SylixOS本身的機制實現相同的功能。當然需要對SylixOS本身提供的機制與方法有所了解,才能夠輕松地進行接口替換。


本文出自 “12547908” 博客,請務必保留此出處http://12557908.blog.51cto.com/12547908/1964420

驅動移植過程中DMA內存相關接口替換