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

时间:2009-06-03 21:08:51

标签: linux usb disconnect

我正在编写一个系统(X-Platform Windows / Linux),它使用FTDI USB芯片与自定义设备对话。我使用他们的D2XX驱动程序进行设备打开/关闭/读/写。到现在为止还挺好。

我需要知道设备何时断开连接,以便程序可以正常响应。目前,在Windows下,应用程序突然意外关闭。在Linux下,当设备断开连接时,会出现sgementation故障。

我在Windows下找到了关于侦听WM_DEVICECHANGE消息的信息。但是,我还没有找到如何在Windows下检测此事件。存在与内核交互的设备驱动程序级别的信息。但是,我无法弄清楚如何在应用程序级别执行此操作。 FTDI驱动程序不提供任何此类服务。

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

有人能指出我正确的方向吗?

非常感谢! 朱迪

5 个答案:

答案 0 :(得分:3)

您可能希望使用HALfreedesktop.org的硬件抽象层)。

将来您可能希望使用DeviceKit。这个项目修复了HAL的许多问题。虽然它(但我认为只是Fedora)尚未被所有主要发行版采用,所以你可能现在不想使用它。

编辑:正如Jeach所说,你也可以使用udev。我不建议这样做,因为它的级别要低得多,编程也比较困难,但如果延迟非常非常重要,那么这可能是最好的选择。

答案 1 :(得分:1)

虽然我要告诉你的内容不会直接回答你的问题,但它可能会暗示你的下一步行动。

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

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

希望它有所帮助......祝你好运!

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

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是正确的,但它似乎对我有效,直到现在。

答案 4 :(得分:0)

不要忘记你有两个问题:

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

Zifre已经解决了第一个问题。

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