diff --git a/Controllers/SkydimoHIDController/RGBController_SkydimoHID.cpp b/Controllers/SkydimoHIDController/RGBController_SkydimoHID.cpp new file mode 100644 index 000000000..5d08bf128 --- /dev/null +++ b/Controllers/SkydimoHIDController/RGBController_SkydimoHID.cpp @@ -0,0 +1,118 @@ +/*---------------------------------------------------------*\ +| RGBController_SkydimoHID.cpp | +| | +| RGBController for Skydimo HID devices | +| | +| Bartholomew Ho 09 Jul 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SkydimoHID.h" + +/**------------------------------------------------------------------*\ + @name Skydimo SK0902 + @category HeadsetStand + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSkydimoHIDControllers + @comment +\*-------------------------------------------------------------------*/ + +static const unsigned int skydimo_hid_led_positions[SKYDIMO_HID_LED_COUNT][2] = +{ + {0, 6}, {0, 5}, {0, 4}, {0, 3}, {0, 2}, {0, 1}, {0, 0}, + {1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {1, 6}, + {2, 6}, {2, 5}, {2, 4}, {2, 3}, {2, 2}, {2, 1}, {2, 0}, + {3, 0}, {3, 1}, {3, 2}, {3, 3}, {3, 4}, {3, 5}, {3, 6}, + {4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, + {5, 0}, {5, 1}, {5, 2}, {5, 3}, {5, 4}, {5, 5}, {5, 6}, + {6, 6}, {6, 5}, {6, 4}, {6, 3}, {6, 2}, {6, 1}, {6, 0} +}; + +RGBController_SkydimoHID::RGBController_SkydimoHID(SkydimoHIDController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetDeviceName(); + vendor = "Skydimo"; + type = DEVICE_TYPE_HEADSET_STAND; + description = "Skydimo HID Device"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode direct_mode; + direct_mode.name = "Direct"; + direct_mode.value = 0; + direct_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR; + direct_mode.color_mode = MODE_COLORS_PER_LED; + modes.push_back(direct_mode); + + SetupZones(); +} + +RGBController_SkydimoHID::~RGBController_SkydimoHID() +{ + Shutdown(); + + delete controller; +} + +void RGBController_SkydimoHID::SetupZones() +{ + zone led_zone; + led_zone.name = "Matrix"; + led_zone.type = ZONE_TYPE_MATRIX; + led_zone.leds_min = SKYDIMO_HID_LED_COUNT; + led_zone.leds_max = SKYDIMO_HID_LED_COUNT; + led_zone.leds_count = SKYDIMO_HID_LED_COUNT; + + led_zone.matrix_map.height = SKYDIMO_HID_MATRIX_SIZE; + led_zone.matrix_map.width = SKYDIMO_HID_MATRIX_SIZE; + led_zone.matrix_map.map.assign(SKYDIMO_HID_MATRIX_SIZE * SKYDIMO_HID_MATRIX_SIZE, SKYDIMO_HID_MATRIX_NA); + + /*-----------------------------------------------------*\ + | Construct the matrix map | + \*-----------------------------------------------------*/ + for(unsigned int led_idx = 0; led_idx < SKYDIMO_HID_LED_COUNT; led_idx++) + { + unsigned int column = skydimo_hid_led_positions[led_idx][0]; + unsigned int row = skydimo_hid_led_positions[led_idx][1]; + + led_zone.matrix_map.map[(row * SKYDIMO_HID_MATRIX_SIZE) + column] = led_idx; + } + + zones.push_back(led_zone); + + for(unsigned int led_idx = 0; led_idx < SKYDIMO_HID_LED_COUNT; led_idx++) + { + led new_led; + new_led.name = "LED " + std::to_string(led_idx + 1); + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_SkydimoHID::DeviceUpdateLEDs() +{ + controller->SetLEDs(colors); +} + +void RGBController_SkydimoHID::DeviceUpdateZoneLEDs(int) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SkydimoHID::DeviceUpdateSingleLED(int) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SkydimoHID::DeviceUpdateMode() +{ +} diff --git a/Controllers/SkydimoHIDController/RGBController_SkydimoHID.h b/Controllers/SkydimoHIDController/RGBController_SkydimoHID.h new file mode 100644 index 000000000..cb42574c8 --- /dev/null +++ b/Controllers/SkydimoHIDController/RGBController_SkydimoHID.h @@ -0,0 +1,32 @@ +/*---------------------------------------------------------*\ +| RGBController_SkydimoHID.h | +| | +| RGBController for Skydimo HID devices | +| | +| Bartholomew Ho 09 Jul 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SkydimoHIDController.h" + +class RGBController_SkydimoHID : public RGBController +{ +public: + RGBController_SkydimoHID(SkydimoHIDController* controller_ptr); + ~RGBController_SkydimoHID(); + + void SetupZones(); + + void DeviceUpdateLEDs(); + void DeviceUpdateZoneLEDs(int zone); + void DeviceUpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + SkydimoHIDController* controller; +}; diff --git a/Controllers/SkydimoHIDController/SkydimoHIDController.cpp b/Controllers/SkydimoHIDController/SkydimoHIDController.cpp new file mode 100644 index 000000000..54c68d7cb --- /dev/null +++ b/Controllers/SkydimoHIDController/SkydimoHIDController.cpp @@ -0,0 +1,195 @@ +/*---------------------------------------------------------*\ +| SkydimoHIDController.cpp | +| | +| Driver for Skydimo HID devices | +| | +| Bartholomew Ho 09 Jul 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include "LogManager.h" +#include "SkydimoHIDController.h" +#include "StringUtils.h" + +static unsigned char CalculateCRC8(const unsigned char* data, unsigned int length) +{ + unsigned char crc = 0x00; + + for(unsigned int byte_idx = 0; byte_idx < length; byte_idx++) + { + crc ^= data[byte_idx]; + + for(unsigned int bit_idx = 0; bit_idx < SKYDIMO_HID_CRC_BIT_COUNT; bit_idx++) + { + if(crc & SKYDIMO_HID_CRC_HIGH_BIT) + { + crc = (unsigned char)((crc << 1) ^ SKYDIMO_HID_CRC_POLYNOMIAL); + } + else + { + crc = (unsigned char)(crc << 1); + } + } + } + + return(crc); +} + +SkydimoHIDController::SkydimoHIDController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; + + wchar_t name_string[SKYDIMO_HID_NAME_BUFFER_SIZE]; + int result; + + result = hid_get_manufacturer_string(dev, name_string, SKYDIMO_HID_NAME_BUFFER_SIZE); + if(result == 0) + { + device_name = StringUtils::wstring_to_string(name_string); + } + else + { + device_name = "Skydimo"; + LOG_WARNING("[SkydimoHIDController] Failed to read manufacturer string for %s", path); + } + + result = hid_get_product_string(dev, name_string, SKYDIMO_HID_NAME_BUFFER_SIZE); + if(result == 0) + { + device_name.append(" ").append(StringUtils::wstring_to_string(name_string)); + } + else + { + device_name.append(" HID Device"); + LOG_WARNING("[SkydimoHIDController] Failed to read product string for %s", path); + } + + wchar_t serial_string[SKYDIMO_HID_SERIAL_BUFFER_SIZE]; + result = hid_get_serial_number_string(dev, serial_string, SKYDIMO_HID_SERIAL_BUFFER_SIZE); + if(result == 0) + { + serial = StringUtils::wstring_to_string(serial_string); + } + else + { + serial = ""; + LOG_DEBUG("[SkydimoHIDController] No serial number available for %s", path); + } +} + +SkydimoHIDController::~SkydimoHIDController() +{ + if(dev) + { + hid_close(dev); + } +} + +std::string SkydimoHIDController::GetDeviceName() const +{ + return(device_name); +} + +std::string SkydimoHIDController::GetSerial() const +{ + return(serial); +} + +std::string SkydimoHIDController::GetLocation() const +{ + return("HID: " + location); +} + +bool SkydimoHIDController::SendPacket(unsigned char start_led, const unsigned char* chunk, unsigned int chunk_length) +{ + unsigned char packet[SKYDIMO_HID_PACKET_SIZE]; + memset(packet, 0x00, sizeof(packet)); + + packet[0] = SKYDIMO_HID_REPORT_ID; + packet[SKYDIMO_HID_PACKET_START_LED_INDEX] = start_led; + packet[SKYDIMO_HID_PACKET_RESERVED_INDEX] = 0x00; + + if(chunk_length > SKYDIMO_HID_PACKET_DATA_SIZE) + { + chunk_length = SKYDIMO_HID_PACKET_DATA_SIZE; + } + + memcpy(&packet[SKYDIMO_HID_PACKET_DATA_OFFSET], chunk, chunk_length); + + /*-----------------------------------------------------*\ + | Calculate CRC8 over all bytes before the checksum | + \*-----------------------------------------------------*/ + packet[SKYDIMO_HID_PACKET_SIZE - 1] = CalculateCRC8(packet, SKYDIMO_HID_PACKET_SIZE - 1); + + int bytes_written = hid_write(dev, packet, SKYDIMO_HID_PACKET_SIZE); + + if(bytes_written != SKYDIMO_HID_PACKET_SIZE) + { + LOG_ERROR("[SkydimoHIDController] Failed to write RGB packet at LED %u (%d/%u bytes): %ls", + (unsigned int)start_led, bytes_written, (unsigned int)SKYDIMO_HID_PACKET_SIZE, hid_error(dev)); + return(false); + } + + return(true); +} + +void SkydimoHIDController::SetLEDs(const std::vector& colors) +{ + std::lock_guard guard(device_mutex); + + unsigned char frame_data[SKYDIMO_HID_FRAME_SIZE]; + memset(frame_data, 0, sizeof(frame_data)); + + /*-----------------------------------------------------*\ + | Convert colors to GRB format | + \*-----------------------------------------------------*/ + for(unsigned int led_idx = 0; (led_idx < colors.size()) && (led_idx < SKYDIMO_HID_LED_COUNT); led_idx++) + { + unsigned int color_offset = led_idx * SKYDIMO_HID_BYTES_PER_LED; + + frame_data[color_offset + 0] = RGBGetGValue(colors[led_idx]); + frame_data[color_offset + 1] = RGBGetRValue(colors[led_idx]); + frame_data[color_offset + 2] = RGBGetBValue(colors[led_idx]); + } + + /*-----------------------------------------------------*\ + | Send the frame in packet-sized chunks | + \*-----------------------------------------------------*/ + for(unsigned int frame_offset = 0; frame_offset < SKYDIMO_HID_FRAME_SIZE; frame_offset += SKYDIMO_HID_PACKET_DATA_SIZE) + { + unsigned int bytes_remaining = SKYDIMO_HID_FRAME_SIZE - frame_offset; + unsigned int chunk_length = (bytes_remaining < SKYDIMO_HID_PACKET_DATA_SIZE) + ? bytes_remaining + : SKYDIMO_HID_PACKET_DATA_SIZE; + unsigned char start_led = (unsigned char)(frame_offset / SKYDIMO_HID_BYTES_PER_LED); + + if(!SendPacket(start_led, &frame_data[frame_offset], chunk_length)) + { + return; + } + } + + /*-----------------------------------------------------*\ + | Send the frame-end packet | + \*-----------------------------------------------------*/ + unsigned char end_packet[SKYDIMO_HID_PACKET_SIZE]; + memset(end_packet, 0x00, sizeof(end_packet)); + + end_packet[0] = SKYDIMO_HID_REPORT_ID; + end_packet[SKYDIMO_HID_END_COMMAND_INDEX] = SKYDIMO_HID_END_COMMAND_VALUE; + end_packet[SKYDIMO_HID_END_SUBCOMMAND_INDEX] = SKYDIMO_HID_END_SUBCOMMAND_VALUE; + end_packet[SKYDIMO_HID_END_LED_COUNT_INDEX] = SKYDIMO_HID_LED_COUNT; + end_packet[SKYDIMO_HID_END_MARKER_INDEX] = SKYDIMO_HID_END_MARKER_VALUE; + + int bytes_written = hid_write(dev, end_packet, SKYDIMO_HID_PACKET_SIZE); + + if(bytes_written != SKYDIMO_HID_PACKET_SIZE) + { + LOG_ERROR("[SkydimoHIDController] Failed to write frame-end packet (%d/%u bytes): %ls", + bytes_written, (unsigned int)SKYDIMO_HID_PACKET_SIZE, hid_error(dev)); + } +} diff --git a/Controllers/SkydimoHIDController/SkydimoHIDController.h b/Controllers/SkydimoHIDController/SkydimoHIDController.h new file mode 100644 index 000000000..c9d7aa447 --- /dev/null +++ b/Controllers/SkydimoHIDController/SkydimoHIDController.h @@ -0,0 +1,64 @@ +/*---------------------------------------------------------*\ +| SkydimoHIDController.h | +| | +| Driver for Skydimo HID devices | +| | +| Bartholomew Ho 09 Jul 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include "RGBControllerInterface.h" + +#define SKYDIMO_HID_LED_COUNT 49 +#define SKYDIMO_HID_MATRIX_SIZE 7 +#define SKYDIMO_HID_BYTES_PER_LED 3 +#define SKYDIMO_HID_FRAME_SIZE (SKYDIMO_HID_LED_COUNT * SKYDIMO_HID_BYTES_PER_LED) +#define SKYDIMO_HID_PACKET_SIZE 64 +#define SKYDIMO_HID_PACKET_DATA_SIZE 60 +#define SKYDIMO_HID_PACKET_DATA_OFFSET 3 +#define SKYDIMO_HID_REPORT_ID 0x01 +#define SKYDIMO_HID_PACKET_START_LED_INDEX 1 +#define SKYDIMO_HID_PACKET_RESERVED_INDEX 2 +#define SKYDIMO_HID_MATRIX_NA 0xFFFFFFFF +#define SKYDIMO_HID_CRC_POLYNOMIAL 0x07 +#define SKYDIMO_HID_CRC_HIGH_BIT 0x80 +#define SKYDIMO_HID_CRC_BIT_COUNT 8 +#define SKYDIMO_HID_NAME_BUFFER_SIZE 255 +#define SKYDIMO_HID_SERIAL_BUFFER_SIZE 128 +#define SKYDIMO_HID_END_MARKER_INDEX 60 +#define SKYDIMO_HID_END_MARKER_VALUE 0x1E +#define SKYDIMO_HID_END_COMMAND_INDEX 1 +#define SKYDIMO_HID_END_COMMAND_VALUE 0xFF +#define SKYDIMO_HID_END_SUBCOMMAND_INDEX 2 +#define SKYDIMO_HID_END_SUBCOMMAND_VALUE 0xFF +#define SKYDIMO_HID_END_LED_COUNT_INDEX 3 + +class SkydimoHIDController +{ +public: + SkydimoHIDController(hid_device* dev_handle, const char* path); + ~SkydimoHIDController(); + + std::string GetDeviceName() const; + std::string GetSerial() const; + std::string GetLocation() const; + + void SetLEDs(const std::vector& colors); + +private: + hid_device* dev; + std::string device_name; + std::string serial; + std::string location; + std::mutex device_mutex; + + bool SendPacket(unsigned char start_led, const unsigned char* chunk, unsigned int chunk_length); +}; diff --git a/Controllers/SkydimoHIDController/SkydimoHIDControllerDetect.cpp b/Controllers/SkydimoHIDController/SkydimoHIDControllerDetect.cpp new file mode 100644 index 000000000..581323d24 --- /dev/null +++ b/Controllers/SkydimoHIDController/SkydimoHIDControllerDetect.cpp @@ -0,0 +1,50 @@ +/*---------------------------------------------------------*\ +| SkydimoHIDControllerDetect.cpp | +| | +| Detector for Skydimo HID devices | +| | +| Bartholomew Ho 09 Jul 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 "RGBController_SkydimoHID.h" +#include "SkydimoHIDController.h" + +/*---------------------------------------------------------*\ +| Skydimo HID vendor and product IDs | +\*---------------------------------------------------------*/ +#define SKYDIMO_VID 0x1A86 +#define SKYDIMO_SK0902_PID 0xE316 +#define SKYDIMO_SK0902_INTERFACE 0 +#define SKYDIMO_SK0902_USAGE_PAGE 0xFF00 +#define SKYDIMO_SK0902_USAGE 0x01 + +DetectedControllers DetectSkydimoHIDControllers(hid_device_info* info, const std::string&) +{ + DetectedControllers detected_controllers; + hid_device* dev; + + dev = hid_open_path(info->path); + + if(dev) + { + SkydimoHIDController* controller = new SkydimoHIDController(dev, info->path); + RGBController_SkydimoHID* rgb_controller = new RGBController_SkydimoHID(controller); + + detected_controllers.push_back(rgb_controller); + LOG_INFO("[SkydimoHIDControllerDetect] Detected Skydimo SK0902 at %s", info->path); + } + else + { + LOG_WARNING("[SkydimoHIDControllerDetect] Failed to open Skydimo SK0902 at %s", info->path); + } + + return(detected_controllers); +} + +REGISTER_HID_DETECTOR_IPU("Skydimo SK0902", DetectSkydimoHIDControllers, SKYDIMO_VID, SKYDIMO_SK0902_PID, SKYDIMO_SK0902_INTERFACE, SKYDIMO_SK0902_USAGE_PAGE, SKYDIMO_SK0902_USAGE); diff --git a/Controllers/SkydimoSerialController/RGBController_SkydimoSerial.cpp b/Controllers/SkydimoSerialController/RGBController_SkydimoSerial.cpp new file mode 100644 index 000000000..482369c4e --- /dev/null +++ b/Controllers/SkydimoSerialController/RGBController_SkydimoSerial.cpp @@ -0,0 +1,147 @@ +/*---------------------------------------------------------*\ +| RGBController_SkydimoSerial.cpp | +| | +| RGBController for Skydimo serial devices | +| | +| Bartholomew Ho 30 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include "RGBController_SkydimoSerial.h" + +/**------------------------------------------------------------------*\ + @name Skydimo Serial + @category LEDStrip + @type Serial + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectSkydimoSerialControllers + @comment +\*-------------------------------------------------------------------*/ + +RGBController_SkydimoSerial::RGBController_SkydimoSerial(SkydimoSerialController* controller_ptr) +{ + controller = controller_ptr; + + name = controller->GetName(); + vendor = "Skydimo"; + type = DEVICE_TYPE_LEDSTRIP; + description = "Skydimo Serial Device"; + serial = controller->GetSerial(); + location = controller->GetLocation(); + + mode direct_mode; + direct_mode.name = "Direct"; + direct_mode.value = 0; + direct_mode.flags = MODE_FLAG_HAS_PER_LED_COLOR; + direct_mode.color_mode = MODE_COLORS_PER_LED; + modes.push_back(direct_mode); + + SetupZones(); +} + +RGBController_SkydimoSerial::~RGBController_SkydimoSerial() +{ + Shutdown(); + + controller->SetBlack(); + delete controller; +} + +void RGBController_SkydimoSerial::SetupZones() +{ + bool first_run = zones.empty(); + + leds.clear(); + colors.clear(); + zones.resize(1); + + zones[0].leds_min = controller->GetMinLEDCount(); + zones[0].leds_max = controller->GetMaxLEDCount(); + + if(first_run) + { + if(controller->IsResizable()) + { + zones[0].flags = ZONE_FLAG_MANUALLY_CONFIGURABLE_SIZE + | ZONE_FLAG_MANUALLY_CONFIGURABLE_NAME + | ZONE_FLAG_MANUALLY_CONFIGURABLE_TYPE + | ZONE_FLAG_MANUALLY_CONFIGURABLE_MATRIX_MAP + | ZONE_FLAG_MANUALLY_CONFIGURABLE_SEGMENTS; + } + } + + if(!(zones[0].flags & ZONE_FLAG_MANUALLY_CONFIGURED_NAME)) + { + zones[0].name = "Output 1"; + } + + if(!(zones[0].flags & ZONE_FLAG_MANUALLY_CONFIGURED_SIZE)) + { + zones[0].leds_count = controller->GetLEDCount(); + } + + if(!(zones[0].flags & ZONE_FLAG_MANUALLY_CONFIGURED_TYPE)) + { + zones[0].type = controller->GetMatrixMap().empty() ? ZONE_TYPE_LINEAR : ZONE_TYPE_MATRIX; + } + + if(!(zones[0].flags & ZONE_FLAG_MANUALLY_CONFIGURED_MATRIX_MAP)) + { + zones[0].matrix_map.height = controller->GetMatrixHeight(); + zones[0].matrix_map.width = controller->GetMatrixWidth(); + zones[0].matrix_map.map = controller->GetMatrixMap(); + } + + for(unsigned int led_idx = 0; led_idx < zones[0].leds_count; led_idx++) + { + led new_led; + new_led.name = zones[0].name + ", LED " + std::to_string(led_idx + 1); + + leds.push_back(new_led); + } + + SetupColors(); +} + +void RGBController_SkydimoSerial::DeviceConfigureZone(int zone_idx) +{ + unsigned int zone_index = (unsigned int)zone_idx; + + if(zone_index < zones.size()) + { + if(zones[zone_index].flags & ZONE_FLAG_MANUALLY_CONFIGURED_SIZE) + { + controller->SetLEDCount(zones[zone_index].leds_count); + } + else + { + controller->SetLEDCount(controller->GetMinLEDCount()); + } + + zones[zone_index].leds_count = controller->GetLEDCount(); + SetupZones(); + } +} + +void RGBController_SkydimoSerial::DeviceUpdateLEDs() +{ + controller->SetLEDs(colors); +} + +void RGBController_SkydimoSerial::DeviceUpdateZoneLEDs(int) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SkydimoSerial::DeviceUpdateSingleLED(int) +{ + DeviceUpdateLEDs(); +} + +void RGBController_SkydimoSerial::DeviceUpdateMode() +{ +} diff --git a/Controllers/SkydimoSerialController/RGBController_SkydimoSerial.h b/Controllers/SkydimoSerialController/RGBController_SkydimoSerial.h new file mode 100644 index 000000000..ff473f544 --- /dev/null +++ b/Controllers/SkydimoSerialController/RGBController_SkydimoSerial.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_SkydimoSerial.h | +| | +| RGBController for Skydimo serial devices | +| | +| Bartholomew Ho 30 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "SkydimoSerialController.h" + +class RGBController_SkydimoSerial : public RGBController +{ +public: + RGBController_SkydimoSerial(SkydimoSerialController* controller_ptr); + ~RGBController_SkydimoSerial(); + + void SetupZones(); + void DeviceConfigureZone(int zone_idx); + + void DeviceUpdateLEDs(); + void DeviceUpdateZoneLEDs(int zone); + void DeviceUpdateSingleLED(int led); + void DeviceUpdateMode(); + +private: + SkydimoSerialController* controller; +}; diff --git a/Controllers/SkydimoSerialController/SkydimoSerialController.cpp b/Controllers/SkydimoSerialController/SkydimoSerialController.cpp new file mode 100644 index 000000000..523c36fa3 --- /dev/null +++ b/Controllers/SkydimoSerialController/SkydimoSerialController.cpp @@ -0,0 +1,548 @@ +/*---------------------------------------------------------*\ +| SkydimoSerialController.cpp | +| | +| Driver for Skydimo serial devices | +| | +| Bartholomew Ho 30 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#include +#include +#include "LogManager.h" +#include "SkydimoSerialController.h" + +enum SkydimoLayoutKind +{ + SKYDIMO_LAYOUT_NONE, + SKYDIMO_LAYOUT_STRIP_1, + SKYDIMO_LAYOUT_PERIMETER_3, + SKYDIMO_LAYOUT_PERIMETER_4, + SKYDIMO_LAYOUT_SIDES_2 +}; + +struct SkydimoModelEntry +{ + const char* model; + SkydimoLayoutKind layout; + unsigned int zone_leds[4]; + unsigned int total_leds; + unsigned int max_leds; +}; + +static const SkydimoModelEntry skydimo_model_entries[] = +{ + { "SK0121", SKYDIMO_LAYOUT_PERIMETER_3, { 13, 25, 13, 0 }, 51, 0 }, + { "SK0124", SKYDIMO_LAYOUT_PERIMETER_3, { 14, 26, 14, 0 }, 54, 0 }, + { "SK0127", SKYDIMO_LAYOUT_PERIMETER_3, { 17, 31, 17, 0 }, 65, 0 }, + { "SK0132", SKYDIMO_LAYOUT_PERIMETER_3, { 20, 37, 20, 0 }, 77, 0 }, + { "SK0134", SKYDIMO_LAYOUT_PERIMETER_3, { 15, 41, 15, 0 }, 71, 0 }, + { "SK0149", SKYDIMO_LAYOUT_PERIMETER_3, { 19, 69, 19, 0 }, 107, 0 }, + { "SK0201", SKYDIMO_LAYOUT_SIDES_2, { 20, 20, 0, 0 }, 40, 0 }, + { "SK0202", SKYDIMO_LAYOUT_SIDES_2, { 30, 30, 0, 0 }, 60, 0 }, + { "SK0204", SKYDIMO_LAYOUT_SIDES_2, { 25, 25, 0, 0 }, 50, 0 }, + { "SK0402", SKYDIMO_LAYOUT_STRIP_1, { 72, 0, 0, 0 }, 72, 0 }, + { "SK0403", SKYDIMO_LAYOUT_STRIP_1, { 96, 0, 0, 0 }, 96, 0 }, + { "SK0404", SKYDIMO_LAYOUT_STRIP_1, { 144, 0, 0, 0 }, 144, 0 }, + { "SK0410", SKYDIMO_LAYOUT_NONE, { 0, 0, 0, 0 }, 0, 300 }, + { "SK0801", SKYDIMO_LAYOUT_STRIP_1, { 2, 0, 0, 0 }, 2, 0 }, + { "SK0802", SKYDIMO_LAYOUT_STRIP_1, { 18, 0, 0, 0 }, 18, 0 }, + { "SK0803", SKYDIMO_LAYOUT_STRIP_1, { 10, 0, 0, 0 }, 10, 0 }, + { "SK0901", SKYDIMO_LAYOUT_STRIP_1, { 14, 0, 0, 0 }, 14, 0 }, + { "SK0E01", SKYDIMO_LAYOUT_STRIP_1, { 16, 0, 0, 0 }, 16, 0 }, + { "SK0F01", SKYDIMO_LAYOUT_SIDES_2, { 29, 29, 0, 0 }, 58, 0 }, + { "SK0F02", SKYDIMO_LAYOUT_SIDES_2, { 25, 25, 0, 0 }, 50, 0 }, + { "SK0H01", SKYDIMO_LAYOUT_STRIP_1, { 2, 0, 0, 0 }, 2, 0 }, + { "SK0H02", SKYDIMO_LAYOUT_STRIP_1, { 4, 0, 0, 0 }, 4, 0 }, + { "SK0K01", SKYDIMO_LAYOUT_STRIP_1, { 120, 0, 0, 0 }, 120, 0 }, + { "SK0K02", SKYDIMO_LAYOUT_STRIP_1, { 15, 0, 0, 0 }, 15, 0 }, + { "SK0L21", SKYDIMO_LAYOUT_PERIMETER_4, { 13, 25, 13, 25 }, 76, 0 }, + { "SK0L24", SKYDIMO_LAYOUT_PERIMETER_4, { 14, 26, 14, 26 }, 80, 0 }, + { "SK0L27", SKYDIMO_LAYOUT_PERIMETER_4, { 17, 31, 17, 31 }, 96, 0 }, + { "SK0L32", SKYDIMO_LAYOUT_PERIMETER_4, { 20, 37, 20, 37 }, 114, 0 }, + { "SK0L34", SKYDIMO_LAYOUT_PERIMETER_4, { 15, 41, 15, 41 }, 112, 0 }, + { "SK0M01", SKYDIMO_LAYOUT_STRIP_1, { 24, 0, 0, 0 }, 24, 0 }, + { "SK0N01", SKYDIMO_LAYOUT_STRIP_1, { 256, 0, 0, 0 }, 256, 0 }, + { "SK0N02", SKYDIMO_LAYOUT_STRIP_1, { 1024, 0, 0, 0 }, 1024, 0 }, + { "SK0N03", SKYDIMO_LAYOUT_STRIP_1, { 253, 0, 0, 0 }, 253, 0 }, + { "SK0S01", SKYDIMO_LAYOUT_STRIP_1, { 32, 0, 0, 0 }, 32, 0 }, + { "SKA124", SKYDIMO_LAYOUT_PERIMETER_3, { 18, 34, 18, 0 }, 70, 0 }, + { "SKA127", SKYDIMO_LAYOUT_PERIMETER_3, { 20, 41, 20, 0 }, 81, 0 }, + { "SKA132", SKYDIMO_LAYOUT_PERIMETER_3, { 25, 45, 25, 0 }, 95, 0 }, + { "SKA134", SKYDIMO_LAYOUT_PERIMETER_3, { 21, 51, 21, 0 }, 93, 0 }, + { "SKB124", SKYDIMO_LAYOUT_PERIMETER_4, { 18, 34, 18, 34 }, 104, 0 }, + { "SKB127", SKYDIMO_LAYOUT_PERIMETER_4, { 20, 41, 20, 41 }, 122, 0 }, + { "SKB132", SKYDIMO_LAYOUT_PERIMETER_4, { 25, 44, 25, 44 }, 138, 0 }, + { "SKB134", SKYDIMO_LAYOUT_PERIMETER_4, { 21, 50, 21, 50 }, 142, 0 } +}; + +#define SKYDIMO_MODEL_ENTRY_COUNT (sizeof(skydimo_model_entries) / sizeof(skydimo_model_entries[0])) + +static const unsigned char skydimo_serial_frame_header[] = +{ + 0x41, 0x64, 0x61, 0x00 +}; + +static std::string StringToUpper(const std::string& value) +{ + std::string result = value; + + for(unsigned int char_idx = 0; char_idx < result.size(); char_idx++) + { + result[char_idx] = (char)std::toupper((unsigned char)result[char_idx]); + } + + return(result); +} + +static const SkydimoModelEntry* FindModelEntry(const std::string& model) +{ + for(unsigned int entry_idx = 0; entry_idx < SKYDIMO_MODEL_ENTRY_COUNT; entry_idx++) + { + if(model == skydimo_model_entries[entry_idx].model) + { + return(&skydimo_model_entries[entry_idx]); + } + } + + return(nullptr); +} + +static std::string BytesToHex(const std::vector& bytes, unsigned int offset) +{ + static const char hex[] = "0123456789ABCDEF"; + std::string result; + + result.reserve((bytes.size() - offset) * 2); + + for(unsigned int byte_idx = offset; byte_idx < bytes.size(); byte_idx++) + { + result.push_back(hex[(bytes[byte_idx] >> 4) & 0x0F]); + result.push_back(hex[bytes[byte_idx] & 0x0F]); + } + + return(result); +} + +SkydimoSerialController::SkydimoSerialController(const std::string& port_name_arg) +{ + port_name = port_name_arg; + model_name = "Skydimo UNKNOWN"; + model_id = "UNKNOWN"; + serial_id = "000000"; + port_open = serial_port_interface.serial_open(port_name.c_str(), SKYDIMO_SERIAL_BAUD_RATE); + present = false; + resizable = true; + leds_count = SKYDIMO_SERIAL_DEFAULT_LED_COUNT; + leds_min = SKYDIMO_SERIAL_DEFAULT_LED_COUNT; + leds_max = SKYDIMO_SERIAL_DEFAULT_MAX_LED_COUNT; + matrix_width = 0; + matrix_height = 0; + + if(!port_open) + { + LOG_DEBUG("[SkydimoSerialController] Failed to open serial port %s", port_name.c_str()); + } +} + +SkydimoSerialController::~SkydimoSerialController() +{ + +} + +bool SkydimoSerialController::IsPresent() +{ + if(present) + { + return(true); + } + + present = QueryInfo(); + return(present); +} + +bool SkydimoSerialController::QueryInfo() +{ + if(!port_open) + { + return(false); + } + + serial_port_interface.serial_flush_rx(); + + char query[] = SKYDIMO_SERIAL_QUERY; + int bytes_written = serial_port_interface.serial_write(query, SKYDIMO_SERIAL_QUERY_LENGTH); + + if(bytes_written != SKYDIMO_SERIAL_QUERY_LENGTH) + { + LOG_DEBUG("[SkydimoSerialController] Failed to send identification query to %s (%d/%u bytes)", + port_name.c_str(), bytes_written, (unsigned int)SKYDIMO_SERIAL_QUERY_LENGTH); + return(false); + } + + std::vector response; + + for(unsigned int attempt = 0; attempt < SKYDIMO_SERIAL_QUERY_ATTEMPTS; attempt++) + { + char buffer[SKYDIMO_SERIAL_READ_BUFFER_SIZE]; + int bytes_read = serial_port_interface.serial_read(buffer, SKYDIMO_SERIAL_READ_BUFFER_SIZE); + + if(bytes_read < 0) + { + LOG_DEBUG("[SkydimoSerialController] Failed to read identification response from %s", port_name.c_str()); + return(false); + } + + if(bytes_read > 0) + { + response.insert(response.end(), buffer, buffer + bytes_read); + } + + if(response.size() >= SKYDIMO_SERIAL_MAX_RESPONSE_SIZE) + { + break; + } + + if(!response.empty()) + { + unsigned char last = response[response.size() - 1]; + unsigned char prev = response.size() >= 2 ? response[response.size() - 2] : 0; + + if((last == '\r') || (last == '\n') || (prev == '\r')) + { + break; + } + } + } + + while(!response.empty() && ((response.back() == '\r') || (response.back() == '\n'))) + { + response.pop_back(); + } + + if(response.size() < SKYDIMO_SERIAL_MODEL_ID_LENGTH) + { + LOG_TRACE("[SkydimoSerialController] No Skydimo identification response from %s", port_name.c_str()); + return(false); + } + + if((std::toupper(response[0]) != 'S') || (std::toupper(response[1]) != 'K')) + { + LOG_TRACE("[SkydimoSerialController] Ignoring non-Skydimo serial device at %s", port_name.c_str()); + return(false); + } + + model_id = StringToUpper(std::string(response.begin(), response.begin() + SKYDIMO_SERIAL_MODEL_ID_LENGTH)); + model_name = "Skydimo " + model_id; + serial_id = "000000"; + + unsigned int comma_offset = (unsigned int)response.size(); + + for(unsigned int byte_idx = 0; byte_idx < response.size(); byte_idx++) + { + if(response[byte_idx] == ',') + { + comma_offset = byte_idx; + break; + } + } + + if(comma_offset < response.size()) + { + if(comma_offset < SKYDIMO_SERIAL_MODEL_ID_LENGTH) + { + LOG_DEBUG("[SkydimoSerialController] Invalid identification response from %s", port_name.c_str()); + return(false); + } + + if((comma_offset + 1) < response.size()) + { + serial_id = BytesToHex(response, comma_offset + 1); + } + } + + ConfigureOutput(); + return(true); +} + +void SkydimoSerialController::ConfigureOutput() +{ + const SkydimoModelEntry* entry = FindModelEntry(model_id); + + resizable = true; + leds_count = SKYDIMO_SERIAL_DEFAULT_LED_COUNT; + leds_min = SKYDIMO_SERIAL_DEFAULT_LED_COUNT; + leds_max = SKYDIMO_SERIAL_DEFAULT_MAX_LED_COUNT; + matrix_width = 0; + matrix_height = 0; + matrix_map.clear(); + + if((entry != nullptr) && (entry->max_leds != 0)) + { + leds_max = entry->max_leds; + } + + if((entry == nullptr) || (entry->layout == SKYDIMO_LAYOUT_NONE)) + { + return; + } + + resizable = false; + leds_count = entry->total_leds; + leds_min = entry->total_leds; + leds_max = entry->total_leds; + + if(entry->layout == SKYDIMO_LAYOUT_STRIP_1) + { + return; + } + + const unsigned int zone_1_leds = entry->zone_leds[0]; + const unsigned int zone_2_leds = entry->zone_leds[1]; + const unsigned int zone_3_leds = entry->zone_leds[2]; + const unsigned int zone_4_leds = entry->zone_leds[3]; + + if(entry->layout == SKYDIMO_LAYOUT_PERIMETER_4) + { + matrix_height = std::max(zone_1_leds, zone_3_leds) + 2; + matrix_width = std::max(zone_2_leds, zone_4_leds) + 2; + } + else if(entry->layout == SKYDIMO_LAYOUT_PERIMETER_3) + { + matrix_height = std::max(zone_1_leds, zone_3_leds) + 1; + matrix_width = zone_2_leds + 2; + } + else + { + matrix_height = std::max(zone_1_leds, zone_2_leds) + 2; + matrix_width = std::max(3U, ((16 * matrix_height) + 4) / 9); + } + + matrix_map.assign(matrix_width * matrix_height, SKYDIMO_SERIAL_MATRIX_NA); + + unsigned int led_index = 0; + + if(entry->layout == SKYDIMO_LAYOUT_SIDES_2) + { + unsigned int placed = 0; + int y = (int)matrix_height - 2; + + while((placed < zone_1_leds) && (y >= 1)) + { + SetMatrixCell((unsigned int)y, 0, led_index); + placed++; + y--; + } + } + else + { + unsigned int placed = 0; + int y = entry->layout == SKYDIMO_LAYOUT_PERIMETER_3 ? (int)matrix_height - 1 + : (int)matrix_height - 2; + + while((placed < zone_1_leds) && (y >= 1)) + { + SetMatrixCell((unsigned int)y, matrix_width - 1, led_index); + placed++; + y--; + } + } + + if((entry->layout == SKYDIMO_LAYOUT_PERIMETER_3) || (entry->layout == SKYDIMO_LAYOUT_PERIMETER_4)) + { + unsigned int placed = 0; + int x = (int)matrix_width - 2; + + while((placed < zone_2_leds) && (x >= 1)) + { + SetMatrixCell(0, (unsigned int)x, led_index); + placed++; + x--; + } + } + else if(entry->layout == SKYDIMO_LAYOUT_SIDES_2) + { + unsigned int placed = 0; + unsigned int y = 1; + + while((placed < zone_2_leds) && (y <= (matrix_height - 2))) + { + SetMatrixCell(y, matrix_width - 1, led_index); + placed++; + y++; + } + } + + unsigned int end_y = entry->layout == SKYDIMO_LAYOUT_PERIMETER_3 ? matrix_height - 1 : matrix_height - 2; + unsigned int placed = 0; + unsigned int y = 1; + + while((placed < zone_3_leds) && (y <= end_y)) + { + SetMatrixCell(y, 0, led_index); + placed++; + y++; + } + + if(entry->layout == SKYDIMO_LAYOUT_PERIMETER_4) + { + placed = 0; + unsigned int x = 1; + + while((placed < zone_4_leds) && (x <= (matrix_width - 2))) + { + SetMatrixCell(matrix_height - 1, x, led_index); + placed++; + x++; + } + } + + if(led_index != entry->total_leds) + { + LOG_ERROR("[SkydimoSerialController] Invalid matrix map for %s (%u/%u LEDs placed)", + model_id.c_str(), led_index, entry->total_leds); + } +} + +void SkydimoSerialController::SetMatrixCell(unsigned int y, unsigned int x, unsigned int& led_index) +{ + if((y < matrix_height) && (x < matrix_width)) + { + matrix_map[(y * matrix_width) + x] = led_index++; + } +} + +bool SkydimoSerialController::WriteFrame(const std::vector& colors, unsigned int count) +{ + std::lock_guard guard(device_mutex); + + if(!port_open) + { + return(false); + } + + if(count > colors.size()) + { + count = (unsigned int)colors.size(); + } + + std::vector packet; + packet.reserve(sizeof(skydimo_serial_frame_header) + SKYDIMO_SERIAL_FRAME_COUNT_SIZE + (count * SKYDIMO_SERIAL_BYTES_PER_LED)); + + for(unsigned int header_idx = 0; header_idx < sizeof(skydimo_serial_frame_header); header_idx++) + { + packet.push_back(skydimo_serial_frame_header[header_idx]); + } + + packet.push_back((unsigned char)((count >> 8) & 0xFF)); + packet.push_back((unsigned char)(count & 0xFF)); + + for(unsigned int led_idx = 0; led_idx < count; led_idx++) + { + packet.push_back(RGBGetRValue(colors[led_idx])); + packet.push_back(RGBGetGValue(colors[led_idx])); + packet.push_back(RGBGetBValue(colors[led_idx])); + } + + int packet_size = (int)packet.size(); + int total_bytes_written = 0; + + while(total_bytes_written < packet_size) + { + int bytes_written = serial_port_interface.serial_write((char*)packet.data() + total_bytes_written, + packet_size - total_bytes_written); + + if(bytes_written <= 0) + { + LOG_ERROR("[SkydimoSerialController] Failed to write RGB frame to %s (%d/%d bytes)", + port_name.c_str(), total_bytes_written, packet_size); + return(false); + } + + total_bytes_written += bytes_written; + } + + return(true); +} + +void SkydimoSerialController::SetLEDs(const std::vector& colors) +{ + WriteFrame(colors, leds_count); +} + +void SkydimoSerialController::SetBlack() +{ + std::vector black_colors; + + black_colors.resize(leds_count, ToRGBColor(0, 0, 0)); + WriteFrame(black_colors, leds_count); +} + +void SkydimoSerialController::SetLEDCount(unsigned int led_count) +{ + if(!resizable) + { + return; + } + + if((led_count < leds_min) || (led_count > leds_max)) + { + LOG_WARNING("[SkydimoSerialController] Ignoring invalid LED count %u for %s (valid range %u-%u)", + led_count, model_id.c_str(), leds_min, leds_max); + return; + } + + leds_count = led_count; +} + +std::string SkydimoSerialController::GetName() const +{ + return(model_name); +} + +std::string SkydimoSerialController::GetSerial() const +{ + return(serial_id); +} + +std::string SkydimoSerialController::GetLocation() const +{ + return("Serial: " + port_name); +} + +bool SkydimoSerialController::IsResizable() const +{ + return(resizable); +} + +unsigned int SkydimoSerialController::GetLEDCount() const +{ + return(leds_count); +} + +unsigned int SkydimoSerialController::GetMinLEDCount() const +{ + return(leds_min); +} + +unsigned int SkydimoSerialController::GetMaxLEDCount() const +{ + return(leds_max); +} + +unsigned int SkydimoSerialController::GetMatrixWidth() const +{ + return(matrix_width); +} + +unsigned int SkydimoSerialController::GetMatrixHeight() const +{ + return(matrix_height); +} + +const std::vector& SkydimoSerialController::GetMatrixMap() const +{ + return(matrix_map); +} diff --git a/Controllers/SkydimoSerialController/SkydimoSerialController.h b/Controllers/SkydimoSerialController/SkydimoSerialController.h new file mode 100644 index 000000000..2b0705fda --- /dev/null +++ b/Controllers/SkydimoSerialController/SkydimoSerialController.h @@ -0,0 +1,82 @@ +/*---------------------------------------------------------*\ +| SkydimoSerialController.h | +| | +| Driver for Skydimo serial devices | +| | +| Bartholomew Ho 30 Jun 2026 | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-or-later | +\*---------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include "RGBController.h" +#include "serial_port.h" + +#define SKYDIMO_SERIAL_BAUD_RATE 115200 +#define SKYDIMO_SERIAL_QUERY "Moni-A" +#define SKYDIMO_SERIAL_QUERY_LENGTH 6 +#define SKYDIMO_SERIAL_MODEL_ID_LENGTH 6 +#define SKYDIMO_SERIAL_READ_BUFFER_SIZE 64 +#define SKYDIMO_SERIAL_MAX_RESPONSE_SIZE 63 +#define SKYDIMO_SERIAL_QUERY_ATTEMPTS 10 +#define SKYDIMO_SERIAL_DEFAULT_LED_COUNT 0 +#define SKYDIMO_SERIAL_DEFAULT_MAX_LED_COUNT 150 +#define SKYDIMO_SERIAL_BYTES_PER_LED 3 +#define SKYDIMO_SERIAL_FRAME_COUNT_SIZE 2 +#define SKYDIMO_SERIAL_MATRIX_NA 0xFFFFFFFF + +class SkydimoSerialController +{ +public: + SkydimoSerialController(const std::string& port_name_arg); + ~SkydimoSerialController(); + + bool IsPresent(); + + void SetLEDs(const std::vector& colors); + void SetBlack(); + void SetLEDCount(unsigned int led_count); + + std::string GetName() const; + std::string GetSerial() const; + std::string GetLocation() const; + + bool IsResizable() const; + unsigned int GetLEDCount() const; + unsigned int GetMinLEDCount() const; + unsigned int GetMaxLEDCount() const; + + unsigned int GetMatrixWidth() const; + unsigned int GetMatrixHeight() const; + const std::vector& GetMatrixMap() const; + +private: + bool QueryInfo(); + void ConfigureOutput(); + void SetMatrixCell(unsigned int y, unsigned int x, unsigned int& led_index); + bool WriteFrame(const std::vector& colors, unsigned int count); + + std::string port_name; + std::string model_name; + std::string model_id; + std::string serial_id; + + bool port_open; + bool present; + bool resizable; + + unsigned int leds_count; + unsigned int leds_min; + unsigned int leds_max; + unsigned int matrix_width; + unsigned int matrix_height; + + std::vector matrix_map; + serial_port serial_port_interface; + std::mutex device_mutex; +}; diff --git a/Controllers/SkydimoSerialController/SkydimoSerialControllerDetect.cpp b/Controllers/SkydimoSerialController/SkydimoSerialControllerDetect.cpp new file mode 100644 index 000000000..fd37fce32 --- /dev/null +++ b/Controllers/SkydimoSerialController/SkydimoSerialControllerDetect.cpp @@ -0,0 +1,55 @@ +/*---------------------------------------------------------*\ +| SkydimoSerialControllerDetect.cpp | +| | +| Detector for Skydimo serial devices | +| | +| Bartholomew Ho 30 Jun 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 "RGBController_SkydimoSerial.h" +#include "SkydimoSerialController.h" +#include "find_usb_serial_port.h" + +/*---------------------------------------------------------*\ +| Skydimo serial vendor and product IDs | +\*---------------------------------------------------------*/ +#define SKYDIMO_SERIAL_VID 0x1A86 +#define SKYDIMO_SERIAL_PID 0x7523 + +DetectedControllers DetectSkydimoSerialControllers() +{ + DetectedControllers detected_controllers; + std::vector ports = find_usb_serial_port(SKYDIMO_SERIAL_VID, SKYDIMO_SERIAL_PID); + + for(unsigned int port_idx = 0; port_idx < ports.size(); port_idx++) + { + SkydimoSerialController* controller = new SkydimoSerialController(ports[port_idx]); + + if(controller->IsPresent()) + { + RGBController_SkydimoSerial* rgb_controller = new RGBController_SkydimoSerial(controller); + detected_controllers.push_back(rgb_controller); + LOG_INFO("[SkydimoSerialControllerDetect] Detected %s at %s", + controller->GetName().c_str(), ports[port_idx].c_str()); + } + else + { + delete controller; + } + } + + return(detected_controllers); +} + +REGISTER_DETECTOR("Skydimo Serial", DetectSkydimoSerialControllers); +/*---------------------------------------------------------------------------------------------------------*\ +| Entries for dynamic UDEV rules | +| | +| DUMMY_DEVICE_DETECTOR("Skydimo Serial", DetectSkydimoSerialControllers, 0x1A86, 0x7523 ) | +\*---------------------------------------------------------------------------------------------------------*/