diff --git a/src/core/UsbHidDevice.cpp b/src/core/UsbHidDevice.cpp index 34921e7..e04a41b 100644 --- a/src/core/UsbHidDevice.cpp +++ b/src/core/UsbHidDevice.cpp @@ -114,19 +114,45 @@ int UsbHidDevice::write_device(unsigned char* buf, int length) int UsbHidDevice::connect() { - device_handle = hid_open(vid, pid, NULL); - if (!device_handle) { - Logger(TLogLevel::logERROR) << "error opening hid device vid=" << vid << " pid=" << pid << " reason=" << hidapi_error(NULL) << std::endl; - return EXIT_FAILURE; - } - ref_count++; + struct hid_device_info *dev_info = hid_enumerate(vid, pid); + if (!dev_info) { + Logger(TLogLevel::logERROR) << "error enumerating hid device with vid=" << vid << " pid=" << pid << " reason=" << hidapi_error(NULL) << std::endl; + hid_free_enumeration(dev_info); + return EXIT_FAILURE; + } - if (hid_set_nonblocking(device_handle, 1) == -1) { - Logger(TLogLevel::logERROR) << "error in hid_set_nonblocking vid=" << vid << " pid=" << pid << " reason=" << hidapi_error(device_handle) << std::endl; - return EXIT_FAILURE; - } + device_handle = hid_open_path(dev_info->path); + if (!device_handle) { + Logger(TLogLevel::logERROR) << "error opening hid device vid=" << vid << " pid=" << pid << " reason=" << hidapi_error(NULL) << std::endl; + hid_free_enumeration(dev_info); + return EXIT_FAILURE; + } + + if (hid_set_nonblocking(device_handle, 1) == -1) { + Logger(TLogLevel::logERROR) << "error in hid_set_nonblocking vid=" << vid << " pid=" << pid << " reason=" << hidapi_error(device_handle) << std::endl; + hid_free_enumeration(dev_info); + return EXIT_FAILURE; + } + + ref_count++; + + if (dev_info->next) { + Logger(TLogLevel::logWARNING) << "found more than one device with vid=" << vid << " pid=" << pid << " Only the first device is used now" << std::endl; + } + + Logger(TLogLevel::logDEBUG) << "device opened: vid=" << vid << " pid=" << pid << std::endl; + + hid_free_enumeration(dev_info); + + return EXIT_SUCCESS; +} + +int UsbHidDevice::connect(hid_device* _device_handle) +{ + ref_count++; + device_handle = _device_handle; + Logger(TLogLevel::logDEBUG) << "device connect: vid=" << vid << " pid=" << pid << std::endl; - Logger(TLogLevel::logDEBUG) << "UsbHidDevice connect successful. vid=" << vid << " pid=" << pid << std::endl; return EXIT_SUCCESS; } diff --git a/src/core/UsbHidDevice.h b/src/core/UsbHidDevice.h index f409d58..bce2fdf 100644 --- a/src/core/UsbHidDevice.h +++ b/src/core/UsbHidDevice.h @@ -31,6 +31,7 @@ class UsbHidDevice : public Device unsigned short vid = 0; unsigned short pid = 0; int connect(); + int connect(hid_device* _device_handle); virtual void start(); virtual void stop(int time_out); void release(); diff --git a/src/core/XPanel.cpp b/src/core/XPanel.cpp index 7382d3e..9833dd0 100644 --- a/src/core/XPanel.cpp +++ b/src/core/XPanel.cpp @@ -127,29 +127,29 @@ PLUGIN_API int XPluginEnable(void) float flight_loop_callback(float, float, int, void*) { - for (const auto &it : config.device_configs) + for (const auto& it : config.device_configs) { // check and set LED states - for (const auto &triggers : it.light_triggers) + for (const auto& triggers : it.light_triggers) { - for (auto &trigger : triggers.second) + for (auto& trigger : triggers.second) { trigger->evaluate_and_store_action(); } } // check and set 7 segment display states - for (const auto &display : it.multi_displays) + for (const auto& display : it.multi_displays) { display.second->evaluate_and_store_dataref_value(); } - for (const auto &display : it.generic_displays) + for (const auto& display : it.generic_displays) { display.second->evaluate_and_store_dataref_value(); } // update the FIP devices - for (auto &screen : it.fip_screens) + for (auto& screen : it.fip_screens) { screen.second->evaluate_and_store_screen_action(); } @@ -183,7 +183,7 @@ void stop_and_clear_xpanel_plugin() XPLMUnregisterFlightLoopCallback(flight_loop_callback, NULL); LuaHelper::get_instace()->close(); - for (auto &dev : devices) + for (auto& dev : devices) { if (dev != NULL) { @@ -226,6 +226,39 @@ extern std::filesystem::path get_plugin_path() return plugin_path; } +template +int enumerate_and_add_hid_devices(DeviceConfiguration& it) +{ + Device* device; + struct hid_device_info* dev_info; + struct hid_device_info* dev_info_first; + + dev_info = hid_enumerate(it.vid, it.pid); + if (!dev_info) { + Logger(TLogLevel::logERROR) << "error enumerating hid device with vid=" << it.vid << " pid=" << it.pid << std::endl; + hid_free_enumeration(dev_info); + return EXIT_FAILURE; + } + dev_info_first = dev_info; + do + { + Logger(TLogLevel::logDEBUG) << "add new panel device. vid =" << it.vid << " pid = " << it.pid << std::endl; + device = new T(it); + devices.push_back(device); + + hid_device* hid_dev = hid_open_path(dev_info->path); + hid_set_nonblocking(hid_dev, 1); + ((T*)device)->connect(hid_dev); + device->start(); + device->thread_handle = new std::thread(&T::thread_func, (T*)device); + LuaHelper::get_instace()->register_hid_device((UsbHidDevice*)device); + dev_info = dev_info->next; + } while (dev_info); + hid_free_enumeration(dev_info_first); + + return EXIT_SUCCESS; +} + int init_and_start_xpanel_plugin(void) { char aircraft_file_name[256]; @@ -277,7 +310,7 @@ int init_and_start_xpanel_plugin(void) Device* device; - for (auto &it : config.device_configs) + for (auto& it : config.device_configs) { switch (it.device_type) { case DeviceType::SAITEK_MULTI: @@ -287,12 +320,7 @@ int init_and_start_xpanel_plugin(void) if (it.pid == 0) it.pid = 0x0d06; Logger(TLogLevel::logDEBUG) << "add new saitek multi panel device" << std::endl; - device = new SaitekMultiPanel(it); - devices.push_back(device); - device->connect(); - device->start(); - device->thread_handle = new std::thread(&SaitekMultiPanel::thread_func, (SaitekMultiPanel*)device); - LuaHelper::get_instace()->register_hid_device((UsbHidDevice*)device); + enumerate_and_add_hid_devices(it); break; case DeviceType::HOME_COCKPIT: // set default vid & pid if it's not set in config file @@ -300,13 +328,8 @@ int init_and_start_xpanel_plugin(void) it.vid = 0x2341; if (it.pid == 0) it.pid = 0x8036; - Logger(TLogLevel::logDEBUG) << "add new homecockpit device" << std::endl; - device = new ArduinoHomeCockpit(it); - devices.push_back(device); - device->connect(); - device->start(); - device->thread_handle = new std::thread(&ArduinoHomeCockpit::thread_func, (ArduinoHomeCockpit*)device); - LuaHelper::get_instace()->register_hid_device((UsbHidDevice*)device); + Logger(TLogLevel::logDEBUG) << "add new homecockpit devices" << std::endl; + enumerate_and_add_hid_devices(it); break; case DeviceType::SAITEK_RADIO: // set default vid & pid if it's not set in config file @@ -314,13 +337,8 @@ int init_and_start_xpanel_plugin(void) it.vid = 0x06a3; if (it.pid == 0) it.pid = 0x0d05; - Logger(TLogLevel::logDEBUG) << "add new saitek radio panel device" << std::endl; - device = new SaitekRadioPanel(it); - devices.push_back(device); - device->connect(); - device->start(); - device->thread_handle = new std::thread(&SaitekRadioPanel::thread_func, (SaitekRadioPanel*)device); - LuaHelper::get_instace()->register_hid_device((UsbHidDevice*)device); + Logger(TLogLevel::logDEBUG) << "add new saitek radio devices" << std::endl; + enumerate_and_add_hid_devices(it); break; case DeviceType::SAITEK_SWITCH: // set default vid & pid if it's not set in config file @@ -328,13 +346,8 @@ int init_and_start_xpanel_plugin(void) it.vid = 0x06a3; if (it.pid == 0) it.pid = 0x0d67; - Logger(TLogLevel::logDEBUG) << "add new saitek switch panel device" << std::endl; - device = new SaitekSwitchPanel(it); - devices.push_back(device); - device->connect(); - device->start(); - device->thread_handle = new std::thread(&SaitekSwitchPanel::thread_func, (SaitekSwitchPanel*)device); - LuaHelper::get_instace()->register_hid_device((UsbHidDevice*)device); + Logger(TLogLevel::logDEBUG) << "add new saitek switch panel devices" << std::endl; + enumerate_and_add_hid_devices(it); break; case DeviceType::LOGITECH_FIP: Logger(TLogLevel::logDEBUG) << "add new FIP device" << std::endl; @@ -345,22 +358,12 @@ int init_and_start_xpanel_plugin(void) device->thread_handle = new std::thread(&FIPDevice::thread_func, (FIPDevice*)device); break; case DeviceType::TRC1000_PFD: - Logger(TLogLevel::logDEBUG) << "add new TRC1000 PFD device" << std::endl; - device = new TRC1000PFD(it); - devices.push_back(device); - device->connect(); - device->start(); - device->thread_handle = new std::thread(&TRC1000PFD::thread_func, (TRC1000PFD*)device); - LuaHelper::get_instace()->register_hid_device((UsbHidDevice*)device); + Logger(TLogLevel::logDEBUG) << "add new TRC1000 PFD devices" << std::endl; + enumerate_and_add_hid_devices(it); break; case DeviceType::TRC1000_AUDIO: - Logger(TLogLevel::logDEBUG) << "add new TRC1000 Audio device" << std::endl; - device = new TRC1000Audio(it); - devices.push_back(device); - device->connect(); - device->start(); - device->thread_handle = new std::thread(&TRC1000Audio::thread_func, (TRC1000Audio*)device); - LuaHelper::get_instace()->register_hid_device((UsbHidDevice*)device); + Logger(TLogLevel::logDEBUG) << "add new TRC1000 Audio devices" << std::endl; + enumerate_and_add_hid_devices(it); break; default: Logger(TLogLevel::logERROR) << "unknown device type" << std::endl; @@ -382,7 +385,7 @@ PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void*) switch (inMsg) { case XPLM_MSG_AIRPORT_LOADED: Logger(TLogLevel::logTRACE) << "XPLM_MSG_AIRPORT_LOADED: message received" << std::endl; - + if (plugin_already_initialized) stop_and_clear_xpanel_plugin(); diff --git a/src/devices/arduino-homecockpit/ArduinoHomeCockpit.cpp b/src/devices/arduino-homecockpit/ArduinoHomeCockpit.cpp index 95f5a35..d6c1c32 100644 --- a/src/devices/arduino-homecockpit/ArduinoHomeCockpit.cpp +++ b/src/devices/arduino-homecockpit/ArduinoHomeCockpit.cpp @@ -148,6 +148,27 @@ int ArduinoHomeCockpit::read_board_configuration(std::string file_name, unsigned return exit_status; } +int ArduinoHomeCockpit::connect(hid_device* _device_handle) +{ + if (_device_handle == NULL) + { + if (UsbHidDevice::connect() != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "Arduin Home Cockpit connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + else + { + if (UsbHidDevice::connect(_device_handle) != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "Arduin Home Cockpit connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + int ArduinoHomeCockpit::connect() { return UsbHidDevice::connect(); diff --git a/src/devices/arduino-homecockpit/ArduinoHomeCockpit.h b/src/devices/arduino-homecockpit/ArduinoHomeCockpit.h index 1e04dfa..dfa5869 100644 --- a/src/devices/arduino-homecockpit/ArduinoHomeCockpit.h +++ b/src/devices/arduino-homecockpit/ArduinoHomeCockpit.h @@ -24,6 +24,7 @@ class ArduinoHomeCockpit :public UsbHidDevice const std::string TOKEN_DISPLAY = "display:id=\"([a-zA-Z0-9_-]+)\",width=([0-9]+)"; public: ArduinoHomeCockpit(DeviceConfiguration& config); + int connect(hid_device* _device_handle); int connect(); void start(); void stop(int timeout); diff --git a/src/devices/saitek-multi/SaitekMultiPanel.cpp b/src/devices/saitek-multi/SaitekMultiPanel.cpp index dcdb002..b220318 100644 --- a/src/devices/saitek-multi/SaitekMultiPanel.cpp +++ b/src/devices/saitek-multi/SaitekMultiPanel.cpp @@ -67,6 +67,27 @@ SaitekMultiPanel::SaitekMultiPanel(DeviceConfiguration& config) :UsbHidDevice(co } } +int SaitekMultiPanel::connect(hid_device* _device_handle) +{ + if (_device_handle == NULL) + { + if (UsbHidDevice::connect() != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "SaitekMultiPanel connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + else + { + if (UsbHidDevice::connect(_device_handle) != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "SaitekMultiPanel connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + int SaitekMultiPanel::connect() { unsigned char buff[WRITE_BUFFER_SIZE]; diff --git a/src/devices/saitek-multi/SaitekMultiPanel.h b/src/devices/saitek-multi/SaitekMultiPanel.h index 9df2017..edcbbab 100644 --- a/src/devices/saitek-multi/SaitekMultiPanel.h +++ b/src/devices/saitek-multi/SaitekMultiPanel.h @@ -19,6 +19,7 @@ class SaitekMultiPanel : public UsbHidDevice std::vector multi_displays; public: SaitekMultiPanel(DeviceConfiguration &config); + int connect(hid_device* _device_handle); int connect(); void start(); void stop(int timeout); diff --git a/src/devices/saitek-radio/SaitekRadioPanel.cpp b/src/devices/saitek-radio/SaitekRadioPanel.cpp index b807b35..36424d9 100644 --- a/src/devices/saitek-radio/SaitekRadioPanel.cpp +++ b/src/devices/saitek-radio/SaitekRadioPanel.cpp @@ -64,10 +64,26 @@ SaitekRadioPanel::SaitekRadioPanel(DeviceConfiguration& config) :UsbHidDevice(co int SaitekRadioPanel::connect() { - if (UsbHidDevice::connect() != EXIT_SUCCESS) + return connect(NULL); +} + +int SaitekRadioPanel::connect(hid_device* _device_handle) +{ + if (_device_handle == NULL) { - Logger(TLogLevel::logERROR) << "SaitekRadioPanel connect. Error during connect" << std::endl; - return EXIT_FAILURE; + if (UsbHidDevice::connect() != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "SaitekRadioPanel connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + else + { + if (UsbHidDevice::connect(_device_handle) != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "SaitekRadioPanel connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } } read_device(read_buffer, read_buffer_size); diff --git a/src/devices/saitek-radio/SaitekRadioPanel.h b/src/devices/saitek-radio/SaitekRadioPanel.h index a5f6bc8..f530bd3 100644 --- a/src/devices/saitek-radio/SaitekRadioPanel.h +++ b/src/devices/saitek-radio/SaitekRadioPanel.h @@ -19,6 +19,7 @@ class SaitekRadioPanel : public UsbHidDevice public: SaitekRadioPanel(DeviceConfiguration& config); int connect(); + int connect(hid_device* _device_handle); void start(); void stop(int timeout); }; diff --git a/src/devices/saitek-switch/SaitekSwitchPanel.cpp b/src/devices/saitek-switch/SaitekSwitchPanel.cpp index a6826f1..2e720b8 100644 --- a/src/devices/saitek-switch/SaitekSwitchPanel.cpp +++ b/src/devices/saitek-switch/SaitekSwitchPanel.cpp @@ -53,6 +53,27 @@ SaitekSwitchPanel::SaitekSwitchPanel(DeviceConfiguration& config) :UsbHidDevice( register_lights(switch_lights); } +int SaitekSwitchPanel::connect(hid_device* _device_handle) +{ + if (_device_handle == NULL) + { + if (UsbHidDevice::connect() != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "SaitekSwitchPanel connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + else + { + if (UsbHidDevice::connect(_device_handle) != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "SaitekSwitchPanel connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + int SaitekSwitchPanel::connect() { unsigned char buff[WRITE_BUFFER_SIZE]; diff --git a/src/devices/saitek-switch/SaitekSwitchPanel.h b/src/devices/saitek-switch/SaitekSwitchPanel.h index 3af8ce4..f64dcff 100644 --- a/src/devices/saitek-switch/SaitekSwitchPanel.h +++ b/src/devices/saitek-switch/SaitekSwitchPanel.h @@ -17,6 +17,7 @@ class SaitekSwitchPanel : public UsbHidDevice std::vector switch_lights; public: SaitekSwitchPanel(DeviceConfiguration& config); + int connect(hid_device* _device_handle); int connect(); void start(); void stop(int timeout); diff --git a/src/devices/trc-1000/TRC1000.cpp b/src/devices/trc-1000/TRC1000.cpp index 82602b3..e94dc88 100644 --- a/src/devices/trc-1000/TRC1000.cpp +++ b/src/devices/trc-1000/TRC1000.cpp @@ -121,6 +121,27 @@ void TRC1000::thread_func() Logger(TLogLevel::logDEBUG) << "TRC1000 thread_func: exit vid=" << vid << " pid=" << pid << std::endl; } +int TRC1000::connect(hid_device* _device_handle) +{ + if (_device_handle == NULL) + { + if (UsbHidDevice::connect() != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "TRC1000 connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + else + { + if (UsbHidDevice::connect(_device_handle) != EXIT_SUCCESS) + { + Logger(TLogLevel::logERROR) << "TRC1000 connect. Error during connect" << std::endl; + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + int TRC1000::connect() { if (UsbHidDevice::connect() != EXIT_SUCCESS) diff --git a/src/devices/trc-1000/TRC1000.h b/src/devices/trc-1000/TRC1000.h index 3ada98c..87460da 100644 --- a/src/devices/trc-1000/TRC1000.h +++ b/src/devices/trc-1000/TRC1000.h @@ -34,6 +34,7 @@ class TRC1000 : public UsbHidDevice TRC1000(DeviceConfiguration& config, int _read_buffer_size, int _write_buffer_size); ~TRC1000(); virtual void thread_func(); + int connect(hid_device* _device_handle); int connect(); void start(); void stop(int timeout); diff --git a/test/mock_hid_api.cpp b/test/mock_hid_api.cpp index a6f8cd3..392e870 100644 --- a/test/mock_hid_api.cpp +++ b/test/mock_hid_api.cpp @@ -5,8 +5,10 @@ */ #pragma once -#include"hidapi.h" +#include "hidapi.h" #include +#include +#include int device = 123; int vid = 0; @@ -19,6 +21,38 @@ extern int HID_API_EXPORT HID_API_CALL hid_init() return 0; } +extern struct hid_device_info HID_API_EXPORT* HID_API_CALL hid_enumerate(unsigned short vid, unsigned short pid) +{ + hid_device_info* dev_info = new hid_device_info(); + dev_info->next = NULL; + + dev_info->product_id = pid; + dev_info->vendor_id = vid; + dev_info->manufacturer_string = L"NorbiTest"; + dev_info->serial_number = L"12345ABCD"; + + std::stringstream ss; + ss << vid << ' ' << pid; + dev_info->path = _strdup(ss.str().c_str()); + + return dev_info; +} + +extern HID_API_EXPORT hid_device* HID_API_CALL hid_open_path(const char* path) +{ + std::stringstream ss; + ss << path; + ss >> vid >> pid; + + hid_device_open = true; + return (hid_device*)&device; +} + +extern void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info* devs) +{ + free(devs); +} + extern HID_API_EXPORT hid_device* HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t* serial_number) { vid = vendor_id;