0%

应用层遍历所有接入的USB设备(2)

查询描述符

USB驱动对外提供了一系列用来查询描述符的控制码,所有控制码在 usbioctl.h 头文件中定义。

从结构上来说,所有的USB设备起始于 根控制器(Host Controller),然后下边挂接 HUB 进行扩展,
整体是一个树形结构。根控制器 可以同时存在多个,但一般情况下只有一个 根控制器,每个 根控制器
对外提供的访问名称为 "\\.\HCDx" ,x为数字,比如第一个 根控制器"\\.\HCD0"

查询的流程

工作流程:
1、从 根控制器 开始,查询 RootHub 名称,然后打开 RootHub 设备。
2、查询 RootHub 设备 端口 的数量,查询每个 端口 连接的设备信息。
3、如果 端口 连接的是 扩展Hub 设备,跟 RootHub 一样查询并打开设备。
4、如果 端口 连接的不是Hub设备,就可以查询这个设备的所有信息。

工作流程

如下为相关代码

1
2
3
4
5
6
// 宏定义
#define HUBNAME_LEN 128
#define HUBNAME_LEN_WIDE 256
typedef struct _HUB_NAME {
WCHAR szNameW[HUBNAME_LEN];
} HUB_NAME, *PHUB_NAME;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BOOL EnumHost()
{
// 打开HCD0设备
HANDLE hHCDevice = CreateFileW(
L"\\\\.\\HCD0",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hHCDevice == INVALID_HANDLE_VALUE) return FALSE;
// 查询RootHub设备名
HUB_NAME stRootHub = { 0 };
// 例如:\\.\USB#ROOT_HUB30#4&5375334&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
BOOL bRet = GetRootHubName(hHCDevice, stRootHub);
CloseHandle(hHCDevice); // 先关句柄
if (!bRet) return FALSE;
// 遍历RootHub下的设备
if (!EnumHub(stRootHub)) return FALSE;
return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOL GetRootHubName(HANDLE hHCDevice, HUB_NAME &stRootHub)
{
DWORD dwSize = 0;
char szBuffer[HUBNAME_LEN_WIDE] = { 0 };
PUSB_ROOT_HUB_NAME pHubName = (PUSB_ROOT_HUB_NAME)szBuffer;
HUB_NAME stTemp = { 0 };
// 获取连接到Host Controller的Root Hub名称
BOOL isSuccess = DeviceIoControl(
hHCDevice,
IOCTL_USB_GET_ROOT_HUB_NAME,
NULL, 0,
pHubName, HUBNAME_LEN_WIDE,
&dwSize, NULL);
if (!isSuccess) return FALSE;
if (pHubName->ActualLength > HUBNAME_LEN_WIDE) return FALSE;
// 拼接字符串
wcscpy_s(stRootHub.szwName, HUBNAME_LEN, L"\\\\.\\");
wcscat_s(stRootHub.szwName, HUBNAME_LEN, pHubName->RootHubName);
return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
BOOL EnumHub(HUB_NAME &stHubName)
{
// 尝试打开Hub设备
HANDLE hHubDevice = CreateFileW(
stHubName.szwName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hHubDevice == INVALID_HANDLE_VALUE) return FALSE;
// 遍历Hub设备的节点信息,用来获取端口数量
ULONG nSize = 0;
USB_NODE_INFORMATION stHubInfo = { UsbHub };
BOOL isSuccess = DeviceIoControl(
hHubDevice,
IOCTL_USB_GET_NODE_INFORMATION,
&stHubInfo, sizeof(USB_NODE_INFORMATION),
&stHubInfo, sizeof(USB_NODE_INFORMATION),
&nSize, NULL);
if (!isSuccess)
{
CloseHandle(hHubDevice);
return FALSE;
}
// 检索Hub的所有端口内容
isSuccess = EnumHubPorts(hHubDevice,
stHubInfo.u.HubInformation.HubDescriptor.bNumberOfPorts);
CloseHandle(hHubDevice);
return isSuccess;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
BOOL EnumHubPorts(HANDLE hHubDevice, UCHAR nNumPorts)
{
// 申请连接节点信息的空间
DWORD dwSize = sizeof(USB_NODE_CONNECTION_INFORMATION) +
sizeof(USB_PIPE_INFO) * 30;
PUSB_NODE_CONNECTION_INFORMATION pConnInfo =
(PUSB_NODE_CONNECTION_INFORMATION)malloc(dwSize);
if (pConnInfo == NULL) return FALSE;
// 循环检索端口,端口从1开始,没有0端口
BOOL isSuccess = FALSE;
HUB_NAME stExtHub = { 0 };
PUSB_DESCRIPTOR_REQUEST pDescRequest = NULL;
for (UCHAR i = 1; i <= nNumPorts; i++)
{
memset(pConnInfo, 0, dwSize);
pConnInfo->ConnectionIndex = i;
// 获取对应端口连接设备的信息
isSuccess = DeviceIoControl(
hHubDevice,
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
pConnInfo, dwSize,
pConnInfo, dwSize,
&dwSize, NULL);
// 访问失败,进入下次循环
if (!isSuccess) continue;
// 端口未连接设备,进入下次循环
if (pConnInfo->ConnectionStatus != DeviceConnected) continue;
// 如果端口连接的是HUB
if (pConnInfo->DeviceIsHub)
{
memset(&stExtHub, 0, sizeof(HUB_NAME));
if (GetExtHubName(hHubDevice, i, stExtHub))
{
// 遍历扩展Hub下的设备
EnumHub(stExtHub);
}
continue;
}
//////////////////////////////////////////////////////
// 设备描述符信息
pConnInfo->DeviceDescriptor;
//////////////////////////////////////////////////////
// 获取配置描述符信息
pDescRequest = GetConfigDescriptor(hHubDevice, i);
if (pDescRequest == NULL) continue;
//////////////////////////////////////////////////////
// 配置描述符中包含接口描述符
GetInterfaceDescriptor(pDescRequest);
free(pDescRequest);
//////////////////////////////////////////////////////
// 获取字符串描述符描述信息
GetStringDescriptor(hHubDevice, i, pConnInfo);
}
// 遍历完毕
free(pConnInfo);
return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
BOOL GetExtHubName(HANDLE hHubDevice, UCHAR bPortIndex, HUB_NAME &stExtHub)
{
// 设置访问的端口
char szBuffer[HUBNAME_LEN_WIDE] = { 0 };
PUSB_NODE_CONNECTION_NAME pNodeName = (PUSB_NODE_CONNECTION_NAME)szBuffer;
pNodeName->ConnectionIndex = bPortIndex;
// 获取扩展Hub名称
ULONG dwSize = 0;
BOOL isSuccess = DeviceIoControl(
hHubDevice,
IOCTL_USB_GET_NODE_CONNECTION_NAME,
pNodeName, HUBNAME_LEN_WIDE,
pNodeName, HUBNAME_LEN_WIDE,
&dwSize, NULL);
if (!isSuccess) return FALSE;
if (pNodeName->ActualLength > HUBNAME_LEN_WIDE) return FALSE;
// 拼接字符串
wcscpy_s(stExtHub.szwName, HUBNAME_LEN, L"\\\\.\\");
wcscat_s(stExtHub.szwName, HUBNAME_LEN, pNodeName->NodeName);
return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
PUSB_DESCRIPTOR_REQUEST GetConfigDescriptor(HANDLE hHubDevice, UCHAR bPortIndex)
{
// 构造第1次发送的请求内容
UCHAR bBuffer[sizeof(USB_DESCRIPTOR_REQUEST) +
sizeof(USB_CONFIGURATION_DESCRIPTOR)] = { 0 };
PUSB_DESCRIPTOR_REQUEST pDescReq = (PUSB_DESCRIPTOR_REQUEST)bBuffer;
// 当前HUB下访问的端口索引
pDescReq->ConnectionIndex = bPortIndex;
// USBD will automatically initialize these fields:
// bmRequest = 0x80
// bRequest = 0x06
// We must inititialize these fields:
// wValue = Descriptor Type (high) and Descriptor Index (low byte)
// wIndex = Zero (or Language ID for String Descriptors)
// wLength = Length of descriptor buffer
pDescReq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | 0;
pDescReq->SetupPacket.wLength = sizeof(USB_CONFIGURATION_DESCRIPTOR);
// 第1次发送请求获取实际的长度
DWORD dwSize = 0;
BOOL isSuccess = DeviceIoControl(
hHubDevice,
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
pDescReq, sizeof(bBuffer),
pDescReq, sizeof(bBuffer),
&dwSize, NULL);
// 获取失败则返回
if (!isSuccess) return NULL;
// 构造第2次发送的请求内容
PUSB_CONFIGURATION_DESCRIPTOR pConfigDesc =
(PUSB_CONFIGURATION_DESCRIPTOR)(pDescReq + 1);
dwSize = sizeof(USB_DESCRIPTOR_REQUEST) + pConfigDesc->wTotalLength;
// 申请实际需要的内存空间
pDescReq = (PUSB_DESCRIPTOR_REQUEST)malloc(dwSize);
if (pDescReq == NULL) return NULL;
memset(pDescReq, 0, dwSize);
// 当前HUB下访问的端口索引
pDescReq->ConnectionIndex = bPortIndex;
// USBD will automatically initialize these fields:
// bmRequest = 0x80
// bRequest = 0x06
// We must inititialize these fields:
// wValue = Descriptor Type (high) and Descriptor Index (low byte)
// wIndex = Zero (or Language ID for String Descriptors)
// wLength = Length of descriptor buffer
pDescReq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | 0;
pDescReq->SetupPacket.wLength = pConfigDesc->wTotalLength;
// 第2次发送请求获取实际的内容
isSuccess = DeviceIoControl(
hHubDevice,
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
pDescReq, dwSize,
pDescReq, dwSize,
&dwSize, NULL);
// 获取失败则返回
if (!isSuccess)
{
free(pDescReq);
return NULL;
}
return pDescReq;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUSB_INTERFACE_DESCRIPTOR GetInterfaceDescriptor(PUSB_DESCRIPTOR_REQUEST pDescReq)
{
// 初始化数据
PUSB_CONFIGURATION_DESCRIPTOR pConfigDesc =
PUSB_CONFIGURATION_DESCRIPTOR(pDescReq + 1);
PUCHAR pDescEnd = (PUCHAR)pConfigDesc + pConfigDesc->wTotalLength;
PUSB_COMMON_DESCRIPTOR pCommonDesc = (PUSB_COMMON_DESCRIPTOR)pConfigDesc;
// 循环获取Interface Descriptor信息
while (((PUCHAR)pCommonDesc + sizeof(USB_COMMON_DESCRIPTOR) < pDescEnd)
&& ((PUCHAR)pCommonDesc + pCommonDesc->bLength <= pDescEnd))
{
if (pCommonDesc->bDescriptorType == USB_INTERFACE_DESCRIPTOR_TYPE)
{
// 目前只获取第1个(默认)接口信息
return (PUSB_INTERFACE_DESCRIPTOR)pCommonDesc;
}
// 下一个描述信息
pCommonDesc = (PUSB_COMMON_DESCRIPTOR)
((PUCHAR)pCommonDesc + pCommonDesc->bLength);
}
return NULL;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
BOOL GetAllStringDescriptor(
IN HANDLE hHubDevice, IN UCHAR bPortIndex,
IN PUSB_NODE_CONNECTION_INFORMATION pConnInfo)
{
// 获取String Descriptor的语言列表
PUSB_DESCRIPTOR_REQUEST pLangReq =
GetStringDescriptor(hHubDevice, bPortIndex, 0, 0);
if (pLangReq == NULL) return FALSE;
// 语言ID的处理(语言ID是USHORT大小)
PUSB_STRING_DESCRIPTOR pStringLang =
(PUSB_STRING_DESCRIPTOR)(pLangReq + 1);
// 检测语言信息长度
if (pStringLang->bLength < sizeof(USB_STRING_DESCRIPTOR))
return FALSE;
USHORT numLanguageIDs = (pStringLang->bLength -
sizeof(USB_STRING_DESCRIPTOR) + sizeof(WCHAR)) / 2;
PUSHORT pLanguageIDs = (PUSHORT)&pStringLang->bString[0];
// 根据语言列表获取描述信息(这里只获取第1个)
PUSB_DESCRIPTOR_REQUEST pDescReq = NULL;
PUSB_STRING_DESCRIPTOR pStringDesc = NULL;
// 获取序列号信息
if (pConnInfo->DeviceDescriptor.iSerialNumber != 0)
{
pDescReq = GetStringDescriptor(
hHubDevice, bPortIndex,
pConnInfo->DeviceDescriptor.iSerialNumber,
pLanguageIDs[0]);
if (pDescReq != NULL)
{
// 跳过描述请求的头部
pStringDesc = (PUSB_STRING_DESCRIPTOR)(pDescReq + 1);
// ……
free(pDescReq);
}
}
free(pLangReq);
return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
PUSB_DESCRIPTOR_REQUEST GetStringDescriptor(
HANDLE hHubDevice, UCHAR bPortIndex,
UCHAR bDescIndex, USHORT wLanguageID)
{
// 构造发送的请求内容
DWORD dwSize = sizeof(USB_DESCRIPTOR_REQUEST) + 256;
PUSB_DESCRIPTOR_REQUEST pDescReq = (PUSB_DESCRIPTOR_REQUEST)malloc(dwSize);
if (pDescReq == NULL) return NULL;
memset(pDescReq, 0, dwSize);
// 当前HUB下访问的端口索引
pDescReq->ConnectionIndex = bPortIndex;
// USBD will automatically initialize these fields:
// bmRequest = 0x80
// bRequest = 0x06
// We must inititialize these fields:
// wValue = Descriptor Type (high) and Descriptor Index (low byte)
// wIndex = Zero (or Language ID for String Descriptors)
// wLength = Length of descriptor buffer
pDescReq->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) | bDescIndex;
pDescReq->SetupPacket.wIndex = wLanguageID;
pDescReq->SetupPacket.wLength = 256;
// 获取连接设备的描述信息
BOOL isSuccess = DeviceIoControl(
hHubDevice,
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
pDescReq, dwSize,
pDescReq, dwSize,
&dwSize, NULL);
// 获取失败则返回
if (!isSuccess)
{
free(pDescReq);
return NULL;
}
return pDescReq;
}