当前位置: 首页 > 工具软件 > DAL > 使用案例 >

SBL中的HAL和DAL层

汤昊
2023-12-01
短语解释:
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
 类似资料: