ZCU106 VCU Linux驅動轉裸機驅動篇(一)

ZCU106 VCU Linux驅動轉裸機驅動

前言

由於某某某原因,本人的畢設上最好用裸機驅動VCU編解碼,故應該把Linux驅動單獨拿出來驅動,下面開始拿出來了哈哈

準備

1、vcu_ctrl_sw源碼,這是上層控制VCU驅動的,可以直接編解碼的,源碼鏈接

https://github.com/Xilinx/vcu-ctrl-sw

2、準備驅動源碼

https://github.com/Xilinx/vcu-modules

開始

1、首先坑定還是找ctrl_sw中app的open、close、iotcl、read、write等等,直接打開source insight讀取源碼,直接搜open函數,如下:

#include <errno.h>
#include "lib_rtos/lib_rtos.h"
#include "lib_common/IDriver.h"

static int Open(AL_TDriver* driver, const char* device)
{
  (void)driver;
  void* drv = Rtos_DriverOpen(device);

  if(drv == NULL)
    return -1;
  return (int)(uintptr_t)drv;
}

static void Close(AL_TDriver* driver, int fd)
{
  (void)driver;
  Rtos_DriverClose((void*)(intptr_t)fd);
}

static AL_EDriverError ErrnoToDriverError(int err)
{
  if(err == ENOMEM)
    return DRIVER_ERROR_NO_MEMORY;

  if(err == EINVAL || err == EPERM)
    return DRIVER_ERROR_CHANNEL;

  return DRIVER_ERROR_UNKNOWN;
}

static AL_DriverVtable hardwareDriverVtable =
{
  &Open,//這裏就是直接open
  &Close,
  &PostMessage,//其實就是ioctl
};

static AL_TDriver hardwareDriver =
{
  &hardwareDriverVtable
};

AL_TDriver* AL_GetHardwareDriver()
{
  return &hardwareDriver;
}

這裏看一下發送信息的函數,所有信息都是通post_message接口發送

static AL_EDriverError PostMessage(AL_TDriver* driver, int fd, long unsigned int messageId, void* data)
{
  (void)driver;

  while(true)
  {
    int iRet;
	//不是poll下載信息,那便是上傳唄~
    if(messageId != AL_POLL_MSG)
      iRet = Rtos_DriverIoctl((void*)(intptr_t)fd, messageId, data);
    else
    {
    //這裏是下載mcu中的控制信息
      iRet = Rtos_DriverPoll((void*)(intptr_t)fd, *(int*)data);
      
      if(iRet == 0)
        return DRIVER_TIMEOUT;
    }

    int errdrv = errno;

    if(iRet < 0)
    {
      if((errdrv == EAGAIN) || (errdrv == EINTR))
        continue;
      return ErrnoToDriverError(errdrv);
    }

    return DRIVER_SUCCESS;
  }
}

然後把open、close、postmessage中的open以及close打開如下:

#include <sys/ioctl.h>
#include <fcntl.h>

void* Rtos_DriverOpen(char const* name)
{
  int fd = open(name, O_RDWR);

  if(fd == -1)
    return NULL;
  return (void*)(intptr_t)fd;
}

void Rtos_DriverClose(void* drv)
{
  int fd = (int)(intptr_t)drv;
  close(fd);
}

int Rtos_DriverIoctl(void* drv, unsigned long int req, void* data)
{
  int fd = (int)(intptr_t)drv;
  return ioctl(fd, req, data);
}

其實就是調用了底層的open、ioctl、close等,這裏就是直接控制硬件驅動函數了,然後我們的任務就是找到調用AL_GetHardwareDriver這個函數的地方,然後搜查:

static unique_ptr<CIpDevice> createMcuIpDevice()
{
  auto device = make_unique<CIpDevice>();

  device->m_pAllocator.reset(createDmaAllocator("/dev/allegroIP"), &AL_Allocator_Destroy);

  if(!device->m_pAllocator)
    throw runtime_error("Can't open DMA allocator");

  device->m_pScheduler = AL_SchedulerMcu_Create(AL_GetHardwareDriver(), device->m_pAllocator.get());

  if(!device->m_pScheduler)
    throw std::runtime_error("Failed to create MCU scheduler");

  return device;
}


