Как обнаружить отключение USB-устройства под Linux / Qt / C ++ - PullRequest
5 голосов
/ 04 июня 2009

Я пишу систему (X-Platform Windows / Linux), которая взаимодействует с пользовательским устройством с использованием USB-чипа FTDI. Я использую их драйвер D2XX для открытия / закрытия / чтения / записи устройства. Все идет нормально.

Мне нужно знать, когда устройство отключено, чтобы программа могла ответить изящно. В настоящее время под Windows приложение неожиданно неожиданно закрывается. В Linux, когда устройство отключено, возникает ошибка сегментирования.

Я нашел информацию под Windows о прослушивании сообщения WM_DEVICECHANGE. Однако я не нашел, как обнаружить это событие под Windows. Есть информация для уровня драйвера устройства, взаимодействующего с ядром. Тем не менее, я не могу понять, как это сделать на уровне приложения. Драйвер FTDI не предлагает никакой такой услуги.

Система написана с использованием фреймворка Qt с C ++. Драйвер устройства является драйвером FTDI D2XX.

Кто-нибудь может указать мне правильное направление?

Большое спасибо заранее! Judy

Ответы [ 5 ]

3 голосов
/ 04 июня 2009

Возможно, вы захотите использовать HAL ( freedesktop.org Уровень аппаратной абстракции).

В будущем вы, вероятно, захотите использовать DeviceKit . Это проект, исправляющий многие проблемы с HAL. Хотя он еще не был принят всеми основными дистрибутивами (я думаю, только Fedora), так что вы, вероятно, не хотите использовать его прямо сейчас.

Редактировать: Как сказал Джич, вы также можете использовать udev . Я бы не советовал, поскольку это намного более низкий уровень и его сложнее программировать, но если задержка очень важна, то это может быть лучшим вариантом.

1 голос
/ 31 октября 2014

Очевидно, что вам нужно написать разные реализации для разных операционных систем, если вы не хотите создавать поток для непрерывной работы:

FT_ListDevices(&numDevs, nullptr, FT_LIST_NUMBER_ONLY);

и перечислить устройства, если numDevs изменился по сравнению с предыдущими проверками.

Если вы похожи на меня и не очень любите проводить такие непрерывные опросы на своих USB-устройствах, вам придется настроить свою операционную систему.

Вот ссылка на пример кода из FTDI: http://www.ftdichip.com/Support/SoftwareExamples/CodeExamples/VC.htm

Пример 7 показывает, как обнаружить USB-вставку и удаление в Windows: 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);

некоторые функции и переменные явно не определены при копировании / вставке этого кода. Я веду список обнаруженных устройств для проверки пути и другую информацию, например, идентификатор местоположения вставленного устройства. Позже мне нужен идентификатор местоположения для FT_OpenEx через FT_OPEN_BY_LOCATION. Чтобы получить идентификатор местоположения, я прочитал содержимое следующих файлов:

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 правильный, но мне показалось, что он работал до сих пор.

1 голос
/ 24 ноября 2011

Недавно у меня был проект, который включал чтение через чип FTDI. Я также попытался использовать libftdi, но обнаружил, что гораздо проще использовать / dev / ttyUSB * для чтения и записи. Таким образом, вы можете использовать QFile ('/ dev / ttyUSB *') для записи и чтения. Вы также можете проверить, существует ли устройство на самом деле, и оно не будет отключено. Конечно, это не очень «независимый от платформы» способ. Чтобы получить независимый от платформы метод, вы можете использовать последовательную библиотеку для Qt.

1 голос
/ 04 июня 2009

Хотя то, что я собираюсь вам сказать, не даст прямого ответа на ваш вопрос, оно может дать вам подсказку относительно вашего следующего шага.

Я использую правила udev, настроенные в /etc/udev/rules.d/, которые запускают различные сценарии. Когда USB-устройство подключается / отключается, я запускаю скрипт, который отправляет сигнал HUP в мой двоичный файл. Поскольку мои требования могут справиться с небольшим отставанием, для меня это прекрасно работает.

Но я хочу сказать, что, возможно, существует библиотека udev, на которую можно ссылаться и регистрировать события программно (вместо сценариев).

Надеюсь, это поможет ... удачи!

0 голосов
/ 04 июня 2009

Не забывайте, что у вас есть две проблемы:

  • Обнаружение устройства вставки / удаления
  • Правильно завершает вашу заявку.

Первая проблема была решена Zifre.

Но вторая проблема остается: ваше Linux-приложение не должно иметь сегфагов при удалении устройства, и я думаю, что две проблемы не связаны: если устройство удаляется в середине системного вызова записи или чтения, то эти вызов будет возвращен с ошибкой, прежде чем вы получите какое-либо уведомление, и это не должно вызывать ошибки в вашем приложении.

...