libusb是一个提供USB设备访问的跨平台用户模式程序库。该项目最新网址:http://www.libusb.info, 支持主流的操作系统:Linux、Mac OS X、 Windows、OpenBSD/NetBSD、Solaris、Haiku,支持USB 1.0到3.1的所有版本。

使用场景

从事软件开发这么多年来好像还一直未遇到与usb设备相关的开发工作,直到这次开发刷机工具的过程中才有了这样一个需求。软件功能比较简单,选择好刷机文件检测手机插入之后判断手机当前处于何种状态做相应的处理,针对刷机的具体处理暂且不表,手机插拔状态的检测成了我优先要解决的问题,采用adb和fastboot轮询的方式当然也可以做到,但这样就不够优雅了,并且如果手机没有开启adb的时候也无法检测到手机是否插入。libusb名声在外,早些年其实已经知道它,但因为没有使用它的需求所以也一直未认真了解过。

当然,对于我目前的需求来说,libusb的高级功能我也使用不到,仅仅使用了它的hotplug通知,所以这篇日志主要还是记录下来本次使用libusb的经验和遇到的坑。

相关API链接:http://libusb.sourceforge.net/api-1.0/group__hotplug.html

测试程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libusb-1.0/libusb.h>static int LIBUSB_CALL
usb_arrived_callback(struct libusb_context *ctx, struct libusb_device *dev,libusb_hotplug_event event, void *userdata)
{struct libusb_device_handle *handle;struct libusb_device_descriptor desc;unsigned char buf[512];int rc;libusb_get_device_descriptor(dev, &desc);printf("Add usb device: \n");printf("\tCLASS(0x%x) SUBCLASS(0x%x) PROTOCOL(0x%x)\n",desc.bDeviceClass, desc.bDeviceSubClass, desc.bDeviceProtocol);printf("\tVENDOR(0x%x) PRODUCT(0x%x)\n", desc.idVendor, desc.idProduct);rc = libusb_open(dev, &handle);if (LIBUSB_SUCCESS != rc) {printf("Could not open USB device\n");return 0;}memset(buf, 0, sizeof(buf));rc = libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, buf, sizeof(buf));if (rc < 0) {printf("Get Manufacturer failed\n");} else {printf("\tManufacturer: %s\n", buf);}memset(buf, 0, sizeof(buf));rc = libusb_get_string_descriptor_ascii(handle, desc.iProduct, buf, sizeof(buf));if (rc < 0) {printf("Get Product failed\n");} else {printf("\tProduct: %s\n", buf);}memset(buf, 0, sizeof(buf));rc = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf));if (rc < 0) {printf("Get SerialNumber failed\n");} else {printf("\tSerialNumber: %s\n", buf);}libusb_close(handle);return 0;
}static int LIBUSB_CALL
usb_left_callback(struct libusb_context *ctx, struct libusb_device *dev,libusb_hotplug_event event, void *userdata)
{struct libusb_device_descriptor desc;libusb_get_device_descriptor(dev, &desc);printf("Remove usb device: CLASS(0x%x) SUBCLASS(0x%x) iSerialNumber(0x%x)\n",desc.bDeviceClass, desc.bDeviceSubClass, desc.iSerialNumber);return 0;
}int main(int argc, char **argv)
{libusb_hotplug_callback_handle usb_arrived_handle;libusb_hotplug_callback_handle usb_left_handle;libusb_context *ctx;int rc;libusb_init(&ctx);rc = libusb_hotplug_register_callback(ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,LIBUSB_HOTPLUG_NO_FLAGS, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,LIBUSB_HOTPLUG_MATCH_ANY, usb_arrived_callback, NULL, &usb_arrived_handle);if (LIBUSB_SUCCESS != rc) {printf("Error to register usb arrived callback\n");goto failure;}rc = libusb_hotplug_register_callback(ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,LIBUSB_HOTPLUG_NO_FLAGS, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,LIBUSB_HOTPLUG_MATCH_ANY, usb_left_callback, NULL, &usb_left_handle);if (LIBUSB_SUCCESS != rc) {printf("Error to register usb left callback\n");goto failure;}while (1) {libusb_handle_events_completed(ctx, NULL);usleep(1000);}libusb_hotplug_deregister_callback(ctx, usb_arrived_handle);libusb_hotplug_deregister_callback(ctx, usb_left_handle);libusb_exit(ctx);return 0;failure:libusb_exit(ctx);return EXIT_FAILURE;
}

