Как обнаружить отключение USB-устройства в Linux / Qt / C ++

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

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

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

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

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

Большое спасибо заранее! Джуди


person Community    schedule 03.06.2009    source источник


Ответы (5)


Вероятно, вы захотите использовать HAL (freedesktop.org).

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

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

person Zifre    schedule 03.06.2009
comment
Спасибо большое за Ваш ответ. Я посмотрю на HAL. Я хотел бы оставаться на максимально высоком уровне, чтобы задержка не была проблемой. Джуди - person ; 18.06.2009

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

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

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

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

person Jeach    schedule 03.06.2009
comment
Спасибо! Я покопаюсь в udev и библиотеках и посмотрю, смогу ли что-нибудь придумать. - person ; 18.06.2009

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

person Daniël Sonck    schedule 24.11.2011

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

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;

Я не могу гарантировать, что идентификатор местоположения правильный, но до сих пор мне казалось, что он работает.

person Alex    schedule 31.10.2014

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

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

К первой проблеме обратился Зифре.

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

person shodanex    schedule 04.06.2009
comment
Действительный пункт. Я думаю, что это библиотека FTDI D2XX, которая создает segfault при следующем вызове к ней после того, как устройство было отключено. Я проведу небольшое тестирование и проверю. Версия библиотеки FTDI D2XX для Linux, похоже, подвержена ошибкам. - person ; 18.06.2009