shared_ptr<CIpDevice> CreateIpDevice(bool bUseRefSoftware, int iSchedulerType, AL_TEncSettings& Settings, function<AL_TIpCtrl* (AL_TIpCtrl*)> wrapIpCtrl, bool trackDma, int eVqDescr)
{
  (void)bUseRefSoftware, (void)Settings, (void)wrapIpCtrl, (void)eVqDescr, (void)trackDma;



  if(iSchedulerType == SCHEDULER_TYPE_MCU)
    return createMcuIpDevice();

  throw runtime_error("No support for this scheduling type");
}

這裏就是用C++寫的,就是創建了一個encode的device的實例,然後找調用CreateIpDevice的地方,如下,其實就是main函數了,哈哈我們是反着找的,其實正常是正着找,但要了解其內核驅動如何工作就是要了解中間到底如何調用的

void SafeMain(int argc, char** argv)
{
  ConfigFile cfg;
  SetDefaults(cfg);

  auto& FileInfo = cfg.FileInfo;
  auto& Settings = cfg.Settings;
  auto& StreamFileName = cfg.BitstreamFileName;
  auto& RecFileName = cfg.RecFileName;
  auto& RunInfo = cfg.RunInfo;

  ParseCommandLine(argc, argv, cfg);

  DisplayVersionInfo();

  AL_Settings_SetDefaultParam(&Settings);
  SetMoreDefaults(cfg);

  if(!RecFileName.empty() || !cfg.RunInfo.sMd5Path.empty())
    Settings.tChParam[0].eOptions = (AL_EChEncOption)(Settings.tChParam[0].eOptions | AL_OPT_FORCE_REC);



  ValidateConfig(cfg);



  function<AL_TIpCtrl* (AL_TIpCtrl*)> wrapIpCtrl = GetIpCtrlWrapper(RunInfo);

  auto pIpDevice = CreateIpDevice(!RunInfo.bUseBoard, RunInfo.iSchedulerType, Settings, wrapIpCtrl, RunInfo.trackDma, RunInfo.eVQDescr);

  if(!pIpDevice)
    throw runtime_error("Can't create IpDevice");


  auto hFinished = Rtos_CreateEvent(false);
  auto scopeMutex = scopeExit([&]() {
    Rtos_DeleteEvent(hFinished);
  });

  // --------------------------------------------------------------------------------
  // Create Encoder
  auto pAllocator = pIpDevice->m_pAllocator.get();
  auto pScheduler = pIpDevice->m_pScheduler;

  AL_TBufPoolConfig StreamBufPoolConfig = GetStreamBufPoolConfig(Settings, FileInfo);
  BufPool StreamBufPool(pAllocator, StreamBufPoolConfig);
  /* instantiation has to be before the Encoder instantiation to get the destroying order right */
  BufPool SrcBufPool;

  int frameBuffersCount = 2 + Settings.tChParam[0].tGopParam.uNumB;
#if AL_ENABLE_TWOPASS

  // the LookAhead needs LookAheadSize source buffers to work
  if(AL_TwoPassMngr_HasLookAhead(cfg.Settings))
    frameBuffersCount += cfg.Settings.LookAhead;
#endif
  auto QpBufPoolConfig = GetQpBufPoolConfig(Settings, Settings.tChParam[0], frameBuffersCount);
  BufPool QpBufPool(pAllocator, QpBufPoolConfig);


  unique_ptr<EncoderSink> enc;
  enc.reset(new EncoderSink(cfg, pScheduler, pAllocator, QpBufPool
                            ));


  enc->BitstreamOutput = createBitstreamWriter(StreamFileName, cfg);
  enc->m_done = ([&]() {
    Rtos_SetEvent(hFinished);
  });

  IFrameSink* firstSink = enc.get();

#if AL_ENABLE_TWOPASS
  unique_ptr<EncoderLookAheadSink> encFirstPassLA;

  if(AL_TwoPassMngr_HasLookAhead(cfg.Settings))
  {
    encFirstPassLA.reset(new EncoderLookAheadSink(cfg, pScheduler, pAllocator, QpBufPool
                                                  ));
    encFirstPassLA->next = firstSink;
    firstSink = encFirstPassLA.get();
  }
#endif

  // Input/Output Format conversion
  shared_ptr<AL_TBuffer> SrcYuv;
  vector<uint8_t> YuvBuffer;
  bool shouldConvert = ConvertSrcBuffer(Settings.tChParam[0], FileInfo, YuvBuffer, SrcYuv);


  shared_ptr<AL_TBuffer> RecYuv;
  vector<uint8_t> RecYuvBuffer;

  if(!RecFileName.empty())
  {
    RecYuv = AllocateConversionBuffer(RecYuvBuffer, Settings.tChParam[0].uWidth, Settings.tChParam[0].uHeight, cfg.RecFourCC);
    enc->RecOutput = createFrameWriter(RecFileName, cfg, RecYuv.get(), 0);
  }


  if(!cfg.RunInfo.sMd5Path.empty())
  {
    auto multisink = unique_ptr<MultiSink>(new MultiSink);
    multisink->sinks.push_back(move(enc->RecOutput));
    multisink->sinks.push_back(createMd5Calculator(cfg.RunInfo.sMd5Path, cfg, RecYuv.get()));
    enc->RecOutput = move(multisink);
  }


  for(unsigned int i = 0; i < StreamBufPoolConfig.uNumBuf; ++i)
  {
    AL_TBuffer* pStream = StreamBufPool.GetBuffer(AL_BUF_MODE_NONBLOCK);
    assert(pStream);

    if(cfg.RunInfo.printPictureType)
    {
      AL_TMetaData* pMeta = (AL_TMetaData*)AL_PictureMetaData_Create();
      assert(pMeta);
      auto const attached = AL_Buffer_AddMetaData(pStream, pMeta);
      assert(attached);
    }

    AL_HEncoder hEnc = enc->hEnc;

#if AL_ENABLE_TWOPASS

    // the Lookahead needs one stream buffer to work (2 in AVC multi-core)
    if(AL_TwoPassMngr_HasLookAhead(cfg.Settings) && i < ((Settings.tChParam[0].eProfile & AL_PROFILE_AVC) ? 2 : 1))
      hEnc = encFirstPassLA->hEnc;
#endif
    bool bRet = AL_Encoder_PutStreamBuffer(hEnc, pStream);
    assert(bRet);
    AL_Buffer_Unref(pStream);
  }


  unique_ptr<RepeaterSink> prefetch;

  if(g_numFrameToRepeat > 0)
  {
    prefetch.reset(new RepeaterSink(g_numFrameToRepeat, cfg.RunInfo.iMaxPict));
    prefetch->next = firstSink;
    firstSink = prefetch.get();
    cfg.RunInfo.iMaxPict = g_numFrameToRepeat;
    frameBuffersCount = max(frameBuffersCount, g_numFrameToRepeat);
  }

  TFrameInfo FrameInfo = GetFrameInfo(cfg.FileInfo, Settings.tChParam[0]);
  auto const eSrcMode = Settings.tChParam[0].eSrcMode;

  /* source compression case */
  auto pSrcConv = CreateSrcConverter(FrameInfo, eSrcMode, Settings.tChParam[0]);

  InitSrcBufPool(pAllocator, shouldConvert, pSrcConv, FrameInfo, eSrcMode, frameBuffersCount, SrcBufPool);
  ifstream YuvFile;
  PrepareInput(YuvFile, cfg.YUVFileName, cfg.FileInfo, cfg);

  int iPictCount = 0;
  int iReadCount = 0;
  bool bRet = true;

  while(bRet)
  {
    AL_64U uBeforeTime = Rtos_GetTime();
    bRet = sendInputFileTo(YuvFile, SrcBufPool, SrcYuv.get(), cfg, pSrcConv.get(), firstSink, iPictCount, iReadCount);

    AL_64U uAfterTime = Rtos_GetTime();

    if((uAfterTime - uBeforeTime) < cfg.RunInfo.uInputSleepInMilliseconds)
      Rtos_Sleep(cfg.RunInfo.uInputSleepInMilliseconds - (uAfterTime - uBeforeTime));
  }

  Rtos_WaitEvent(hFinished, AL_WAIT_FOREVER);

  if(auto err = GetEncoderLastError())
    throw codec_error(EncoderErrorToString(err), err);
}

/******************************************************************************/

int main(int argc, char** argv)
{
  try
  {
    SafeMain(argc, argv);
    return 0;
  }
  catch(codec_error const& error)
  {
    cerr << endl << "Codec error: " << error.what() << endl;
    return error.GetCode();
  }
  catch(runtime_error const& error)
  {
    cerr << endl << "Exception caught: " << error.what() << endl;
    return 1;
  }
}

嗯嗯嗯,明天在分析吧,c++用的有點多,我得複習下語法了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章