虽然利用DriverStudio的工程向导为我们生成了测试程序示例,但它大量使用了DriverStudio自己的封装库,现在来介绍在不使用DriverStudio封装库的情况下如何编写测试程序。
1、打开设备的两种方式
我们知道打开设备用CreateFile函数,其第一个参数应指向欲打开的USB设备,它可以是Device Interface,也可以是Device Symbolic Link(设备连接符或叫设备名)。要么用Interface打开USB设备,要么用Symbolic Link打开(不知正确与否),在利用DriverStudio工程向导生成时USB驱动程序的过程中可选择应用程序打开此USB设备的方式(见下图),在这时就确定了将来该用何种方式来打开设备。
1)用Interface方式打开设备
首先取得设备的GUID(什么是GUID?请看这篇文章:USB编程之二(常见设备类型的GUID)),然后利用设备GUID来得到设备Interface,最后就可以用CreateFile函数来打开设备了。
GUID由驱动程序定义,如上一节的驱动程序实例中对GUID的定义如下:
- #define Easy_USB_51_ProgramerDevice_CLASS_GUID \
- { 0x47589a1e, 0xad02, 0x455e, { 0xa9, 0xf7, 0xcc, 0xb2, 0xd5, 0xe, 0x68, 0x54 } }
还可以利用第三方工具取得设备GUID,如USBView,还有DriverStudio自带的Symbolic Viewer),下面是用Symbolic Viewer查得的上一节驱动实例的GUID
我们这里利用DEFINE_GUID宏来定义设备GUID,内容如下:
- DEFINE_GUID(Easy_USB_51_ProgramerDevice_CLASS_GUID,
- 0xC713541C, 0x3C65, 0x474A, 0x8E, 0xBC, 0x25, 0x87, 0x51, 0x49, 0xD2, 0x05);
DEFINE_GUID宏在initguid.h中定义的,所以需要在加入对此文件的引用:
另外,我们将要用到的一些函数需要setupapi.h和setupapi.lib,它们是VC6自带的,所以需要添加对这两个文件的引用:
①#include "Setupapi.h"
②选择菜单“Project”->“Settings”,切换到“Link”页,在“Object/library modules”中填入:setupapi.lib
好了,现在贴出以Interface方式打开设备的代码:
-
- void CWR_CtrlLEDDlg::OnOpendevByInterface()
- {
- LONG ii = 0;
- HDEVINFO hDeviceInfo;
- DWORD bufferSize;
- SP_DEVICE_INTERFACE_DATA interfaceData;
- PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail;
-
-
- hDeviceInfo = SetupDiGetClassDevs(
- (LPGUID)&Easy_USB_51_ProgramerDevice_CLASS_GUID,
- NULL,
- NULL,
- DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
- );
- if (hDeviceInfo == INVALID_HANDLE_VALUE)
- {
- AfxMessageBox("SetupDiGetClassDevs failed");
- return;
- }
-
-
- interfaceData.cbSize = sizeof(interfaceData);
-
- for (ii = 0;
- SetupDiEnumDeviceInterfaces(
- hDeviceInfo,
- NULL,
- (LPGUID)&Easy_USB_51_ProgramerDevice_CLASS_GUID,
- ii,
- &interfaceData);
- ++ii)
- {
-
- if (!SetupDiGetDeviceInterfaceDetail(
- hDeviceInfo,
- &interfaceData,
- NULL,
- 0,
- &bufferSize,
- NULL))
- {
- if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
- {
- TRACE("Error: couldn't get interface detail\n");
-
- continue;
- }
- }
-
-
- deviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(bufferSize);
- if (deviceDetail == NULL)
- {
- TRACE("Error: Buffer allocation failed\n");
- continue;
- }
-
-
- deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
-
-
- if (!SetupDiGetDeviceInterfaceDetail(
- hDeviceInfo,
- &interfaceData,
- deviceDetail,
- bufferSize,
- NULL,
- NULL))
- {
- TRACE("Error: SetupDiGetDeviceInterfaceDetail failed\n");
- free(deviceDetail);
- continue;
- }
-
-
- SetupDiDestroyDeviceInfoList(hDeviceInfo);
-
-
- m_hDev = CreateFile(
- deviceDetail->DevicePath,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL
- );
-
-
- free(deviceDetail);
-
- if (m_hDev == INVALID_HANDLE_VALUE)
- {
- AfxMessageBox("Error: CreateFile failed\n");
- return;
- }
-
- AfxMessageBox("设备成功打开!");
-
- GetDlgItem(IDC_BTN_OPENDEV_BY_INTERFACE)->EnableWindow(FALSE);
- GetDlgItem(IDC_BTN_OPENDEV_BY_NAME)->EnableWindow(FALSE);
-
- EnableControl(TRUE);
- }
- }
2)用Symbolic Link方式打开设备
Driver Studio通过KUnitizedName函数生成设备的Symbolic Link,我们再利用CreateFile来打开设备,其一般形式为:
- hDevice = CreateFile("\\\\.\\OMNIPORT3",
- GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL ,
- NULL);
其中前缀“\\\\.\\”表示Createfile试图打开一个设备,然后紧跟设备名,其设备名可以利用DDK提供的“Device Tree”工具来查询,也可以查看DriverStudio向导生成的代码中KUnitizedName实例代码(位于AddDevice功能函数内),如果第一个参数为:L”TestDevice”,则设备名一般应为TestDevice0
在上一节实例中生成的驱动程序是以Interface方式打开的,现在我们重新配置此项目,在配置向导第“Step 12 of 14”中,打开设备(Open Device)选择“Symbolic Link”选项,并填写Symbolic Link的值为:Easy_USB_51_Programer_SymbolicDevice:
|
下载驱动源码 (需要放到C:\Easy_USB_51_Programer_Symbolic目录) |
安装驱动程序,然后就可以用Symbolic Link的方式打开设备了,而Symbolic Link的值就应该为Easy_USB_51_Programer_SymbolicDevice0,这个值在配置向导第“Step 12 of 14”中已经填好(但其后要加“0”),通过查看驱动源程序也可以得到(位于AddDevice功能函数的KUnitizedName实例代码的第一个参数),也可用第三方工具如Symbolic Link Viewer查询,下图是利用DriverStudio提供的Symbolic Link Viewer工具查询到的Symbolic Link值:
好了,现在贴出以Symbolic Link方式打开设备的代码:
-
- void CWR_CtrlLEDDlg::OnOpendevBySymbolicLink()
- {
- char *sLinkName = "\\\\.\\Easy_USB_51_Programer_SymbolicDevice0";
-
- m_hDev = CreateFile(sLinkName,
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL);
-
- if (m_hDev == INVALID_HANDLE_VALUE)
- {
- AfxMessageBox("Error: CreateFile failed");
- return;
- }
-
- AfxMessageBox("设备成功打开!");
- }
2、读写设备
读写设备除了可以通过WriteFile和ReadFile函数以外,还可以利用DeviceIoControl函数。WriteFile最终会调用驱动程序的IRP_MJ_READ实例代码(DriverStudio里是Write实例函数),ReadFile最终会调用驱动程序的IRP_MJ_ WRITE实例代码(DriverStudio里是Read实例函数),而DeviceIoControl最终会调用驱动程序的IRP_MJ_DEVICE_CONTROL实例代码(DriverStudio里是DeviceControl)。
DeviceIoControl的第二个参数为操作代码,在驱动程序里的定义形如IOCTL_XXXX格式,对于自定义的设备操作代码,一般用CTL_CODE宏定义,我们需要拷贝上一节驱动程序中对IOCTL_LED和IOCTL_GET_KEY两个操作代码的定义(在使用前需要包含头文件:#include "Winioctl.h")
- #define IOCTL_LED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_WRITE_DATA)
- #define IOCTL_GET_KEY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
下面放上利用MFC编写的一个基于对话的程序,它主要实现与上一节中测试程序相似的功能(通过端点2读写数据,通过控制端点控制扩展板EXT-BOARD-A上的LED状态,通过控制端点读取扩展板EXT-BOARD-A上的按键的状态),另外还测试了两种打开设备的方式(设备安装驱动程序后就决定了设备只能以某一种方式打开,用不正确的方式打开会报错)。
应用程序界面
(呵呵,我已经投PCB了,所以没有用手工制作的那个原始东东来展示)
注意:这节的实例还需要再改一下设备程序,把之前main函数里的控制LED的代码删除,即
这句需要删除,而通过控制端点控制LED和读取按键状态的代码在《厂商自定义USB设备固件程序及特性》一节里已经介绍了。
|