5

我正在编写一个系统(X-Platform Windows/Linux),它使用 FTDI USB 芯片与自定义设备通信。我使用他们的 D2XX 驱动程序来打开/关闭/读取/写入设备。到目前为止,一切都很好。

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

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

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

谁能指出我正确的方向?

提前非常感谢!朱迪

4

5 回答 5

3

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

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

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

于 2009-06-03T21:21:25.873 回答
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 是正确的,但直到现在它似乎对我有用。

于 2014-10-31T19:39:19.443 回答
1

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

于 2011-11-24T19:44:07.930 回答
1

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

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

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

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

于 2009-06-03T21:38:00.630 回答
0

不要忘记这里有两个问题:

  • 检测设备插入/移除
  • 正确终止您的应用程序。

Zifre 解决了第一个问题。

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

于 2009-06-04T12:12:56.530 回答