短语解释:
HAL:Hardware Abstraction Layer
DAL:Device Abstraction Layer
DDI:Device Driver Interface
HAL层定义底层硬件操作,而DAL层位于驱动和应用层之间,DAL API定义在DDI文件中,应用层调用DDI接口使用驱动提供的服务;
1. DAL框架层
DAL层为每个设备分配一个DeviceId,应用层使用Attach方法把DeviceId和DalDeviceHandle关联在一起;
DalDeviceHandle结构体定义如下:
struct DalDeviceHandle
{
uint32 dwDalHandleId;
const DalInterface *pVtbl;
void *pClientCtxt;
};
@dwDalHandleId使用预设值
DALDEVICE_INTERFACE_HANDLE_ID;
@pVtbl是DalInterface类型成员,定义设备通用方法和自定义方法;
@pClientCtxt指向客户端上下文变量;
DalInterface结构体定义如下:
struct DalInterface
{
struct DalDevice DalDevice;
//User will add their prototypes here.
};
DalDevice结构体定义通用方法,用户自定义方法可以添加到DalInterface中;
struct DalDevice
{
DALResult (*Attach)(const char*,DALDEVICEID,DalDeviceHandle **);
uint32 (*Detach)(DalDeviceHandle *);
DALResult (*Init)(DalDeviceHandle *);
DALResult (*DeInit)(DalDeviceHandle *);
DALResult (*Open)(DalDeviceHandle *, uint32);
DALResult (*Close)(DalDeviceHandle *);
DALResult (*Info)(DalDeviceHandle *, DalDeviceInfo *, uint32);
DALResult (*PowerEvent)(DalDeviceHandle *, DalPowerCmd, DalPowerDomain);
DALResult (*SysRequest)(DalDeviceHandle *, DalSysReq, const void *, uint32, void *,uint32, uint32*);
};
DAL层定义了一系列以"DalDevice_*"开头的帮助函数来调用这些通用方法;
DALResult DalDevice_Init(DalDeviceHandle *_h)
DALResult DalDevice_DeInit(DalDeviceHandle *_h)
DALResult DalDevice_Open(DalDeviceHandle *_h, uint32 mode)
DALResult DalDevice_Close(DalDeviceHandle *_h)
DAL层还定义了DAL_DeviceAttach方法,查找与DeviceId关联的DalDeviceHandle,如果是第一次执行Attach方法,还需要执行
DalDevice_Init操作 :
DALResult DAL_DeviceAttach( DALDEVICEID DevId, DalDeviceHandle **phDalDevice)
{
DALResult ret = _DALSYSCMN_GetDeviceObject(DevId, NULL, phDalDevice);
if(DAL_SUCCESS == ret)
{
//if a remote handle the remote side does the Init
if(!DALISREMOTEHANDLE(*phDalDevice))
{
if(1 == DAL_GET_DEVICE_ATTACH_COUNT(*phDalDevice))
{
ret = DalDevice_Init(*phDalDevice);
if(DAL_SUCCESS != ret)
{
DAL_DeviceDetach(*phDalDevice);
*phDalDevice = NULL;
}
}
}
}
return ret;
}
_DALSYSCMN_GetDeviceObjec方法从
gDALProcessDriverModList数组查找DalDeviceHandle,找到以后会调用设备定义的Attach函数;
static DALResult _DALSYSCMN_GetDeviceObject(DALDEVICEID DevId, const char *pszArg, DalDeviceHandle **phDevice)
{
DALREG_DriverInfoList *pModDriverInfoList = gDALProcessDriverModList[0];
uint32 dwDriverIdx = 0;
*phDevice = NULL;
for(dwDriverIdx =0;dwDriverIdx<pModDriverInfoList->dwLen;dwDriverIdx++)
{
uint32 dwDeviceIdx = 0;
DALREG_DriverInfo *pDriverInfo = pModDriverInfoList->pDriverInfo[dwDriverIdx];
for(dwDeviceIdx=0;dwDeviceIdx<pDriverInfo->dwNumDevices;dwDeviceIdx++)
{
if(DevId == pDriverInfo->pDeviceId[dwDeviceIdx])
return
pDriverInfo->pfnDALNewFunc(pszArg,DevId,phDevice);
}
}
return DAL_ERROR;
}
gDALProcessDriverModLis数组定义了所有注册的DAL设备驱动,定义如下:
gDALProcessDriverModList[0] = &gDALModDriverInfoList;
static DALREG_DriverInfo * DALDriverInfoArr[] = {
& DALI2C_DriverInfo,
& DALSystem_DriverInfo,
& DALVAdc_DriverInfo,
& DALChipInfo_DriverInfo,
& DALPlatformInfo_DriverInfo,
& DALTLMM_DriverInfo,
};
DALREG_DriverInfoList gDALModDriverInfoList = {6, DALDriverInfoArr};
以DALTLMM_DriverInfo为例,其定义如下:
static DALDEVICEID DalTLMM_DeviceId[2] = {DALDEVICEID_TLMM,
DALDEVICEID_TLMM_MASTER};
const DALREG_DriverInfo
DALTLMM_DriverInfo = { TLMM_DalTlmm_Attach,
2,
DalTLMM_DeviceId
};
所以要使用DAL服务,需要先在DALDriverInforArr数组中注册驱动,然后调用Dal_DeviceAttach方法关联设备,该方法内部会调用设备的Attach和Init方法,这样就可以使用设备提供的服务了;
下面以DalTlmm为例,简单介绍应用层如何使用DAL服务:
DalDeviceHandle *htlmm;
/* init DAL mod */
boot_DALSYS_InitMod(NULL);
result = DAL_DeviceAttach(DALDEVICEID_TLMM, &htlmm);
if (result != DAL_SUCCESS)
return FALSE;
result = DalDevice_Open(htlmm, DAL_OPEN_SHARED);
if (result != DAL_SUCCESS)
goto detach;
/* configure rx gpio as pulldown to detect floating RX */
gpio_config = DAL_GPIO_CFG(gpio_rx, 0, DAL_GPIO_INPUT, DAL_GPIO_PULL_DOWN, DAL_GPIO_2MA);
result = DalTlmm_ConfigGpio(htlmm, gpio_config, DAL_TLMM_GPIO_DISABLE);
if (result != DAL_SUCCESS)
goto close;
busywait(100);
result = DalTlmm_GpioIn(htlmm, gpio_config, &value);
首先调用
boot_DALSYS_InitMod初始化DAL模块,接着调用
DAL_DeviceAttach,
DalDevice_Open方法关联设备,
DalTlmm_ConfigGpio,
DalTlmm_GpioIn是设备驱动自定义方法;
2. TLMM驱动
本节以TLMM为例,介绍HAL层和DAL层的设计方法;
1) HAL层
源代码:
systemdrivers/tlmm/src/HALtlmm.h
systemdrivers/tlmm/hw/v2/HALtlmm.c
宏定义:
#define HAL_GPIO_NUMBER(config) (((config)&0x3FF0)>>4)
#define HAL_GPIO_OUTVAL(config) (((config)&0x1E00000)>>0x15)
#define HAL_DRVSTR_VAL(config) (((config)&0x1E0000)>>17)
#define HAL_PULL_VAL(config) (((config)&0x18000)>>15)
#define HAL_DIR_VAL(config) (((config)&0x4000)>>14)
#define HAL_FUNC_VAL(config) ((config)&0xF)
// cfg=>register value
#define HAL_GPIO_CONFIG_MASK(cfg) \
((HAL_PULL_VAL(cfg) << 0x0) | \
(HAL_FUNC_VAL(cfg) << 0x2) | \
(HAL_DRVSTR_VAL(cfg) << 0x6) | \
(HAL_DIR_VAL(cfg) << 0x9) )
31 23 15 7
0000 000|0 000|0 000|0 0|0|00 0000 0000 |0000
| val | drv |pul|d| gpio |func
HAL层定义TLMM基本操作:
void HAL_tlmm_Init( HAL_tlmm_InitType* pInit ) // HAL初始化函数
void HAL_tlmm_WriteConfig( uint32 nGpioNumber, uint32 nConfig ) // TLMM配置函数
void HAL_tlmm_ConfigGpio( uint32 nWhichConfig ) // GPIO配置函数
boolean HAL_tlmm_ReadGpio( uint32 nWhichConfig ) // GPIO读函数
void HAL_tlmm_WriteGpio( uint32 nWhichConfig, boolean bValue ) // GPIO写函数
2) DAL代码:
源代码:
systemdrivers/tlmm/src/DALTLMM.h
systemdrivers/tlmm/src/DALTLMM.c
systemdrivers/tlmm/src/DALTLMMFwk.c // 定义TLMM框架层
systemdrivers/tlmm/src/DALTLMMInfo.c // 定义TLMM设备驱动
TLMM DAL层定义DAL通用方法和TLMM自定义方法:
// Attach和Init方法在Dal_DeviceAttach中调用,Detach和Deinit方法在Dal_DeviceDetach中调用
DALResult TLMM_DalTlmm_Attach(const char *pszArg, DALDEVICEID DeviceId, DalDeviceHandle **phDalDevice)
DALResult TLMM_DalTlmm_Init(DalDeviceHandle *h)
DALResult TLMM_DalTlmm_DeInit(DalDeviceHandle *h)
// Open和Close方法在DalDevice_Open和DalDevie_Close中调用
DALResult TLMM_DalTlmm_Open(DalDeviceHandle* h, uint32 mode)
DALResult TLMM_DalTlmm_Close(DalDeviceHandle* h)
// TLMM方法
DALResult TLMM_DalTlmm_ConfigGpio( DalDeviceHandle * h, DALGpioSignalType gpio_config, DALGpioEnableType enable)
DALResult TLMM_DalTlmm_GpioIn( DalDeviceHandle * h, DALGpioSignalType gpio_config, DALGpioValueType* value)
DALResult TLMM_DalTlmm_GpioOut( DalDeviceHandle * h, DALGpioSignalType gpio_config, DALGpioValueType value)
TLMM_DalTlmm_Attach是TLMM设备自定义的Attach方法,返回DalDeviceHandle:
DALResult TLMM_DalTlmm_Attach(const char *pszArg, DALDEVICEID DeviceId,
DalDeviceHandle **phDalDevice)
{
DALResult nErr;
static TLMMClientCtxt ClientCtxt;
TLMMClientCtxt *pClientCtxt = &ClientCtxt;
*phDalDevice = NULL;
nErr = DALFW_AttachToDevice(DALDEVICEID_TLMM,(DALDrvCtxt *)&TLMM_DriverCtxt,
(DALClientCtxt *)pClientCtxt);
if (DAL_SUCCESS == nErr)
{
TLMM_InitInterface(pClientCtxt);
TLMM_DalTlmm_AddRef(&(pClientCtxt->DalTlmmHandle));
*phDalDevice = (DalDeviceHandle *)&(pClientCtxt->DalTlmmHandle);
}
return nErr;
}
TLMM_InitInterface方法初始化设备接口,主要是定义DAL通用接口和自定义接口:
TLMM_InitInterface(TLMMClientCtxt* pclientCtxt)
{
static const DalTlmm vtbl = {
{
TLMM_DalTlmm_Attach,
TLMM_DalTlmm_Detach,
TLMM_DalTlmm_Init,
TLMM_DalTlmm_DeInit,
... ...
} ,
TLMM_DalTlmm_ConfigGpio,
TLMM_DalTlmm_ConfigGpioGroup,
TLMM_DalTlmm_GpioIn,
TLMM_DalTlmm_GpioOut,
... ...
};
... ...
}
sbl1使用xml配置设备属性,DAL层代码通常还包括xml文件的解析操作:
systemdrivers/tlmm/config/msm8953/TLMMChipset.xml:配置tlmm属性和睡眠状态;
<device id=DALDEVICEID_TLMM>
<props name="tlmm_base" type=DALPROP_ATTR_TYPE_UINT32>
0x01000000
</props>
<props name="tlmm_offset" type=DALPROP_ATTR_TYPE_UINT32>
0x0
</props>
<props name="tlmm_total_gpio" type=DALPROP_ATTR_TYPE_UINT32>
142
</props>
<props name="tlmm_sleep" type=DALPROP_ATTR_TYPE_UINT32_SEQ_PTR>
DALTLMMBSP_LowPowerCfg
</props>
<props name="tlmm_ports" type=DALPROP_ATTR_TYPE_UINT32_SEQ_PTR>
tlmm_port_cfg
</props>
</device>
// 取得DeviceId关联的xml文件句柄
DALSYS_GetDALPropertyHandle(DALDEVICEID DeviceId,DALSYSPropertyHandle hDALProps)
// 获取xml中定义的属性值
DALSYS_GetPropertyValue(DALSYSPropertyHandle hDALProps, const char *pszName, uint32 dwId, DALSYSPropertyVar *pDALPropVar)
DALDEVICEID_TLMM关联的xml文件是DalTLMM.xml,读取该文件并解析"tlmm_base"等属性:
DALSYS_GetDALPropertyHandle(DALDEVICEID_TLMM, hNonDalProps);
eResult = DALSYS_GetPropertyValue( hNonDalProps, "tlmm_base", 0, &tPropVar );
tHALInit.nBaseAddress = (uint32)tPropVar.Val.dwVal;
3) DDI代码
源文件:
api/systemdrivers/DDITlmm.h
DDI定义DAL API接口,包含常用宏定义和帮助函数等;
宏定义:
#define DAL_GPIO_NUMBER(config) (((config) >> 4) & 0x3FF)
#define DAL_GPIO_FUNCTION(config) ((config) & 0xF)
#define DAL_GPIO_DIRECTION(config) (((config) >> 14) & 0x1)
#define DAL_GPIO_PULL(config) (((config) >> 15) & 0x3)
#define DAL_GPIO_DRIVE(config) (((config) >> 17) & 0xF)
// gpio => cfg
#define DAL_GPIO_CFG(gpio, func, dir, pull, drive) \
(((gpio) & 0x3FF)<< 4 | \
((func) & 0xF)| \
((dir) & 0x1) << 14| \
((pull) & 0x3) << 15| \
((drive)& 0xF) << 17| DAL_GPIO_VERSION)
驱动开发人员需要为设备驱动实现DAL结构:
// DalInterface结构:
struct DalTlmm
{
struct DalDevice DalDevice;
DALResult (*ConfigGpio)(DalDeviceHandle * _h, DALGpioSignalType gpio_config, DALGpioEnableType enable);
DALResult (*ConfigGpioGroup)(DalDeviceHandle * _h, DALGpioEnableType enable, DALGpioSignalType* gpio_group, uint32 size);
... ...
}
// DalDeviceHandle结构
struct DalTlmmHandle
{
uint32 dwDalHandleId;
const DalTlmm * pVtbl;
void *pClientCtxt;
};
// DalClientCtxt结构
struct TLMMClientCtxt
{
uint32 dwRefs;
uint32 dwAccessMode;
void *pPortCtxt;
TLMMDevCtxt *pDevCtxt;
DalTlmmHandle DalTlmmHandle; // 指向DalDeviceHandle结构
};
在DDI接口里面还提供一个简单的Attach方法,关联设备:
#define DAL_TlmmDeviceAttach(DevId,hDalDevice)\
DAL_DeviceAttachEx(NULL,DevId,DALTLMM_INTERFACE_VERSION,hDalDevice)
自定义TLMM操作方法:
DALResult DalTlmm_ConfigGpio(DalDeviceHandle * _h, DALGpioSignalType gpio_config, DALGpioEnableType enable);
DALResult DalTlmm_GpioIn ( DalDeviceHandle *_h, DALGpioSignalType gpio_config, DALGpioValueType*value );
DALResult DalTlmm_GpioOut ( DalDeviceHandle *_h, DALGpioSignalType gpio_config, DALGpioValueType value );
3. 在UEFI中使用DAL
DAL层代码分布在UEFI库模块和DXE驱动模块中,DXE驱动模块以Protocol形式提供服务;
QcomPkg/Library/DALCommonLib:DALCommonLib是DAL框架库,这个目录下有两个inf文件,用于生成DALCommonLoaderLib和DALCommonDxeLib两个库,供XBLLoader和XBLCore(UEFI)使用;
QcomPkg/Drivers/DALSYSDxe:DAL框架层驱动模块,封装DALCommonLib,并提供驱动模块注册服务,DXE驱动模块常驻内存,可以在启动阶段一直为应用层提供服务;
EFI_DALSYS_PROTOCOL定义如下:
typedef struct _EFI_DALSYS_PROTOCOL {
UINT64 revision;
EFI_DALSYS_PROTOCOL_INIT init;
EFI_DALSYS_PROTOCOL_DEINIT deinit;
} EFI_DALSYS_PROTOCOL;
其中init服务注册驱动,并以表的形式返回DALSYS接口:
VOID _DALSYS_Init(DALREG_DriverInfoList *pModDriverList,
DALProps *pDALModStringDevDrvInfoList,
DALSYSFncPtrTbl **pFncTblPtr)
{
// DALSYS函数列表
static DALSYSFncPtrTbl DALSYSFncPtrTbl = {
... ...
DAL_DeviceAttach,
DAL_DeviceAttachEx,
... ...
};
*pFncTblPtr = &DALSYSFncPtrTbl;
// 注册驱动
if(pModDriverList && pModDriverList->dwLen)
DALSYSCMN_RegisterModDriverList(pModDriverList);
}
注册驱动就是把驱动添加到
gDALProcessDriverModList数组中
DALSYSCMN_RegisterModDriverList(DALREG_DriverInfoList *pModDriverList)
{
uint32 dwModIdx = 0;
for(dwModIdx=0;dwModIdx<DALSYSCMN_MAX_NUM_MODS_PER_PROCESS;dwModIdx++)
{
if(NULL == gDALProcessDriverModList[dwModIdx])
{
gDALProcessDriverModList[dwModIdx] = pModDriverList;
return DAL_SUCCESS;
}
}
... ...
}
QcomPkg/Library/DALModEnvLib:DALModEnvLib库封装DALSYSDxe驱动,这是UEFI常用方法,应用层可以像访问lib库一样使用UEFI驱动提供的服务;
DALSYS_InitMod方法使用init服务注册驱动,如果pCfg为NULL,则仅返回DALSYS服务表;
void DALSYS_InitMod(DALSYSConfig * pCfg)
{
if (pCfg)
{
static int configDefined = 0;
if (configDefined)
{
DEBUG ((EFI_D_ERROR,
"Module cannot re-initialize DAL module environment\n"));
ASSERT_EFI_ERROR (0);
}
configDefined = 1;
// populate the DALProps structure which contains
// the string device info list
gpDALModStringDeviceInfoList->dwDeviceSize =
pCfg->stringDeviceInfoList.dwLen;
gpDALModStringDeviceInfoList->pDevices =
pCfg->stringDeviceInfoList.pDevices;
gpDALModDriverInfoList = &pCfg->numericDriverInfoList;
getDALSYSProtocol();
gpDALSYSInitFncPtr->init(gpDALModDriverInfoList,
gpDALModStringDeviceInfoList, &gpDALSYSFncPtr);
}
else
{
if (!gpDALSYSFncPtr)
{
getDALSYSProtocol();
gpDALSYSInitFncPtr->init(NULL, NULL, &gpDALSYSFncPtr);
}
}
}
以TLMM驱动为例,注册过程如下:
static DALDEVICEID DalTlmm_DeviceId[1] = { DALDEVICEID_TLMM };
static DALREG_DriverInfo DALTLMM_DriverInfo = { TLMM_DalTlmm_Attach, 1, DalTlmm_DeviceId };
static DALREG_DriverInfo * DALDriverInfoArr[1] = { &DALTLMM_DriverInfo };
static DALSYSConfig DALSYSModConfig =
{
{0, NULL}, // string device info
{1, DALDriverInfoArr} // numeric driver info
};
DALSYS_InitMod(&DALSYSModConfig);
TLMM_DalTlmm_Attach 是TLMM Attach方法,在DAL_DeviceAttach过程调用该方法;
QcomPkg/Drivers/tlmmdxe:tlmm驱动,inf文件指定tlmm驱动入口函数TLMMEntryPoint(),该函数负责注册驱动,Attach设备;
TLMMEntryPoint (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
EFI_HANDLE handle = NULL;
EFI_STATUS status;
DALResult result = DAL_ERROR;
DALSYS_InitMod(&DALSYSModConfig);
result = DAL_DeviceAttach(DALDEVICEID_TLMM, &tlmm_handle);
if(DAL_SUCCESS == result)
{
status = gBS->InstallMultipleProtocolInterfaces(&handle, &gEfiTLMMProtocolGuid,
(void **)&TLMMProtocol, NULL, NULL, NULL );
ASSERT_EFI_ERROR(status);
DalDevice_Open(tlmm_handle, DAL_OPEN_SHARED);
}
... ...
return status;
} /* DALTLMMEntryPoint */
tlmm驱动提供如下服务:
static EFI_TLMM_PROTOCOL TLMMProtocol =
{
EFI_TLMM_PROTOCOL_REVISION,
EFI_TLMM_ConfigGpio,
EFI_TLMM_ConfigGpioGroup,
EFI_TLMM_GpioIn,
EFI_TLMM_GpioOut,
EFI_TLMM_SetInactiveConfig
};
TLMM HAL层代码定义在:QcomPkg/Msm8998Pkg/Library/TLMMTargetLib