• libusb获取Windows设备实例路径DevicePath


    libusb 当前版本(1.0.26)libusb.h 头文件提供的接口似乎没有办法获取 Windows 平台相关的设备实例路径,其形如:

    \\?\usb#vid_04ca&pid_7070#5&20d34a76&0&6#{a5dcbf10-6530-11d2-901f-00c04fb951ed}

    只是提供了 libusb_get_port_numbers 之类的接口来获取拓扑结构

    我们可以通过 libusb 源码中平台相关的接口来获取DevicePath,但是使用非公有接口意味着替换版本的时候要注意源码相关的修改。目前找了两种方式:

    1.通过 winusb_device_priv 结构体中的 path 获取

    通过 libusbi.h 头文件中的 usbi_get_device_priv 函数获取 libusb_device 对应的平台相关的数据结构,对应到 windows 平台就是 winusb_device_priv,其包含的 path 字段就是存放DevicePath。

    1. #include "libusb.h"
    2. #include "libusbi.h"
    3. void enum_device()
    4. {
    5. libusb_init(NULL);
    6. libusb_device **device_list;
    7. int count = libusb_get_device_list(NULL, &device_list);
    8. for(int i = 0 ; i < count; i++)
    9. {
    10. libusb_device_descriptor desc;
    11. int ret = libusb_get_device_descriptor(device_list[i], &desc);
    12. if (ret != LIBUSB_SUCCESS) {
    13. continue;
    14. }
    15. winusb_device_priv *priv = (winusb_device_priv*)usbi_get_device_priv(device_list[i]);
    16. if (priv) {
    17. fprintf(stderr, "Device %d path: %s.\n", i, priv->path);
    18. }
    19. }
    20. libusb_free_device_list(device_list, 1);
    21. libusb_exit(NULL);
    22. }

    从 usbi_get_device_priv 函数推测,公有结构和平台相关结构在内存上是紧挨着的:

    1. #define PTR_ALIGN(v) (((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
    2. static inline void *usbi_get_device_priv(struct libusb_device *dev)
    3. {
    4. return (unsigned char *)dev + PTR_ALIGN(sizeof(*dev));
    5. }

    获取 DevicePath 的过程在 windows_winusb.c 的 winusb_get_device_list 和 get_interface_details 函数,使用的 SetupAPI 系列的接口来获取的。有一点需要注意,获取到的 path 内部转换成了大写,见 normalize_path 函数实现。

    不修改库源码,而是直接使用私有接口需要用到的源码文件:

    其中 config.h 文件来自于源码的 msvc 文件夹。

    可能会遇到类型转换报错,自己做下显式转换即可。

    2.通过 libusb_device 结构体中的 session_data 和 Windows 的 DevInst 比较

    session_data 在 windows 平台上目前就是存储的 SP_DEVINFO_DATA 结构的 DevInst 值。相较于第一种方式,我们需要再次用 SetupAPI 的接口枚举一遍设备信息,然后判断 session_data 和 DevInst 是否相等,相等时就可以用 Win32 接口获取 DevicePath 了。

    libusb_device 结构体定义在 libusbi.h 头文件。

    1. #include
    2. #include
    3. #include
    4. #pragma comment(lib, "SetupAPI.lib")
    5. #include
    6. // 具体的设备 GUID 需要 initguid, 如 usbiodef
    7. #include
    8. // USB 设备
    9. // GUID_DEVINTERFACE_USB_DEVICE
    10. #include
    11. void enum_device()
    12. {
    13. // HDEVINFO 标识设备信息集
    14. HDEVINFO info_set;
    15. // SetupDiGetClassDevs 返回包含本地计算机请求的设备信息元素的设备信息集的句柄
    16. // 接口文档:https://learn.microsoft.com/zh-cn/windows/win32/api/setupapi/nf-setupapi-setupdigetclassdevsa
    17. // 若要返回支持任何类的设备接口的设备,设置 DIGCF_DEVICEINTERFACE 和 DIGCF_ALLCLASSES 标志,然后将 ClassGuid 设置为 NULL
    18. //info_set = SetupDiGetClassDevsA(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    19. // 若要仅返回支持指定类的设备接口的设备,设置 DIGCF_DEVICEINTERFACE 标志并使用 ClassGuid 参数提供设备接口类的类 GUID
    20. // 实际使用对应设备的 GUID
    21. GUID device_guid{GUID_DEVINTERFACE_USB_DEVICE};
    22. info_set = SetupDiGetClassDevsA(&device_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    23. // 如果操作失败,返回 INVALID_HANDLE_VALUE
    24. if (info_set == INVALID_HANDLE_VALUE) {
    25. fprintf(stderr, "SetupDiGetClassDevs: [err code] %d.\n", GetLastError());
    26. return;
    27. }
    28. // SP_DEVINFO_DATA 标识设备信息集中的设备
    29. SP_DEVINFO_DATA info_data = { 0 };
    30. info_data.cbSize = sizeof(info_data);
    31. // SetupDiEnumDeviceInfo 枚举设备信息
    32. for (int index = 0; SetupDiEnumDeviceInfo(info_set, index, &info_data); index++)
    33. {
    34. fprintf(stderr, "Device %d %d:\n", index, info_data.DevInst);
    35. // 获取设备实例路径
    36. // SP_DEVICE_INTERFACE_DATA 设备信息集中的设备接口
    37. SP_DEVICE_INTERFACE_DATA interface_data = { 0 };
    38. interface_data.cbSize = sizeof(interface_data);
    39. // SetupDiEnumDeviceInterfaces 枚举包含在设备信息集中的设备接口
    40. BOOL ret = SetupDiEnumDeviceInterfaces(info_set, NULL, (LPGUID)&device_guid, index, &interface_data);
    41. if (!ret) continue;
    42. ULONG required_len = 0;
    43. // SetupDiGetDeviceInterfaceDetail 返回有关设备接口的详细信息
    44. // 第一次调用是获取长度,这里是返回false
    45. SetupDiGetDeviceInterfaceDetailA(info_set, &interface_data, NULL, 0, &required_len, NULL);
    46. if (required_len <= 0) continue;
    47. ULONG predicted_len = required_len;
    48. // SP_INTERFACE_DEVICE_DETAIL_DATA 包含设备接口的路径
    49. SP_INTERFACE_DEVICE_DETAIL_DATA_A detail_data = { 0 };
    50. detail_data.cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_A);
    51. // 检索即插即用设备信息
    52. if (SetupDiGetDeviceInterfaceDetailA(info_set,
    53. &interface_data,
    54. &detail_data,
    55. predicted_len,
    56. &required_len,
    57. &info_data)) {
    58. fprintf(stderr, "Device Instance Path: %s.\n", detail_data.DevicePath);
    59. }
    60. }
    61. // SetupDiDestroyDeviceInfoList 删除设备信息集并释放所有关联的内存
    62. SetupDiDestroyDeviceInfoList(info_set);
    63. }

  • 相关阅读:
    【包过滤防火墙——iptables静态防火墙】的简单使用
    【ARMv8 SIMD和浮点指令编程】NEON 乘法指令——asimdrdm
    将3D MAX设计模型导入NX1988
    RK3588算法盒子maskrom模式下系统烧录
    阿里巴巴专场——第322场周赛题解
    神经网络的三种训练方法,神经网络训练全过程
    夜天之书 #90 再谈 Rust 项目社群治理
    结构体的理解
    功能测试:核心原理、挑战以及解决之道
    图像处理之LCD显示屏的显示控制
  • 原文地址:https://blog.csdn.net/gongjianbo1992/article/details/134376314