最近在做usb 手柄的工作,在网上找到这篇文章,整理了下。慢慢加入我自己的理解。
USB编程知识 一 查找USB设备
USB编程的第一个步骤就是寻找你插入的USB设备.
为了找到你的USB设备,首先要知道你的USB设备的GUID.
一种类型的USB设备的GUID是这个类型唯一的。使用下列的几个函数变可以找到你的USB的设备名字:
SetupDiGetClassDevs()
SetupDiEnumDeviceInfo()
SetupDiGetDeviceRegistryProperty()
SetupDiEnumDeviceInterfaces()
SetupDiGetDeviceInterfaceDetail()
SetupDiDestroyDeviceInfoList()
要仔细的查找一下这些函数的用法,最终你可以得到你要查找的USB设备的设备名字.USB设备的名字不象串口设备的名字那样简单,USB设备的名字是一个长长的字符串,这个字符串是用来继续编程的源头.
USB编程研究之二(DeviceIoControl函数和控制码)
对于USB编程,其中一个函数叫做:
BOOL WINAPI DeviceIoControl(
__in HANDLE hDevice,
__in DWORD dwIoControlCode,
__in LPVOID lpInBuffer,
__in DWORD nInBufferSize,
__out LPVOID lpOutBuffer,
__in DWORD nOutBufferSize,
__out LPDWORD lpBytesReturned,
__in LPOVERLAPPED lpOverlapped
);
这个函数在MSDN中有简单的介绍:
The DeviceIoControl function sends a control code directly to a specified device driver, causing the corresponding device to perform the specified operation.
也就是说,这个函数是对指定的设备驱动发送控制码,导致设备响应来完成指定的操作。
首先来说,DeviceToControl()函数是一个通用函数,从微软的解释中我们就可以看到,它对设备的操作是取决于你发什么样的控制码。在 MSDN中对这个函数介绍的时候,并没有介绍与USB相关的控制码。我也曾经用错了控制码,导致最后使用GetLastError()函数总数得到错误码 50.
与USB相关的控制码都定义在DDK的usbioctl.h头文件中。而对这些控制码的描述可以在在线的MSDN中查到,其路径是:
http://msdn.microsoft.com/en-us/library/aa476234.aspx
MSDN Library-->Win32 and COM Development-->Windows Driver Kit-->Device and Driver Technologies-->Buses and Ports-->Buses-->Buses-->USB-->Reference-->I/O Reqsts for USB Client Drivers
其附近的一些目录也介绍了USB开发的一些信息。
在看其中的控制码的同时,有一个技巧,例如IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION控制码,打开相应的网页,在网页的下面有个结构:USB_DESCRIPTOR_REQST,也就是说 IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION控制码是为了得到 USB_DESCRIPTOR_REQST结构参数的。这样我们就可以把控制码和我们要做的工作对应起来了。
usb的通讯基本步骤如下图所示:
Host controllers ---> Usb root hub ---> usb functions(devices)
基本步骤:
1)打开HCD%X
2) 得到上面的USB root hub
3) 遍历usb root hub上连接的usb 设备。获取信息
4)如果有多个usb口,循环前3步。
下面介绍通讯用的几个IOCTL:
1)USB_HCD_DRIVERKEY_NAME ,用于获取USB设备驱动在注册表中的键名。相应的一个结构体是:
typedef struct _USB_HCD_DRIVERKEY_NAME
{
ULONG ActualLength;
WCHAR DriverKeyName[1];
} USB_HCD_DRIVERKEY_NAME, *PUSB_HCD_DRIVERKEY_NAME;
2)IOCTL_USB_GET_ROOT_HUB_NAME,用于获取root hub 键名。使用的结构体,跟上面一样。
typedef struct _USB_ROOT_HUB_NAME
{
ULONG ActualLength;
WCHAR RootHubName[1];
} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME;
3)IOCTL_USB_GET_NODE_INFORMATION,用于获取连接在root hub上的节点设备信息。也就是我们接在usb口上的所有usb设备的信息,对应的结构体:
typedef struct _USB_NODE_INFORMATION
{
USB_HUB_NODE NodeType;
union {
USB_HUB_INFORMATION HubInformation;
USB_MI_PARENT_INFORMATION MiParentInformation;
} u;
} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION;
typedef struct _USB_MI_PARENT_INFORMATION
{
ULONG NumberOfInterfaces;
} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION;
typedef struct _USB_HUB_INFORMATION
{
USB_HUB_DESCRIPTOR HubDescriptor;
BOOLEAN HubIsBusPowered;
} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION;
typedef struct _USB_HUB_DESCRIPTOR
{
UCHAR bDescriptorLength; // Length of this descriptor
UCHAR bDescriptorType; // Hub configuration type
UCHAR bNumberOfPorts; // number of ports on this hub
USHORT wHubCharacteristics; // Hub Charateristics
UCHAR bPowerOnToPowerGood; // port power on till power good in 2ms
UCHAR bHubControlCurrent; // max current in mA
//
// room for 255 ports power control and removable bitmask
UCHAR bRemoveAndPowerMask[64];
} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR;
4) IOCTL_USB_GET_NODE_CONNECTION_INFORMATION, 用于获取接在usb口上的单个usb设备的信息,对应的结构体:
typedef struct _USB_NODE_CONNECTION_INFORMATION
{
ULONG ConnectionIndex;
USB_DEVICE_DESCRIPTOR DeviceDescriptor;
UCHAR CurrentConfigurationValue;
BOOLEAN LowSpeed;
BOOLEAN DeviceIsHub;
USHORT DeviceAddress;
ULONG NumberOfOpenPipes;
USB_CONNECTION_STATUS ConnectionStatus;
USB_PIPE_INFO PipeList[0];
} USB_NODE_CONNECTION_INFORMATION, *PUSB_NODE_CONNECTION_INFORMATION;
4)IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, 用于获取usb设备的描述信息。
typedef struct _USB_DEVICE_DESCRIPTOR
{
UCHAR bLength;
UCHAR bDescriptorType;
USHORT bcdUSB;
UCHAR bDeviceClass;
UCHAR bDeviceSubClass;
UCHAR bDeviceProtocol;
UCHAR bMaxPacketSize0;
USHORT idVendor;
USHORT idProduct;
USHORT bcdDevice;
UCHAR iManufacturer;
UCHAR iProduct;
UCHAR iSerialNumber;
UCHAR bNumConfigurations;
} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
typedef enum _USB_CONNECTION_STATUS
{
NoDeviceConnected,
DeviceConnected,
/* failure codes, these map to fail reasons */
DeviceFailedEnumeration,
DeviceGeneralFailure,
DeviceCausedOvercurrent,
DeviceNotEnoughPower,
DeviceNotEnoughBandwidth,
DeviceHubNestedTooDeeply,
DeviceInLegacyHub
} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS;
typedef struct _USB_PIPE_INFO
{
USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
ULONG ScheduleOffset;
} USB_PIPE_INFO, *PUSB_PIPE_INFO;
typedef struct _USB_ENDPOINT_DESCRIPTOR
{
UCHAR bLength;
UCHAR bDescriptorType;
UCHAR bEndpointAddress;
UCHAR bmAttributes;
USHORT wMaxPacketSize;
UCHAR bInterval;
} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;
需要注意一点,如果要得到pid,vid,则直接从USB_DEVICE_DESCRIPTOR结构中取出idVendor,idProduct这两项的值就行了。如果要得到序列号,则不是取出 iSerialNumber就可以的。这里的 iSerialNumber仅仅是一个索引值。如果想得到序列号,就需要定义一个结构,然后给设备发送个请求。
USB编程知识之四 Usb device Faq
How can my application detect when my device is attached or removed?
To find out when a device has been attached or removed, catch the WM_DEVICECHANGE messege with the parameter DBT_DEVNODES_CHANGED. Then use SetupDi_ functions to find out what device was attached or removed. A Usenet post on the subject is USB Device Insertion/Removal Event Notification. Also see my example code.
How can my application reset, disable, or restart a USB device?
The DevCon example in the WDK is a command-line utility that shows how to enable, disable, restart, update, remove and query devices using the SetupAPI and CfgMgr32 API functions.
How can I determine which port my device is attached to?
Under Windows, every device has a unique driver key, also called the software key. You can obtain the key from a device using SetupDi functions, and obtain the same key from a hub port using undocumented DeviceIoControl codes. When you have a match, you know which port the device is attached to.
To retrieve the driver key name for a device, call SetupDiGetClassDevs and SetupDiEnumDeviceInfo, then use SetupDiGetDeviceRegistryPropertyA with Property set to SPDRP_DRIVER.
To retrieve the driver key name for a hub port, first find the hubs with SetupDi_ functions and GUID_DEVINTERFACE_USB_HUB.
Obtain a handle to a hub with CreateFile.
Get the number of ports on the hub with IOCTL_USB_GET_NODE_INFORMATION (undocumented IO control code in usbioctl.h).
Find out if a device is attached to a port with IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX.
For each attached device, get the driver key name with IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME (undocumented IO control code in usbioctl.h).
I have two or more identical devices attached to a host. How can I tell which is which?
Store a serial number in the device descriptor or use another vendor-specific identifier. For HID-class devices, applications can read the serial number with the HidD_GetSerialNumberString function.
I've made changes to my device's firmware but Windows doesn't seem to recognize the changes.
With the device attached, remove the device in Windows Device Manager. Then detach and reattach the device.
Is there a limit to the maximum size of a transfer?
See Maximum size of USB transfers on various operating systems (Microsoft Knowledge Base Article 832430).
These values are the maximum amount of data a WDM driver can request to send or receive in a single USB Request Block (URB). An application may request to send or receive more or less than this amount. The driver can handle larger amounts by using multiple URBs. A specific device may have stricter limits on transfer size.
During device testing, we attach many devices that are identical except for the serial numbers. How can I prevent Windows from asking to install a new driver every time a device is attached?
The method described below causes Windows 2000 and XP to ignore a device's serial number. It's recommended for test environments only.
This registry key controls whether Windows uses or ignores device serial numbers:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\UsbFlags]
It's possible to ignore all serial numbers, though this approach is NOT recommended. To ignore all serial numbers, in the above key, change this value to zero:
GlobalDisableSerNumGen = 1
To ignore the serial number for an individual device, create an entry under the above ...\UsbFlags key. The name must start with "IgnoreHWSerNum" followed by the vendor and product ID of the device. A value of 1 = "disable the serial number."
Example (Vendor ID = 0925h, Product ID = 016Ah):
IgnoreHWSerNum0925016A= 1
(Thanks to Dieter Fauth for this answer.)
How is the USB data CRC value calculated?
Here are two examples.
Example 1 from Ron Hemphill.
Example 2 from Barry Twycross:
This is not suitable for use as production code; it is very inefficient:
UInt16 CRC16(void *buffer, UInt32 count)
{
UInt8 *bp;
UInt32 poly, a, b, newBit, lostBit; int i;
poly = 0x8005;
a = 0xffff;
bp = buffer;
while(count--)
{
b = *(bp++);
for(i = 0; i<8; i++)
{
a <<= 1;
newBit = (b & 1);
b >>= 1;
lostBit = a >> 16;
if(newBit != lostBit)
{
a ^= poly;
}
a &= 0xffff;
}
}
return(a^0xffff); }
USB 编程知识之五 Host controller , Hubs, & Functions
USB is implemented in terms of software drivers (which I don't discuss here) and hardware. The hardware divides into host controllers, hubs, and functions:
-
The host controller connects a tree of USB devices to a host (computer, console, and so on). Multiple host controllers can be present. Each host controller anchors one device tree.
-
A hub is a device with multiple ports, into which are plugged other hubs and functions. The root hub integrates with the host controller. All other hubs are external hubs.
-
A function is a device that provides a capability to the host. It plugs into a hub (external or root). Mice, keyboards, cameras, scanners, and printers are examples.
This hardware arranges into a tiered-star topology where each star's center is a hub: both the root hub and external hubs sit at the center of their connected external hubs/functions. Figure 1 reveals this topology.
Configurations, Interfaces, and Endpoints
A function can have one or more configurations that control how the function behaves. Configurations can differ in how much power they consume, whether and how they remotely wake up a suspended computer, and more.
A trackball device that can be configured as a mouse or as a joystick is an example of a function with multiple configurations. Another example is an Integrated Services Digital Network (ISDN) communications device that offers one 128Kb channel or two 56Kb channels.
Each configuration contains one or more interfaces that specify how software accesses the hardware. Interfaces frequently have alternate settings corresponding to different bandwidth requirements.
An interface exposes endpoints that each serve as a data transfer source or a data transfer destination. Each endpoint supports data transfer in a single direction and has a unique numeric identifier. Figure 2 relates endpoints to interfaces and configurations.