DMA 设备驱动模型
DMA 驱动模型简介
SylixOS 中的 DMA 相关驱动代码位于“libsylixos/SylixOS/system/device/dma/”目录下,DMA 驱动多用于外设驱动或总线驱动中。主要功能是从一块物理地址向另一块物理地址搬运数据。本文以 S3C2440 通用 DMA 驱动为例。
DMA 设备库对 DMA 设备进行了封装,设备驱动仅需要提供初始化函数和回调函数即可。
用户在注册 DMA 设备驱动之前,需要先调用 API_DmaDrvInstall 函数安装 DMA 设备库,此函数的功能是向系统内核加载一个 DMA 驱动。用户需在配置好 DMA 控制器之后,调用此函数把驱动加载到内核中。其原型如下:
#include <SylixOS.h>
INT API_DmaDrvInstall (UINT uiChannel,
PLW_DMA_FUNCS pdmafuncs,
size_t stMaxDataBytes)
函数 API_DmaDrvInstall 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。
- 参数 pdmafuncs 是 DMA 驱动加载函数。
- 参数 stMaxDataBytes 是 DMA 驱动最大传输字节数。
DMA 设备需要调用 dmaGetFuncs 函数绑定驱动并创建设备,其函数原型如下:
#include <SylixOS.h>
PLW_DMA_FUNCS dmaGetFuncs (UINT iChannel,
ULONG *pulMaxBytes)
函数 dmaGetFuncs 原型分析:
- 此函数成功返回 PLW_DMA_FUNCS ,失败返回 PX_ERROR 。
- 参数 iChannel 是 DMA 通道号。
- 参数 pulMaxBytes 是 DMA 驱动最大传输字节数。
结构体 PLW_DMA_FUNCS 详细描述如下:
#include <SylixOS.h>
typedef struct lw_dma_funcs {
VOID (*DMAF_pfuncReset)(UINT uiChannel,
struct lw_dma_funcs *pdmafuncs);
INT (*DMAF_pfuncTrans)(UINT uiChannel,
struct lw_dma_funcs *pdmafuncs,
PLW_DMA_TRANSACTION pdmatMsg);
INT (*DMAF_pfuncStatus)(UINT uiChannel,
struct lw_dma_funcs *pdmafuncs);
} LW_DMA_FUNCS;
typedef LW_DMA_FUNCS *PLW_DMA_FUNCS;
- 参数 DMAF_pfuncReset 是复位 DMA 当前的操作。
- 参数 DMAF_pfuncTrans 是启动一次 DMA 传输。
- 参数 DMAF_pfuncStatus 是获得 DMA 当前的工作状态。
结构体中包含三个需要提供给 DMA 设备库的操作函数,其功能分别是复位当前 DMA 操作、启动一次 DMA 传输、获得当前 DMA 工作状态。这三个函数需要驱动程序根据不同的硬件去具体实现。
DMA 回调函数
DMA 驱动支持用户创建自己的回调函数来实现相应的操作。用户可根据实际情况自行添加或删除回调函数,示例如下:
#include <SylixOS.h>
typedef struct {
…
VOID (*DMAT_pfuncStart)(UINT uiChannel,
PVOID pvArg); /* 启动本次传输之前的回调 */
PVOID *DMAT_pvArg; /* 回调函数参数 */
VOID (*DMAT_pfuncCallback)(UINT uiChannel,
PVOID pvArg); /* 本次传输完成后的回调函 */
…
} LW_DMA_TRANSACTION;
- 参数 DMAT_pfuncStart 是启动本次传输之前的回调。
- 参数 DMAT_pvArg 是回调函数参数。
- 参数 DMAT_pfuncCallback 是本次传输完成后的回调。
启动一次 DMA 传输前后,如果需要处理其他事务,可自行填充回调函数,进行相关操作。
DMA 传输参数
LW_DMA_TRANSACTION 结构体中除了一些 DMA 回调函数,还有一些重要的 DMA 参数,结构体定义如下:
#include <SylixOS.h>
typedef struct {
UINT8 *DMAT_pucSrcAddress; /* 源端缓冲区地址 */
UINT8 *DMAT_pucDestAddress; /* 目的端缓冲区地址 */
size_t DMAT_stDataBytes; /* 传输的字节数 */
INT DMAT_iSrcAddrCtl; /* 源端地址方向控制 */
INT DMAT_iDestAddrCtl; /* 目的地址方向控制 */
INT DMAT_iHwReqNum; /* 外设请求端编号 */
BOOL DMAT_bHwReqEn; /* 是否为外设启动传输 */
BOOL DMAT_bHwHandshakeEn; /* 是否使用硬件握手 */
INT DMAT_iTransMode; /* 传输模式, 自定义 */
PVOID DMAT_pvTransParam; /* 传输参数, 自定义 */
ULONG DMAT_ulOption; /* 体系结构相关参数 */
PVOID DMAT_pvArgStart; /* 启动回调参数 */
…
} LW_DMA_TRANSACTION;
typedef LW_DMA_TRANSACTION *PLW_DMA_TRANSACTION;
- 参数 DMAT_pucSrcAddress 是 DMA 源端缓冲区地址。
- 参数 DMAT_pucDestAddress 是 DMA 目的端缓冲区地址。
- 参数 DMAT_stDataBytes 是 DMA 传输的字节数。
- 参数 DMAT_iSrcAddrCtl 是 DMA 源端地址方向控制。
- 参数 DMAT_iDestAddrCtl 是 DMA 目的地址方向控制。
- 参数 DMAT_iHwReqNum 是外设请求端编号。
- 参数 DMAT_bHwReqEn 是 DMA 是否为外设启动传输。
- 参数 DMAT_bHwHandshakeEn 是 DMA 是否使用硬件握手。
- 参数 DMAT_iTransMode 是传输模式,自定义。
- 参数 DMAT_pvTransParam 是传输参数,自定义。
- 参数 DMAT_ulOption 是体系结构相关参数。
- 参数 DMAT_pvArgStart 是启动回调参数。
DMA 操作的是物理地址,所以 Src 和 Dest 地址均为物理地址。有些系统CPU体系架构的CACHE是使用虚拟地址作为索引的,有些是使用物理地址做索引的。
DMA 函数简介
SylixOS 内核中提供很多有关 DMA 操作的内核函数。除 API_DmaDrvInstall 函数是在驱动加载中调用,其余的函数可在 DMA 驱动挂载到内核完成之后调用。用户也可在其他驱动中调用相关函数来控制已经加载的 DMA 驱动完成相关操作。系统内核函数无需用户编写,包含对应头文件传入正确的参数即可使用。
API_DmaReset 函数实现复位制定的 DMA 通道,函数如下:
#include <SylixOS.h>
INT API_DmaReset(UINT uiChannel);
函数 API_DmaReset 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。
API_DmaJobNodeNum 函数可获得当前队列节点数,函数如下:
#include <SylixOS.h>
INT API_DmaJobNodeNum(UINT uiChannel,
INT *piNodeNum);
函数 API_DmaJobNodeNum 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。
- 参数 piNodeNum 是 DMA 驱动函数节点号。
API_DmaMaxNodeNumGet 函数可获得最大队列节点数,函数如下:
#include <SylixOS.h>
INT API_DmaMaxNodeNumGet(UINT uiChannel,
INT *piMaxNodeNum);
函数 API_DmaMaxNodeNumGet 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。
- 参数 piMaxNodeNum 是最大节点数。
API_DmaMaxNodeNumSet 函数可设置最大队列节点数,函数如下:
#include <SylixOS.h>
INT API_DmaMaxNodeNumSet(UINT uiChannel,
INT iMaxNodeNum);
函数 API_DmaMaxNodeNumSet 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。
- 参数 piMaxNodeNum 是最大节点数。
API_DmaJobAdd 函数可添加一个 DMA 传输请求,函数如下:
#include <SylixOS.h>
INT API_DmaJobAdd(UINT uiChannel,
PLW_DMA_TRANSACTION pdmatMsg);
函数 API_DmaJobAdd 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。
- 参数 pdmatMsg 是传入数据指针。
API_DmaGetMaxDataBytes 函数可获得一次可以传输的最大字节数,函数如下:
#include <SylixOS.h>
INT API_DmaGetMaxDataBytes(UINT uiChannel);
函数 API_DmaGetMaxDataBytes 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。
API_DmaFlush 可删除所有被延迟处理的传输请求,函数如下:
#include <SylixOS.h>
INT API_DmaFlush(UINT uiChannel);
函数 API_DmaFlush 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。
DMA 传输完成后的中断处理函数,在 DMA 中断服务程序中需要调用 API_DmaContext 函数,函数如下:
#include <SylixOS.h>
INT API_DmaContext(UINT uiChannel);
函数 API_DmaContext 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 uiChannel 是 DMA 通道号。