1use std::time::Duration;
2
3use rusb::{DeviceHandle, GlobalContext};
4
5const INTERFACE_ID: u8 = 0x01;
6const CONFIGURATION_ID: u8 = 0;
7const VENDOR_ID: u16 = 0x046D;
8const PRODUCT_ID: u16 = 0xC09D;
9
10#[derive(Clone)]
12pub enum Direction {
13 Left = 0x01,
14 Right = 0x06,
15}
16
17#[derive(Debug)]
18pub struct Controller {
19 inner: DeviceHandle<GlobalContext>,
20 timeout: Duration,
21}
22
23impl Controller {
24 pub fn new_with_timeout(timeout: Duration) -> rusb::Result<Self> {
25 let handle = rusb::open_device_with_vid_pid(VENDOR_ID, PRODUCT_ID);
26 match handle {
27 Some(handle) => {
28 handle.set_active_configuration(CONFIGURATION_ID)?;
29 Ok(Self {
30 inner: handle,
31 timeout,
32 })
33 }
34 None => Err(rusb::Error::NoDevice),
35 }
36 }
37
38 pub fn new() -> rusb::Result<Self> {
39 Self::new_with_timeout(Duration::from_secs(2))
40 }
41
42 pub fn set_solid(&self, rgb: [u8; 3]) -> rusb::Result<()> {
45 let [red, green, blue] = rgb;
46 self.command(
47 &[
48 0x11, 0xff, 0x0e, 0x1b, 0x00, 0x01, red, green, blue, 0x00, 0x00, 0x00, 0x00, 0x00,
49 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
50 ],
51 true,
52 )
53 }
54
55 pub fn set_breathe(&self, rgb: [u8; 3], rate: u16, brightness: u8) -> rusb::Result<()> {
56 let [red, green, blue] = rgb;
57 let rate_bytes = rate.to_be_bytes();
58 self.command(
59 &[
60 0x11,
61 0xff,
62 0x0e,
63 0x1b,
64 0x00,
65 0x04,
66 red,
67 green,
68 blue,
69 rate_bytes[0],
70 rate_bytes[1],
71 0x00,
72 brightness,
73 0x00,
74 0x00,
75 0x00,
76 0x01,
77 0x00,
78 0x00,
79 0x00,
80 ],
81 false,
82 )
83 }
84
85 pub fn set_cycle(&self, rate: u16, brightness: u8) -> rusb::Result<()> {
86 let rate_bytes = rate.to_be_bytes();
87 self.command(
88 &[
89 0x11,
90 0xff,
91 0x0e,
92 0x1b,
93 0x00,
94 0x02,
95 0x00,
96 0x00,
97 0x00,
98 0x00,
99 0x00,
100 rate_bytes[0],
101 rate_bytes[1],
102 brightness,
103 0x00,
104 0x00,
105 0x01,
106 0x00,
107 0x00,
108 0x00,
109 ],
110 true,
111 )
112 }
113
114 pub fn set_triple(&self, colors: [[u8; 3]; 3]) -> rusb::Result<()> {
115 self.command(
116 &[
117 0x11,
118 0xff,
119 0x12,
120 0x1b,
121 0x01,
122 colors[0][0],
123 colors[0][1],
124 colors[0][2],
125 0x02,
126 colors[1][0],
127 colors[1][1],
128 colors[1][2],
129 0x03,
130 colors[2][0],
131 colors[2][1],
132 colors[2][2],
133 0x00,
134 0x00,
135 0x00,
136 0x00,
137 ],
138 false,
139 )
140 }
141
142 pub fn set_wave(&self, rate: u16, brightness: u8, direction: Direction) -> rusb::Result<()> {
143 let rate_bytes = rate.to_be_bytes();
144 self.command(
145 &[
146 0x11,
147 0xff,
148 0x0e,
149 0x1b,
150 0x00,
151 0x03,
152 0x00,
153 0x00,
154 0x00,
155 0x00,
156 0x00,
157 0x00,
158 rate_bytes[0],
159 direction as u8,
160 brightness,
161 rate_bytes[1],
162 0x01,
163 0x00,
164 0x00,
165 0x00,
166 ],
167 true,
168 )
169 }
170
171 pub fn set_blend(&self, rate: u16, brightness: u8) -> rusb::Result<()> {
172 let rate_bytes = rate.to_be_bytes();
173 self.command(
174 &[
175 0x11,
176 0xff,
177 0x0e,
178 0x1b,
179 0x00,
180 0x06,
181 0x00,
182 0x00,
183 0x00,
184 0x00,
185 0x00,
186 0x00,
187 rate_bytes[0],
188 rate_bytes[1],
189 brightness,
190 0x00,
191 0x01,
192 0x00,
193 0x00,
194 0x00,
195 ],
196 true,
197 )
198 }
199
200 fn command_prologue(&self) -> rusb::Result<()> {
204 self.inner.detach_kernel_driver(INTERFACE_ID)?;
207 self.inner.claim_interface(INTERFACE_ID)?;
210 Ok(())
211 }
212
213 fn command_epilogue(&self) -> rusb::Result<()> {
217 self.inner.release_interface(INTERFACE_ID)?;
220 self.inner.attach_kernel_driver(INTERFACE_ID)?;
223 Ok(())
224 }
225
226 pub fn command(&self, data: &[u8], disable_ls_memory: bool) -> rusb::Result<()> {
227 self.command_prologue()?;
229
230 if disable_ls_memory {
232 self.inner
233 .write_control(
234 0x21,
235 0x09,
236 0x210,
237 0x01,
238 &[0x10, 0xff, 0x0e, 0x5b, 0x01, 0x03, 0x05],
239 self.timeout,
240 )
241 .unwrap();
242 }
243
244 self.inner
246 .write_control(0x21, 0x09, 0x211, 0x01, data, self.timeout)
247 .unwrap();
248
249 let is_triple_command = data[0..4] == [0x11, 0xff, 0x12, 0x1b];
252 if is_triple_command {
253 self.inner.write_control(
254 0x21,
255 0x09,
256 0x211,
257 0x01,
258 &[
259 0x11, 0xff, 0x12, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
260 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
261 ],
262 self.timeout,
263 )?;
264 }
265
266 self.command_epilogue()?;
267 Ok(())
268 }
269}