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

19,611

Solution 1

You'll probably want to use HAL (freedesktop.org's Hardware Abstraction Layer).

In the future you will probably want to use DeviceKit. It is a project fix the many problems with HAL. It hasn't been adopted by all major distros yet though (I think just Fedora), so you probably don't want to use it right now.

Edit: As Jeach said, you can use udev also. I wouldn't suggest this, as it is much lower level, and harder to program, but if latency is very important, this might be the best option.

Solution 2

I recently had a project which involved reading via an FTDI chip. I also tried using libftdi but found out that it is much simpler to use /dev/ttyUSB* for reading and writing. This way, you can use QFile('/dev/ttyUSB*') to write and read. You can also check if the device actually exists and it won't segfault. Of course, this is not a very 'Platform independent' way. To get a platform independent method, you can use a Serial library for Qt.

Solution 3

Although what I'm about to tell you won't directly answer your question, it may give you a hint as to your next move.

I use udev rules configured in '/etc/udev/rules.d/' which run various scripts. When a USB device gets connected/disconnected I run a script which sends a HUP signal to my binary. Since my requirements can handle a bit of lag it works perfectly fine for me.

But my point is that maybe there is a udev library you can link to and register events programmatically (instead of scripts).

Hope it helps... good luck!

Solution 4

You obviously have to write different implementations for the different operating systems unless you want to create a thread to continuously run:

FT_ListDevices(&numDevs, nullptr, FT_LIST_NUMBER_ONLY);

and enumerate the devices if numDevs changed compared to previous checks.

If you are like me and don't really like to do that sort of continuous polling on your USB devices then you will have to target your specific operating system.

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

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

On Linux I can personally recommend using udev.

This code is for enumerating the 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); 

This code I put in a separate thread that waits to receive an insertion or a removal 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 of the functions and variables are obviously not defined when you copy/paste this code. I keep a list of detected devices to check the path and other information like the location ID of the inserted device. I need the location ID 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 can't guarantee that the locationid is correct but it seemed to work for me until now.

Share:
19,611
Admin
Author by

Admin

Updated on June 04, 2022

Comments

  • Admin
    Admin almost 2 years

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

    I need to know when the device is disconnected so the program can respond gracefully. At present, under Windows the application receives a sudden unexpected close. Under Linux, when the device is disconnected, there is a sgementation fault.

    I have found informaiton under Windows about listening for the WM_DEVICECHANGE message. However, I have not found how to detect this event under Windows. There is information for the device driver level interacting with the kernel. However, I can't figure out how to do this at an application level. The FTDI driver does not offer any such service.

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

    Can anyone point me in the right direction?

    Thanks so much in advance! Judy