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