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++用的有点多,我得复习下语法了。