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

后端 未结 5 1209
孤街浪徒
孤街浪徒 2021-01-03 03:37

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

相关标签:
5条回答
  • 2021-01-03 03:55

    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.

    0 讨论(0)
  • 2021-01-03 03:57

    Don't forget that you have two problems here :

    • Detecting device insertion / removal
    • Properly terminating your application.

    The first problem has been adressed by Zifre.

    But the second problem remains : your Linux app should not be segfaulting when the device is removed, and I think the two problems are unrelated : if the device is removed in the middle of a write or read system call, then those system call will return with an error before you get any notification, and this should not segfault your app.

    0 讨论(0)
  • 2021-01-03 04:04

    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.

    0 讨论(0)
  • 2021-01-03 04:07

    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.

    0 讨论(0)
  • 2021-01-03 04:13

    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!

    0 讨论(0)
提交回复
热议问题