上篇中,我们从驱动层面上探讨了系统是如何为我们PCIe设备分配资源以及如何启动它的。现在的问题是,我们的上层软件如何才能对我们的设备进行访问呢。这其中首先涉及的就是设备的开打与关闭。
1:设备打开
针对本次PCI设备,上层程序打开设备函数的具体代码如下。
/********************************************************************/
/* Open device ident=0, b--FALSE or TRUE */
/********************************************************************/
DLLEXP HANDLE CCONV ClLib_Open( unsigned char ident, BOOLEAN b )
{
HANDLE hHandle = INVALID_HANDLE_VALUE;
GUID *pGuid=(LPGUID)&DEMOPCI_GUID;
HDEVINFO hDevInfo;
SP_INTERFACE_DEVICE_DATA IfDevData;
SP_INTERFACE_DEVICE_DETAIL_DATA *IfDevDetail = NULL;
DWORD ReqLen;
// HDEVINFO as all source device is generated
hDevInfo = SetupDiGetClassDevs(pGuid,
0, // Enumerator
0, //
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE );
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Error processing
return (INVALID_HANDLE_VALUE);
}
// All device is enumerated
IfDevData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
if(SetupDiEnumDeviceInterfaces(hDevInfo, NULL, pGuid, ident, &IfDevData))
{
// A necessary amount of the memory in the buffer is obtained
SetupDiGetDeviceInterfaceDetail(hDevInfo ,&IfDevData, NULL, 0, &ReqLen, NULL);
// Memory allocation to acquire detailed information
IfDevDetail = (SP_INTERFACE_DEVICE_DETAIL_DATA *)(new char[ReqLen]);
if(!IfDevDetail)
{
return (INVALID_HANDLE_VALUE);
}
IfDevDetail->cbSize=sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
// Detailed information (pass) is actually acquired
if(SetupDiGetDeviceInterfaceDetail(hDevInfo,&IfDevData, IfDevDetail, ReqLen, NULL, NULL))
{
// Open the driver
if(b == TRUE)
{
sg_bOverlapFlag = 1;
hHandle=CreateFile(IfDevDetail->DevicePath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
NULL);
}
else
{
sg_bOverlapFlag = 0;
hHandle=CreateFile(IfDevDetail->DevicePath, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
NULL);
}
}
// Opening of allocated memory
delete IfDevDetail;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
return (hHandle);
}
来看几个关键函数:
HDEVINFO SetupDiGetClassDevs(
__in_opt const GUID *ClassGuid,
__in_opt PCTSTR Enumerator,
__in_opt HWND hwndParent,
__in DWORD Flags
);
该函数用于获取指定类别的设备信息结构的句柄。值得注意的是参数*ClassGuid应指定成我们驱动安装时的.inf文件中Guid号(DEMOPCI_GUID),这样才能保证正确得到对应的设备句柄。
BOOL SetupDiEnumDeviceInterfaces(
__in HDEVINFO DeviceInfoSet,
__in_opt PSP_DEVINFO_DATA DeviceInfoData,
__in const GUID *InterfaceClassGuid,
__in DWORD MemberIndex,
__out PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
);
该函数枚举所有指定类别的设备接口。
BOOL SetupDiGetDeviceInterfaceDetail( __in HDEVINFO DeviceInfoSet, __in PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, __out_opt PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, __in DWORD DeviceInterfaceDetailDataSize, __out_opt PDWORD RequiredSize, __out_opt PSP_DEVINFO_DATA DeviceInfoData );该函数返回设备接口的设置信息。
HANDLE WINAPI CreateFile( __in LPCTSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile );该函数应用很广泛,可以用来创建或者打开文件或者设备。本例中我们可以通过设置参数dwFlagsAndAttributes(FILE_FLAG_OVERLAPPED)来决定设备是异步还是同步方式打开。
2:设备关闭
相比起前面的设备打开程序,设备关闭显得要简单许多,直接调用CloseHandle函数注销设备句柄即可。
/********************************************************************/
/* Close device */
/********************************************************************/
DLLEXP int CCONV ClLib_Close( HANDLE hHandle )
{
BOOL bRet;
bRet = CloseHandle(hHandle);
if(bRet)
return RTN_OK;
else
return RTN_ERR;
}
小结:通过上述打开设备函数,我们已经得到相应设备的句柄,后续操作中直接使用该句柄就能实现对设备的访问了(如寄存器读写等)。