/*---------------------------------------------------------*\ | DetectionManager.cpp | | | | OpenRGB Detection Manager handles detecting supported | | devices and initializing their RGBControllers | | | | Adam Honse 08 Jan 2026 | | | | This file is part of the OpenRGB project | | SPDX-License-Identifier: GPL-2.0-or-later | \*---------------------------------------------------------*/ #include #include "DetectionManager.h" #include "LogManager.h" #include "pci_ids.h" #include "ProfileManager.h" #include "ResourceManager.h" #include "SettingsManager.h" #include "StringUtils.h" #ifdef __linux__ #include #endif using namespace std::chrono_literals; /*---------------------------------------------------------*\ | DetectionManager name for log entries | \*---------------------------------------------------------*/ const char* DETECTIONMANAGER = "DetectionManager"; /*---------------------------------------------------------*\ | Define a macro for QT lupdate to parse | \*---------------------------------------------------------*/ #define QT_TRANSLATE_NOOP(scope, x) x /*---------------------------------------------------------*\ | Warning Strings | \*---------------------------------------------------------*/ const char* I2C_ERR_WIN = QT_TRANSLATE_NOOP("DetectionManager", "

Some internal devices may not be detected:

" "

One or more I2C or SMBus interfaces failed to initialize.

" "

RGB DRAM modules, some motherboards' onboard RGB lighting, and RGB Graphics Cards, will not be available in OpenRGB without I2C or SMBus.

" "

How to fix this:

" "

On Windows, this is usually caused by a failure to load the PawnIO driver.

" "

You must first install PawnIO, then you must OpenRGB as administrator in order to access these devices.

" "

See help.openrgb.org for additional troubleshooting steps if you keep seeing this message.

" "

If you are not using internal RGB on a desktop this message is not important to you.

"); const char* I2C_ERR_LINUX = QT_TRANSLATE_NOOP("DetectionManager", "

Some internal devices may not be detected:

" "

One or more I2C or SMBus interfaces failed to initialize.

" "

RGB DRAM modules, some motherboards' onboard RGB lighting, and RGB Graphics Cards, will not be available in OpenRGB without I2C or SMBus.

" "

How to fix this:

" "

On Linux, this is usually because the i2c-dev module is not loaded.

" "

You must load the i2c-dev module along with the correct i2c driver for your motherboard. " "This is usually i2c-piix4 for AMD systems and i2c-i801 for Intel systems.

" "

See help.openrgb.org for additional troubleshooting steps if you keep seeing this message.

" "

If you are not using internal RGB on a desktop this message is not important to you.

"); const char* UDEV_MISSING = QT_TRANSLATE_NOOP("DetectionManager", "

WARNING:

" "

The OpenRGB udev rules are not installed.

" "

Most devices will not be available unless running OpenRGB as root.

" "

If using AppImage, Flatpak, or self-compiled versions of OpenRGB you must install the udev rules manually

" "

See https://openrgb.org/udev to install the udev rules manually

"); const char* UDEV_MUTLI = QT_TRANSLATE_NOOP("DetectionManager", "

WARNING:

" "

Multiple OpenRGB udev rules are installed.

" "

The udev rules file 60-openrgb.rules is installed in both /etc/udev/rules.d and /usr/lib/udev/rules.d.

" "

Multiple udev rules files can conflict, it is recommended to remove one of them.

