如何检测Linux / Qt / C ++下的USB设备断开连接

我正在编写一个使用FTDI USB芯片与定制设备通信的系统(X-Platform Windows / Linux)。 我用他们的D2XX驱动程序打开/closures/读/写设备。 到现在为止还挺好。

我需要知道什么时候设备断开连接,所以程序可以正常响应。 目前,Windows下的应用程序收到突如其来的意外。 在Linux下,当设备断开时,存在严重故障。

我已经在Windows下find有关侦听WM_DEVICECHANGE消息的信息。 但是,我还没有find如何在Windows下检测到这个事件。 有设备驱动程序级别与内核进行交互的信息。 但是,我不知道如何在应用程序级别执行此操作。 FTDI驱动程序不提供任何此类服务。

系统是用C ++编写的Qt框架。 设备驱动程序是FTDI的D2XX驱动程序。

任何人都可以指向正确的方向吗?

非常感谢! 朱迪

Solutions Collecting From Web of "如何检测Linux / Qt / C ++下的USB设备断开连接"

你可能会想使用HAL ( freedesktop.org的硬件抽象层)。

将来你可能会想使用DeviceKit 。 这是一个项目解决与HAL的许多问题。 但是它并没有被所有主要的发行版所采用(我想Fedora),所以你现在可能不想使用它。

编辑:正如Jeach所说,你也可以使用udev 。 我不会提出这个建议,因为它水平低得多,编程也很难,但是如果延迟非常重要,这可能是最好的选择。

尽管我要告诉你的东西不会直接回答你的问题,但可能会给你一个提示,说明你的下一步行动。

我使用'/etc/udev/rules.d/'中配置的udev规则来运行各种脚本。 当USB设备连接/断开连接时,我将运行一个脚本,将HUP信号发送到我的二进制文件。 由于我的要求可以处理一些滞后,这对我来说是完美的。

但我的观点是,也许有一个udev库,你可以链接到和注册事件编程(而不是脚本)。

希望它有帮助…祝你好运!

我最近有一个项目涉及通过FTDI芯片阅读。 我也尝试使用libftdi,但发现使用/ dev / ttyUSB *进行读写操作要简单得多。 这样,你可以使用QFile('/ dev / ttyUSB *')来写和读。 您也可以检查设备是否存在,并且不会出现段错误。 当然,这不是一个“平台独立”的方式。 要获得独立于平台的方法,您可以使用Qt的串行库。

你显然必须为不同的操作系统编写不同的实现,除非你想创建一个线程来连续运行:

FT_ListDevices(&numDevs, nullptr, FT_LIST_NUMBER_ONLY); 

并且如果numDevs与之前的检查相比改变了枚举设备。

如果你像我一样,不想在你的USB设备上进行这种连续轮询,那么你将不得不针对你的特定操作系统。

以下是FTDI的一些示例代码的链接: http : //www.ftdichip.com/Support/SoftwareExamples/CodeExamples/VC.htm

示例7显示了如何在Windows上检测USB插入和删除: http : //www.ftdichip.com/Support/Documents/AppNotes/AN_152_Detecting_USB_%20Device_Insertion_and_Removal.pdf

在Linux上,我可以亲自推荐使用udev。

此代码用于枚举设备:

 #include <sys/types.h> #include <dirent.h> #include <cstdlib> #include <libudev.h> #include <fcntl.h> struct udev *udev = udev_new(); if (!udev) { cout << "Can't create udev" <<endl; } struct udev_enumerate *enumerate = udev_enumerate_new(udev); udev_enumerate_add_match_subsystem(enumerate, "usb"); udev_enumerate_scan_devices(enumerate); struct udev_list_entry *dev_list_entry, *devices = udev_enumerate_get_list_entry(enumerate); struct udev_device *dev; udev_list_entry_foreach(dev_list_entry, devices) { const char *path; path = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(udev, path); if( udev_device_get_devnode(dev) != nullptr ){ string vendor = (std::string) udev_device_get_sysattr_value(dev, "idVendor"); string product = (std::string) udev_device_get_sysattr_value(dev, "idProduct"); string description = (std::string)udev_device_get_sysattr_value(dev, "product"); cout << vendor << product << description << endl; } udev_device_unref(dev); } udev_enumerate_unref(enumerate); 

