diff --git a/DetectionManager.cpp b/DetectionManager.cpp index 39c16ee14..cc117ff32 100644 --- a/DetectionManager.cpp +++ b/DetectionManager.cpp @@ -11,6 +11,8 @@ \*---------------------------------------------------------*/ #include +#include +#include #include "DetectionManager.h" #include "JsonUtils.h" #include "LogManager.h" @@ -2068,3 +2070,107 @@ bool DetectionManager::IsAnyDimmDetectorEnabled(json &detector_settings) } return false; } + +#ifdef __linux__ +/*---------------------------------------------------------*\ +| Udev rules generation function | +\*---------------------------------------------------------*/ +bool DetectionManager::GenerateUdevRules(const std::string& filepath) +{ + LOG_INFO("[%s] Generating udev rules file: %s", DETECTIONMANAGER, filepath.c_str()); + + FILE* output_file = fopen(filepath.c_str(), "w"); + if(!output_file) + { + LOG_ERROR("[%s] Failed to open output file: %s", DETECTIONMANAGER, filepath.c_str()); + return false; + } + + /*-----------------------------------------------------*\ + | Write udev rules header | + \*-----------------------------------------------------*/ + fprintf(output_file, "#---------------------------------------------------------------#\n"); + fprintf(output_file, "# OpenRGB udev rules - Generated by DetectionManager #\n"); + fprintf(output_file, "#---------------------------------------------------------------#\n"); + fprintf(output_file, "\n"); + fprintf(output_file, "#---------------------------------------------------------------#\n"); + fprintf(output_file, "# User I2C/SMBus Access #\n"); + fprintf(output_file, "#---------------------------------------------------------------#\n"); + fprintf(output_file, "KERNEL==\"i2c-[0-99]*\", TAG+=\"uaccess\"\n"); + fprintf(output_file, "\n"); + fprintf(output_file, "#---------------------------------------------------------------#\n"); + fprintf(output_file, "# Super I/O Access #\n"); + fprintf(output_file, "#---------------------------------------------------------------#\n"); + fprintf(output_file, "KERNEL==\"port\", TAG+=\"uaccess\"\n"); + fprintf(output_file, "\n"); + + /*-----------------------------------------------------*\ + | Group detectors by name to avoid duplicate headers | + \*-----------------------------------------------------*/ + std::map>> detector_groups; + + for(std::size_t detector_idx = 0; detector_idx < hid_specific_detectors.size(); detector_idx++) + { + HIDDeviceDetectorBlock& detector = hid_specific_detectors[detector_idx]; + + if(detector.vid == HID_VID_ANY || detector.pid == HID_PID_ANY) + { + continue; + } + + detector_groups[detector.name].push_back({(uint16_t)detector.vid, (uint16_t)detector.pid}); + } + + for(std::size_t detector_idx = 0; detector_idx < hid_wrapped_specific_detectors.size(); detector_idx++) + { + HIDWrappedDeviceDetectorBlock& detector = hid_wrapped_specific_detectors[detector_idx]; + + if(detector.vid == HID_VID_ANY || detector.pid == HID_PID_ANY) + { + continue; + } + + std::string group_name = detector.name + " (libusb)"; + detector_groups[group_name].push_back({(uint16_t)detector.vid, (uint16_t)detector.pid}); + } + + /*-----------------------------------------------------*\ + | Write grouped HID device rules | + \*-----------------------------------------------------*/ + for(const std::pair>>& group : detector_groups) + { + fprintf(output_file, "#---------------------------------------------------------------#\n"); + fprintf(output_file, "# %s\n", group.first.c_str()); + fprintf(output_file, "#---------------------------------------------------------------#\n"); + + std::string device_name_tag = group.first; + for(auto it = device_name_tag.begin(); it != device_name_tag.end();) + { + if(*it == ' ') + { + *it = '_'; + ++it; + } + else if(!isalnum(*it)) + { + it = device_name_tag.erase(it); + } + else + { + ++it; + } + } + + for(const std::pair& vid_pid : group.second) + { + fprintf(output_file, "SUBSYSTEMS==\"usb|hidraw\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", TAG+=\"uaccess\", TAG+=\"%s\"\n", vid_pid.first, vid_pid.second, device_name_tag.c_str()); + } + fprintf(output_file, "\n"); + } + + fclose(output_file); + + LOG_INFO("[%s] Successfully generated udev rules file: %s", DETECTIONMANAGER, filepath.c_str()); + return true; +} +#endif diff --git a/DetectionManager.h b/DetectionManager.h index 57b35529d..7e515b49d 100644 --- a/DetectionManager.h +++ b/DetectionManager.h @@ -205,6 +205,13 @@ public: std::string GetDetectionString(); void WaitForDetection(); +#ifdef __linux__ + /*-----------------------------------------------------*\ + | Udev rules generation function | + \*-----------------------------------------------------*/ + bool GenerateUdevRules(const std::string& filepath); +#endif + private: /*-----------------------------------------------------*\ | Static pointer to shared instance of DetectionManager | diff --git a/cli.cpp b/cli.cpp index 2e506e054..361e710eb 100644 --- a/cli.cpp +++ b/cli.cpp @@ -22,6 +22,10 @@ #include "ResourceManager.h" #include "RGBController.h" +#ifdef __linux__ +#include "DetectionManager.h" +#endif + /*---------------------------------------------------------*\ | Quirk for MSVC; which doesn't support this | | case-insensitive function | @@ -452,6 +456,9 @@ void OptionHelp() help_text += "--autostart-check Check if OpenRGB starting at login is enabled.\n"; help_text += "--autostart-disable Disable OpenRGB starting at login.\n"; help_text += "--autostart-enable arguments Enable OpenRGB to start at login. Requires arguments to give to OpenRGB at login.\n"; +#ifdef __linux__ + help_text += "--generate-udev-rules [filename] Generate the OpenRGB udev rules file and save it to the given filename.\n"; +#endif std::cout << help_text << std::endl; } @@ -957,6 +964,13 @@ bool OptionSaveProfile(std::string argument) return(true); } +#ifdef __linux__ +void OptionGenerateUdevRules(std::string argument) +{ + DetectionManager::get()->GenerateUdevRules(argument); +} +#endif + int ProcessOptions(Options* options, std::vector& rgb_controllers) { unsigned int ret_flags = 0; @@ -1134,6 +1148,18 @@ int ProcessOptions(Options* options, std::vector& rgb_controlle arg_index++; } +#ifdef __linux__ + /*-------------------------------------------------*\ + | --generate-udev-rules | + \*-------------------------------------------------*/ + else if(option == "--generate-udev-rules") + { + OptionGenerateUdevRules(arg_path.generic_u8string()); + + arg_index++; + } +#endif + /*-------------------------------------------------*\ | Invalid option | \*-------------------------------------------------*/ diff --git a/cli.h b/cli.h index 4bf68bbd3..8b291bb7e 100644 --- a/cli.h +++ b/cli.h @@ -26,4 +26,5 @@ enum RET_FLAG_CLI_POST_DETECTION = 32, RET_FLAG_START_SERVER = 64, RET_FLAG_NO_AUTO_CONNECT = 128, + RET_FLAG_GENERATE_UDEV_RULES = 256, };