1 /*
2  * This file is part of OpenCorsairLink.
3  * Copyright (C) 2017-2019  Sean Nelson <audiohacked@gmail.com>
4 
5  * OpenCorsairLink is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 2 of the License, or
8  * any later version.
9 
10  * OpenCorsairLink is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14 
15  * You should have received a copy of the GNU General Public License
16  * along with OpenCorsairLink.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "device.h"
20 #include "driver.h"
21 #include "lowlevel/coolit.h"
22 #include "print.h"
23 #include "protocol/coolit.h"
24 
25 #include <errno.h>
26 #include <libusb.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 int
corsairlink_coolit_fan_count(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)33 corsairlink_coolit_fan_count(
34     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
35 {
36     int rr;
37     uint8_t response[64];
38     uint8_t commands[64];
39     memset( response, 0, sizeof( response ) );
40     memset( commands, 0, sizeof( commands ) );
41 
42     uint8_t ii = 0;
43 
44     commands[++ii] = CommandId++;
45     commands[++ii] = ReadOneByte;
46     commands[++ii] = FAN_Count;
47 
48     commands[0] = ii;
49 
50     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
51     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
52 
53     ctrl->fan_count = response[2] - 1; // we subtract 1 because count includes pump
54 
55     return rr;
56 }
57 
58 int
corsairlink_coolit_fan_print_mode(uint8_t mode,uint16_t data,char * modestr,uint8_t modestr_size)59 corsairlink_coolit_fan_print_mode(
60     uint8_t mode, uint16_t data, char* modestr, uint8_t modestr_size )
61 {
62     int rr = 0;
63     uint8_t isConnected = mode & 0x80;
64     uint8_t is4pin = mode & 0x01;
65     uint8_t real_mode = mode & 0x0E;
66 
67     if ( !isConnected )
68         snprintf( modestr, modestr_size, "Not connected or failed" );
69     else if ( real_mode == COOLIT_Performance )
70         snprintf( modestr, modestr_size, "Performance Mode (%s)", is4pin ? "4PIN" : "3PIN" );
71     else if ( real_mode == COOLIT_Balanced )
72         snprintf( modestr, modestr_size, "Balanced Mode (%s)", is4pin ? "4PIN" : "3PIN" );
73     else if ( real_mode == COOLIT_Quiet )
74         snprintf( modestr, modestr_size, "Quiet Mode (%s)", is4pin ? "4PIN" : "3PIN" );
75     else if ( real_mode == COOLIT_Default )
76         snprintf( modestr, modestr_size, "Default Mode (%s)", is4pin ? "4PIN" : "3PIN" );
77     else if ( real_mode == COOLIT_FixedPWM )
78         snprintf(
79             modestr, modestr_size, "Fixed PWM Mode (%s) set to %d%%", is4pin ? "4PIN" : "3PIN",
80             ( data + 1 ) * 100 / 256 );
81     else if ( real_mode == COOLIT_FixedRPM )
82         snprintf(
83             modestr, modestr_size, "Fixed RPM Mode (%s) set to %d", is4pin ? "4PIN" : "3PIN",
84             data );
85     else if ( real_mode == COOLIT_Custom )
86         snprintf( modestr, modestr_size, "Custom Curve Mode (%s)", is4pin ? "4PIN" : "3PIN" );
87     return rr;
88 }
89 
90 int
corsairlink_coolit_fan_mode_read(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)91 corsairlink_coolit_fan_mode_read(
92     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
93 {
94     int rr;
95     uint8_t response[64];
96     uint8_t commands[64];
97     memset( response, 0, sizeof( response ) );
98     memset( commands, 0, sizeof( commands ) );
99 
100     uint8_t ii = 0;
101     commands[++ii] = CommandId++;
102     commands[++ii] = WriteOneByte;
103     commands[++ii] = FAN_Select;
104     commands[++ii] = ctrl->channel;
105 
106     commands[++ii] = CommandId++;
107     commands[++ii] = ReadOneByte;
108     commands[++ii] = FAN_Mode;
109 
110     commands[0] = ii;
111 
112     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
113     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
114 
115     ctrl->mode = response[4];
116 
117     return rr;
118 }
119 
120 int
corsairlink_coolit_fan_mode_read_rpm(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)121 corsairlink_coolit_fan_mode_read_rpm(
122     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
123 {
124     int rr;
125     uint8_t response[64];
126     uint8_t commands[64];
127     memset( response, 0, sizeof( response ) );
128     memset( commands, 0, sizeof( commands ) );
129 
130     uint8_t ii = 0;
131     commands[++ii] = CommandId++;
132     commands[++ii] = WriteOneByte;
133     commands[++ii] = FAN_Select;
134     commands[++ii] = ctrl->channel;
135 
136     commands[++ii] = CommandId++;
137     commands[++ii] = ReadOneByte;
138     commands[++ii] = FAN_Mode;
139 
140     commands[0] = ii;
141 
142     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
143     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
144 
145     ctrl->mode = response[4];
146 
147     ii = 0;
148     memset( response, 0, sizeof( response ) );
149     memset( commands, 0, sizeof( commands ) );
150 
151     commands[++ii] = CommandId++;
152     commands[++ii] = ReadTwoBytes;
153     commands[++ii] = FAN_FixedRPM;
154     commands[0] = ii;
155     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
156     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
157     ctrl->speed_rpm = ( response[3] << 8 ) + response[2];
158 
159     return rr;
160 }
161 
162 int
corsairlink_coolit_fan_mode_read_pwm(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)163 corsairlink_coolit_fan_mode_read_pwm(
164     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
165 {
166     int rr;
167     uint8_t response[64];
168     uint8_t commands[64];
169     memset( response, 0, sizeof( response ) );
170     memset( commands, 0, sizeof( commands ) );
171 
172     uint8_t ii = 0;
173     commands[++ii] = CommandId++;
174     commands[++ii] = WriteOneByte;
175     commands[++ii] = FAN_Select;
176     commands[++ii] = ctrl->channel;
177 
178     commands[++ii] = CommandId++;
179     commands[++ii] = ReadOneByte;
180     commands[++ii] = FAN_Mode;
181 
182     commands[0] = ii;
183 
184     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
185     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
186 
187     ctrl->mode = response[4];
188 
189     ii = 0;
190     memset( response, 0, sizeof( response ) );
191     memset( commands, 0, sizeof( commands ) );
192 
193     commands[++ii] = CommandId++;
194     commands[++ii] = ReadOneByte;
195     commands[++ii] = FAN_FixedPWM;
196     commands[0] = ii;
197     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
198     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
199     ctrl->speed_pwm = response[2];
200 
201     return rr;
202 }
203 
204 int
corsairlink_coolit_fan_mode_performance(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)205 corsairlink_coolit_fan_mode_performance(
206     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
207 {
208     int rr;
209     uint8_t response[64];
210     uint8_t commands[64];
211     memset( response, 0, sizeof( response ) );
212     memset( commands, 0, sizeof( commands ) );
213 
214     uint8_t ii = 0;
215     commands[++ii] = CommandId++;
216     commands[++ii] = WriteOneByte;
217     commands[++ii] = FAN_Select;
218     commands[++ii] = ctrl->channel;
219 
220     commands[++ii] = CommandId++;
221     commands[++ii] = WriteOneByte;
222     commands[++ii] = FAN_Mode;
223 
224     commands[++ii] = COOLIT_Performance;
225 
226     commands[0] = ii;
227 
228     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
229     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
230 
231     return rr;
232 }
233 
234 int
corsairlink_coolit_fan_mode_balanced(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)235 corsairlink_coolit_fan_mode_balanced(
236     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
237 {
238     int rr;
239     uint8_t response[64];
240     uint8_t commands[64];
241     memset( response, 0, sizeof( response ) );
242     memset( commands, 0, sizeof( commands ) );
243 
244     uint8_t ii = 0;
245     commands[++ii] = CommandId++;
246     commands[++ii] = WriteOneByte;
247     commands[++ii] = FAN_Select;
248     commands[++ii] = ctrl->channel;
249 
250     commands[++ii] = CommandId++;
251     commands[++ii] = WriteOneByte;
252     commands[++ii] = FAN_Mode;
253 
254     commands[++ii] = COOLIT_Balanced;
255 
256     commands[0] = ii;
257 
258     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
259     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
260 
261     return rr;
262 }
263 
264 int
corsairlink_coolit_fan_mode_quiet(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)265 corsairlink_coolit_fan_mode_quiet(
266     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
267 {
268     int rr;
269     uint8_t response[64];
270     uint8_t commands[64];
271     memset( response, 0, sizeof( response ) );
272     memset( commands, 0, sizeof( commands ) );
273 
274     uint8_t ii = 0;
275     commands[++ii] = CommandId++;
276     commands[++ii] = WriteOneByte;
277     commands[++ii] = FAN_Select;
278     commands[++ii] = ctrl->channel;
279 
280     commands[++ii] = CommandId++;
281     commands[++ii] = WriteOneByte;
282     commands[++ii] = FAN_Mode;
283 
284     commands[++ii] = COOLIT_Quiet;
285 
286     commands[0] = ii;
287 
288     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
289     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
290 
291     return rr;
292 }
293 
294 int
corsairlink_coolit_fan_mode_default(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)295 corsairlink_coolit_fan_mode_default(
296     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
297 {
298     int rr;
299     uint8_t response[64];
300     uint8_t commands[64];
301     memset( response, 0, sizeof( response ) );
302     memset( commands, 0, sizeof( commands ) );
303 
304     uint8_t ii = 0;
305     commands[++ii] = CommandId++;
306     commands[++ii] = WriteOneByte;
307     commands[++ii] = FAN_Select;
308     commands[++ii] = ctrl->channel;
309 
310     commands[++ii] = CommandId++;
311     commands[++ii] = WriteOneByte;
312     commands[++ii] = FAN_Mode;
313 
314     commands[++ii] = COOLIT_Default;
315 
316     commands[0] = ii;
317 
318     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
319     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
320 
321     return rr;
322 }
323 
324 int
corsairlink_coolit_fan_mode_rpm(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)325 corsairlink_coolit_fan_mode_rpm(
326     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
327 {
328     int rr;
329     uint8_t response[64];
330     uint8_t commands[64];
331     memset( response, 0, sizeof( response ) );
332     memset( commands, 0, sizeof( commands ) );
333 
334     uint8_t ii = 0;
335     commands[++ii] = CommandId++;
336     commands[++ii] = WriteOneByte;
337     commands[++ii] = FAN_Select;
338     commands[++ii] = ctrl->channel;
339 
340     commands[++ii] = CommandId++;
341     commands[++ii] = WriteOneByte;
342     commands[++ii] = FAN_Mode;
343 
344     commands[++ii] = COOLIT_FixedRPM;
345     commands[++ii] = CommandId++;
346     commands[++ii] = WriteTwoBytes;
347     commands[++ii] = FAN_FixedRPM;
348     commands[++ii] = ctrl->speed_rpm & 0xFF;
349     commands[++ii] = ( ctrl->speed_rpm >> 8 ) & 0xFF;
350 
351     commands[0] = ii;
352 
353     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
354     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
355 
356     return rr;
357 }
358 
359 int
corsairlink_coolit_fan_mode_pwm(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)360 corsairlink_coolit_fan_mode_pwm(
361     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
362 {
363     int rr;
364     uint8_t response[64];
365     uint8_t commands[64];
366     memset( response, 0, sizeof( response ) );
367     memset( commands, 0, sizeof( commands ) );
368 
369     uint8_t ii = 0;
370     commands[++ii] = CommandId++;
371     commands[++ii] = WriteOneByte;
372     commands[++ii] = FAN_Select;
373     commands[++ii] = ctrl->channel;
374 
375     commands[++ii] = CommandId++;
376     commands[++ii] = WriteOneByte;
377     commands[++ii] = FAN_Mode;
378 
379     commands[++ii] = COOLIT_FixedPWM;
380     commands[++ii] = CommandId++;
381     commands[++ii] = WriteOneByte;
382     commands[++ii] = FAN_FixedPWM;
383     commands[++ii] = ctrl->speed_pwm & 0xFF;
384 
385     commands[0] = ii;
386 
387     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
388     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
389 
390     return rr;
391 }
392 
393 int
corsairlink_coolit_fan_mode_custom(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)394 corsairlink_coolit_fan_mode_custom(
395     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
396 {
397     int rr;
398     uint8_t response[64];
399     uint8_t commands[64];
400     memset( response, 0, sizeof( response ) );
401     memset( commands, 0, sizeof( commands ) );
402 
403     uint8_t ii = 0;
404     commands[++ii] = CommandId++;
405     commands[++ii] = WriteOneByte;
406     commands[++ii] = FAN_Select;
407     commands[++ii] = ctrl->channel;
408 
409     commands[++ii] = CommandId++;
410     commands[++ii] = WriteOneByte;
411     commands[++ii] = FAN_Mode;
412 
413     commands[++ii] = COOLIT_Custom;
414 
415     commands[0] = ii;
416 
417     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
418     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
419 
420     return rr;
421 }
422 
423 int
corsairlink_coolit_fan_curve(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)424 corsairlink_coolit_fan_curve(
425     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
426 {
427     int rr;
428     uint8_t response[64];
429     uint8_t commands[64];
430     memset( response, 0, sizeof( response ) );
431     memset( commands, 0, sizeof( commands ) );
432 
433     // commands[0] = FanCurve;
434     // commands[1] = UnknownFanCurve;
435 
436     uint8_t ii = 0;
437 
438     commands[++ii] = CommandId++;
439     commands[++ii] = WriteThreeBytes;
440     commands[++ii] = FAN_TempTable;
441     commands[++ii] = 0x0A;
442 
443     commands[++ii] = ctrl->table[0].temperature;
444     commands[++ii] = 0x00;
445     commands[++ii] = ctrl->table[1].temperature;
446     commands[++ii] = 0x00;
447     commands[++ii] = ctrl->table[2].temperature;
448     commands[++ii] = 0x00;
449     commands[++ii] = ctrl->table[3].temperature;
450     commands[++ii] = 0x00;
451     commands[++ii] = ctrl->table[4].temperature;
452     commands[++ii] = 0x00;
453 
454     commands[++ii] = CommandId++;
455     commands[++ii] = WriteThreeBytes;
456     commands[++ii] = FAN_RPMTable;
457     commands[++ii] = 0x0A;
458 
459     commands[++ii] = ctrl->table[0].speed;
460     commands[++ii] = 0x00;
461     commands[++ii] = ctrl->table[1].speed;
462     commands[++ii] = 0x00;
463     commands[++ii] = ctrl->table[2].speed;
464     commands[++ii] = 0x00;
465     commands[++ii] = ctrl->table[3].speed;
466     commands[++ii] = 0x00;
467     commands[++ii] = ctrl->table[4].speed;
468     commands[++ii] = 0x00;
469 
470     commands[0] = ii;
471     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
472     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
473 
474     return rr;
475 }
476 
477 int
corsairlink_coolit_fan_speed(struct corsair_device_info * dev,struct libusb_device_handle * handle,struct fan_control * ctrl)478 corsairlink_coolit_fan_speed(
479     struct corsair_device_info* dev, struct libusb_device_handle* handle, struct fan_control* ctrl )
480 {
481     int rr;
482     uint8_t response[64];
483     uint8_t commands[64];
484     memset( response, 0, sizeof( response ) );
485     memset( commands, 0, sizeof( commands ) );
486 
487     uint8_t ii = 0;
488 
489     commands[++ii] = CommandId++;
490     commands[++ii] = WriteOneByte;
491     commands[++ii] = FAN_Select;
492     commands[++ii] = ctrl->channel;
493 
494     commands[++ii] = CommandId++;
495     commands[++ii] = ReadTwoBytes;
496     commands[++ii] = FAN_ReadRPM;
497 
498     commands[++ii] = CommandId++;
499     commands[++ii] = ReadTwoBytes;
500     commands[++ii] = FAN_MaxRecordedRPM;
501 
502     commands[0] = ii;
503 
504     rr = dev->driver->write( handle, dev->write_endpoint, commands, 64 );
505     rr = dev->driver->read( handle, dev->read_endpoint, response, 64 );
506 
507     msg_debug2( "Speed: %02X %02X\n", response[5], response[4] );
508     msg_debug2( "Max Speed: %02X %02X\n", response[9], response[8] );
509     ctrl->speed_rpm = ( response[5] << 8 ) + response[4];
510     ctrl->max_speed = ( response[9] << 8 ) + response[8];
511 
512     return rr;
513 }
514