这个代码我放在一个单独的线程,等待接收插入或删除事件

 struct udev_device *dev; struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev"); udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", NULL); udev_monitor_enable_receiving(mon); int fd = udev_monitor_get_fd(mon); int flags = fcntl(fd, F_GETFL, 0); if (flags == -1){ debugError("Can't get flags for fd"); } flags &= ~O_NONBLOCK; fcntl(fd, F_SETFL, flags); fd_set fds; FD_ZERO(&fds); FD_SET(fd, &fds); while( _running ){ cout << "waiting for udev" << endl; dev = udev_monitor_receive_device(mon); if (dev && udev_device_get_devnode(dev) != nullptr ) { string action = (std::string)udev_device_get_action(dev); if( action == "add" ){ cout << "do something with your device... " << endl; } else { string path = (std::string)udev_device_get_devnode(dev); for( auto device : _DevicesList ){ if( device.getPath() == path ){ _DevicesList.erase(iter); cout << "Erased Device from list" << endl; break; } } } udev_device_unref(dev); } } udev_monitor_unref(mon); 

当你复制/粘贴这段代码时,一些函数和变量显然是没有定义的。 我保留一个检测到的设备列表来检查路径和其他信息,如插入设备的位置ID。 我稍后需要通过FT_OPEN_BY_LOCATION将位置ID发送到FT_OpenEx。 要获取位置ID,我读了下列文件的内容:

 string getFileContent(string file ){ string content = ""; ifstream readfile( file ); if( readfile.is_open() ){ getline(readfile, content ); readfile.close(); } return content; } string usbdirectory = "/sys/bus/usb/devices"; string dev1content = getFileContent(usbdirectory+"/usb"+udev_device_get_sysattr_value(dev, "busnum" )+"/dev"); int dev1num = std::atoi(dev1content.substr(dev1content.find_first_of(":")+1).c_str()); string dev2content = (std::string)udev_device_get_sysattr_value(dev, "dev"); int dev2num = std::atoi(dev2content.substr(dev2content.find_first_of(":")+1).c_str()); int locationid = dev1num+dev2num+257; 

我不能保证locationid是正确的,但直到现在,它似乎仍然适用于我。

不要忘了你在这里有两个问题:

  • 检测设备插入/移除
  • 正确地终止你的申请。

第一个问题已经被Zifre提出。

但是第二个问题仍然存在:当你的Linux应用程序被删除时,你的Linux应用程序不应该被暂停,我认为这两个问题是无关的:如果在写入或读取系统调用中删除设备,那么这些系统调用将返回在收到任何通知之前发生错误,并且这不应该将您的应用程序段错误。

有效的点。 我认为这是FTDI D2XX库,当设备断开连接后,下一次调用时会创建segfault。 我会做一些测试和验证。 FTDI D2XX库的Linux版本似乎很容易出现段错误。 – 朱迪邓肯6月18日19:03

我也正在使用FTDI芯片的定制设备。 我发现jd2xx Linux驱动运行一个单独的pthread('readerThread',我认为它被称为)。 这个单独的线程似乎做了一些投票。 这个线程是产生段错误的原因,很可能是因为它假设读取时总是有两个FTDI状态字节可用。

FTDI jd2xx驱动在windows和linux上都是封闭的,所以我不能确定。 在查看堆栈和系统调用跟踪之后,这就是我所要做的。

我目前正在尝试在Linux中使用开源的libftdi来完成我们所需要的(它不会段错误)。 这两个库在linux中都使用libusb。 但是我遇到了一些问题,因为看起来jd2xx驱动执行一些奇怪的魔法(保持闭源的好理由,你不希望任何人获得魔法)。