Add support for Lian Li SL Infinity fan controller
authorGreg Burri <greg.burri@gmail.com>
Sun, 28 Apr 2024 21:04:06 +0000 (23:04 +0200)
committerGreg Burri <greg.burri@gmail.com>
Sun, 28 Apr 2024 21:04:06 +0000 (23:04 +0200)
Cargo.toml
src/lian_li_sl_infinity.rs [new file with mode: 0644]
src/machine.rs
src/main.rs
src/tests.rs

index 8ef1511..a69afbe 100644 (file)
@@ -21,7 +21,7 @@ flexi_logger = "0.28"
 log-panics = { version = "2", features = ["with-backtrace"] }
 log = "0.4"
 
-windows-service = "0.6"
+windows-service = "0.7"
 
 # HIDAPI is a library which allows an application to interface with
 # USB and Bluetooth HID-Class devices.
@@ -38,7 +38,7 @@ crc = "3.0"
 # netcorehost = "0.15"
 
 [dependencies.windows]
-version = "0.54"
+version = "0.56"
 features = [
     "Win32_Foundation",
     "Win32_Security",
diff --git a/src/lian_li_sl_infinity.rs b/src/lian_li_sl_infinity.rs
new file mode 100644 (file)
index 0000000..d16a4a3
--- /dev/null
@@ -0,0 +1,87 @@
+use crate::rgb::RGB;
+
+const LIANLI_VID: u16 = 0x0CF2;
+const LIANLI_UNI_HUB_SLINF_PID: u16 = 0xA102;
+
+const UNIHUB_SLINF_LED_MODE_STATIC_COLOR: u8 = 0x01;
+const UNIHUB_SLINF_LED_SPEED_000: u8 = 0x02;
+const UNIHUB_SLINF_LED_DIRECTION_LTR: u8 = 0x00;
+const UNIHUB_SLINF_LED_BRIGHTNESS_100: u8 = 0x00;
+
+const UNIHUB_SLINF_TRANSACTION_ID: u8 = 0xE0;
+
+const BUFFER_SIZE: usize = 353;
+
+const NB_LEDS_PER_FAN: u8 = 8;
+const NB_LEDS_PER_SIDE: u8 = 12;
+
+// Specific hardcoded values (should be given in the constructor).
+const CHANNEL_COUNT: u8 = 4; // 2 Channel per line of fans: one for fan itself and one for sides.
+const NB_FAN_PER_CHANNEL: u8 = 2; // 2 fans per channel.
+
+pub struct Device {
+    device: hidapi::HidDevice,
+}
+
+impl Device {
+    pub fn new(api: &hidapi::HidApi) -> Self {
+        let device = Device {
+            device: api.open(LIANLI_VID, LIANLI_UNI_HUB_SLINF_PID).unwrap(),
+        };
+
+        device
+    }
+
+    fn send_start_action(&self, channel_id: u8) {
+        let mut buffer = [0u8; 5];
+        buffer[0x00] = UNIHUB_SLINF_TRANSACTION_ID;
+        buffer[0x01] = 0x10;
+        buffer[0x02] = 0x60;
+        buffer[0x03] = 1 + channel_id / 2;
+        buffer[0x04] = NB_FAN_PER_CHANNEL;
+
+        let n_write = self.device.write(&buffer).unwrap();
+        assert_eq!(n_write, BUFFER_SIZE);
+    }
+
+    fn send_commit_data(&self, channel_id: u8) {
+        let mut buffer = [0u8; 6];
+        buffer[0x00] = UNIHUB_SLINF_TRANSACTION_ID;
+        buffer[0x01] = 0x10 + channel_id;
+        buffer[0x02] = UNIHUB_SLINF_LED_MODE_STATIC_COLOR;
+        buffer[0x03] = UNIHUB_SLINF_LED_SPEED_000;
+        buffer[0x04] = UNIHUB_SLINF_LED_DIRECTION_LTR;
+        buffer[0x05] = UNIHUB_SLINF_LED_BRIGHTNESS_100;
+
+        let n_write = self.device.write(&buffer).unwrap();
+        assert_eq!(n_write, BUFFER_SIZE);
+    }
+
+    pub fn set_color(&self, color: &RGB) {
+        for channel_id in 0..CHANNEL_COUNT {
+            self.send_start_action(channel_id);
+
+            let mut buffer = [0u8; BUFFER_SIZE];
+            buffer[0x00] = UNIHUB_SLINF_TRANSACTION_ID;
+            buffer[0x01] = 0x30 + channel_id;
+
+            let nb_leds = if channel_id % 2 == 0 {
+                NB_LEDS_PER_FAN * NB_FAN_PER_CHANNEL
+            } else {
+                NB_LEDS_PER_SIDE * NB_FAN_PER_CHANNEL
+            };
+
+            for i in 0..(26 as usize) {
+                let pos = i * 3 + 2;
+                buffer[pos] = color.red;
+                buffer[pos + 1] = color.blue;
+                buffer[pos + 2] = color.green;
+            }
+
+            let n_write = self.device.write(&buffer).unwrap();
+            assert_eq!(n_write, buffer.len());
+
+            self.send_commit_data(channel_id);
+        }
+    }
+}
index ac60e72..1012c00 100644 (file)
@@ -3,7 +3,7 @@ use nvapi::sys::i2c;
 
 use crate::{
     /*a770,*/ asus_aura_usb, corsair_lighting_pro, corsair_vengeance, cpu_temperature,
-    intel_arc, rgb,
+    intel_arc, lian_li_sl_infinity, rgb,
 };
 
 const RGB_FUSION2_GPU_REG_COLOR: u8 = 0x40;
@@ -75,6 +75,7 @@ impl Machine for MachineJiji {
 pub struct MachineLyssMetal {
     crosshair_device: asus_aura_usb::Device,
     corsair_lignting_pro: corsair_lighting_pro::Device,
+    lian_li_sl_infinity: lian_li_sl_infinity::Device,
     gpus: Vec<nvapi::PhysicalGpu>,
 }
 
@@ -97,6 +98,7 @@ impl MachineLyssMetal {
                     blue: 40,
                 },
             ),
+            lian_li_sl_infinity: lian_li_sl_infinity::Device::new(&api),
             gpus: nvapi::PhysicalGpu::enumerate()?,
         };
 