"); /*---------------------------------------------------------*\ | Default hidapi wrappter that just uses default hidapi | \*---------------------------------------------------------*/ const hidapi_wrapper default_hidapi_wrapper = { NULL, (hidapi_wrapper_send_feature_report) hid_send_feature_report, (hidapi_wrapper_get_feature_report) hid_get_feature_report, (hidapi_wrapper_get_serial_number_string) hid_get_serial_number_string, (hidapi_wrapper_open_path) hid_open_path, (hidapi_wrapper_enumerate) hid_enumerate, (hidapi_wrapper_free_enumeration) hid_free_enumeration, (hidapi_wrapper_close) hid_close, (hidapi_wrapper_error) hid_error, #if(HID_HOTPLUG_ENABLED) (hidapi_wrapper_hotplug_register_callback) hid_hotplug_register_callback, (hidapi_wrapper_hotplug_deregister_callback) hid_hotplug_deregister_callback, #endif }; /*---------------------------------------------------------*\ | BasicHIDBlock Implementation | \*---------------------------------------------------------*/ bool BasicHIDBlock::compare(hid_device_info* info) { return((vid == info->vendor_id) && (pid == info->product_id) #ifdef USE_HID_USAGE && ((usage_page == HID_USAGE_PAGE_ANY) || (usage_page == info->usage_page)) && ((usage == HID_USAGE_ANY) || (usage == info->usage)) && ((interface == HID_INTERFACE_ANY) || (interface == info->interface_number)) #else && ((interface == HID_INTERFACE_ANY) || (interface == info->interface_number)) #endif ); } /*---------------------------------------------------------*\ | DetectionManager Global Instance Pointer | \*---------------------------------------------------------*/ DetectionManager* DetectionManager::instance; DetectionManager::DetectionManager() { /*-----------------------------------------------------*\ | Initialize global instance pointer the when created | | There should only ever be one instance of | | DetectionManager | \*-----------------------------------------------------*/ if(!instance) { instance = this; } /*-----------------------------------------------------*\ | If, for whatever reason, DetectionManager already | | exists, delete this instance as only one should exist | \*-----------------------------------------------------*/ else { delete this; return; } /*-----------------------------------------------------*\ | Initialize variables | \*-----------------------------------------------------*/ detection_in_progress = false; detection_percent = 100; detection_percent_denominator = 0; detection_string = ""; dynamic_detectors_processed = false; initial_detection = true; #ifdef __linux__ #ifdef __GLIBC__ hidapi_libusb_handle = nullptr; #endif #endif /*-----------------------------------------------------*\ | Start the background thread | \*-----------------------------------------------------*/ background_thread_running = true; BackgroundThread = new std::thread(&DetectionManager::BackgroundThreadFunction, this); } DetectionManager::~DetectionManager() { /*-----------------------------------------------------*\ | Exit HID in background thread | \*-----------------------------------------------------*/ RunInBackgroundThread(std::bind(&DetectionManager::BackgroundHIDExit, this)); /*-----------------------------------------------------*\ | Mark the background detection thread as not running | | and then wake it up so it knows that it has to stop | \*-----------------------------------------------------*/ background_thread_running = false; BackgroundThreadScheduledFunctionStart.notify_one(); /*-----------------------------------------------------*\ | Stop the background thread | \*-----------------------------------------------------*/ if(BackgroundThread) { BackgroundThread->join(); delete BackgroundThread; BackgroundThread = nullptr; } } /*---------------------------------------------------------*\ | DetectionManager Global Instance Accessor | \*---------------------------------------------------------*/ DetectionManager* DetectionManager::get() { /*-----------------------------------------------------*\ | If DetectionManager does not exist yet, create it | \*-----------------------------------------------------*/ if(!instance) { instance = new DetectionManager(); } return instance; } /*---------------------------------------------------------*\ | Functions to access device lists | \*---------------------------------------------------------*/ std::vector& DetectionManager::GetI2CBuses() { return(i2c_buses); } std::vector& DetectionManager::GetRGBControllers() { return(rgb_controllers); } /*---------------------------------------------------------*\ | I2C Bus Detector Registration Function | \*---------------------------------------------------------*/ void DetectionManager::RegisterI2CBusDetector(I2CBusDetectorFunction detector) { i2c_bus_detectors.push_back(detector); } /*---------------------------------------------------------*\ | RGBController Detector Registration Functions | \*---------------------------------------------------------*/ void DetectionManager::RegisterDeviceDetector(std::string name, DeviceDetectorFunction detector) { device_detector_strings.push_back(name); device_detectors.push_back(detector); } void DetectionManager::RegisterDynamicDetector(std::string name, DynamicDetectorFunction detector) { dynamic_detector_strings.push_back(name); dynamic_detectors.push_back(detector); } void DetectionManager::RegisterHIDDeviceDetector(std::string name, HIDDeviceDetectorFunction detector, uint16_t vid, uint16_t pid, int interface, int usage_page, int usage) { HIDDeviceDetectorBlock block; block.name = name; block.vid = vid; block.pid = pid; block.function = detector; block.interface = interface; block.usage_page = usage_page; block.usage = usage; hid_device_detectors.push_back(block); } void DetectionManager::RegisterHIDWrappedDeviceDetector(std::string name, HIDWrappedDeviceDetectorFunction detector, uint16_t vid, uint16_t pid, int interface, int usage_page, int usage) { HIDWrappedDeviceDetectorBlock block; block.name = name; block.vid = vid; block.pid = pid; block.function = detector; block.interface = interface; block.usage_page = usage_page; block.usage = usage; hid_wrapped_device_detectors.push_back(block); } void DetectionManager::RegisterI2CDeviceDetector(std::string name, I2CDeviceDetectorFunction detector) { i2c_device_detector_strings.push_back(name); i2c_device_detectors.push_back(detector); } void DetectionManager::RegisterI2CDRAMDeviceDetector(std::string name, I2CDRAMDeviceDetectorFunction detector, uint16_t jedec_id, uint8_t dram_type) { I2CDRAMDeviceDetectorBlock block; block.name = name; block.function = detector; block.jedec_id = jedec_id; block.dram_type = dram_type; i2c_dram_device_detectors.push_back(block); } void DetectionManager::RegisterI2CPCIDeviceDetector(std::string name, I2CPCIDeviceDetectorFunction detector, uint16_t ven_id, uint16_t dev_id, uint16_t subven_id, uint16_t subdev_id, uint8_t i2c_addr) { I2CPCIDeviceDetectorBlock block; block.name = name; block.function = detector; block.ven_id = ven_id; block.dev_id = dev_id; block.subven_id = subven_id; block.subdev_id = subdev_id; block.i2c_addr = i2c_addr; i2c_pci_device_detectors.push_back(block); } /*---------------------------------------------------------*\ | Pre-Detection Hook Function Registration Function | \*---------------------------------------------------------*/ void DetectionManager::RegisterPreDetectionHook(PreDetectionHookFunction hook) { pre_detection_hooks.push_back(hook); } /*---------------------------------------------------------*\ | Detection Callback Registration Functions | \*---------------------------------------------------------*/ void DetectionManager::RegisterDetectionCallback(DetectionCallback new_callback, void * new_callback_arg) { DetectionCallbackMutex.lock(); for(std::size_t idx = 0; idx < DetectionCallbacks.size(); idx++) { if(DetectionCallbacks[idx] == new_callback && DetectionCallbackArgs[idx] == new_callback_arg) { DetectionCallbackMutex.unlock(); LOG_TRACE("[%s] Tried to register an already registered detection callback, skipping. Total callbacks registered: %d", DETECTIONMANAGER, DetectionCallbacks.size()); return; } } DetectionCallbacks.push_back(new_callback); DetectionCallbackArgs.push_back(new_callback_arg); DetectionCallbackMutex.unlock(); LOG_TRACE("[%s] Registered detection callback. Total callbacks registered: %d", DETECTIONMANAGER, DetectionCallbacks.size()); } void DetectionManager::UnregisterDetectionCallback(DetectionCallback callback, void *callback_arg) { DetectionCallbackMutex.lock(); for(std::size_t idx = 0; idx < DetectionCallbacks.size(); idx++) { if(DetectionCallbacks[idx] == callback && DetectionCallbackArgs[idx] == callback_arg) { DetectionCallbacks.erase(DetectionCallbacks.begin() + idx); DetectionCallbackArgs.erase(DetectionCallbackArgs.begin() + idx); } } DetectionCallbackMutex.unlock(); LOG_TRACE("[%s] Unregistered detection callback. Total callbacks registered: %d", DETECTIONMANAGER, DetectionCallbacks.size()); } /*---------------------------------------------------------*\ | Functions for registering I2C buses from within detectors | \*---------------------------------------------------------*/ void DetectionManager::RegisterI2CBus(i2c_smbus_interface *bus) { LOG_INFO("[%s] Registering I2C interface: %s Device %04X:%04X Subsystem: %04X:%04X", DETECTIONMANAGER, bus->device_name, bus->pci_vendor, bus->pci_device, bus->pci_subsystem_vendor, bus->pci_subsystem_device); /*-----------------------------------------------------*\ | Add the new bus to the list | \*-----------------------------------------------------*/ i2c_buses.push_back(bus); /*-----------------------------------------------------*\ | Signal I2C Bus List Update | \*-----------------------------------------------------*/ SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_I2C_BUS_REGISTERED); } /*---------------------------------------------------------*\ | Functions for registering RGBControllers from within | | detectors | \*---------------------------------------------------------*/ void DetectionManager::RegisterRGBController(RGBController *rgb_controller) { LOG_INFO("[%s] Registering RGB controller %s", DETECTIONMANAGER, rgb_controller->GetName().c_str()); /*-----------------------------------------------------*\ | Mark this controller as locally owned | \*-----------------------------------------------------*/ rgb_controller->flags &= ~CONTROLLER_FLAG_REMOTE; rgb_controller->flags |= CONTROLLER_FLAG_LOCAL; /*-----------------------------------------------------*\ | Load sizes for the new controller | \*-----------------------------------------------------*/ ResourceManager::get()->GetProfileManager()->LoadControllerFromListWithOptions(rgb_controllers_sizes, detection_size_entry_used, rgb_controller, true, false); /*-----------------------------------------------------*\ | Add the new controller to the list | \*-----------------------------------------------------*/ rgb_controllers.push_back(rgb_controller); /*-----------------------------------------------------*\ | Signal Device List Update | \*-----------------------------------------------------*/ SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_RGBCONTROLLER_REGISTERED); } void DetectionManager::UnregisterRGBController(RGBController* rgb_controller) { LOG_INFO("[%s] Unregistering RGB controller %s", DETECTIONMANAGER, rgb_controller->GetName().c_str()); /*-----------------------------------------------------*\ | Clear callbacks from the controller before removal | \*-----------------------------------------------------*/ rgb_controller->ClearCallbacks(); /*-----------------------------------------------------*\ | Find the controller to remove and remove it from the | | master list | \*-----------------------------------------------------*/ std::vector::iterator rgb_it = std::find(rgb_controllers.begin(), rgb_controllers.end(), rgb_controller); if(rgb_it != rgb_controllers.end()) { rgb_controllers.erase(rgb_it); } /*-----------------------------------------------------*\ | Signal Device List Update | \*-----------------------------------------------------*/ SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_RGBCONTROLLER_UNREGISTERED); } /*---------------------------------------------------------*\ | Detection state functions | \*---------------------------------------------------------*/ void DetectionManager::AbortDetection() { LOG_INFO("[%s] Detection abort requested", DETECTIONMANAGER); detection_in_progress = false; detection_percent = 100; detection_string = "Stopping"; } void DetectionManager::BeginDetection() { /*-----------------------------------------------------*\ | Perform pre-detection setup | \*-----------------------------------------------------*/ bool detection_ready = ProcessPreDetection(); /*-----------------------------------------------------*\ | Run DetectDevices function in background thread if | | ready to run detection | \*-----------------------------------------------------*/ if(detection_ready) { RunInBackgroundThread(std::bind(&DetectionManager::BackgroundDetectDevices, this)); } } unsigned int DetectionManager::GetDetectionPercent() { return(detection_percent.load()); } std::string DetectionManager::GetDetectionString() { return(detection_string); } void DetectionManager::WaitForDetection() { DetectDevicesMutex.lock(); DetectDevicesMutex.unlock(); } /*---------------------------------------------------------*\ | DetectionManager Background Thread Functions | \*---------------------------------------------------------*/ void DetectionManager::BackgroundThreadFunction() { /*-----------------------------------------------------*\ | The background thread that runs scheduled coroutines | | when applicable | | Stays asleep if nothing is scheduled | | NOTE: this thread owns the HIDAPI library internal | | objects on MacOS | | hid_init and hid_exit may not be called outside of | | this thread. Calling hid_exit outside of this thread | | WILL cause an immediate CRASH on MacOS. | | BackgroundThreadStateMutex will be UNLOCKED as long | | as the thread is suspended. It locks automatically | | when any coroutine is running. However, it seems to | | be necessary to be separate from the | | DeviceDetectionMutex, even though their states are | | nearly identical. | \------------------------------------------------------*/ std::unique_lock lock(BackgroundThreadStateMutex); while(background_thread_running) { if(BackgroundThreadScheduledFunction) { std::function coroutine = nullptr; std::swap(BackgroundThreadScheduledFunction, coroutine); try { coroutine(); } catch(std::exception& e) { LOG_ERROR("[%s] Unhandled exception in coroutine; e.what(): %s", DETECTIONMANAGER, e.what()); } catch(...) { LOG_ERROR("[%s] Unhandled exception in coroutine", DETECTIONMANAGER); } } /*-------------------------------------------------*\ | This line will cause the thread to suspend until | | the condition variable is triggered | | NOTE: it may be subject to "spurious wakeups" | \*-------------------------------------------------*/ BackgroundThreadScheduledFunctionStart.wait(lock); } } void DetectionManager::RunInBackgroundThread(std::function coroutine) { if(std::this_thread::get_id() == BackgroundThread->get_id()) { /*-------------------------------------------------*\ | We are already in the background thread - don't | | schedule the call, run it immediately | \*-------------------------------------------------*/ coroutine(); } else { /*-------------------------------------------------*\ | Lock the background thread state mutex and assign | | the new function to the background thread, then | | trigger the background thread to start it | \*-------------------------------------------------*/ BackgroundThreadStateMutex.lock(); if(BackgroundThreadScheduledFunction != nullptr) { LOG_WARNING("[%s] Detection coroutine: assigned a new coroutine when one was already scheduled - probably two rescan events sent at once", DETECTIONMANAGER); } BackgroundThreadScheduledFunction = coroutine; BackgroundThreadStateMutex.unlock(); BackgroundThreadScheduledFunctionStart.notify_one(); } } /*---------------------------------------------------------*\ | Functions that must be run in the background thread | \*---------------------------------------------------------*/ void DetectionManager::BackgroundDetectDevices() { /*-----------------------------------------------------*\ | Lock detection mutex | \*-----------------------------------------------------*/ DetectDevicesMutex.lock(); /*-----------------------------------------------------*\ | Set detection in progress flag | \*-----------------------------------------------------*/ detection_in_progress = true; /*-----------------------------------------------------*\ | Initialize local variables | \*-----------------------------------------------------*/ hid_device_info* current_hid_device; json detector_settings; hid_device_info* hid_devices = NULL; bool hid_safe_mode = false; unsigned int initial_detection_delay_ms = 0; LOG_INFO("------------------------------------------------------"); LOG_INFO("| Start device detection |"); LOG_INFO("------------------------------------------------------"); /*-----------------------------------------------------*\ | Start at 0% detection progress | \*-----------------------------------------------------*/ detection_percent = 0; detection_string = ""; SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); /*-----------------------------------------------------*\ | Open device disable list and read in disabled | | device strings | \*-----------------------------------------------------*/ detector_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Detectors"); /*-----------------------------------------------------*\ | Check HID safe mode setting | \*-----------------------------------------------------*/ if(detector_settings.contains("hid_safe_mode")) { hid_safe_mode = detector_settings["hid_safe_mode"]; } /*-----------------------------------------------------*\ | Check initial detection delay setting | \*-----------------------------------------------------*/ if(detector_settings.contains("initial_detection_delay_ms")) { initial_detection_delay_ms = detector_settings["initial_detection_delay_ms"]; } /*-----------------------------------------------------*\ | If configured, delay detection for the configured | | time only on first detection | \*-----------------------------------------------------*/ if(initial_detection) { if(initial_detection_delay_ms != 0) { detection_string = "Waiting for detection delay"; SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); LOG_INFO("[%s] Delaying detection for %d ms", DETECTIONMANAGER, initial_detection_delay_ms); std::this_thread::sleep_for(initial_detection_delay_ms * 1ms); } /*-------------------------------------------------*\ | Also initialize HID on first detection, as this | | must be called from the background thread | \*-------------------------------------------------*/ BackgroundHIDInit(); initial_detection = false; } /*-----------------------------------------------------*\ | Reset the size entry used flags vector | \*-----------------------------------------------------*/ detection_size_entry_used.resize(rgb_controllers_sizes.size()); for(std::size_t size_idx = 0; size_idx < detection_size_entry_used.size(); size_idx++) { detection_size_entry_used[size_idx] = false; } /*-----------------------------------------------------*\ | Enumerate HID devices unless using HID safe mode | \*-----------------------------------------------------*/ if(!hid_safe_mode) { hid_devices = hid_enumerate(0, 0); } /*-----------------------------------------------------*\ | Calculate the percentage denominator by adding the | | number of I2C and miscellaneous detectors and the | | number of enumerated HID devices | | | | Start by iterating through all HID devices in list to | | get a total count | \*-----------------------------------------------------*/ detection_percent_i2c_count = (unsigned int)i2c_device_detectors.size(); detection_percent_i2c_dram_count = (unsigned int)i2c_dram_device_detectors.size(); detection_percent_i2c_pci_count = (unsigned int)i2c_pci_device_detectors.size(); if(hid_safe_mode) { detection_percent_hid_count = (unsigned int)hid_device_detectors.size(); } else { current_hid_device = hid_devices; detection_percent_hid_count = 0; while(current_hid_device) { detection_percent_hid_count++; current_hid_device = current_hid_device->next; } } detection_percent_other_count = (unsigned int)device_detectors.size(); detection_percent_denominator = detection_percent_i2c_count + detection_percent_i2c_dram_count + detection_percent_i2c_pci_count + detection_percent_other_count + detection_percent_hid_count; /*-----------------------------------------------------*\ | Detect I2C interfaces | \*-----------------------------------------------------*/ BackgroundDetectI2CBuses(); /*-----------------------------------------------------*\ | Detect I2C devices | \*-----------------------------------------------------*/ BackgroundDetectI2CDevices(detector_settings); /*-----------------------------------------------------*\ | Detect I2C DRAM devices | \*-----------------------------------------------------*/ BackgroundDetectI2CDRAMDevices(detector_settings); /*-----------------------------------------------------*\ | Detect I2C PCI devices | \*-----------------------------------------------------*/ BackgroundDetectI2CPCIDevices(detector_settings); /*-----------------------------------------------------*\ | Detect HID devices | \*-----------------------------------------------------*/ if(hid_safe_mode) { BackgroundDetectHIDDevicesSafe(detector_settings); } else { #if(HID_HOTPLUG_ENABLED) StopHIDHotplug(); StartHIDHotplug(); #else BackgroundDetectHIDDevices(hid_devices, detector_settings); #endif } /*-----------------------------------------------------*\ | Detect Wrapped HID devices (GLIBC Linux only) | \*-----------------------------------------------------*/ #ifdef __linux__ #ifdef __GLIBC__ BackgroundDetectHIDDevicesWrapped(hid_devices, detector_settings); #endif #endif /*-----------------------------------------------------*\ | Detect other devices | \*-----------------------------------------------------*/ BackgroundDetectOtherDevices(detector_settings); /*-----------------------------------------------------*\ | Signal that detection progress reached 100% | \*-----------------------------------------------------*/ detection_percent = 100; SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); /*-----------------------------------------------------*\ | Signal that detection is complete | \*-----------------------------------------------------*/ SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_COMPLETE); LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detection completed |"); LOG_INFO("------------------------------------------------------"); /*-----------------------------------------------------*\ | Clear detection in progress flag | \*-----------------------------------------------------*/ detection_in_progress = false; /*-----------------------------------------------------*\ | Unlock detection mutex | \*-----------------------------------------------------*/ DetectDevicesMutex.unlock(); #ifdef __linux__ /*-----------------------------------------------------*\ | Check if the udev rules exist | \*-----------------------------------------------------*/ bool udev_not_exist = false; bool udev_multiple = false; if(access("/etc/udev/rules.d/60-openrgb.rules", F_OK) != 0) { if(access("/usr/lib/udev/rules.d/60-openrgb.rules", F_OK) != 0) { udev_not_exist = true; } } else { if(access("/usr/lib/udev/rules.d/60-openrgb.rules", F_OK) == 0) { udev_multiple = true; } } /*-----------------------------------------------------*\ | If the udev rules file is not installed, show a dialog| \*-----------------------------------------------------*/ if(udev_not_exist) { LOG_DIALOG("%s", UDEV_MISSING); udev_multiple = false; i2c_interface_fail = false; } /*-----------------------------------------------------*\ | If multiple udev rules files are installed, show a | | dialog | \*-----------------------------------------------------*/ if(udev_multiple) { LOG_DIALOG("%s", UDEV_MUTLI); i2c_interface_fail = false; } #endif /*-----------------------------------------------------*\ | If any i2c interfaces failed to detect due to an | | error condition, show a dialog | \*-----------------------------------------------------*/ if(i2c_interface_fail) { #ifdef _WIN32 LOG_DIALOG("%s", I2C_ERR_WIN); #endif #ifdef __linux__ LOG_DIALOG("%s", I2C_ERR_LINUX); #endif } } void DetectionManager::BackgroundDetectI2CBuses() { LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detecting I2C interfaces |"); LOG_INFO("------------------------------------------------------"); detection_percent = 0; detection_string = "Detecting I2C interfaces"; SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); i2c_interface_fail = false; for(std::size_t i2c_bus_detector_idx = 0; i2c_bus_detector_idx < i2c_bus_detectors.size(); i2c_bus_detector_idx++) { if(i2c_bus_detectors[i2c_bus_detector_idx]() == false) { i2c_interface_fail = true; } } } void DetectionManager::BackgroundDetectI2CDevices(json detector_settings) { LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detecting I2C devices |"); LOG_INFO("------------------------------------------------------"); for(std::size_t i2c_detector_idx = 0; i2c_detector_idx < i2c_device_detectors.size(); i2c_detector_idx++) { /*-------------------------------------------------*\ | Update detection string | \*-------------------------------------------------*/ detection_string = i2c_device_detector_strings[i2c_detector_idx].c_str(); /*-------------------------------------------------*\ | Check if this detector is enabled | \*-------------------------------------------------*/ bool this_device_enabled = true; if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) { this_device_enabled = detector_settings["detectors"][detection_string]; } LOG_DEBUG("[%s] %s is %s", DETECTIONMANAGER, detection_string.c_str(), ((this_device_enabled == true) ? "enabled" : "disabled")); /*-------------------------------------------------*\ | If enabled, signal progress changed and call the | | detector | \*-------------------------------------------------*/ if(this_device_enabled) { SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); DetectedControllers detected_controllers = i2c_device_detectors[i2c_detector_idx](i2c_buses); for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { RegisterRGBController(detected_controllers[detected_controller_idx]); } } LOG_TRACE("[%s] %s detection end", DETECTIONMANAGER, detection_string.c_str()); /*-------------------------------------------------*\ | Update detection percent | \*-------------------------------------------------*/ unsigned int detection_percent_numerator = i2c_detector_idx; detection_percent = (unsigned int)(100.0f * (detection_percent_numerator / detection_percent_denominator)); } } void DetectionManager::BackgroundDetectI2CDRAMDevices(json detector_settings) { LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detecting I2C DRAM modules |"); LOG_INFO("------------------------------------------------------"); detection_string = "Reading DRAM SPD Information"; SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); for(std::size_t bus = 0; bus < i2c_buses.size() && IsAnyDimmDetectorEnabled(detector_settings); bus++) { IF_DRAM_SMBUS(i2c_buses[bus]->pci_vendor, i2c_buses[bus]->pci_device) { std::vector slots; SPDMemoryType dram_type = SPD_RESERVED; for(uint8_t spd_addr = 0x50; spd_addr < 0x58; spd_addr++) { SPDDetector spd(i2c_buses[bus], spd_addr, dram_type); if(spd.is_valid()) { SPDWrapper accessor(spd); dram_type = spd.memory_type(); LOG_INFO("[%s] Detected occupied slot %d, bus %d, type %s", DETECTIONMANAGER, spd_addr - 0x50 + 1, bus, spd_memory_type_name[dram_type]); LOG_DEBUG("[%s] JEDEC ID: 0x%04x", DETECTIONMANAGER, accessor.jedec_id()); slots.push_back(accessor); } } for(std::size_t i2c_detector_idx = 0; i2c_detector_idx < i2c_dram_device_detectors.size(); i2c_detector_idx++) { if((i2c_dram_device_detectors[i2c_detector_idx].dram_type == dram_type) && is_jedec_in_slots(slots, i2c_dram_device_detectors[i2c_detector_idx].jedec_id)) { detection_string = i2c_dram_device_detectors[i2c_detector_idx].name.c_str(); /*-------------------------------------*\ | Check if this detector is enabled | \*-------------------------------------*/ bool this_device_enabled = true; if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) { this_device_enabled = detector_settings["detectors"][detection_string]; } LOG_DEBUG("[%s] %s is %s", DETECTIONMANAGER, detection_string.c_str(), ((this_device_enabled == true) ? "enabled" : "disabled")); if(this_device_enabled) { SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); std::vector matching_slots = slots_with_jedec(slots, i2c_dram_device_detectors[i2c_detector_idx].jedec_id); DetectedControllers detected_controllers = i2c_dram_device_detectors[i2c_detector_idx].function(i2c_buses[bus], matching_slots, i2c_dram_device_detectors[i2c_detector_idx].name); for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { RegisterRGBController(detected_controllers[detected_controller_idx]); } } LOG_TRACE("[%s] %s detection end", DETECTIONMANAGER, detection_string.c_str()); } /*-----------------------------------------*\ | Update detection percent | \*-----------------------------------------*/ unsigned int detection_percent_numerator = detection_percent_i2c_count + i2c_detector_idx; detection_percent = (unsigned int)(100.0f * (detection_percent_numerator / detection_percent_denominator)); } } } } void DetectionManager::BackgroundDetectI2CPCIDevices(json detector_settings) { LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detecting I2C PCI devices |"); LOG_INFO("------------------------------------------------------"); for(std::size_t i2c_detector_idx = 0; i2c_detector_idx < i2c_pci_device_detectors.size(); i2c_detector_idx++) { detection_string = i2c_pci_device_detectors[i2c_detector_idx].name.c_str(); /*-------------------------------------------------*\ | Check if this detector is enabled | \*-------------------------------------------------*/ bool this_device_enabled = true; if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) { this_device_enabled = detector_settings["detectors"][detection_string]; } LOG_DEBUG("[%s] %s is %s", DETECTIONMANAGER, detection_string.c_str(), ((this_device_enabled == true) ? "enabled" : "disabled")); if(this_device_enabled) { SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); for(std::size_t bus = 0; bus < i2c_buses.size(); bus++) { if(i2c_buses[bus]->pci_vendor == i2c_pci_device_detectors[i2c_detector_idx].ven_id && i2c_buses[bus]->pci_device == i2c_pci_device_detectors[i2c_detector_idx].dev_id && i2c_buses[bus]->pci_subsystem_vendor == i2c_pci_device_detectors[i2c_detector_idx].subven_id && i2c_buses[bus]->pci_subsystem_device == i2c_pci_device_detectors[i2c_detector_idx].subdev_id) { DetectedControllers detected_controllers = i2c_pci_device_detectors[i2c_detector_idx].function(i2c_buses[bus], i2c_pci_device_detectors[i2c_detector_idx].i2c_addr, i2c_pci_device_detectors[i2c_detector_idx].name); for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { RegisterRGBController(detected_controllers[detected_controller_idx]); } } } } LOG_TRACE("[%s] %s detection end", DETECTIONMANAGER, detection_string.c_str()); /*-------------------------------------------------*\ | Update detection percent | \*-------------------------------------------------*/ unsigned int detection_percent_numerator = detection_percent_i2c_count + detection_percent_i2c_dram_count + i2c_detector_idx; detection_percent = (unsigned int)(100.0f * (detection_percent_numerator / detection_percent_denominator)); } } void DetectionManager::BackgroundDetectHIDDevices(hid_device_info* hid_devices, json detector_settings) { LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detecting HID devices |"); LOG_INFO("------------------------------------------------------"); hid_device_info* current_hid_device = hid_devices; /*-----------------------------------------------------*\ | Iterate through all devices in list and run detectors | \*-----------------------------------------------------*/ unsigned int hid_device_count = 0; while(current_hid_device) { RunHIDDetector(current_hid_device, detector_settings); RunHIDWrappedDetector(&default_hidapi_wrapper, current_hid_device, detector_settings); /*-------------------------------------------------*\ | Update detection percent | \*-------------------------------------------------*/ hid_device_count++; unsigned int detection_percent_numerator = detection_percent_i2c_count + detection_percent_i2c_dram_count + detection_percent_i2c_pci_count + hid_device_count; detection_percent = (unsigned int)(100.0f * (detection_percent_numerator / detection_percent_denominator)); /*-------------------------------------------------*\ | Move on to the next HID device | \*-------------------------------------------------*/ current_hid_device = current_hid_device->next; } /*-----------------------------------------------------*\ | Done using the device list, free it | \*-----------------------------------------------------*/ hid_free_enumeration(hid_devices); } void DetectionManager::BackgroundDetectHIDDevicesSafe(json detector_settings) { LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detecting HID devices |"); LOG_INFO("| with safe mode |"); LOG_INFO("------------------------------------------------------"); /*-----------------------------------------------------*\ | Loop through all available detectors. If all | | required information matches, run the detector | \*-----------------------------------------------------*/ for(std::size_t hid_detector_idx = 0; hid_detector_idx < hid_device_detectors.size(); hid_detector_idx++) { HIDDeviceDetectorBlock & detector = hid_device_detectors[hid_detector_idx]; LOG_VERBOSE("[%s] Trying to run detector for [%s] (for %04x:%04x)", DETECTIONMANAGER, detector.name.c_str(), detector.vid, detector.pid); hid_device_info* current_hid_device = hid_enumerate(detector.vid, detector.pid); while(current_hid_device) { if(detector.compare(current_hid_device)) { detection_string = detector.name.c_str(); /*-----------------------------------------*\ | Check if this detector is enabled or | | needs to be added to the settings list | \*-----------------------------------------*/ bool this_device_enabled = true; if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) { this_device_enabled = detector_settings["detectors"][detection_string]; } LOG_DEBUG("[%s] %s is %s", DETECTIONMANAGER, detection_string.c_str(), ((this_device_enabled == true) ? "enabled" : "disabled")); if(this_device_enabled) { SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); DetectedControllers detected_controllers = detector.function(current_hid_device, hid_device_detectors[hid_detector_idx].name); for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { RegisterRGBController(detected_controllers[detected_controller_idx]); } } LOG_TRACE("[%s] %s detection end", DETECTIONMANAGER, detection_string.c_str()); } current_hid_device = current_hid_device->next; /*---------------------------------------------*\ | Update detection percent | \*---------------------------------------------*/ unsigned int detection_percent_numerator = detection_percent_i2c_count + detection_percent_i2c_dram_count + detection_percent_i2c_pci_count + hid_detector_idx; detection_percent = (unsigned int)(100.0f * (detection_percent_numerator / detection_percent_denominator)); } hid_free_enumeration(current_hid_device); } } #ifdef __linux__ #ifdef __GLIBC__ void DetectionManager::BackgroundDetectHIDDevicesWrapped(hid_device_info* hid_devices, json detector_settings) { LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detecting libusb HID devices |"); LOG_INFO("------------------------------------------------------"); /*-----------------------------------------------------*\ | Load the libhidapi-libusb library | \*-----------------------------------------------------*/ if(hidapi_libusb_handle) { hid_devices = hidapi_libusb_wrapper.hid_enumerate(0, 0); hid_device_info* current_hid_device = hid_devices; /*-------------------------------------------------*\ | Iterate through all devices in list and run | | detectors | \*-------------------------------------------------*/ unsigned int hid_device_count = 0; while(current_hid_device) { RunHIDWrappedDetector(&hidapi_libusb_wrapper, current_hid_device, detector_settings); /*---------------------------------------------*\ | Update detection percent | \*---------------------------------------------*/ hid_device_count++; detection_percent = (unsigned int)(100.0f * ((i2c_device_detectors.size() + i2c_dram_device_detectors.size() + i2c_pci_device_detectors.size() + hid_device_count) / detection_percent_denominator)); /*---------------------------------------------*\ | Move on to the next HID device | \*---------------------------------------------*/ current_hid_device = current_hid_device->next; } /*-------------------------------------------------*\ | Done using the device list, free it | \*-------------------------------------------------*/ hidapi_libusb_wrapper.hid_free_enumeration(hid_devices); } } #endif #endif void DetectionManager::BackgroundDetectOtherDevices(json detector_settings) { LOG_INFO("------------------------------------------------------"); LOG_INFO("| Detecting other devices |"); LOG_INFO("------------------------------------------------------"); for(std::size_t detector_idx = 0; detector_idx < device_detectors.size(); detector_idx++) { detection_string = device_detector_strings[detector_idx].c_str(); /*-------------------------------------------------*\ | Check if this detector is enabled | \*-------------------------------------------------*/ bool this_device_enabled = true; if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) { this_device_enabled = detector_settings["detectors"][detection_string]; } LOG_DEBUG("[%s] %s is %s", DETECTIONMANAGER, detection_string.c_str(), ((this_device_enabled == true) ? "enabled" : "disabled")); if(this_device_enabled) { SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); DetectedControllers detected_controllers = device_detectors[detector_idx](); for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { RegisterRGBController(detected_controllers[detected_controller_idx]); } } LOG_TRACE("[%s] %s detection end", DETECTIONMANAGER, detection_string.c_str()); /*-------------------------------------------------*\ | Update detection percent | \*-------------------------------------------------*/ unsigned int detection_percent_numerator = detection_percent_i2c_count + detection_percent_i2c_dram_count + detection_percent_i2c_pci_count + detection_percent_hid_count + detector_idx; detection_percent = (unsigned int)(100.0f * (detection_percent_numerator / detection_percent_denominator)); } } void DetectionManager::BackgroundHIDExit() { /*-----------------------------------------------------*\ | Exit HID interface | \*-----------------------------------------------------*/ int hid_status = hid_exit(); LOG_DEBUG("[%s] Exiting HID interface: %s", DETECTIONMANAGER, ((hid_status == 0) ? "Success" : "Failed")); } void DetectionManager::BackgroundHIDInit() { /*-----------------------------------------------------*\ | Initialize HID interface | \*-----------------------------------------------------*/ int hid_status = hid_init(); LOG_DEBUG("[%s] Initializing HID interfaces: %s", DETECTIONMANAGER, ((hid_status == 0) ? "Success" : "Failed")); #ifdef __linux__ #ifdef __GLIBC__ /*-----------------------------------------------------*\ | Load the libhidapi-libusb library | \*-----------------------------------------------------*/ #if(HID_HOTPLUG_ENABLED) hidapi_libusb_handle = dlopen("libhidapi-hotplug-libusb.so", RTLD_NOW | RTLD_NODELETE | RTLD_DEEPBIND); #else hidapi_libusb_handle = dlopen("libhidapi-libusb.so", RTLD_NOW | RTLD_NODELETE | RTLD_DEEPBIND); #endif if(hidapi_libusb_handle) { /*-------------------------------------------------*\ | Create a wrapper with the libusb functions | \*-------------------------------------------------*/ hidapi_libusb_wrapper = { .dyn_handle = hidapi_libusb_handle, .hid_send_feature_report = (hidapi_wrapper_send_feature_report) dlsym(hidapi_libusb_handle,"hid_send_feature_report"), .hid_get_feature_report = (hidapi_wrapper_get_feature_report) dlsym(hidapi_libusb_handle,"hid_get_feature_report"), .hid_get_serial_number_string = (hidapi_wrapper_get_serial_number_string) dlsym(hidapi_libusb_handle,"hid_get_serial_number_string"), .hid_open_path = (hidapi_wrapper_open_path) dlsym(hidapi_libusb_handle,"hid_open_path"), .hid_enumerate = (hidapi_wrapper_enumerate) dlsym(hidapi_libusb_handle,"hid_enumerate"), .hid_free_enumeration = (hidapi_wrapper_free_enumeration) dlsym(hidapi_libusb_handle,"hid_free_enumeration"), .hid_close = (hidapi_wrapper_close) dlsym(hidapi_libusb_handle,"hid_close"), .hid_error = (hidapi_wrapper_error) dlsym(hidapi_libusb_handle,"hid_free_enumeration"), #if(HID_HOTPLUG_ENABLED) .hid_hotplug_register_callback = (hidapi_wrapper_hotplug_register_callback) dlsym(hidapi_libusb_handle,"hid_hotplug_register_callback"), .hid_hotplug_deregister_callback = (hidapi_wrapper_hotplug_deregister_callback) dlsym(hidapi_libusb_handle,"hid_hotplug_deregister_callback"), #endif }; } #endif #endif } /*---------------------------------------------------------*\ | Functions to run detectors | \*---------------------------------------------------------*/ void DetectionManager::RunHIDDetector(hid_device_info* current_hid_device, json detector_settings) { if(LogManager::get()->getLoglevel() >= LL_DEBUG) { const char* manu_name = StringUtils::wchar_to_char(current_hid_device->manufacturer_string); const char* prod_name = StringUtils::wchar_to_char(current_hid_device->product_string); LOG_DEBUG("[%s] %04X:%04X U=%04X P=0x%04X I=%d - %-25s - %s", DETECTIONMANAGER, current_hid_device->vendor_id, current_hid_device->product_id, current_hid_device->usage, current_hid_device->usage_page, current_hid_device->interface_number, manu_name, prod_name); } detection_string = ""; SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); /*-----------------------------------------------------*\ | Loop through all available detectors. If all | | required information matches, run the detector. | \*-----------------------------------------------------*/ for(std::size_t hid_detector_idx = 0; hid_detector_idx < hid_device_detectors.size(); hid_detector_idx++) { HIDDeviceDetectorBlock & detector = hid_device_detectors[hid_detector_idx]; if(detector.compare(current_hid_device)) { detection_string = detector.name.c_str(); /*---------------------------------------------*\ | Check if this detector is enabled or needs to | | be added to the settings list | \*---------------------------------------------*/ bool this_device_enabled = true; if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) { this_device_enabled = detector_settings["detectors"][detection_string]; } LOG_DEBUG("[%s] %s is %s", DETECTIONMANAGER, detection_string.c_str(), ((this_device_enabled == true) ? "enabled" : "disabled")); if(this_device_enabled) { SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); DetectedControllers detected_controllers = detector.function(current_hid_device, detector.name); for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { #if(HID_HOTPLUG_ENABLED) int handle; hid_hotplug_register_callback(current_hid_device->vendor_id, current_hid_device->product_id, HID_API_HOTPLUG_EVENT_DEVICE_LEFT, 0, &DetectionManager::UnplugCallbackFunction, detected_controllers[detected_controller_idx], &handle); #endif RegisterRGBController(detected_controllers[detected_controller_idx]); } } break; } } } void DetectionManager::RunHIDWrappedDetector(const hidapi_wrapper* wrapper, hid_device_info* current_hid_device, json detector_settings) { if(LogManager::get()->getLoglevel() >= LL_DEBUG) { const char* manu_name = StringUtils::wchar_to_char(current_hid_device->manufacturer_string); const char* prod_name = StringUtils::wchar_to_char(current_hid_device->product_string); LOG_DEBUG("[%s] %04X:%04X U=%04X P=0x%04X I=%d - %-25s - %s", DETECTIONMANAGER, current_hid_device->vendor_id, current_hid_device->product_id, current_hid_device->usage, current_hid_device->usage_page, current_hid_device->interface_number, manu_name, prod_name); } detection_string = ""; SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); /*-----------------------------------------------------*\ | Loop through all available wrapped HID detectors. If | | all required information matches, run the detector. | \*-----------------------------------------------------*/ for(std::size_t hid_detector_idx = 0; hid_detector_idx < hid_wrapped_device_detectors.size(); hid_detector_idx++) { HIDWrappedDeviceDetectorBlock & detector = hid_wrapped_device_detectors[hid_detector_idx]; if(detector.compare(current_hid_device)) { detection_string = detector.name.c_str(); /*---------------------------------------------*\ | Check if this detector is enabled or needs to | | be added to the settings list | \*---------------------------------------------*/ bool this_device_enabled = true; if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string)) { this_device_enabled = detector_settings["detectors"][detection_string]; } LOG_DEBUG("[%s] %s is %s", DETECTIONMANAGER, detection_string.c_str(), ((this_device_enabled == true) ? "enabled" : "disabled")); if(this_device_enabled) { SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_PROGRESS_CHANGED); DetectedControllers detected_controllers = detector.function(*wrapper, current_hid_device, detector.name); for(std::size_t detected_controller_idx = 0; detected_controller_idx < detected_controllers.size(); detected_controller_idx++) { #if(HID_HOTPLUG_ENABLED) int handle; wrapper->hid_hotplug_register_callback(current_hid_device->vendor_id, current_hid_device->product_id, HID_API_HOTPLUG_EVENT_DEVICE_LEFT, 0, &DetectionManager::WrappedUnplugCallbackFunction, detected_controllers[detected_controller_idx], &handle); #endif RegisterRGBController(detected_controllers[detected_controller_idx]); } } break; } } } /*---------------------------------------------------------*\ | Detection processing functions | \*---------------------------------------------------------*/ void DetectionManager::ProcessCleanup() { WaitForDetection(); #if(HID_HOTPLUG_ENABLED) StopHIDHotplug(); #endif /*-----------------------------------------------------*\ | Make a copy of the list so that the controllers can | | be deleted after the list is cleared | \*-----------------------------------------------------*/ std::vector rgb_controllers_copy = rgb_controllers; /*-----------------------------------------------------*\ | Clear the controllers list | \*-----------------------------------------------------*/ rgb_controllers.clear(); /*-----------------------------------------------------*\ | Signal the list cleared callback | \*-----------------------------------------------------*/ SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_RGBCONTROLLER_LIST_CLEARED); /*-----------------------------------------------------*\ | Delete the controllers | \*-----------------------------------------------------*/ for(RGBController* rgb_controller : rgb_controllers_copy) { delete rgb_controller; } /*-----------------------------------------------------*\ | Make a copy of the list so that the I2C buses can be | | deleted after the list is cleared | \*-----------------------------------------------------*/ std::vector i2c_buses_copy = i2c_buses; /*-----------------------------------------------------*\ | Clear the I2C buses list | \*-----------------------------------------------------*/ i2c_buses.clear(); /*-----------------------------------------------------*\ | Delete the I2C buses | \*-----------------------------------------------------*/ for(i2c_smbus_interface* bus : i2c_buses_copy) { delete bus; } } void DetectionManager::ProcessDynamicDetectors() { for(std::size_t detector_idx = 0; detector_idx < dynamic_detectors.size(); detector_idx++) { dynamic_detectors[detector_idx](); } dynamic_detectors_processed = true; } bool DetectionManager::ProcessPreDetection() { /*-----------------------------------------------------*\ | Check if detection is already in progress before | | continuing | \*-----------------------------------------------------*/ if(detection_in_progress.load()) { return false; } /*-----------------------------------------------------*\ | Process pre-detection hooks | \*-----------------------------------------------------*/ ProcessPreDetectionHooks(); /*-----------------------------------------------------*\ | Process Dynamic Detectors | \*-----------------------------------------------------*/ if(!dynamic_detectors_processed) { ProcessDynamicDetectors(); } /*-----------------------------------------------------*\ | Call detection start callbacks | \*-----------------------------------------------------*/ LOG_TRACE("[%s] Signaling detection start", DETECTIONMANAGER); SignalUpdate(DETECTIONMANAGER_UPDATE_REASON_DETECTION_STARTED); /*-----------------------------------------------------*\ | Update the detector settings | \*-----------------------------------------------------*/ UpdateDetectorSettings(); /*-----------------------------------------------------*\ | Initialize sizes list | \*-----------------------------------------------------*/ rgb_controllers_sizes = ResourceManager::get()->GetProfileManager()->GetControllerListFromSizes(); /*-----------------------------------------------------*\ | Clean up any existing detected devices | \*-----------------------------------------------------*/ ProcessCleanup(); return true; } void DetectionManager::ProcessPreDetectionHooks() { for(std::size_t hook_idx = 0; hook_idx < pre_detection_hooks.size(); hook_idx++) { pre_detection_hooks[hook_idx](); } } void DetectionManager::UpdateDetectorSettings() { json detector_settings; bool save_settings = false; /*-----------------------------------------------------*\ | Open device disable list and read in disabled device | | strings | \*-----------------------------------------------------*/ detector_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Detectors"); /*-----------------------------------------------------*\ | Loop through all I2C detectors and see if any need to | | be saved to the settings | \*-----------------------------------------------------*/ for(std::size_t i2c_detector_idx = 0; i2c_detector_idx < i2c_device_detectors.size(); i2c_detector_idx++) { detection_string = i2c_device_detector_strings[i2c_detector_idx].c_str(); if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) { detector_settings["detectors"][detection_string] = true; save_settings = true; } } /*-----------------------------------------------------*\ | Loop through all I2C DRAM detectors and see if any | | need to be saved to the settings | \*-----------------------------------------------------*/ for(std::size_t i2c_detector_idx = 0; i2c_detector_idx < i2c_dram_device_detectors.size(); i2c_detector_idx++) { detection_string = i2c_dram_device_detectors[i2c_detector_idx].name.c_str(); if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) { detector_settings["detectors"][detection_string] = true; save_settings = true; } } /*-----------------------------------------------------*\ | Loop through all I2C PCI detectors and see if any | | need to be saved to the settings | \*-----------------------------------------------------*/ for(std::size_t i2c_pci_detector_idx = 0; i2c_pci_detector_idx < i2c_pci_device_detectors.size(); i2c_pci_detector_idx++) { detection_string = i2c_pci_device_detectors[i2c_pci_detector_idx].name.c_str(); if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) { detector_settings["detectors"][detection_string] = true; save_settings = true; } } /*-----------------------------------------------------*\ | Loop through all HID detectors and see if any need to | | be saved to the settings | \*-----------------------------------------------------*/ for(std::size_t hid_detector_idx = 0; hid_detector_idx < hid_device_detectors.size(); hid_detector_idx++) { detection_string = hid_device_detectors[hid_detector_idx].name.c_str(); if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) { detector_settings["detectors"][detection_string] = true; save_settings = true; } } /*-----------------------------------------------------*\ | Loop through all HID wrapped detectors and see if any | | need to be saved to the settings | \*-----------------------------------------------------*/ for(std::size_t hid_wrapped_detector_idx = 0; hid_wrapped_detector_idx < hid_wrapped_device_detectors.size(); hid_wrapped_detector_idx++) { detection_string = hid_wrapped_device_detectors[hid_wrapped_detector_idx].name.c_str(); if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) { detector_settings["detectors"][detection_string] = true; save_settings = true; } } /*-----------------------------------------------------*\ | Loop through remaining detectors and see if any need | | to be saved to the settings | \*-----------------------------------------------------*/ for(std::size_t detector_idx = 0; detector_idx < device_detectors.size(); detector_idx++) { detection_string = device_detector_strings[detector_idx].c_str(); if(!(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detection_string))) { detector_settings["detectors"][detection_string] = true; save_settings = true; } } /*-----------------------------------------------------*\ | If there were any setting changes that need to be | | saved, set the settings in the settings manager and | | save them. | \*-----------------------------------------------------*/ if(save_settings) { LOG_INFO("[%s] Saving detector settings", DETECTIONMANAGER); ResourceManager::get()->GetSettingsManager()->SetSettings("Detectors", detector_settings); ResourceManager::get()->GetSettingsManager()->SaveSettings(); } } #if(HID_HOTPLUG_ENABLED) /*---------------------------------------------------------*\ | HID hotplug management functions | \*---------------------------------------------------------*/ void DetectionManager::StartHIDHotplug() { hid_hotplug_register_callback(0, 0, HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED, HID_API_HOTPLUG_ENUMERATE, &DetectionManager::HotplugCallbackFunction, nullptr, &hotplug_callback_handle); #ifdef __linux__ #ifdef __GLIBC__ if(hidapi_libusb_handle != nullptr) { hidapi_libusb_wrapper.hid_hotplug_register_callback(0, 0, HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED, HID_API_HOTPLUG_ENUMERATE, &DetectionManager::WrappedHotplugCallbackFunction, nullptr, &libusb_hotplug_callback_handle); } #endif #endif } void DetectionManager::StopHIDHotplug() { hid_hotplug_deregister_callback(hotplug_callback_handle); #ifdef __linux__ #ifdef __GLIBC__ if(hidapi_libusb_handle != nullptr) { hidapi_libusb_wrapper.hid_hotplug_deregister_callback(libusb_hotplug_callback_handle); } #endif #endif } /*---------------------------------------------------------*\ | HID hotplug callback functions | \*---------------------------------------------------------*/ int DetectionManager::HotplugCallbackFunction(hid_hotplug_callback_handle callback_handle, hid_device_info *device, hid_hotplug_event event, void *user_data) { /*-----------------------------------------------------*\ | Open device disable list and read in disabled | | device strings | \*-----------------------------------------------------*/ json detector_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Detectors"); DetectionManager* dm = DetectionManager::get(); if(event == HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED) { LOG_INFO("[%s] HID device connected: [%04x:%04x]", DETECTIONMANAGER, device->vendor_id, device->product_id); dm->RunHIDDetector(device, detector_settings); dm->RunHIDWrappedDetector(&default_hidapi_wrapper, device, detector_settings); } return 0; } int DetectionManager::UnplugCallbackFunction(hid_hotplug_callback_handle callback_handle, hid_device_info *device, hid_hotplug_event event, void *user_data) { DetectionManager* dm = DetectionManager::get(); if(event == HID_API_HOTPLUG_EVENT_DEVICE_LEFT) { LOG_INFO("[%s] HID device disconnected: [%04x:%04x]", DETECTIONMANAGER, device->vendor_id, device->product_id); std::string location = "HID: "; location.append(device->path); // User data is the pointer to the controller being removed RGBController* controller = (RGBController*)(user_data); LOG_DEBUG("Checking device location:\"%s\":\"%s\"", location.c_str(), controller->location.c_str()); if(controller && controller->location == location) { dm->UnregisterRGBController(controller); delete controller; return 1; } } return 0; } #ifdef __linux__ #ifdef __GLIBC__ int DetectionManager::WrappedHotplugCallbackFunction(hid_hotplug_callback_handle callback_handle, hid_device_info *device, hid_hotplug_event event, void *user_data) { /*-----------------------------------------------------*\ | Open device disable list and read in disabled | | device strings | \*-----------------------------------------------------*/ json detector_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Detectors"); DetectionManager* dm = DetectionManager::get(); if(event == HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED) { LOG_INFO("[%s] libusb HID device connected: [%04x:%04x]", DETECTIONMANAGER, device->vendor_id, device->product_id); dm->RunHIDDetector(device, detector_settings); dm->RunHIDWrappedDetector(&DetectionManager::get()->hidapi_libusb_wrapper, device, detector_settings); } return 0; } int DetectionManager::WrappedUnplugCallbackFunction(hid_hotplug_callback_handle callback_handle, hid_device_info *device, hid_hotplug_event event, void *user_data) { DetectionManager* dm = DetectionManager::get(); if(event == HID_API_HOTPLUG_EVENT_DEVICE_LEFT) { LOG_INFO("[%s] libusb HID device disconnected: [%04x:%04x]", DETECTIONMANAGER, device->vendor_id, device->product_id); std::string location = "HID: "; location.append(device->path); // User data is the pointer to the controller being removed RGBController* controller = (RGBController*)(user_data); LOG_DEBUG("Checking device location:\"%s\":\"%s\"", location.c_str(), controller->location.c_str()); if(controller && controller->location == location) { dm->UnregisterRGBController(controller); delete controller; return 1; } } return 0; } #endif #endif #endif /*---------------------------------------------------------*\ | Function for signalling DetectionManager updates to | | registered callbacks | \*---------------------------------------------------------*/ void DetectionManager::SignalUpdate(unsigned int update_reason) { DetectionCallbackMutex.lock(); for(std::size_t callback_idx = 0; callback_idx < DetectionCallbacks.size(); callback_idx++) { DetectionCallbacks[callback_idx](DetectionCallbackArgs[callback_idx], update_reason); } DetectionCallbackMutex.unlock(); LOG_TRACE("[%s] Update signalled: %d.", DETECTIONMANAGER, update_reason); } bool DetectionManager::IsAnyDimmDetectorEnabled(json &detector_settings) { for(std::size_t i2c_detector_idx = 0; i2c_detector_idx < i2c_dram_device_detectors.size(); i2c_detector_idx++) { std::string detector_name_string = i2c_dram_device_detectors[i2c_detector_idx].name.c_str(); /*-------------------------------------------------*\ | Check if this detector is enabled | \*-------------------------------------------------*/ if(detector_settings.contains("detectors") && detector_settings["detectors"].contains(detector_name_string) && detector_settings["detectors"][detector_name_string] == true) { return true; } } return false; }