#pragma once #include "UsbContext.h" #include "UsbEndPoint.h" #include "LibusbSupportDriverChecker.h" namespace Incart::Usb { class UsbDispatcher { private: #ifdef WIN_COMPILER const uint8_t m_claimInterfaceNumber = 0; #else const uint8_t m_claimInterfaceNumber = 1; #endif UsbContext* m_usbContext; LibusbSupportDriverChecker m_libusbSupportDriverChecker; // в ОС Windows проверяет работаем ли мы именно с WinUSB драйвером libusb_context* m_context = nullptr; libusb_device* m_device = nullptr; libusb_device** m_devices = nullptr; // таблица устройств, найденных libusb QVector m_realDevices; // текущее множество устройств int m_adress = 0; // QVector m_endpoints; // bool m_hasClaimedInterface = false; public: UsbDispatcher(UsbContext* usbContext, QVector eps) : m_usbContext(usbContext) , m_endpoints(eps) { } ~UsbDispatcher() { freeTransfers(); deleteTransfers(); closeDevice(); deinitUsbLib(); } bool initUsbLib() { int result = libusb_init(&m_context); if (result != 0) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " init error: " + std::to_string(result)); m_context = nullptr; return false; } libusb_set_option(m_context, LIBUSB_OPTION_LOG_LEVEL, 3); // Set debugging output to max level if (m_context == nullptr) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " m_context == nullptr"); return false; } m_usbContext->logInfo(std::string(__PRETTY_FUNCTION__) + " context ok"); return true; } void deinitUsbLib() { freeRealDevicesList(); if (m_context != nullptr) { libusb_exit(m_context); m_context = nullptr; m_usbContext->logInfo(std::string(__PRETTY_FUNCTION__) + " ok"); } else { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " m_context == nullptr"); } } bool receiveLibusbVersion() { const struct libusb_version *version; version = libusb_get_version(); m_usbContext->logInfo(std::string(__PRETTY_FUNCTION__) + " version: " + std::to_string(version->major) + " " + std::to_string(version->micro) + " " + std::to_string(version->nano)); return true; } //------------------------------------- bool openDevice() { m_usbContext->setLastTransferStatus(LIBUSB_TRANSFER_NO_DEVICE); int result; libusb_device* device = getFirstDevice(); if (device == nullptr) { return false; } libusb_device_descriptor descriptor; result = libusb_get_device_descriptor(device, &descriptor); if (result < 0) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_get_device_descriptor error: " + std::to_string(result) + " " + libusb_strerror(result)); return false; } //*m_log << "libusb_get_device_descriptor ok" << descriptor.idVendor << descriptor.idProduct << '\n'; // Проверяем работаем ли мы c драйвером совместимым с libusb (актуально для Windows ОС - должен использоваться WinUsb, а, например, не libusb0) if (!m_libusbSupportDriverChecker.isLibusbSupported(descriptor.idVendor, descriptor.idProduct)) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " is not libusb supported driver!"); return false; } libusb_device_handle* deviceHandle = nullptr; // = m_usb.getDeviceHandle(); result = libusb_open(device, &deviceHandle); if (result != 0) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_open error: " + std::to_string(result) + " " + libusb_strerror(result)); return false; } m_device = device; // сохраняем указатель подключенного устройства m_usbContext->setDeviceHandle(deviceHandle); if (m_usbContext->getDeviceHandle() == nullptr) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " m_deviceHandle == nullptr"); return false; } result = libusb_reset_device(m_usbContext->getDeviceHandle()); if (result != 0) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_reset_device error: " + std::to_string(result) + " " + libusb_strerror(result)); return false; } //QThread::msleep(2); // эмпирическая задержка //получение строкового дескриптора quint8 string_descriptor[100]; result = libusb_get_string_descriptor_ascii(m_usbContext->getDeviceHandle(), descriptor.iProduct, string_descriptor, sizeof(string_descriptor)); std::cout << __PRETTY_FUNCTION__ << " descriptor :" << (char*)string_descriptor << std::endl; if (result >= 0) { // это длина string_descriptor[result] = 0; } else { // это код ошибки m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_get_string_descriptor_ascii error :" + std::to_string(result) + " " + libusb_strerror(result)); return false; } //QThread::msleep(2); // эмпирическая задержка // Если интерфейс занят ядром системы, освобождаем его // данный API может не поддерживаться системой (например, в Windows), в таком случае пропускаем эту проверку result = libusb_kernel_driver_active(m_usbContext->getDeviceHandle(), m_claimInterfaceNumber); if (result == 1) { std::cout << __PRETTY_FUNCTION__ << "Kernel Driver Active" << std::endl; result = libusb_detach_kernel_driver(m_usbContext->getDeviceHandle(), m_claimInterfaceNumber); if (result != LIBUSB_SUCCESS) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_detach_kernel_driver error: " + std::to_string(result) + " " + libusb_strerror(result)); return false; } } else if ((result != 0) && (result != LIBUSB_ERROR_NOT_SUPPORTED)) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_kernel_driver_active error: " + std::to_string(result) + " " + libusb_strerror(result)); return false; } // claim interface std::cout << __PRETTY_FUNCTION__ << " libusb_claim_interface " << std::endl; result = libusb_claim_interface(m_usbContext->getDeviceHandle(), m_claimInterfaceNumber); // interface_number - для разных ОС отличается (1 для Linux) if (result < 0) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " claiming interface error: " + std::to_string(result) + " " + libusb_strerror(result)); return false; } m_hasClaimedInterface = true; //QThread::msleep(2); // эмпирическая задержка m_adress = libusb_get_device_address(device); //QThread::msleep(2); // эмпирическая задержка //*m_log << "OpenDevice ok" << string_descriptor << adress << '\n'; m_usbContext->setLastTransferStatus(LIBUSB_TRANSFER_COMPLETED); //m_transactionExecuter.SetDeviceHandler(m_deviceHandle); return true; } bool closeDevice() { m_usbContext->setLastTransferStatus(LIBUSB_TRANSFER_NO_DEVICE); libusb_device* device = m_device; m_device = nullptr; if (device != nullptr) { if (m_usbContext->getDeviceHandle() != nullptr) { if (m_hasClaimedInterface) { m_hasClaimedInterface = false; int result = libusb_release_interface(m_usbContext->getDeviceHandle(), m_claimInterfaceNumber); if (result < 0) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_release_interface error: " + std::to_string(result) + " " + libusb_strerror(result)); return false; } } libusb_close(m_usbContext->getDeviceHandle()); return true; } else { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_device_handle* deviceHandle == nullptr"); return false; } } else { //m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " libusb_device* device == nullptr"); return false; } } //------------------------------------- private: void freeRealDevicesList() { // необходимо вызывать libusb_free_device_list для освобождения памяти только // после окончания работы с прибором (libusb_close(m_deviceHandle);) иначе память не освободится и будут утечки памяти if (m_devices != nullptr) { libusb_free_device_list(m_devices, 0); m_devices = nullptr; } } QVector& createRealDevicesList() // опрос подключенных usb-устройств { m_realDevices.clear(); // готовим текущее множество устройств // необходимо вызывать libusb_free_device_list для освобождения памяти только // после окончания работы с прибором (libusb_close(m_deviceHandle);) иначе память не освободится и будут утечки памяти freeRealDevicesList(); ssize_t size = libusb_get_device_list( m_context, &m_devices); for (ssize_t i = 0; i < size; i++) { // получаем дескриптор libusb_device_descriptor desc; //дексриптор int ret = libusb_get_device_descriptor(m_devices[i], &desc); if (ret < 0) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " failed to get device descriptor number " + std::to_string(i)); continue; } // проверка уст-ва на нужный PID VID if (desc.idVendor == VENDOR_ID && desc.idProduct == PRODUCT_ID) { m_realDevices << m_devices[i]; // добавляем наше устройство в текущее множество //*m_log << "detect device descriptor number " << i << '\n'; } } //*m_log << "detect devices :" << m_realDevices.size() << '\n'; return m_realDevices; } libusb_device* getFirstDevice() { QVector& realDevices = createRealDevicesList(); //*m_log << "detect devices :" << m_realDevices.size() << '\n'; if(realDevices.size() <= 0) { //*m_log << "m_realDevices.size() == 0" << '\n'; return nullptr; } return realDevices[0]; } public: bool testConnect() { QVector& realDevices = createRealDevicesList(); return std::find(realDevices.begin(), realDevices.end(), m_device) != realDevices.end(); } //------------------------------------- public: void handleUsbEvents() { static struct timeval timeout = {0,1000}; // 1ms static int isLastTransfer = 0; int result = libusb_handle_events_timeout_completed(m_context,&timeout,&isLastTransfer); if ((result != 0) || (isLastTransfer != 0)) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + " error: " + std::to_string(isLastTransfer) + " " + std::to_string(result) + " " + libusb_strerror(result)); m_usbContext->setLastTransferStatus(result == 0 ? LIBUSB_ERROR_BUSY : result); } } bool allocTransfers() { // выделяем 1 раз - иначе не работает bool res = true; for(UsbEndPoint* ep : m_endpoints) { res &= ep->allocTransfer(); } return res; } void deleteTransfers() { // и удаляем 1 раз for(UsbEndPoint* ep : m_endpoints) { ep->deleteTransfer(); } } // ждем завершения транзакции не более фиксированного времени bool freeTransfers() { bool isFinished = false; const int attemptsCount = 1000; for(int i = 0; !isFinished && (i < attemptsCount); i++) { handleUsbEvents(); isFinished = true; for (auto it = m_endpoints.begin(); isFinished && (it != m_endpoints.end()); it++) { if ((*it)->getIsTransferred()) { (*it)->cancelTransfer(); isFinished = false; } } } if (!isFinished) { isFinished = true; for (auto it = m_endpoints.begin(); it != m_endpoints.end(); it++) { if ((*it)->getIsTransferred()) { isFinished = false; (*it)->setIsTransferred(false); } } if (!isFinished) { m_usbContext->logError(std::string(__PRETTY_FUNCTION__) + "+++++ free_transfers() error"); } } return isFinished; } }; } // namespace Incart::Usb