一、Node简单介绍
较官方的定义:Node节点在Camx/Chi架构中至关重要,主要用于处理pipeline下发的request,最终的数据处理都是通过封装好的Node节点来进行的。
简单来说Node用于数据处理;
同时也可以是一些特殊的算法处理,比如EIS,FD,SW Remosaic等等。
大概了解下Node所处的具体位置,如下图:
Node在Camx/Chi中均会存在,Camx中主要位于:android/vendor/qcom/proprietary/camx/src/(hwl/swl),例如:IFE,IPE,BPS,Sensor等。
Chi中主要位于:android/vendor/qcom/proprietary/chi-cdk/oem/qcom/node/,例如:三方EIS,memcpy等。
那两种Node之间有何差异呢?
主要体现在:Camx node是平台已经封装好的节点,一般不需要改动;而Chi node则是提供给vendor自定义实现的节点,用于自定义算法处理。
二、Node如何加载
今天讲解的是Chi Node,大概分为三类,主要关注COMPONENT NODE;
enum class ExternalComponentNodeAlgo
{
COMPONENTNODE = 0, ///< Node
COMPONENTALGORITHM = 1, ///< Algorithm
COMPONENTHVX = 2, ///< Hvx
};
在开机过程中系统会加载很多东西,比如各种应用程序、so文件等等,在加载HAL进程时同样会一起加载Node,我们共同在以下方法中梳理Node的加载流程:
android/vendor/qcom/proprietary/camx/src/core/camxhwenvironment.cpp
//HW初始化方法
CamxResult HwEnvironment::Initialize()
CAMX_ASSERT(NULL != pExternalComponent);
if ((CamxResultSuccess == result) && (NULL != pExternalComponent))
{
result = ProbeChiComponents(pExternalComponent, &m_numExternalComponent);
}
//我们要知道External node在编译成功之后存放于以下目录
static const CHAR ExtCompPath[] = "/vendor/lib64/camera/components";
//ProbeChiComponents方法
CamxResult ProbeChiComponents(
ExternalComponentInfo* pExternalComponentInfo,
UINT* pNumExternalComponent)
//返回so的文件数量
fileCountTypeNode = OsUtils::GetFilesFromPath(ExtCompPath,
FILENAME_MAX,
&soFilesName[0][0],
"*", //VendorName
"node", //CategoryName
,例如com.qti.node.memcpy.so, com.wt.node.sat.so
"*",
"*",
&SharedLibraryExtension[0]);
//循环加载Node节点
while (index < fileCountTypeNode + fileCountTypeStats + fileCountTypeHvx)
{
CamX::OSLIBRARYHANDLE handle = CamX::OsUtils::LibMap(&soFilesName[index][0]);// 1.dlopen的封装方法
if (index < fileCountTypeNode)
{
pNodeEntry = reinterpret_cast<PFCHINODEENTRY>(CamX::OsUtils::LibGetAddr(handle, "ChiNodeEntry")); // 2.dlsys的封装方法
CAMX_ASSERT(NULL != pNodeEntry);
pExternalComponentInfo[index].nodeCallbacks.size = sizeof(ChiNodeCallbacks);
if (NULL != pNodeEntry)
{
pNodeEntry(&pExternalComponentInfo[index].nodeCallbacks);// 3.Node API’s Callbacks
}
if (NULL != pExternalComponentInfo[index].nodeCallbacks.pQueryVendorTag)
{
GetComponentTag(pExternalComponentInfo[index].nodeCallbacks.pQueryVendorTag);
}
pExternalComponentInfo[index].nodeAlgoType = ExternalComponentNodeAlgo::COMPONENTNODE;
}
1.核心方法dlopen
OSLIBRARYHANDLE ChxUtils::LibMap(const CHAR* pLibraryName)
{
OSLIBRARYHANDLE hLibrary = NULL;
const UINT bindFlags = RTLD_NOW | RTLD_LOCAL;
hLibrary = dlopen(pLibraryName, bindFlags);
if (NULL == hLibrary)
{
CHX_LOG_ERROR("Failed to load library %s error %s", pLibraryName, dlerror());
CHX_ASSERT(0 == dlerror());
}
return hLibrary;
}
2.核心方法dlsym
VOID* ChxUtils::LibGetAddr(OSLIBRARYHANDLE hLibrary,
const CHAR* pProcName)
{
VOID* pProcAddr = NULL;
if (hLibrary != NULL)
{
pProcAddr = dlsym(hLibrary, pProcName);
}
return pProcAddr;
}
首先要知道.so 库文件是一个 ELF 文件,可以通过 readelf -s 命令查看对应的.so文件描述,如下图可以看到其中有一个 Name 属性为 ChiNodeEntry。
也就是:CamX通过GetFilesFromPath遍历camera/components目录下的所有so,调用dlopen()来打开目标库获取handle(定位库文件的地址),然后使用dlsym()来得到符号名字为"ChiNodeEntry"的地址,因为所有Node都有一个入口方法ChiNodeEntry,这样便可以加载所有的Node。
那么加载之后如何拿到Node中的方法?
1.这里就需要查看"//ProbeChiComponents方法" 中3的pNodeEntry的数据类型
PFCHINODEENTRY pNodeEntry;
typedef VOID (*PFCHINODEENTRY) (CHINODECALLBACKS* pNodeCallbacks);
函数指针类型;
通过调用pNodeEntry对pExternalComponentInfo[index].nodeCallbacks参数进行取地址赋值,这样就可以使用Node中的方法。
2.以memcpy为例看一下具体操作:
android/vendor/qcom/proprietary/chi-cdk/oem/qcom/node/memcpy/camxchinodememcpy.cpp
//Memcpynode 中的ChiNodeEntry方法
CDK_VISIBILITY_PUBLIC VOID ChiNodeEntry(
CHINODECALLBACKS* pNodeCallbacks)
{
if (NULL != pNodeCallbacks)
{
if (pNodeCallbacks->majorVersion == ChiNodeMajorVersion &&
pNodeCallbacks->size >= sizeof(CHINODECALLBACKS))
{
pNodeCallbacks->majorVersion = ChiNodeMajorVersion;
pNodeCallbacks->minorVersion = ChiNodeMinorVersion;
pNodeCallbacks->pGetCapabilities = MemCpyNodeGetCaps;
pNodeCallbacks->pQueryVendorTag = MemCpyNodeQueryVendorTag;
pNodeCallbacks->pCreate = MemCpyNodeCreate;
pNodeCallbacks->pDestroy = MemCpyNodeDestroy;
pNodeCallbacks->pQueryBufferInfo = MemCpyNodeQueryBufferInfo;
pNodeCallbacks->pSetBufferInfo = MemCpyNodeSetBufferInfo;
pNodeCallbacks->pProcessRequest = MemCpyNodeProcRequest;
pNodeCallbacks->pChiNodeSetNodeInterface = MemCpyNodeSetNodeInterface;
}
}
非常清楚明了,举一反三:其他类型的Node如何加载、Camx如何加载Chi、上层如何加载HAL等等,同理。