内存泄漏系列专题分析之三十二:高通相机CamX ION/dmabuf内存管理机制CmdBuffer

【关注我,后续持续新增专题博文,谢谢!!!】

上一篇我们讲了:

这一篇我们开始讲: 内存泄漏系列专题分析之三十二:高通相机CamX ION/dmabuf内存管理机制CmdBuffer

目录

一、背景

二、:CmdBufferManager管理单元

2.1:CmdBufferManager初始化

2.2:CmdBufferManager获取buffer

[三、:Camx CmdBuffer模块](#三、:Camx CmdBuffer模块)

3.1:CmdBuffer初始化

[3.2 :CmdBuffer核心接口](#3.2 :CmdBuffer核心接口)

[3.3 :CmdBuffer核心接口使用方式](#3.3 :CmdBuffer核心接口使用方式)

[3.4 :CmdBuffer中sensor上电命令形式](#3.4 :CmdBuffer中sensor上电命令形式)

四、:总结


一、背景

Camx HAL层ION Buffer可以分为两类:ImageBuffer和CmdBuffer。这篇博文就CmdBuffer模块进行分析介绍。

了解过高通Camx架构,一般知道bringup的开发并非都在kernel层,chi-cdk用户态也存在大量bringup开发工作,而用户态sensor等xml配置类似内核态dts配置一样,chi-cdk用户态存在大量sensor、马达等硬件bringup代码,需要讲xml配置通过CmdBuffer的方式下发到kernel去执行最终的驱动代码。

二、:CmdBufferManager管理单元

2.1:CmdBufferManager初始化

CmdBufferManager::Initialize是对CmdBufferManager进行初始化,可以看到核心代码只有一行:可以知道最终还是同ImageBuffer一样由CSLAlloc分配ION内存。

cpp 复制代码
X:\bruce\SI\camx\src\core\camxcmdbuffermanager.cpp

CamxResult CmdBufferManager::Initialize(
    const CHAR*             pBufferManagerName,
    const ResourceParams*   pParams)
{
    {

        CAMX_ASSERT(NULL != pBufferManagerName);
        OsUtils::StrLCpy(m_pBufferManagerName, pBufferManagerName, sizeof(m_pBufferManagerName));

        m_params         = *pParams;

        result = InitializePool();
    }

    m_pLock = Mutex::Create("CmdBufferManager");

    return result;
}

CamxResult CmdBufferManager::InitializePool()
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT((0 != m_params.resourceSize) && (0 == (m_params.poolSize % m_params.resourceSize)));

    if ((0 == m_params.resourceSize) || (0 != (m_params.poolSize % m_params.resourceSize)))
    {
        result = CamxResultEInvalidArg;
    }
    else
    {
        SIZE_T  allocationSize  = 0;
        UINT32  flags           = m_params.memFlags;
        m_numResources          = m_params.poolSize / m_params.resourceSize;

        CSLBufferInfo*  pBufferInfo = static_cast<CSLBufferInfo*>(CAMX_CALLOC(sizeof(CSLBufferInfo)));

        if (NULL == m_pParentBufferManager)
        {
            if (NULL != pBufferInfo)
            {
                result = CSLAlloc(m_pBufferManagerName,
                                  pBufferInfo,
                                  allocationSize,
                                  m_params.alignment,
                                  flags,
                                  m_params.pDeviceIndices,
                                  m_params.numDevices);
            }
        }
}

2.2:CmdBufferManager获取buffer

  1. CmdBufferManager获取buffer主要通过GetBuffer和GetBufferForRequest两个接口,返回都是PacketResource,它对应的就是CmdBuffer中CSLBufferInfo,本质就是xml的命令配置。
  2. CmdBufferManager是管理 CmdBuffer的,CmdBufferManager中有2条链表 m_busyPool和m_freePool,其中:m_freePool中存放着空闲的Buffer,m_busyPool中存放着正在被client 使用的Buffer。
cpp 复制代码
CamxResult CmdBufferManager::GetBuffer(
    PacketResource** ppBuffer)
{
    LDLLNode* pNode = m_freePool.RemoveFromHead();

    if (NULL != pNode)
    {
        *ppBuffer = static_cast<PacketResource*>(pNode->pData);

        if (NULL == *ppBuffer)
        {
            result = CamxResultEInvalidState;
        }
        else
        {
            // Check canary
            if (TRUE == (*ppBuffer)->GetUsageFlags().batchedPacket)
            {
                BatchedPacket* pBatchedPacket = static_cast<BatchedPacket*>(*ppBuffer);
                pBatchedPacket->CheckCanary();
            }
            (*ppBuffer)->Reset();
            (*ppBuffer)->SetRequestId(CamxInvalidRequestId);
        }

        CAMX_FREE(pNode);
    }

    m_pLock->Unlock();

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CmdBufferManager::GetBufferForRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CmdBufferManager::GetBufferForRequest(
    UINT64           requestId,
    PacketResource** ppBuffer)
{
    CamxResult result = GetBuffer(ppBuffer);

    m_pLock->Lock();

    if (CamxResultSuccess == result)
    {
        CAMX_ASSERT(NULL != *ppBuffer);

        (*ppBuffer)->SetRequestId(requestId);

        LDLLNode* pNode = static_cast<LDLLNode*>(CAMX_CALLOC(sizeof(LDLLNode)));

        if (NULL != pNode)
        {
            pNode->pData = *ppBuffer;

            m_busyPool.InsertToTail(pNode);

            m_peakNumResourcesUsed = Utils::MaxUINT(m_peakNumResourcesUsed, m_busyPool.NumNodes());
        }
        else
        {
            result = CamxResultENoMemory;
        }
    }

    m_pLock->Unlock();

    return result;
}

三、:Camx CmdBuffer模块

3.1:CmdBuffer初始化

CmdBuffer初始化本质是对PacketResource进行初始化,再对CSLBufferInfo进行封装。

cpp 复制代码
X:\bruce\SI\camx\src\core\camxcmdbuffer.cpp

CamxResult CmdBuffer::Initialize(
    const CmdParams*        pParams,
    const CSLBufferInfo*    pBufferInfo,
    SIZE_T                  offset,
    SIZE_T                  size)
{
    if (NULL == pParams)
    {
        CAMX_LOG_ERROR(CamxLogGroupUtils, "Invalid input params");
        return CamxResultEInvalidPointer;
    }

    CamxResult result = PacketResource::Initialize(pBufferInfo, offset, size);

    return result;
}

CamxResult PacketResource::Initialize(
    const CSLBufferInfo*    pBufferInfo,
    SIZE_T                  offset,
    SIZE_T                  size)
{
    CamxResult result = CamxResultSuccess;

    if ((NULL == pBufferInfo) || (CSLInvalidHandle == pBufferInfo->hHandle))
    {
        result = CamxResultEInvalidArg;
        CAMX_LOG_ERROR(CamxLogGroupUtils, "Invalid packet initialization args");
    }
    else
    {
        m_bufferInfo    = *pBufferInfo;
        m_offsetInBytes = offset;
        m_sizeInBytes   = size;
        m_pUserdata     = NULL;
        m_requestId     = CamxInvalidRequestId;
    }

    return result;
}

3.2 :CmdBuffer核心接口

CmdBuffer核心接口主要是这三个:

  1. CmdBuffer::BeginCommands:CSL开始准备下发命令到kernel
  2. CmdBuffer::CommitCommands:CSL正式提交命令到kernel
  3. CmdBuffer::CancelCommands:CSL取消下发命令到kernel
cpp 复制代码
VOID* CmdBuffer::BeginCommands(
    UINT32 numDwordsToReserve)
{
    VOID* pDwords = NULL;

    if ((GetNumDwordsAvailable() >= numDwordsToReserve) && (0 != numDwordsToReserve))
    {
        pDwords = GetCurrentWriteAddr();
        m_pendingDwords = numDwordsToReserve;
        // Put a canary to check in commit for overrun
        // This requires that we allocate one extra dword for command buffers (in command buffer manager)
        *static_cast<UINT*>(Utils::VoidPtrInc(pDwords, numDwordsToReserve * sizeof(UINT32))) = CamxCanary;
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupUtils, "Failed to reserve words-Dwords needed:%d, Dwords Available:%d",
                    numDwordsToReserve,
                    GetNumDwordsAvailable());
    }

    return pDwords;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CmdBuffer::CommitCommands
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CmdBuffer::CommitCommands()
{
    CamxResult result = CamxResultSuccess;

    CAMX_ASSERT_MESSAGE(GetNumDwordsAvailable() >= m_pendingDwords,
                        "Pending dwords: %d to commit is greater than available dwords: %d",
                        m_pendingDwords, GetNumDwordsAvailable());

    if (0 >= m_pendingDwords)
    {
        CAMX_LOG_ERROR(CamxLogGroupUtils, "Invalid Command buffer state pending dwords: %d",
                       m_pendingDwords);
        result = CamxResultEInvalidState;
    }
    else
    {
        // check for overrun
        CAMX_ASSERT(CamxCanary == *static_cast<UINT*>(Utils::VoidPtrInc(GetCurrentWriteAddr(),
                                                                        m_pendingDwords * sizeof(UINT32))));

        m_resourceUsedDwords += m_pendingDwords;
        m_pendingDwords = 0;
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CmdBuffer::CancelCommands
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult CmdBuffer::CancelCommands()
{
    CamxResult result = CamxResultSuccess;

    m_pendingDwords = 0;

    return result;
}

3.3 :CmdBuffer核心接口使用方式

下面就是OIS和马达bingup代码通过这些接口下发xml命令到kernel

cpp 复制代码
camx/src/swl/sensor/camxois.cpp
result = m_pI2CInfoCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = m_pPowerCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = m_pPowerCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = m_pInitializeCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = pCalibrateCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = pOisModeCmdBuffer->CommitCommands(); in ConfigureOISMode()

camx/src/swl/sensor/camxactuator.cpp
result = pI2CInfoCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = pPowerCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = pPowerCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = pInitializeCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = pMoveFocusCmdBuffer->CommitCommands(); in MoveFocus()

camx/src/swl/sensor/camxactuator.cpp
CmdBuffer* pInitializeCmdBuffer = NULL; in CreateInitializePacket() local
pInitializeCmdBuffer = m_pParentNode->GetCmdBuffer(m_pInitializeCmdManager); in CreateInitializePacket()
(NULL == pInitializeCmdBuffer)) in CreateInitializePacket()
pInitializeCmdBuffer); in CreateInitializePacket()
pCmdBegin = pInitializeCmdBuffer->BeginCommands(initializeCmdSize / sizeof(UINT32)); in CreateInitializePacket()
result = pInitializeCmdBuffer->CommitCommands(); in CreateInitializePacket()
result = pPacket->AddCmdBufferReference(pInitializeCmdBuffer, NULL); in CreateInitializePacket()
m_pInitializeCmdManager->Recycle(pInitializeCmdBuffer); in CreateInitializePacket()

3.4 :CmdBuffer中sensor上电命令形式

以下就是imxxxx_sensor.xml配置的sensor上电的时序,最终会通过代码读取并封装到PacketResource打包成CmdBuffer,再下发大kernel。【camx chi bringup解析xml并下发kernel后面单独介绍,流程漫长】

XML 复制代码
    <powerUpSequence>
      <!--Power setting configuration
          Contains: configType, configValue and delay in milli seconds -->
      <powerSetting>
        <!--Power configuration type
            Supported types are: MCLK, VANA, VDIG, VIO, VAF, RESET, STANDBY -->
        <configType>RESET</configType>
        <!--Configuration value for the type of configuration -->
        <configValue>0</configValue>
        <!--Delay in milli seconds -->
        <delayMs>1</delayMs>
      </powerSetting>
      <!--Power setting configuration
          Contains: configType, configValue and delay in milli seconds -->
      <powerSetting>
        <configType>VANA</configType>
        <configValue>0</configValue>
        <delayMs>1</delayMs>
      </powerSetting>
      <powerSetting>
        <configType>VDIG</configType>
        <configValue>0</configValue>
        <delayMs>1</delayMs>
      </powerSetting>
      <powerSetting>
        <configType>VIO</configType>
        <configValue>0</configValue>
        <delayMs>0</delayMs>
      </powerSetting>
      <powerSetting>
        <configType>MCLK</configType>
        <configValue>24000000</configValue>
        <delayMs>1</delayMs>
      </powerSetting>
      <powerSetting>
        <configType>RESET</configType>
        <configValue>1</configValue>
        <delayMs>18</delayMs>
      </powerSetting>
    </powerUpSequence>

四、:总结

Camx CmdBufferManager和CmdBuffer之间的核心关系如上所述:CmdBufferManager通过GetBuffer的有2条链表 m_busyPool和m_freePool拿到CmdBuffer。

CmdBuffer核心接口主要是这三个:

  1. CmdBuffer::BeginCommands:CSL开始准备下发命令到kernel
  2. CmdBuffer::CommitCommands:CSL正式提交命令到kernel
  3. CmdBuffer::CancelCommands:CSL取消下发命令到kernel

【关注我,后续持续新增专题博文,谢谢!!!】

下一篇讲解:

相关推荐
来来走走4 小时前
Flutter MVVM+provider的基本示例
android·flutter
CYRUS_STUDIO6 小时前
一步步带你移植 FART 到 Android 10,实现自动化脱壳
android·java·逆向
CYRUS_STUDIO6 小时前
FART 主动调用组件深度解析:破解 ART 下函数抽取壳的终极武器
android·java·逆向
蓝倾9769 小时前
淘宝/天猫店铺商品搜索API(taobao.item_search_shop)返回值详解
android·大数据·开发语言·python·开放api接口·淘宝开放平台
Propeller9 小时前
【Android】LayoutInflater 控件实例化的桥梁类
android
国家二级编程爱好者10 小时前
Android开机广播是有序还是无序?广播耗时原因是什么?
android
猿小蔡-Cool10 小时前
Robolectric如何启动一个Activity
android·单元测试·rebolectric
Industio_触觉智能10 小时前
瑞芯微RK3576开发板Android14三屏异显开发教程
android·开发板·瑞芯微·rk3576·多屏异显·rk3576j·三屏异显
unicrom_深圳市由你创科技12 小时前
机器人视觉检测
图像处理·机器人·视觉检测
AI视觉网奇12 小时前
android adb调试 鸿蒙
android