@@ -159,8 +161,9 @@ impl MachineLyssMetal {
 
 impl Machine for MachineLyssMetal {
     fn set_color(&mut self, color: &rgb::RGB) {
-        self.crosshair_device.set_color(&color);
+        self.crosshair_device.set_color(&color).unwrap();
         self.corsair_lignting_pro.set_color(&color);
+        self.lian_li_sl_infinity.set_color(&color);
         // self.set_color_3080ti(&color); // TODO.
     }
 
index fdbb104..014c61d 100644 (file)
@@ -38,6 +38,7 @@ mod intel_arc {
 mod a770;
 mod asus_aura_usb;
 mod corsair_lighting_pro;
+mod lian_li_sl_infinity;
 mod machine;
 mod main_loop;
 mod winring0;
index f01f77a..b49081e 100644 (file)
@@ -3,8 +3,8 @@ use std::collections::HashMap;
 use wmi::{COMLibrary, Variant, WMIConnection};
 
 use crate::{
-    a770, asus_aura_usb, corsair_lighting_pro, corsair_vengeance, cpu_temperature, machine,
-    rgb::RGB, winring0, wrapper_winring0,
+    a770, asus_aura_usb, corsair_lighting_pro, corsair_vengeance, cpu_temperature,
+    lian_li_sl_infinity, machine, rgb::RGB, winring0, wrapper_winring0,
 };
 
 pub fn tests() {
@@ -14,6 +14,7 @@ pub fn tests() {
 
     // test_asus_aura_usb(asus_aura_usb::Motherboard::Asus650e);
     // test_corsair_lighting_pro();
+    test_lianli_sl_infinity();
     // list_usb_devices();
     // test_roccat();
     // test_wmi();
@@ -22,7 +23,7 @@ pub fn tests() {
     // test_3080ti();
     // test_read_temperature_cpu();
     // test_read_temperature_a770();
-    test_read_temperature_3080();
+    // test_read_temperature_3080();
 
     winring0::deinit();
 
@@ -136,6 +137,17 @@ fn test_corsair_lighting_pro() {
     }
 }
 
+fn test_lianli_sl_infinity() {
+    let api = hidapi::HidApi::new().unwrap();
+    let device = lian_li_sl_infinity::Device::new(&api);
+
+    device.set_color(&RGB {
+        red: 0,
+        green: 0,
+        blue: 255,
+    });
+}
+
 fn test_corsair() {
     let corsair_controllers = [
         corsair_vengeance::Controller::new(0x19),