我正在编写一个系统(X-Platform Windows / Linux),它使用FTDI USB芯片与自定义设备对话。我使用他们的D2XX驱动程序进行设备打开/关闭/读/写。到现在为止还挺好。
我需要知道设备何时断开连接,以便程序可以正常响应。目前,在Windows下,应用程序突然意外关闭。在Linux下,当设备断开连接时,会出现sgementation故障。
我在Windows下找到了关于侦听WM_DEVICECHANGE消息的信息。但是,我还没有找到如何在Windows下检测此事件。存在与内核交互的设备驱动程序级别的信息。但是,我无法弄清楚如何在应用程序级别执行此操作。 FTDI驱动程序不提供任何此类服务。
使用Qt框架和C ++编写系统。设备驱动程序是FTDI的D2XX驱动程序。
有人能指出我正确的方向吗?
非常感谢! 朱迪
答案 0 :(得分:3)
您可能希望使用HAL(freedesktop.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,我认为这两个问题是无关的:如果在写或读系统调用中删除设备,那么那些系统在收到任何通知之前,调用将返回错误,这不应该使您的应用程序出现段错误。