blob: e8504267d0e8041d9c82419d4c15a50712f2f013 [file] [log] [blame]
Marius Zachmann40c3a442020-06-26 07:59:36 +02001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * corsair-cpro.c - Linux driver for Corsair Commander Pro
4 * Copyright (C) 2020 Marius Zachmann <mail@mariuszachmann.de>
5 *
6 * This driver uses hid reports to communicate with the device to allow hidraw userspace drivers
7 * still being used. The device does not use report ids. When using hidraw and this driver
8 * simultaniously, reports could be switched.
9 */
10
11#include <linux/bitops.h>
12#include <linux/completion.h>
13#include <linux/hid.h>
14#include <linux/hwmon.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/mutex.h>
18#include <linux/slab.h>
19#include <linux/types.h>
20
21#define USB_VENDOR_ID_CORSAIR 0x1b1c
22#define USB_PRODUCT_ID_CORSAIR_COMMANDERPRO 0x0c10
23#define USB_PRODUCT_ID_CORSAIR_1000D 0x1d00
24
25#define OUT_BUFFER_SIZE 63
26#define IN_BUFFER_SIZE 16
27#define LABEL_LENGTH 11
28#define REQ_TIMEOUT 300
29
30#define CTL_GET_TMP_CNCT 0x10 /*
31 * returns in bytes 1-4 for each temp sensor:
32 * 0 not connected
33 * 1 connected
34 */
35#define CTL_GET_TMP 0x11 /*
36 * send: byte 1 is channel, rest zero
37 * rcv: returns temp for channel in centi-degree celsius
38 * in bytes 1 and 2
39 * returns 17 in byte 0 if no sensor is connected
40 */
41#define CTL_GET_VOLT 0x12 /*
42 * send: byte 1 is rail number: 0 = 12v, 1 = 5v, 2 = 3.3v
43 * rcv: returns millivolt in bytes 1,2
44 */
45#define CTL_GET_FAN_CNCT 0x20 /*
46 * returns in bytes 1-6 for each fan:
47 * 0 not connected
48 * 1 3pin
49 * 2 4pin
50 */
51#define CTL_GET_FAN_RPM 0x21 /*
52 * send: byte 1 is channel, rest zero
53 * rcv: returns rpm in bytes 1,2
54 */
55#define CTL_SET_FAN_FPWM 0x23 /*
56 * set fixed pwm
57 * send: byte 1 is fan number
58 * send: byte 2 is percentage from 0 - 100
59 */
60#define CTL_SET_FAN_TARGET 0x24 /*
61 * set target rpm
62 * send: byte 1 is fan number
63 * send: byte 2-3 is target
64 * device accepts all values from 0x00 - 0xFFFF
65 */
66
67#define NUM_FANS 6
68#define NUM_TEMP_SENSORS 4
69
70struct ccp_device {
71 struct hid_device *hdev;
72 struct device *hwmon_dev;
73 struct completion wait_input_report;
74 struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */
75 u8 *buffer;
76 int pwm[6];
77 int target[6];
78 DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
79 DECLARE_BITMAP(fan_cnct, NUM_FANS);
80 char fan_label[6][LABEL_LENGTH];
81};
82
83/* send command, check for error in response, response in ccp->buffer */
84static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, u8 byte3)
85{
86 unsigned long t;
87 int ret;
88
89 memset(ccp->buffer, 0x00, OUT_BUFFER_SIZE);
90 ccp->buffer[0] = command;
91 ccp->buffer[1] = byte1;
92 ccp->buffer[2] = byte2;
93 ccp->buffer[3] = byte3;
94
95 reinit_completion(&ccp->wait_input_report);
96
97 ret = hid_hw_output_report(ccp->hdev, ccp->buffer, OUT_BUFFER_SIZE);
98 if (ret < 0)
99 return ret;
100
101 t = wait_for_completion_timeout(&ccp->wait_input_report, msecs_to_jiffies(REQ_TIMEOUT));
102 if (!t)
103 return -ETIMEDOUT;
104
105 /* first byte of response is error code */
106 if (ccp->buffer[0] != 0x00) {
107 hid_dbg(ccp->hdev, "device response error: %d", ccp->buffer[0]);
108 return -EIO;
109 }
110
111 return 0;
112}
113
114static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
115{
116 struct ccp_device *ccp = hid_get_drvdata(hdev);
117
118 /* only copy buffer when requested */
119 if (completion_done(&ccp->wait_input_report))
120 return 0;
121
122 memcpy(ccp->buffer, data, min(IN_BUFFER_SIZE, size));
123 complete(&ccp->wait_input_report);
124
125 return 0;
126}
127
128/* requests and returns single data values depending on channel */
129static int get_data(struct ccp_device *ccp, int command, int channel)
130{
131 int ret;
132
133 mutex_lock(&ccp->mutex);
134
135 ret = send_usb_cmd(ccp, command, channel, 0, 0);
136 if (ret)
137 goto out_unlock;
138
139 ret = (ccp->buffer[1] << 8) + ccp->buffer[2];
140
141out_unlock:
142 mutex_unlock(&ccp->mutex);
143 return ret;
144}
145
146static int set_pwm(struct ccp_device *ccp, int channel, long val)
147{
148 int ret;
149
150 if (val < 0 || val > 255)
151 return -EINVAL;
152
153 ccp->pwm[channel] = val;
154
155 /* The Corsair Commander Pro uses values from 0-100 */
156 val = DIV_ROUND_CLOSEST(val * 100, 255);
157
158 mutex_lock(&ccp->mutex);
159
160 ret = send_usb_cmd(ccp, CTL_SET_FAN_FPWM, channel, val, 0);
161
162 mutex_unlock(&ccp->mutex);
163 return ret;
164}
165
166static int set_target(struct ccp_device *ccp, int channel, long val)
167{
168 int ret;
169
170 val = clamp_val(val, 0, 0xFFFF);
171 ccp->target[channel] = val;
172
173 mutex_lock(&ccp->mutex);
174
175 ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val);
176
177 mutex_unlock(&ccp->mutex);
178 return ret;
179}
180
181static int ccp_read_string(struct device *dev, enum hwmon_sensor_types type,
182 u32 attr, int channel, const char **str)
183{
184 struct ccp_device *ccp = dev_get_drvdata(dev);
185
186 switch (type) {
187 case hwmon_fan:
188 switch (attr) {
189 case hwmon_fan_label:
190 *str = ccp->fan_label[channel];
191 return 0;
192 default:
193 break;
194 }
195 break;
196 default:
197 break;
198 }
199
200 return -EOPNOTSUPP;
201}
202
203static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
204 u32 attr, int channel, long *val)
205{
206 struct ccp_device *ccp = dev_get_drvdata(dev);
207 int ret;
208
209 switch (type) {
210 case hwmon_temp:
211 switch (attr) {
212 case hwmon_temp_input:
213 ret = get_data(ccp, CTL_GET_TMP, channel);
214 if (ret < 0)
215 return ret;
216 *val = ret * 10;
217 return 0;
218 default:
219 break;
220 }
221 break;
222 case hwmon_fan:
223 switch (attr) {
224 case hwmon_fan_input:
225 ret = get_data(ccp, CTL_GET_FAN_RPM, channel);
226 if (ret < 0)
227 return ret;
228 *val = ret;
229 return 0;
230 case hwmon_fan_target:
231 /* how to read target values from the device is unknown */
232 /* driver returns last set value or 0 */
233 *val = ccp->target[channel];
234 return 0;
235 default:
236 break;
237 }
238 break;
239 case hwmon_pwm:
240 switch (attr) {
241 case hwmon_pwm_input:
242 /* how to read pwm values from the device is currently unknown */
243 /* driver returns last set value or 0 */
244 *val = ccp->pwm[channel];
245 return 0;
246 default:
247 break;
248 }
249 break;
250 case hwmon_in:
251 switch (attr) {
252 case hwmon_in_input:
253 ret = get_data(ccp, CTL_GET_VOLT, channel);
254 if (ret < 0)
255 return ret;
256 *val = ret;
257 return 0;
258 default:
259 break;
260 }
261 break;
262 default:
263 break;
264 }
265
266 return -EOPNOTSUPP;
267};
268
269static int ccp_write(struct device *dev, enum hwmon_sensor_types type,
270 u32 attr, int channel, long val)
271{
272 struct ccp_device *ccp = dev_get_drvdata(dev);
273
274 switch (type) {
275 case hwmon_pwm:
276 switch (attr) {
277 case hwmon_pwm_input:
278 return set_pwm(ccp, channel, val);
279 default:
280 break;
281 }
282 break;
283 case hwmon_fan:
284 switch (attr) {
285 case hwmon_fan_target:
286 return set_target(ccp, channel, val);
287 default:
288 break;
289 }
290 default:
291 break;
292 }
293
294 return -EOPNOTSUPP;
295};
296
297static umode_t ccp_is_visible(const void *data, enum hwmon_sensor_types type,
298 u32 attr, int channel)
299{
300 const struct ccp_device *ccp = data;
301
302 switch (type) {
303 case hwmon_temp:
304 if (!test_bit(channel, ccp->temp_cnct))
305 break;
306
307 switch (attr) {
308 case hwmon_temp_input:
309 return 0444;
310 case hwmon_temp_label:
311 return 0444;
312 default:
313 break;
314 }
315 break;
316 case hwmon_fan:
317 if (!test_bit(channel, ccp->fan_cnct))
318 break;
319
320 switch (attr) {
321 case hwmon_fan_input:
322 return 0444;
323 case hwmon_fan_label:
324 return 0444;
325 case hwmon_fan_target:
326 return 0644;
327 default:
328 break;
329 }
330 break;
331 case hwmon_pwm:
332 if (!test_bit(channel, ccp->fan_cnct))
333 break;
334
335 switch (attr) {
336 case hwmon_pwm_input:
337 return 0644;
338 default:
339 break;
340 }
341 break;
342 case hwmon_in:
343 switch (attr) {
344 case hwmon_in_input:
345 return 0444;
346 default:
347 break;
348 }
349 break;
350 default:
351 break;
352 }
353
354 return 0;
355};
356
357static const struct hwmon_ops ccp_hwmon_ops = {
358 .is_visible = ccp_is_visible,
359 .read = ccp_read,
360 .read_string = ccp_read_string,
361 .write = ccp_write,
362};
363
364static const struct hwmon_channel_info *ccp_info[] = {
365 HWMON_CHANNEL_INFO(chip,
366 HWMON_C_REGISTER_TZ),
367 HWMON_CHANNEL_INFO(temp,
368 HWMON_T_INPUT,
369 HWMON_T_INPUT,
370 HWMON_T_INPUT,
371 HWMON_T_INPUT
372 ),
373 HWMON_CHANNEL_INFO(fan,
374 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
375 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
376 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
377 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
378 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET,
379 HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_TARGET
380 ),
381 HWMON_CHANNEL_INFO(pwm,
382 HWMON_PWM_INPUT,
383 HWMON_PWM_INPUT,
384 HWMON_PWM_INPUT,
385 HWMON_PWM_INPUT,
386 HWMON_PWM_INPUT,
387 HWMON_PWM_INPUT
388 ),
389 HWMON_CHANNEL_INFO(in,
390 HWMON_I_INPUT,
391 HWMON_I_INPUT,
392 HWMON_I_INPUT
393 ),
394 NULL
395};
396
397static const struct hwmon_chip_info ccp_chip_info = {
398 .ops = &ccp_hwmon_ops,
399 .info = ccp_info,
400};
401
402/* read fan connection status and set labels */
403static int get_fan_cnct(struct ccp_device *ccp)
404{
405 int channel;
406 int mode;
407 int ret;
408
409 ret = send_usb_cmd(ccp, CTL_GET_FAN_CNCT, 0, 0, 0);
410 if (ret)
411 return ret;
412
413 for (channel = 0; channel < NUM_FANS; channel++) {
414 mode = ccp->buffer[channel + 1];
415 if (mode == 0)
416 continue;
417
418 set_bit(channel, ccp->fan_cnct);
419
420 switch (mode) {
421 case 1:
422 scnprintf(ccp->fan_label[channel], LABEL_LENGTH,
423 "fan%d 3pin", channel + 1);
424 break;
425 case 2:
426 scnprintf(ccp->fan_label[channel], LABEL_LENGTH,
427 "fan%d 4pin", channel + 1);
428 break;
429 default:
430 scnprintf(ccp->fan_label[channel], LABEL_LENGTH,
431 "fan%d other", channel + 1);
432 break;
433 }
434 }
435
436 return 0;
437}
438
439/* read temp sensor connection status */
440static int get_temp_cnct(struct ccp_device *ccp)
441{
442 int channel;
443 int mode;
444 int ret;
445
446 ret = send_usb_cmd(ccp, CTL_GET_TMP_CNCT, 0, 0, 0);
447 if (ret)
448 return ret;
449
450 for (channel = 0; channel < NUM_TEMP_SENSORS; channel++) {
451 mode = ccp->buffer[channel + 1];
452 if (mode == 0)
453 continue;
454
455 set_bit(channel, ccp->temp_cnct);
456 }
457
458 return 0;
459}
460
461static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
462{
463 struct ccp_device *ccp;
464 int ret;
465
466 ccp = devm_kzalloc(&hdev->dev, sizeof(*ccp), GFP_KERNEL);
467 if (!ccp)
468 return -ENOMEM;
469
470 ccp->buffer = devm_kmalloc(&hdev->dev, OUT_BUFFER_SIZE, GFP_KERNEL);
471 if (!ccp->buffer)
472 return -ENOMEM;
473
474 ret = hid_parse(hdev);
475 if (ret)
476 return ret;
477
478 ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
479 if (ret)
480 return ret;
481
482 ret = hid_hw_open(hdev);
483 if (ret)
484 goto out_hw_stop;
485
486 ccp->hdev = hdev;
487 hid_set_drvdata(hdev, ccp);
488 mutex_init(&ccp->mutex);
489 init_completion(&ccp->wait_input_report);
490
491 hid_device_io_start(hdev);
492
493 /* temp and fan connection status only updates when device is powered on */
494 ret = get_temp_cnct(ccp);
495 if (ret)
496 goto out_hw_close;
497
498 ret = get_fan_cnct(ccp);
499 if (ret)
500 goto out_hw_close;
501 ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro",
502 ccp, &ccp_chip_info, 0);
503 if (IS_ERR(ccp->hwmon_dev)) {
504 ret = PTR_ERR(ccp->hwmon_dev);
505 goto out_hw_close;
506 }
507
508 return 0;
509
510out_hw_close:
511 hid_hw_close(hdev);
512out_hw_stop:
513 hid_hw_stop(hdev);
514 return ret;
515}
516
517static void ccp_remove(struct hid_device *hdev)
518{
519 struct ccp_device *ccp = hid_get_drvdata(hdev);
520
521 hwmon_device_unregister(ccp->hwmon_dev);
522 hid_hw_close(hdev);
523 hid_hw_stop(hdev);
524}
525
526static const struct hid_device_id ccp_devices[] = {
527 { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_PRODUCT_ID_CORSAIR_COMMANDERPRO) },
528 { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_PRODUCT_ID_CORSAIR_1000D) },
529 { }
530};
531
532static struct hid_driver ccp_driver = {
533 .name = "corsair-cpro",
534 .id_table = ccp_devices,
535 .probe = ccp_probe,
536 .remove = ccp_remove,
537 .raw_event = ccp_raw_event,
538};
539
540MODULE_DEVICE_TABLE(hid, ccp_devices);
541MODULE_LICENSE("GPL");
542
543static int __init ccp_init(void)
544{
545 return hid_register_driver(&ccp_driver);
546}
547
548static void __exit ccp_exit(void)
549{
550 hid_unregister_driver(&ccp_driver);
551}
552
553/*
554 * When compiling this driver as built-in, hwmon initcalls will get called before the
555 * hid driver and this driver would fail to register. late_initcall solves this.
556 */
557late_initcall(ccp_init);
558module_exit(ccp_exit);