C++类之PCIE驱动管理

根据项目需求,师兄写了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;
	}
}












评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值