diff --git a/Controllers/AMBXController/AMBXController.cpp b/Controllers/AMBXController/AMBXController.cpp new file mode 100644 index 00000000..f1de5bae --- /dev/null +++ b/Controllers/AMBXController/AMBXController.cpp @@ -0,0 +1,195 @@ +/*---------------------------------------------------------*\ +| AMBXController.cpp | +| | +| Driver for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "AMBXController.h" +#include "LogManager.h" +#include "StringUtils.h" +#include +#include +#include + +AMBXController::AMBXController(const char* path) +{ + initialized = false; + usb_context = nullptr; + dev_handle = nullptr; + + location = "USB: "; + location += path; + + // Initialize libusb in this instance + if(libusb_init(&usb_context) < 0) + { + return; + } + + // Get the device list + libusb_device** device_list; + ssize_t device_count = libusb_get_device_list(usb_context, &device_list); + + if(device_count < 0) + { + return; + } + + for(ssize_t i = 0; i < device_count; i++) + { + libusb_device* device = device_list[i]; + struct libusb_device_descriptor desc; + + if(libusb_get_device_descriptor(device, &desc) != LIBUSB_SUCCESS) + { + continue; + } + + if(desc.idVendor == AMBX_VID && desc.idProduct == AMBX_PID) + { + uint8_t bus = libusb_get_bus_number(device); + uint8_t address = libusb_get_device_address(device); + + char current_path[32]; + snprintf(current_path, sizeof(current_path), "%d-%d", bus, address); + + if(strcmp(path, current_path) == 0) + { + // Try to open this device + if(libusb_open(device, &dev_handle) != LIBUSB_SUCCESS) + { + continue; + } + + // Try to detach the kernel driver if attached + if(libusb_kernel_driver_active(dev_handle, 0)) + { + libusb_detach_kernel_driver(dev_handle, 0); + } + + // Set auto-detach for Windows compatibility + libusb_set_auto_detach_kernel_driver(dev_handle, 1); + + // Claim the interface + if(libusb_claim_interface(dev_handle, 0) != LIBUSB_SUCCESS) + { + libusb_close(dev_handle); + dev_handle = nullptr; + continue; + } + + // Get string descriptor for serial number if available + if(desc.iSerialNumber != 0) + { + unsigned char serial_str[256]; + int serial_result = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber, + serial_str, sizeof(serial_str)); + if(serial_result > 0) + { + serial = std::string(reinterpret_cast(serial_str), serial_result); + } + } + + // Successfully opened and claimed the device + initialized = true; + break; + } + } + } + + libusb_free_device_list(device_list, 1); +} + +AMBXController::~AMBXController() +{ + if(initialized) + { + // Turn off all lights before closing + unsigned int led_ids[5] = + { + AMBX_LIGHT_LEFT, + AMBX_LIGHT_RIGHT, + AMBX_LIGHT_WALL_LEFT, + AMBX_LIGHT_WALL_CENTER, + AMBX_LIGHT_WALL_RIGHT + }; + + RGBColor colors[5] = { 0, 0, 0, 0, 0 }; + SetLEDColors(led_ids, colors, 5); + } + + if(dev_handle != nullptr) + { + // Release the interface + libusb_release_interface(dev_handle, 0); + + // Close the device + libusb_close(dev_handle); + dev_handle = nullptr; + } + + if(usb_context != nullptr) + { + libusb_exit(usb_context); + usb_context = nullptr; + } +} + +std::string AMBXController::GetDeviceLocation() +{ + return location; +} + +std::string AMBXController::GetSerialString() +{ + return serial; +} + +bool AMBXController::IsInitialized() +{ + return initialized; +} + +void AMBXController::SendPacket(unsigned char* packet, unsigned int size) +{ + if(!initialized || dev_handle == nullptr) + { + return; + } + + int actual_length = 0; + libusb_interrupt_transfer(dev_handle, AMBX_ENDPOINT_OUT, packet, size, &actual_length, 100); +} + +void AMBXController::SetLEDColor(unsigned int led, RGBColor color) +{ + if(!initialized) + { + return; + } + + unsigned char color_buf[6] = + { + AMBX_PACKET_HEADER, + static_cast(led), + AMBX_SET_COLOR, + RGBGetRValue(color), + RGBGetGValue(color), + RGBGetBValue(color) + }; + + SendPacket(color_buf, 6); + + std::this_thread::sleep_for(std::chrono::milliseconds(2)); +} + +void AMBXController::SetLEDColors(unsigned int* leds, RGBColor* colors, unsigned int count) +{ + for(unsigned int i = 0; i < count; i++) + { + SetLEDColor(leds[i], colors[i]); + } +} diff --git a/Controllers/AMBXController/AMBXController.h b/Controllers/AMBXController/AMBXController.h new file mode 100644 index 00000000..a5ea914d --- /dev/null +++ b/Controllers/AMBXController/AMBXController.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------*\ +| AMBXController.h | +| | +| Driver for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include + +#ifdef _WIN32 +#include "dependencies/libusb-1.0.27/include/libusb.h" +#else +#include +#endif + +#define AMBX_VID 0x0471 +#define AMBX_PID 0x083F +#define AMBX_ENDPOINT_OUT 0x02 +#define AMBX_PACKET_HEADER 0xA1 +#define AMBX_SET_COLOR 0x03 + +enum +{ + AMBX_LIGHT_LEFT = 0x0B, + AMBX_LIGHT_RIGHT = 0x1B, + AMBX_LIGHT_WALL_LEFT = 0x2B, + AMBX_LIGHT_WALL_CENTER = 0x3B, + AMBX_LIGHT_WALL_RIGHT = 0x4B +}; + +class AMBXController +{ +public: + AMBXController(const char* path); + ~AMBXController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + bool IsInitialized(); + void SetLEDColor(unsigned int led, RGBColor color); + void SetLEDColors(unsigned int* leds, RGBColor* colors, unsigned int count); + +private: + libusb_context* usb_context; + libusb_device_handle* dev_handle; + std::string location; + std::string serial; + bool initialized; + + void SendPacket(unsigned char* packet, unsigned int size); +}; diff --git a/Controllers/AMBXController/AMBXControllerDetect.cpp b/Controllers/AMBXController/AMBXControllerDetect.cpp new file mode 100644 index 00000000..cae0f4c3 --- /dev/null +++ b/Controllers/AMBXController/AMBXControllerDetect.cpp @@ -0,0 +1,83 @@ +/*---------------------------------------------------------*\ +| AMBXControllerDetect.cpp | +| | +| Detector for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "Detector.h" +#include "AMBXController.h" +#include "RGBController.h" +#include "RGBController_AMBX.h" + +#ifdef _WIN32 +#include "dependencies/libusb-1.0.27/include/libusb.h" +#else +#include +#endif + +/******************************************************************************************\ +* * +* DetectAMBXControllers * +* * +* Detect Philips amBX Gaming devices * +* * +\******************************************************************************************/ + +void DetectAMBXControllers() +{ + libusb_context* ctx = NULL; + + if(libusb_init(&ctx) < 0) + { + return; + } + + libusb_device** devs; + ssize_t num_devs = libusb_get_device_list(ctx, &devs); + + if(num_devs <= 0) + { + libusb_exit(ctx); + return; + } + + for(ssize_t i = 0; i < num_devs; i++) + { + libusb_device* dev = devs[i]; + libusb_device_descriptor desc; + + if(libusb_get_device_descriptor(dev, &desc) != 0) + { + continue; + } + + if(desc.idVendor == AMBX_VID && desc.idProduct == AMBX_PID) + { + uint8_t bus = libusb_get_bus_number(dev); + uint8_t address = libusb_get_device_address(dev); + char device_path[32]; + snprintf(device_path, sizeof(device_path), "%d-%d", bus, address); + + // Use the AMBXController to handle opening and initializing + AMBXController* controller = new AMBXController(device_path); + + if(controller->IsInitialized()) + { + RGBController_AMBX* rgb_controller = new RGBController_AMBX(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } + else + { + delete controller; + } + } + } + + libusb_free_device_list(devs, 1); + libusb_exit(ctx); +} + +REGISTER_DETECTOR("Philips amBX", DetectAMBXControllers); diff --git a/Controllers/AMBXController/RGBController_AMBX.cpp b/Controllers/AMBXController/RGBController_AMBX.cpp new file mode 100644 index 00000000..53d217e9 --- /dev/null +++ b/Controllers/AMBXController/RGBController_AMBX.cpp @@ -0,0 +1,184 @@ +/*---------------------------------------------------------*\ +| RGBController_AMBX.cpp | +| | +| RGB Controller for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#include "RGBController_AMBX.h" + +/**------------------------------------------------------------------*\ + @name Philips amBX + @category Accessory + @type USB + @save :x: + @direct :white_check_mark: + @effects :x: + @detectors DetectAMBXControllers + @comment The Philips amBX Gaming lights system includes left and right + lights and a wall-washer bar with three zones. +\*-------------------------------------------------------------------*/ + +RGBController_AMBX::RGBController_AMBX(AMBXController* controller_ptr) +{ + controller = controller_ptr; + + name = "Philips amBX"; + vendor = "Philips"; + type = DEVICE_TYPE_ACCESSORY; + description = "Philips amBX Gaming Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + SetupZones(); +} + +RGBController_AMBX::~RGBController_AMBX() +{ + delete controller; +} + +void RGBController_AMBX::SetupZones() +{ + // Set up zones + zone side_lights_zone; + side_lights_zone.name = "Side Lights"; + side_lights_zone.type = ZONE_TYPE_LINEAR; + side_lights_zone.leds_min = 2; + side_lights_zone.leds_max = 2; + side_lights_zone.leds_count = 2; + side_lights_zone.matrix_map = NULL; + zones.push_back(side_lights_zone); + + zone wallwasher_zone; + wallwasher_zone.name = "Wallwasher"; + wallwasher_zone.type = ZONE_TYPE_LINEAR; + wallwasher_zone.leds_min = 3; + wallwasher_zone.leds_max = 3; + wallwasher_zone.leds_count = 3; + wallwasher_zone.matrix_map = NULL; + zones.push_back(wallwasher_zone); + + // Set up LEDs + led left_light; + left_light.name = "Left"; + left_light.value = AMBX_LIGHT_LEFT; + leds.push_back(left_light); + + led right_light; + right_light.name = "Right"; + right_light.value = AMBX_LIGHT_RIGHT; + leds.push_back(right_light); + + led wall_left; + wall_left.name = "Wall Left"; + wall_left.value = AMBX_LIGHT_WALL_LEFT; + leds.push_back(wall_left); + + led wall_center; + wall_center.name = "Wall Center"; + wall_center.value = AMBX_LIGHT_WALL_CENTER; + leds.push_back(wall_center); + + led wall_right; + wall_right.name = "Wall Right"; + wall_right.value = AMBX_LIGHT_WALL_RIGHT; + leds.push_back(wall_right); + + SetupColors(); +} + +void RGBController_AMBX::ResizeZone(int /*zone*/, int /*new_size*/) +{ + // This device does not support resizing zones +} + +void RGBController_AMBX::DeviceUpdateLEDs() +{ + if(!controller->IsInitialized()) + { + return; + } + + unsigned int led_values[5]; + RGBColor led_colors[5]; + + for(unsigned int led_idx = 0; led_idx < leds.size(); led_idx++) + { + led_values[led_idx] = leds[led_idx].value; + led_colors[led_idx] = colors[led_idx]; + } + + controller->SetLEDColors(led_values, led_colors, static_cast(leds.size())); +} + +void RGBController_AMBX::UpdateZoneLEDs(int zone) +{ + if(!controller->IsInitialized()) + { + return; + } + + unsigned int start_idx = 0; + unsigned int zone_size = 0; + + // Calculate start index and size + for(unsigned int z_idx = 0; z_idx < zones.size(); z_idx++) + { + if(z_idx == (unsigned int)zone) + { + zone_size = zones[z_idx].leds_count; + break; + } + + start_idx += zones[z_idx].leds_count; + } + + unsigned int led_values[5]; + RGBColor led_colors[5]; + + for(unsigned int led_idx = 0; led_idx < zone_size; led_idx++) + { + unsigned int current_idx = start_idx + led_idx; + led_values[led_idx] = leds[current_idx].value; + led_colors[led_idx] = colors[current_idx]; + } + + controller->SetLEDColors(led_values, led_colors, zone_size); +} + +void RGBController_AMBX::UpdateSingleLED(int led) +{ + if(!controller->IsInitialized()) + { + return; + } + + unsigned int led_value = leds[led].value; + RGBColor color = colors[led]; + controller->SetLEDColor(led_value, color); +} + +void RGBController_AMBX::DeviceUpdateMode() +{ + if(!controller->IsInitialized()) + { + return; + } + + DeviceUpdateLEDs(); +} + +void RGBController_AMBX::SetCustomMode() +{ + active_mode = 0; +} diff --git a/Controllers/AMBXController/RGBController_AMBX.h b/Controllers/AMBXController/RGBController_AMBX.h new file mode 100644 index 00000000..29c772bb --- /dev/null +++ b/Controllers/AMBXController/RGBController_AMBX.h @@ -0,0 +1,33 @@ +/*---------------------------------------------------------*\ +| RGBController_AMBX.h | +| | +| RGB Controller for Philips amBX Gaming lights | +| | +| This file is part of the OpenRGB project | +| SPDX-License-Identifier: GPL-2.0-only | +\*---------------------------------------------------------*/ + +#pragma once + +#include "RGBController.h" +#include "AMBXController.h" + +class RGBController_AMBX : public RGBController +{ +public: + RGBController_AMBX(AMBXController* controller_ptr); + ~RGBController_AMBX(); + + void SetupZones(); + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void SetCustomMode(); + +private: + AMBXController* controller; +};