How to detect USB device disconnection under Linux / Qt / C ++

I am writing a system (X-Platform Windows / Linux) that talks to a user device using a FTDI USB chip. I use their D2XX driver to open / close / read / write devices. So far, so good.

I need to know when the device is turned off, so the program can respond gracefully. Currently, on Windows, an application unexpectedly closes unexpectedly. On Linux, when the device is disconnected, an sgementation error occurs.

I found informaiton on Windows about listening to the WM_DEVICECHANGE message. However, I did not find how to detect this event under Windows. There is information for the device driver level that interacts with the kernel. However, I cannot figure out how to do this at the application level. The FTDI driver does not offer such a service.

The system is written using the Qt framework with C ++. The device driver is the FTDI D2XX driver.

Can someone point me in the right direction?

Thanks a lot! Judy

+4
source share
5 answers

You probably want to use the HAL ( freedesktop.org Hardware Abstraction Layer.)

In the future, you will probably want to use DeviceKit . This is a project that fixes many problems with HAL. However, it has not been accepted by all major distributions (although I think it's just Fedora), so you probably don't want to use it right now.

Edit: As Jeh said, you can use udev . I would not suggest this, since it is much lower and more difficult to program, but if latency is very important, this might be the best option.

+3
source

Although what I am going to tell you does not directly answer your question, it may give you a hint of your next step.

I use udev rules configured in '/etc/udev/rules.d/' which run various scripts. When the USB device plugs in / unplug, I run a script that sends a HUP signal to my binary. Since my requirements can cope with a slight lag, this works fine for me.

But I want to say that perhaps there is a udev library with which you can reference and log events programmatically (instead of scripts).

Hope this helps ... good luck!

+1
source

I recently had a project that included reading through an FTDI chip. I also tried using libftdi, but found that it is much easier to use / dev / ttyUSB * for reading and writing. This way you can use QFile ('/ dev / ttyUSB *) for writing and reading. You can also check if the device really exists and it will not be executed. Of course, this is not a very "platform independent" way. To get a platform-independent method, you can use the serial library for Qt.

+1
source

You will obviously have to write different implementations for different operating systems if you do not want to create a thread for continuous launch:

FT_ListDevices(&numDevs, nullptr, FT_LIST_NUMBER_ONLY); 

and list devices if numDevs has changed from previous checks.

If you are like me and don’t really like doing such a continuous survey on your USB devices, you need to target your specific operating system.

Here is a link to some sample code from FTDI: http://www.ftdichip.com/Support/SoftwareExamples/CodeExamples/VC.htm

Example 7 shows how to determine the insertion and removal of USB in windows: http://www.ftdichip.com/Support/Documents/AppNotes/AN_152_Detecting_USB_%20Device_Insertion_and_Removal.pdf

On Linux, I personally recommend using udev.

This code is for listing devices:

 #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); 

I put this code in a separate thread, which is waiting for an insert or delete event

 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); 

some functions and variables are obviously not defined when copying / pasting this code. I keep a list of discovered devices for checking the path and other information, such as the location identifier of the inserted device. I need a location identifier later to FT_OpenEx via FT_OPEN_BY_LOCATION. To get the location id, I read the contents of the following files:

 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; 

I cannot guarantee the correct location, but it seems to have worked so far.

+1
source

Do not forget that you have two problems:

  • Device Discovery / Removal
  • The correct termination of your application.

The first issue was addressed by Zifre.

But the second problem remains: your Linux application should not be segfault when you delete the device, and I think the two problems are not connected: if the device is deleted in the middle of a system call for writing or reading, then this system call will return an error before you Receive a notification and this should not violate your application.

0
source

Source: https://habr.com/ru/post/891290/


All Articles