这几年开发环境一直使用MacBook,编译之后运行看起来一切顺利。libusb号称跨平台,因此撸起袖子就开始干了,然后就遇到了后面我要说的一些坑,如果你也有我类似的需求,并且希望让程序跨平台运行,那么在选择libusb的时候可以参考一下。运行结果:

lidroid@lidroid-MacBook-Pro ~/libusb-test $ ./test
Add usb device:CLASS(0x0) SUBCLASS(0x0) PROTOCOL(0x0)VENDOR(0x18d1) PRODUCT(0x4ee2)Manufacturer: HuaweiProduct: Nexus 6PSerialNumber: CVH7N15B10001899
Remove usb device: CLASS(0x0) SUBCLASS(0x0) iSerialNumber(0x3)

使用心得

  1. 利用vendorId和productId过滤目标设备。从测试程序中可以看出,在回调中通过 libusb_get_device_descriptor 获取设备描述结构后,其成员idVendor和idProduct就是我们要的数据,比如我们刷机程序当前选择的firmware支持某个厂商的某个型号手机,那么其它手机插入之后我们将自动过滤。我的作法简单粗暴,有一个DeviceSpec类列出了支持的设备项,每个项目包含vendorId和productId,另外就是Android手机正常启动状态adb模式和bootloader下productId是不一样的,我们可以通过这个区分adb模式和fastboot模式。

  2. 通过serialNumber来唯一标识设备。由于我的刷机工具支持同时对多台手机刷机,通过vendorId和productId只能对应同一型号设备,如何唯一标识每个设备我使用了serialNumber,如果你有更好的数据可以唯一标识设备请记得告诉我。由于程序中针对设备的操作都是异步的,因此有了唯一标识我才能在接下来针对设备的一系列操作中准确地维护各个设备的刷机状态。

简单一个字『坑』才能形容我遇到这些坑的心情。

  1. USB设备插入和拔除的回调我们能做的事是不一样的。插入的回调中我们可以获取到设备描述之后通过 libusb_open 打开USB设备,从而获取到serialNumber,但是设备拔除之后的回调中 libusb_open 就没办法工作了,可是我们使用serialNumber作为设备唯一标识我们如何判断拔除的到底是哪个设备?目前我只能使用笨办法,维护一个插入设备的列表,拔除回调中遍历当前所有设备再比较得出哪个设备被拔除了。如果你有更好的方法请告诉我,我这个做法实在是不优雅!

  2. 由于刷机过程需要重启并且还会在正常启动和bootloader两种模式间切换,会触发多次插入和拔除的回调,因此程序中维护设备列表时不能在拔除事件发生时简单地从列表中移除,需要自行维护好设备的模式和状态。

  3. 没有深究过libusb源代码,看起来回调应该是工作在同一个线程中,但实际上回调可能被同时执行。在我的程序中出现过这样的情况,手机未开启adb插入电脑时 usb_arrived_callback 被执行,开启adb调试时 usb_left_callbackusb_arrived_callback 相继被执行,这下问题来了,由于设备移除时需要遍历当前所有设备,并且与我保存的列表对比才能知道哪个设备被移除,在执行 usb_left_callback 尚未结束的时候 usb_arrived_callback 就被调用了,这就导致了 usb_left_callback 迟于最后一次 usb_arrived_callback 执行结束,于是自己维护的设备状态不对了,调试这个问题简直让人崩溃。由于本次项目我使用的是QT,因此在回调中使用了QT的信号来触发,并且让信号排队处理,最终才把这个坑填上。
    connect(this, SIGNAL(usbArriveSignal(libusb_device*)), this, SLOT(addDevice(libusb_device*)), Qt::QueuedConnection); connect(this, SIGNAL(usbLeftSignal()), this, SLOT(setLeftDeviceModes()), Qt::QueuedConnection);

  4. 最严重的坑来了,libusb在windows上不支持hotplug。当我在Mac下一切准备就绪转到windows下准备编译发布的时候真的崩溃了,注册回调就失败了,对比了一下返回值在头文件中的定义才知道不支持,后来在github上才看到 关于这个问题的issue。看了一些网上关于windows平台上的USB插拔检测的文章,本次工具使用的是QML,发现基本上没有适合我的,目前在考虑使用libusbK解决windows平台上的问题,或许等我正式发布这个工具的时候libusb的新版本就解决了这个问题。

教训

  • 首次使用的第三方库或者新的技术架构一定要充分地测试关键技术点,不要等到了正式产品开发阶段才发现问题,这会导致整个产品技术架构的调整或者大大影响开发周期。

  • 跨平台技术一定要在产品关键技术点上在各个平台上测试通过再进行正式产品的开发。

