根据项目需求,师兄写了PCIE板卡的驱动,而我负责通过调用这些API函数对PCIE板卡进行管理。为了更好地管理PCIE板卡,根据功能需求,我将这些API函数封装到自己的类中。师兄提供的API接口函数如下:
XilPci_CommonBufferProperties | 获取驱动申请的公共缓冲区的属性 |
XilPci_CommonBufferMap | 将公共缓冲区的地址映射到用户空间 |
XilPci_CommonBufferUnmap | 从用户空间解映射公共缓冲区 |
XilPci_DeviceClose | 释放一个板卡设备 |
XilPci_DeviceFind | 查找一个设备 |
XilPci_DeviceReset | 复位板卡设备 |
XilPci_DeviceOpen | 选择一个板卡设备 |
XilPci_DmaChannelOpen | 打开和初始化一个DMA通道 |
XilPci_DmaChannelClose | 释放一个DMA通道 |
XilPci_NotificationRegisterFor | 注册一个中断通知 |
XilPci_NotificationStatus | 中断通知对象的状态 |
XilPci_NotificationWait | 等待一个中断通知事件 |
XilPci_PciRegisterRead | 读取PCI配置寄存器 |
XilPci_PciRegisterWrite | 写PCI配置寄存器 |
XilPci_PhysicalMemoryAllocate | 分配物理内存 |
XilPci_PhysicalMemoryFree | 释放物理内存 |
XilPci_PhysicalMemoryMap | 将物理内存地址映射为用户空间地址 |
XilPci_PhysicalMemoryUnmap | 解映射物理内存地址 |
XilPci_XilRegisterRead | 读取板卡自定义寄存器的值 |
XilPci_XilRegisterWrite | 写板卡自定义寄存器 |
|
class PCIEManager
{
public:
static PCIEManager& GetInstance();
bool openBoard(std::string& errorInfo = std::string()); //打开板卡
bool closeBoard(); //关闭板卡
bool resetBoard(std::string& errorInfo = std::string()); //复位板卡
bool isOpened() const; //板卡是否打开
int getDMABufLength()const; //获取板卡的DMA缓存大小
U64 getDmaUserAddr()const; //获取用户地址
bool read(void *pBuf, const int len); //从板卡读取数据
bool write(void *pBuf, const int len); //往板卡写数据
bool isReadyRead(); //是否准备好读
bool isReadyWrite(); //是否准备好写
int isExistFrameHeader(); //是否存在帧头
U32 frameHeaderOffset(); //帧头的偏移地址
U32 getDmaSendBitrate() const; //获取DMA发图比特率
U32 getDmaRecvBitrate() const;
bool setDmaSendBitrate(U32 value); //设置DMA发图比特率
bool setDmaRecvBitrate(U32 value); //设置DMA收图比特率
U32 PCIERegRead(U16 offset); //获取特定地址寄存器值
XIL_STATUS PCIERegWrite(U16 offset,U32 ValueToWrite); //向特定地址寄存器写值
private:
PCIEManager();
struct PCIEManagerPrivate; //声明一个结构体,用于存储必要的信息
std::auto_ptr<PCIEManagerPrivate> pData; //auto_ptr就是动态分配对象以及在对象不再需要时执行自动清理
const U8 PCIE_READ_TLP_SIZE;
const U8 PCIE_WRITE_TLP_SIZE;
};
首先看一下结构体里面包含哪些信息
struct PCIEManager::PCIEManagerPrivate
{
XIL_DEVICE_OBJECT m_Device; //设备对象
XIL_PHYSICAL_MEM m_BoardConfig;//代表板子5分参数 //包含common buffer的相关信息
bool m_BoardState; //板卡的状态
TinyCriticalSection m_cs; //互斥量,自己已经初始化
U8 *pDmaBufferWrite; //DMA写缓存指针
U8 *pDmaBufferRead; //DMA读缓存指针
XIL_PHYSICAL_MEM PciBufferWrite; //PCIE写缓存信息
XIL_PHYSICAL_MEM PciBufferRead; //PCIE读缓存信息
HANDLE hDmaSendDone; //发图事件句柄
HANDLE hDmaRecvDone; //收图事件句柄
U32 sendBitRate; //发图比特率
U32 recvBitRate; //收图比特率
PCIEManagerPrivate()
{
m_BoardState = false;
//memset为新申请的内存做初始化工作
memset( &m_Device, 0, sizeof(m_Device)); //设备对象
memset( &m_BoardConfig, 0, sizeof(m_BoardConfig));
memset( &hDmaRecvDone,0, sizeof(hDmaRecvDone));
memset( &hDmaSendDone,0, sizeof(hDmaSendDone));
memset( &sendBitRate,0, sizeof(sendBitRate));
memset( &recvBitRate,0, sizeof(recvBitRate));
memset( &PciBufferRead,0, sizeof(PciBufferRead));
//PciBufferWrite为什么不用初始化**************************************************************
}
};
类PCIEManager的构造函数:
PCIEManager::PCIEManager():pData(new PCIEManagerPrivate), PCIE_READ_TLP_SIZE(32),
PCIE_WRITE_TLP_SIZE(32)
{
}
功能说明:构造函数为结构体指针变量pData申请空间,对变量PCIE_READ_TLP_SIZE和PCIE_WRITE_TLP_SIZE赋值初始化。
为结构体指针变量pData申请空间时会启动结构体的析构函数吗?
然后依次看功能函数的具体实现:
static PCIEManager& GetInstance();
PCIEManager& PCIEManager::GetInstance()
{
static PCIEManager s_PCIEManager;
return s_PCIEManager;
}
该函数声明前面有关键词static,表明在没有创建PCIEManager对象的情况下可以直接调用该函数。函数功能说明:创建一个静态变量(对象)s_PCIEManager,并返回该对象,每次调用该函数时返回的都是同一个对象。
bool PCIEManager::openBoard(std::string& errorInfo /*= std::string()*/)
{
XIL_STATUS status; //函数执行完返回的状态信息
XIL_DEVICE_KEY DeviceKey; //包含查找规则
XIL_DMA_PARAMS DmaParams; //DMA传输参数
memset(&DeviceKey, PCI_FIELD_IGNORE, sizeof(XIL_DEVICE_KEY));
if (XilPci_DeviceFind( &DeviceKey, (U8)0)!= ApiSuccess)
{
errorInfo = "找不到PCIE设备";
return false;
}
if(XilPci_DeviceOpen(&DeviceKey, &pData->m_Device) != ApiSuccess)
{
errorInfo = "打开PCIE设备失败";
return false;
}
pData->m_BoardConfig.Size = COMMON_BUFFER_SIZE;
status = XilPci_PhysicalMemoryAllocate( &pData->m_Device, &pData->m_BoardConfig, TRUE );
status = XilPci_PhysicalMemoryMap(&pData->m_Device,&pData->m_BoardConfig);
memset((U8*)pData->m_BoardConfig.UserAddr, 0, pData->m_BoardConfig.Size );
if(XilPci_DmaChannelOpen(&pData->m_Device,0,NULL) != ApiSuccess)
{
errorInfo = "无法打开DMA0通道";
return false;
}
if( XilPci_DmaChannelOpen(&pData->m_Device,1,NULL) != ApiSuccess)
{
errorInfo = "无法打开DMA1通道";
return false;
}
pData->hDmaSendDone = CreateEvent( NULL, TRUE, FALSE, NULL); //手动、无信号*************************
pData->hDmaRecvDone = CreateEvent( NULL, TRUE, FALSE, NULL);
status = XilPci_Win32NotificationRegisterFor(&pData->m_Device,pData->hDmaSendDone,0);
status = XilPci_Win32NotificationRegisterFor(&pData->m_Device,pData->hDmaRecvDone,1);
ResetEvent(pData->hDmaSendDone);
ResetEvent(pData->hDmaRecvDone);
memset(&DmaParams, 0, sizeof(XIL_DMA_PARAMS));
DmaParams.ReadTlpSize = PCIE_READ_TLP_SIZE;
DmaParams.ReadTlpCount = pData->m_BoardConfig.Size/(DmaParams.ReadTlpSize * 4 );
DmaParams.ReadTlpAddr = pData->m_BoardConfig.PhysicalAddr;
DmaParams.WriteTlpSize = PCIE_WRITE_TLP_SIZE;
DmaParams.WriteTlpCount = pData->m_BoardConfig.Size/(DmaParams.WriteTlpSize * 4 );
DmaParams.WriteTlpAddr = pData->m_BoardConfig.PhysicalAddr;
DmaParams.bIsSingleFrameMode = FALSE;
if ( XilPci_DmaSetTlpParams(&pData->m_Device,0, &DmaParams) != ApiSuccess ) //设置通道0的参数***************
{
return false;
}
if ( XilPci_DmaSetTlpParams(&pData->m_Device,1, &DmaParams) != ApiSuccess ) //设置通道1的参数***************
{
return false;
}
errorInfo = "打开成功";
pData->m_BoardState = true;
return true;
}
函数功能说明:打开板卡步骤如下
1)初始化XIL_DEVICE_KEY类型变量 DeviceKey,该变量包含查找规则
2)查找设备,打开设备(API)
3)设置common buffer的大小,分配物理内存,将物理内存映射到用户地址,并初始化用户地址指向的区域
4)打开DMA通道0(用于发图),打开DMA通道1(用于收图)
5)创建发图事件和收图事件,并注册发图事件中断和收图事件中断(API)
6)给XIL_DMA_PARAMS变量赋值(用之前应该初始化),并调用API设置通道0、1的DMA传输参数。
7)完成以上后,将板卡状态设置为打开(true)。
bool PCIEManager::closeBoard()
{
XIL_STATUS status;
if(pData->m_BoardState)
{
CloseHandle( pData->hDmaSendDone );
CloseHandle( pData->hDmaRecvDone );
status = XilPci_PhysicalMemoryUnmap( &pData->m_Device, &pData->m_BoardConfig );
status = XilPci_PhysicalMemoryFree( &pData->m_Device, &pData->m_BoardConfig );
XilPci_DmaChannelClose( &pData->m_Device, 0);
XilPci_DmaChannelClose( &pData->m_Device, 1);
XilPci_DeviceClose( &pData->m_Device );
pData->m_BoardState = false;
return true;
}
return false;
}
函数功能说明:关闭板卡步骤如下(假设现在板卡为打开状态)
1)关闭发图内核对象,关闭收图内核对象(也可以叫做释放句柄)
2)解除物理内存映射,释放物理内存(API)
3)关闭通道0、1
4)关闭设备
5)执行玩以上步骤后将板卡状态设置为关闭
好像将2)和3)的顺序交换一下更加合理
bool PCIEManager::resetBoard(std::string& errorInfo /*= std::string()*/)
{
if (isOpened() == TRUE)
{
XIL_STATUS status = XilPci_DeviceReset(&pData->m_Device);
if ( status == ApiSuccess )
{
errorInfo = "板卡复位成功";
return true;
}
}
errorInfo = "板卡复位失败";
return false;
}
函数功能说明:直接调用API可以对设备对象进行复位(具体复位了哪些东西)
bool PCIEManager::read(void *pBuf, const int len)
{
DWORD EventStatus;
pData->m_cs.Lock();
if ( XilPci_DmaTransfer( &pData->m_Device, (U8)0, (U8)2,0 ) != ApiSuccess)
return false;
else
{
EventStatus = WaitForSingleObject( pData->hDmaRecvDone, INFINITE );
switch (EventStatus)
{
case WAIT_OBJECT_0:
// Event was signaled, reset it
ResetEvent(pData->hDmaRecvDone);
memcpy(pBuf, (U8*)pData->m_BoardConfig.UserAddr, pData->m_BoardConfig.Size );
pData->m_cs.Unlock();
return true;
case WAIT_TIMEOUT:
// Event not signaled within timeout period
pData->m_cs.Unlock();
return false;
case WAIT_ABANDONED:
// Should only get if the event is a mutex
pData->m_cs.Unlock();
return false;
case WAIT_FAILED:
// Check if handle was invalid
if (GetLastError() == ERROR_INVALID_HANDLE)
return false;
pData->m_cs.Unlock();
break;
}
return false;
}
}
函数功能说明:
pData->m_cs.Lock();
在对用户地址指向的区域(common buffer)进行操作之间,进入临界区
XilPci_DmaTransfer( &pData->m_Device, (U8)0, (U8)2,0 )
启动DMA传输,数据传输到用户地址指向的区域后,激活收图事件(API自己内部完成的)
EventStatus = WaitForSingleObject( pData->hDmaRecvDone, INFINITE );
收到事件激活信号后,继续执行后面的程序,完成后面的操作之后推出临界区
bool PCIEManager::write(void *pBuf, const int len)
{
DWORD EventStatus;
XIL_STATUS status;
pData->m_cs.Lock();
memcpy((U8*)pData->m_BoardConfig.UserAddr, (U8*)pBuf,pData->m_BoardConfig.Size);
if ( XilPci_DmaTransfer( &pData->m_Device, (U8)1, (U8)1,0 ) != ApiSuccess)
return false;
else
{
// EventStatus = WaitForSingleObject( pData->hDmaSendDone[i], 5*1000 );
EventStatus = WaitForSingleObject( pData->hDmaSendDone, INFINITE );
switch (EventStatus)
{
case WAIT_OBJECT_0:
// Event was signaled, reset it
ResetEvent(pData->hDmaSendDone);
pData->m_cs.Unlock();
return true;
case WAIT_TIMEOUT:
// Event not signaled within timeout period
pData->m_cs.Unlock();
return false;
case WAIT_ABANDONED:
// Should only get if the event is a mutex
pData->m_cs.Unlock();
return false;
case WAIT_FAILED:
// Check if handle was invalid
if (GetLastError() == ERROR_INVALID_HANDLE)
return false;
pData->m_cs.Unlock();
break;
}
return false;
}
}