作者:李代斌lidroid
链接:https://www.jianshu.com/p/e522fa5798d2
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

使用libusb检测USB设备插拔状态相关推荐

  1. java 监控usb端口插拔_监控USB设备插拔

    最近做了UKey加密中设计到USB设备. 因UKEy是用来加密和执行PC与项目间通信加密的介质.从作用范围来讲不是传统意义上U盘作为存储介质来使用.其实熟悉网银驱动DR应该了解.在网银系统安全上一个最 ...

  2. C#监视USB设备插拔动作

    参考:https://docs.microsoft.com/zh-cn/windows/desktop/DevIO/device-management-messages 1.编写USBMonitor处 ...

  3. QT在windows上检测USB端口插拔

    (使用版本为5.12.0) 需要的头文件有以下: #include <windows.h> #include <dbt.h> #include <devguid.h> ...

  4. C# 监听USB设备插拔动态(防多触发)

    一:通过winform窗体WndProc 局限于窗体程序中,不推荐使用 //事件代码 private const int WM_DEVICECHANGE = 0x219; //设备改变 private ...

  5. Android 监听USB设备插拔事件

    通过注册广播的方式监听USB设备 . 广播Action UsbManager.ACTION_USB_DEVICE_ATTACHED Usb设备插入或者挂载事件通知 UsbManager.ACTION_ ...

  6. Lan8720 网线插拔状态检测

    目的:能够实时的检测网线的插拔状态.并能够根据网线的插拔状态通知到到应用层,让应用层做相应的处理. 1.解决问题的根本方法就是看lan8720的数据手册!!!! 如果说你用过一款芯片,而没有去研究过它 ...

  7. QT在Windows下检测USB设备热拔插的思路

    一.问题描述: 使用QT开发视频会议时需要实现实时检测USB摄像头/麦克风拔插的功能,这里主要涉及到对一些Windows API的了解以及windows系统的设备管理识别不同种设备时的原理,在实现过程 ...

  8. USB及手机平板设备插拔响应解决方案

    USB及手机平板设备插拔响应解决方案 (一).基本原理:WM_DEVICECHANGE消息响应 一般WM_DEVICECHANGE只发给顶层窗口.你可以自己创建一个隐藏的顶层窗口来接收这个消息. (二 ...

  9. shell 判断网线插拔_linux检测网线插拔状态

    Shell查看网线插拔状态: 使用ifconfig命令,如果含有"RUNNING",说明网线接入,否则就没有. 例: ifconfig ifconfig eth0 ifconfig ...

最新文章

  1. 关于debug.keystore文件用法以及错误处理
  2. Xshell通过SSH连接阿里云报错“服务器发送了一个意外的数据包” xshell连接ubuntu
  3. Latex中bib文件制作(参考文献制作)
  4. 深入理解JVM内存区域与内存分配
  5. zookeeper启动失败+jps中没有QuorumPeerMain
  6. JavaScript 里三个点 ... 的用法
  7. wstring和string简单正则表达式使用
  8. 自动化运维工具(ansible入门教程)
  9. 算术表达式:前缀表达式、中缀表达式、后缀表达式相互转换(手算法)
  10. python爬取裁判文书_使用selenium爬取裁判文书网
  11. b85主板装服务器系统,[U盘装系统]技嘉B85主板U盘装系统图文教程
  12. IT软件下载地址大全
  13. 3.JDBC基础代码书写
  14. 运放电压和电流负反馈的讨论
  15. “添翼杯”人工智能创新应用大赛垃圾分类之模型部分
  16. 数据结构(C语言版)——顺序栈(代码版)
  17. 外周传出神经的递质有,外周神经系统神经递质
  18. [旅游]300元走遍上海周边最美的古镇
  19. DotNetTextBox V3.0 所见即所得编辑器控件Ver3.2.7 Free(免费版)
  20. BitShares 2.0 多节点私链部署

热门文章

  1. wce实现hash注入
  2. 浅谈——从潜意识到创造性思维
  3. 阿里巴巴的资源库,下载快!
  4. linux驱动--多点触控协议(multi-touch-protocol)
  5. poj 3271 LilyPad
  6. java 加背景颜色_Java 给Word文档添加背景颜色
  7. 内容分发技术平台_内容分发平台及排名_编辑机器人|Giiso智搜
  8. 常用的Internet的即时通信(IM)软件-腾讯QQ官方版提供下载
  9. 黔西特产小吃大头菜,有叫麻辣脆
  10. 星宝云库浅谈:媒体人的日子;为什么不好过