Commit | Line | Data |
---|---|---|
0e35f63f AS |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
cdbe34da | 3 | * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, |
ceaa2240 AS |
4 | * Quadro, High Flow Next, Aquaero, Aquastream Ultimate, Leakshield, |
5 | * High Flow USB/MPS Flow family) | |
0e35f63f | 6 | * |
2fd3eec1 | 7 | * Aquacomputer devices send HID reports (with ID 0x01) every second to report |
e0f6c370 | 8 | * sensor values, except for devices that communicate through the |
ceaa2240 | 9 | * legacy way (currently, Poweradjust 3 and High Flow USB/MPS Flow family). |
0e35f63f AS |
10 | * |
11 | * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com> | |
229b159c | 12 | * Copyright 2022 Jack Doan <me@jackdoan.com> |
0e35f63f AS |
13 | */ |
14 | ||
752b9279 | 15 | #include <linux/crc16.h> |
0e35f63f | 16 | #include <linux/debugfs.h> |
56b930dc | 17 | #include <linux/delay.h> |
0e35f63f AS |
18 | #include <linux/hid.h> |
19 | #include <linux/hwmon.h> | |
20 | #include <linux/jiffies.h> | |
56b930dc | 21 | #include <linux/ktime.h> |
0e35f63f | 22 | #include <linux/module.h> |
752b9279 | 23 | #include <linux/mutex.h> |
0e35f63f | 24 | #include <linux/seq_file.h> |
2fd3eec1 | 25 | #include <asm/unaligned.h> |
0e35f63f | 26 | |
2fd3eec1 | 27 | #define USB_VENDOR_ID_AQUACOMPUTER 0x0c70 |
2c552111 | 28 | #define USB_PRODUCT_ID_AQUAERO 0xf001 |
229b159c | 29 | #define USB_PRODUCT_ID_FARBWERK 0xf00a |
cdbe34da | 30 | #define USB_PRODUCT_ID_QUADRO 0xf00d |
2fd3eec1 AS |
31 | #define USB_PRODUCT_ID_D5NEXT 0xf00e |
32 | #define USB_PRODUCT_ID_FARBWERK360 0xf010 | |
752b9279 | 33 | #define USB_PRODUCT_ID_OCTO 0xf011 |
aed80bb9 | 34 | #define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012 |
b3d3be6c | 35 | #define USB_PRODUCT_ID_LEAKSHIELD 0xf014 |
19692f17 | 36 | #define USB_PRODUCT_ID_AQUASTREAMXT 0xf0b6 |
7505dab7 | 37 | #define USB_PRODUCT_ID_AQUASTREAMULT 0xf00b |
e0f6c370 | 38 | #define USB_PRODUCT_ID_POWERADJUST3 0xf0bd |
ceaa2240 | 39 | #define USB_PRODUCT_ID_HIGHFLOW 0xf003 |
0e35f63f | 40 | |
7505dab7 AS |
41 | enum kinds { |
42 | d5next, farbwerk, farbwerk360, octo, quadro, | |
19692f17 | 43 | highflownext, aquaero, poweradjust3, aquastreamult, |
ceaa2240 | 44 | aquastreamxt, leakshield, highflow |
7505dab7 | 45 | }; |
0e35f63f | 46 | |
2fd3eec1 AS |
47 | static const char *const aqc_device_names[] = { |
48 | [d5next] = "d5next", | |
229b159c | 49 | [farbwerk] = "farbwerk", |
752b9279 | 50 | [farbwerk360] = "farbwerk360", |
cdbe34da | 51 | [octo] = "octo", |
aed80bb9 | 52 | [quadro] = "quadro", |
2c552111 | 53 | [highflownext] = "highflownext", |
b3d3be6c | 54 | [leakshield] = "leakshield", |
19692f17 | 55 | [aquastreamxt] = "aquastreamxt", |
e0f6c370 | 56 | [aquaero] = "aquaero", |
7505dab7 | 57 | [aquastreamult] = "aquastreamultimate", |
ceaa2240 AS |
58 | [poweradjust3] = "poweradjust3", |
59 | [highflow] = "highflow" /* Covers MPS Flow devices */ | |
2fd3eec1 | 60 | }; |
0e35f63f | 61 | |
2fd3eec1 AS |
62 | #define DRIVER_NAME "aquacomputer_d5next" |
63 | ||
64 | #define STATUS_REPORT_ID 0x01 | |
65 | #define STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */ | |
ad2f0811 | 66 | #define SERIAL_PART_OFFSET 2 |
2fd3eec1 | 67 | |
752b9279 | 68 | #define CTRL_REPORT_ID 0x03 |
6c83ccb1 | 69 | #define AQUAERO_CTRL_REPORT_ID 0x0b |
752b9279 | 70 | |
56b930dc AS |
71 | #define CTRL_REPORT_DELAY 200 /* ms */ |
72 | ||
752b9279 AS |
73 | /* The HID report that the official software always sends |
74 | * after writing values, currently same for all devices | |
75 | */ | |
76 | #define SECONDARY_CTRL_REPORT_ID 0x02 | |
77 | #define SECONDARY_CTRL_REPORT_SIZE 0x0B | |
78 | ||
79 | static u8 secondary_ctrl_report[] = { | |
80 | 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6 | |
81 | }; | |
82 | ||
6c83ccb1 LA |
83 | /* Secondary HID report values for Aquaero */ |
84 | #define AQUAERO_SECONDARY_CTRL_REPORT_ID 0x06 | |
85 | #define AQUAERO_SECONDARY_CTRL_REPORT_SIZE 0x07 | |
86 | ||
87 | static u8 aquaero_secondary_ctrl_report[] = { | |
88 | 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00 | |
89 | }; | |
90 | ||
e0f6c370 | 91 | /* Report IDs for legacy devices */ |
19692f17 AS |
92 | #define AQUASTREAMXT_STATUS_REPORT_ID 0x04 |
93 | ||
e0f6c370 AS |
94 | #define POWERADJUST3_STATUS_REPORT_ID 0x03 |
95 | ||
ceaa2240 AS |
96 | #define HIGHFLOW_STATUS_REPORT_ID 0x02 |
97 | ||
d0450fc1 LA |
98 | /* Data types for reading and writing control reports */ |
99 | #define AQC_8 0 | |
100 | #define AQC_BE16 1 | |
101 | ||
2c552111 | 102 | /* Info, sensor sizes and offsets for most Aquacomputer devices */ |
ad2f0811 LA |
103 | #define AQC_SERIAL_START 0x3 |
104 | #define AQC_FIRMWARE_VERSION 0xD | |
105 | ||
8bcb02bd | 106 | #define AQC_SENSOR_SIZE 0x02 |
fdbfd330 | 107 | #define AQC_SENSOR_NA 0x7FFF |
654c9735 AS |
108 | #define AQC_FAN_PERCENT_OFFSET 0x00 |
109 | #define AQC_FAN_VOLTAGE_OFFSET 0x02 | |
110 | #define AQC_FAN_CURRENT_OFFSET 0x04 | |
111 | #define AQC_FAN_POWER_OFFSET 0x06 | |
112 | #define AQC_FAN_SPEED_OFFSET 0x08 | |
0e35f63f | 113 | |
2c552111 LA |
114 | /* Specs of the Aquaero fan controllers */ |
115 | #define AQUAERO_SERIAL_START 0x07 | |
116 | #define AQUAERO_FIRMWARE_VERSION 0x0B | |
117 | #define AQUAERO_NUM_FANS 4 | |
118 | #define AQUAERO_NUM_SENSORS 8 | |
119 | #define AQUAERO_NUM_VIRTUAL_SENSORS 8 | |
3d2e9f58 | 120 | #define AQUAERO_NUM_CALC_VIRTUAL_SENSORS 4 |
2c552111 | 121 | #define AQUAERO_NUM_FLOW_SENSORS 2 |
6c83ccb1 | 122 | #define AQUAERO_CTRL_REPORT_SIZE 0xa93 |
bd1e92f9 LA |
123 | #define AQUAERO_CTRL_PRESET_ID 0x5c |
124 | #define AQUAERO_CTRL_PRESET_SIZE 0x02 | |
125 | #define AQUAERO_CTRL_PRESET_START 0x55c | |
2c552111 LA |
126 | |
127 | /* Sensor report offsets for Aquaero fan controllers */ | |
128 | #define AQUAERO_SENSOR_START 0x65 | |
129 | #define AQUAERO_VIRTUAL_SENSOR_START 0x85 | |
3d2e9f58 | 130 | #define AQUAERO_CALC_VIRTUAL_SENSOR_START 0x95 |
2c552111 LA |
131 | #define AQUAERO_FLOW_SENSORS_START 0xF9 |
132 | #define AQUAERO_FAN_VOLTAGE_OFFSET 0x04 | |
133 | #define AQUAERO_FAN_CURRENT_OFFSET 0x06 | |
134 | #define AQUAERO_FAN_POWER_OFFSET 0x08 | |
135 | #define AQUAERO_FAN_SPEED_OFFSET 0x00 | |
136 | static u16 aquaero_sensor_fan_offsets[] = { 0x167, 0x173, 0x17f, 0x18B }; | |
137 | ||
866e630a LA |
138 | /* Control report offsets for the Aquaero fan controllers */ |
139 | #define AQUAERO_TEMP_CTRL_OFFSET 0xdb | |
bd1e92f9 LA |
140 | #define AQUAERO_FAN_CTRL_MIN_PWR_OFFSET 0x04 |
141 | #define AQUAERO_FAN_CTRL_MAX_PWR_OFFSET 0x06 | |
142 | #define AQUAERO_FAN_CTRL_SRC_OFFSET 0x10 | |
143 | static u16 aquaero_ctrl_fan_offsets[] = { 0x20c, 0x220, 0x234, 0x248 }; | |
866e630a | 144 | |
d5d896b8 | 145 | /* Specs of the D5 Next pump */ |
654c9735 AS |
146 | #define D5NEXT_NUM_FANS 2 |
147 | #define D5NEXT_NUM_SENSORS 1 | |
e2769f5e | 148 | #define D5NEXT_NUM_VIRTUAL_SENSORS 8 |
d5d896b8 AS |
149 | #define D5NEXT_CTRL_REPORT_SIZE 0x329 |
150 | ||
151 | /* Sensor report offsets for the D5 Next pump */ | |
152 | #define D5NEXT_POWER_CYCLES 0x18 | |
153 | #define D5NEXT_COOLANT_TEMP 0x57 | |
654c9735 AS |
154 | #define D5NEXT_PUMP_OFFSET 0x6c |
155 | #define D5NEXT_FAN_OFFSET 0x5f | |
156 | #define D5NEXT_5V_VOLTAGE 0x39 | |
f4caa262 | 157 | #define D5NEXT_12V_VOLTAGE 0x37 |
d5d896b8 | 158 | #define D5NEXT_VIRTUAL_SENSORS_START 0x3f |
1ed5036b | 159 | static u16 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; |
0e35f63f | 160 | |
d5d896b8 AS |
161 | /* Control report offsets for the D5 Next pump */ |
162 | #define D5NEXT_TEMP_CTRL_OFFSET 0x2D /* Temperature sensor offsets location */ | |
163 | static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (from 0-100%) */ | |
09e89309 | 164 | |
7505dab7 AS |
165 | /* Specs of the Aquastream Ultimate pump */ |
166 | /* Pump does not follow the standard structure, so only consider the fan */ | |
167 | #define AQUASTREAMULT_NUM_FANS 1 | |
168 | #define AQUASTREAMULT_NUM_SENSORS 2 | |
169 | ||
170 | /* Sensor report offsets for the Aquastream Ultimate pump */ | |
171 | #define AQUASTREAMULT_SENSOR_START 0x2D | |
172 | #define AQUASTREAMULT_PUMP_OFFSET 0x51 | |
173 | #define AQUASTREAMULT_PUMP_VOLTAGE 0x3D | |
174 | #define AQUASTREAMULT_PUMP_CURRENT 0x53 | |
175 | #define AQUASTREAMULT_PUMP_POWER 0x55 | |
176 | #define AQUASTREAMULT_FAN_OFFSET 0x41 | |
177 | #define AQUASTREAMULT_PRESSURE_OFFSET 0x57 | |
178 | #define AQUASTREAMULT_FLOW_SENSOR_OFFSET 0x37 | |
179 | #define AQUASTREAMULT_FAN_VOLTAGE_OFFSET 0x02 | |
180 | #define AQUASTREAMULT_FAN_CURRENT_OFFSET 0x00 | |
181 | #define AQUASTREAMULT_FAN_POWER_OFFSET 0x04 | |
182 | #define AQUASTREAMULT_FAN_SPEED_OFFSET 0x06 | |
183 | static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET }; | |
184 | ||
d5d896b8 | 185 | /* Spec and sensor report offset for the Farbwerk RGB controller */ |
229b159c JD |
186 | #define FARBWERK_NUM_SENSORS 4 |
187 | #define FARBWERK_SENSOR_START 0x2f | |
229b159c | 188 | |
d5d896b8 | 189 | /* Specs of the Farbwerk 360 RGB controller */ |
e2769f5e | 190 | #define FARBWERK360_NUM_SENSORS 4 |
e2769f5e | 191 | #define FARBWERK360_NUM_VIRTUAL_SENSORS 16 |
662d20b3 | 192 | #define FARBWERK360_CTRL_REPORT_SIZE 0x682 |
d5d896b8 AS |
193 | |
194 | /* Sensor report offsets for the Farbwerk 360 */ | |
195 | #define FARBWERK360_SENSOR_START 0x32 | |
196 | #define FARBWERK360_VIRTUAL_SENSORS_START 0x3a | |
197 | ||
198 | /* Control report offsets for the Farbwerk 360 */ | |
662d20b3 | 199 | #define FARBWERK360_TEMP_CTRL_OFFSET 0x8 |
0e35f63f | 200 | |
d5d896b8 | 201 | /* Specs of the Octo fan controller */ |
752b9279 | 202 | #define OCTO_NUM_FANS 8 |
752b9279 | 203 | #define OCTO_NUM_SENSORS 4 |
e2769f5e | 204 | #define OCTO_NUM_VIRTUAL_SENSORS 16 |
120584c7 | 205 | #define OCTO_NUM_FLOW_SENSORS 1 |
654c9735 | 206 | #define OCTO_CTRL_REPORT_SIZE 0x65F |
d5d896b8 AS |
207 | |
208 | /* Sensor report offsets for the Octo */ | |
209 | #define OCTO_POWER_CYCLES 0x18 | |
210 | #define OCTO_SENSOR_START 0x3D | |
211 | #define OCTO_VIRTUAL_SENSORS_START 0x45 | |
120584c7 | 212 | #define OCTO_FLOW_SENSOR_OFFSET 0x7B |
1ed5036b | 213 | static u16 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; |
752b9279 | 214 | |
d5d896b8 AS |
215 | /* Control report offsets for the Octo */ |
216 | #define OCTO_TEMP_CTRL_OFFSET 0xA | |
bf7b5a12 | 217 | #define OCTO_FLOW_PULSES_CTRL_OFFSET 0x6 |
d5d896b8 | 218 | /* Fan speed offsets (0-100%) */ |
752b9279 AS |
219 | static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE }; |
220 | ||
d5d896b8 | 221 | /* Specs of Quadro fan controller */ |
cdbe34da AS |
222 | #define QUADRO_NUM_FANS 4 |
223 | #define QUADRO_NUM_SENSORS 4 | |
e2769f5e | 224 | #define QUADRO_NUM_VIRTUAL_SENSORS 16 |
a2ba7ee2 | 225 | #define QUADRO_NUM_FLOW_SENSORS 1 |
cdbe34da | 226 | #define QUADRO_CTRL_REPORT_SIZE 0x3c1 |
d5d896b8 AS |
227 | |
228 | /* Sensor report offsets for the Quadro */ | |
229 | #define QUADRO_POWER_CYCLES 0x18 | |
230 | #define QUADRO_SENSOR_START 0x34 | |
231 | #define QUADRO_VIRTUAL_SENSORS_START 0x3c | |
cdbe34da | 232 | #define QUADRO_FLOW_SENSOR_OFFSET 0x6e |
1ed5036b | 233 | static u16 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; |
cdbe34da | 234 | |
d5d896b8 AS |
235 | /* Control report offsets for the Quadro */ |
236 | #define QUADRO_TEMP_CTRL_OFFSET 0xA | |
6ff838f2 | 237 | #define QUADRO_FLOW_PULSES_CTRL_OFFSET 0x6 |
d5d896b8 | 238 | static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed offsets (0-100%) */ |
cdbe34da | 239 | |
d5d896b8 | 240 | /* Specs of High Flow Next flow sensor */ |
aed80bb9 | 241 | #define HIGHFLOWNEXT_NUM_SENSORS 2 |
a2ba7ee2 | 242 | #define HIGHFLOWNEXT_NUM_FLOW_SENSORS 1 |
d5d896b8 AS |
243 | |
244 | /* Sensor report offsets for the High Flow Next */ | |
aed80bb9 AS |
245 | #define HIGHFLOWNEXT_SENSOR_START 85 |
246 | #define HIGHFLOWNEXT_FLOW 81 | |
247 | #define HIGHFLOWNEXT_WATER_QUALITY 89 | |
248 | #define HIGHFLOWNEXT_POWER 91 | |
249 | #define HIGHFLOWNEXT_CONDUCTIVITY 95 | |
250 | #define HIGHFLOWNEXT_5V_VOLTAGE 97 | |
251 | #define HIGHFLOWNEXT_5V_VOLTAGE_USB 99 | |
252 | ||
b3d3be6c AS |
253 | /* Specs of the Leakshield */ |
254 | #define LEAKSHIELD_NUM_SENSORS 2 | |
255 | ||
256 | /* Sensor report offsets for Leakshield */ | |
257 | #define LEAKSHIELD_PRESSURE_ADJUSTED 285 | |
258 | #define LEAKSHIELD_TEMPERATURE_1 265 | |
259 | #define LEAKSHIELD_TEMPERATURE_2 287 | |
260 | #define LEAKSHIELD_PRESSURE_MIN 291 | |
261 | #define LEAKSHIELD_PRESSURE_TARGET 293 | |
262 | #define LEAKSHIELD_PRESSURE_MAX 295 | |
263 | #define LEAKSHIELD_PUMP_RPM_IN 101 | |
264 | #define LEAKSHIELD_FLOW_IN 111 | |
265 | #define LEAKSHIELD_RESERVOIR_VOLUME 313 | |
266 | #define LEAKSHIELD_RESERVOIR_FILLED 311 | |
267 | ||
19692f17 AS |
268 | /* Specs of the Aquastream XT pump */ |
269 | #define AQUASTREAMXT_SERIAL_START 0x3a | |
270 | #define AQUASTREAMXT_FIRMWARE_VERSION 0x32 | |
271 | #define AQUASTREAMXT_NUM_FANS 2 | |
272 | #define AQUASTREAMXT_NUM_SENSORS 3 | |
273 | #define AQUASTREAMXT_FAN_STOPPED 0x4 | |
274 | #define AQUASTREAMXT_PUMP_CONVERSION_CONST 45000000 | |
275 | #define AQUASTREAMXT_FAN_CONVERSION_CONST 5646000 | |
276 | #define AQUASTREAMXT_SENSOR_REPORT_SIZE 0x42 | |
277 | ||
278 | /* Sensor report offsets and info for Aquastream XT */ | |
279 | #define AQUASTREAMXT_SENSOR_START 0xd | |
280 | #define AQUASTREAMXT_FAN_VOLTAGE_OFFSET 0x7 | |
281 | #define AQUASTREAMXT_FAN_STATUS_OFFSET 0x1d | |
282 | #define AQUASTREAMXT_PUMP_VOLTAGE_OFFSET 0x9 | |
283 | #define AQUASTREAMXT_PUMP_CURR_OFFSET 0xb | |
284 | static u16 aquastreamxt_sensor_fan_offsets[] = { 0x13, 0x1b }; | |
285 | ||
e0f6c370 AS |
286 | /* Specs of the Poweradjust 3 */ |
287 | #define POWERADJUST3_NUM_SENSORS 1 | |
288 | #define POWERADJUST3_SENSOR_REPORT_SIZE 0x32 | |
289 | ||
290 | /* Sensor report offsets for the Poweradjust 3 */ | |
291 | #define POWERADJUST3_SENSOR_START 0x03 | |
292 | ||
ceaa2240 AS |
293 | /* Specs of the High Flow USB */ |
294 | #define HIGHFLOW_NUM_SENSORS 2 | |
295 | #define HIGHFLOW_NUM_FLOW_SENSORS 1 | |
296 | #define HIGHFLOW_SENSOR_REPORT_SIZE 0x76 | |
297 | ||
298 | /* Sensor report offsets for the High Flow USB */ | |
299 | #define HIGHFLOW_FIRMWARE_VERSION 0x3 | |
300 | #define HIGHFLOW_SERIAL_START 0x9 | |
301 | #define HIGHFLOW_FLOW_SENSOR_OFFSET 0x23 | |
302 | #define HIGHFLOW_SENSOR_START 0x2b | |
303 | ||
2fd3eec1 | 304 | /* Labels for D5 Next */ |
752b9279 AS |
305 | static const char *const label_d5next_temp[] = { |
306 | "Coolant temp" | |
307 | }; | |
0e35f63f | 308 | |
2fd3eec1 AS |
309 | static const char *const label_d5next_speeds[] = { |
310 | "Pump speed", | |
311 | "Fan speed" | |
312 | }; | |
0e35f63f | 313 | |
2fd3eec1 AS |
314 | static const char *const label_d5next_power[] = { |
315 | "Pump power", | |
316 | "Fan power" | |
0e35f63f AS |
317 | }; |
318 | ||
2fd3eec1 AS |
319 | static const char *const label_d5next_voltages[] = { |
320 | "Pump voltage", | |
321 | "Fan voltage", | |
f4caa262 AS |
322 | "+5V voltage", |
323 | "+12V voltage" | |
0e35f63f AS |
324 | }; |
325 | ||
2fd3eec1 AS |
326 | static const char *const label_d5next_current[] = { |
327 | "Pump current", | |
328 | "Fan current" | |
0e35f63f AS |
329 | }; |
330 | ||
2c552111 | 331 | /* Labels for Aquaero, Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */ |
2fd3eec1 AS |
332 | static const char *const label_temp_sensors[] = { |
333 | "Sensor 1", | |
334 | "Sensor 2", | |
335 | "Sensor 3", | |
2c552111 LA |
336 | "Sensor 4", |
337 | "Sensor 5", | |
338 | "Sensor 6", | |
339 | "Sensor 7", | |
340 | "Sensor 8" | |
0e35f63f AS |
341 | }; |
342 | ||
e2769f5e AS |
343 | static const char *const label_virtual_temp_sensors[] = { |
344 | "Virtual sensor 1", | |
345 | "Virtual sensor 2", | |
346 | "Virtual sensor 3", | |
347 | "Virtual sensor 4", | |
348 | "Virtual sensor 5", | |
349 | "Virtual sensor 6", | |
350 | "Virtual sensor 7", | |
351 | "Virtual sensor 8", | |
352 | "Virtual sensor 9", | |
353 | "Virtual sensor 10", | |
354 | "Virtual sensor 11", | |
355 | "Virtual sensor 12", | |
356 | "Virtual sensor 13", | |
357 | "Virtual sensor 14", | |
358 | "Virtual sensor 15", | |
359 | "Virtual sensor 16", | |
360 | }; | |
361 | ||
3d2e9f58 AS |
362 | static const char *const label_aquaero_calc_temp_sensors[] = { |
363 | "Calc. virtual sensor 1", | |
364 | "Calc. virtual sensor 2", | |
365 | "Calc. virtual sensor 3", | |
366 | "Calc. virtual sensor 4" | |
367 | }; | |
368 | ||
752b9279 AS |
369 | static const char *const label_fan_power[] = { |
370 | "Fan 1 power", | |
371 | "Fan 2 power", | |
372 | "Fan 3 power", | |
373 | "Fan 4 power", | |
374 | "Fan 5 power", | |
375 | "Fan 6 power", | |
376 | "Fan 7 power", | |
377 | "Fan 8 power" | |
378 | }; | |
379 | ||
380 | static const char *const label_fan_voltage[] = { | |
381 | "Fan 1 voltage", | |
382 | "Fan 2 voltage", | |
383 | "Fan 3 voltage", | |
384 | "Fan 4 voltage", | |
385 | "Fan 5 voltage", | |
386 | "Fan 6 voltage", | |
387 | "Fan 7 voltage", | |
388 | "Fan 8 voltage" | |
389 | }; | |
390 | ||
391 | static const char *const label_fan_current[] = { | |
392 | "Fan 1 current", | |
393 | "Fan 2 current", | |
394 | "Fan 3 current", | |
395 | "Fan 4 current", | |
396 | "Fan 5 current", | |
397 | "Fan 6 current", | |
398 | "Fan 7 current", | |
399 | "Fan 8 current" | |
400 | }; | |
401 | ||
120584c7 AS |
402 | /* Labels for Octo fan speeds */ |
403 | static const char *const label_octo_speeds[] = { | |
404 | "Fan 1 speed", | |
405 | "Fan 2 speed", | |
406 | "Fan 3 speed", | |
407 | "Fan 4 speed", | |
408 | "Fan 5 speed", | |
409 | "Fan 6 speed", | |
410 | "Fan 7 speed", | |
411 | "Fan 8 speed", | |
412 | "Flow speed [dL/h]", | |
413 | }; | |
414 | ||
cdbe34da AS |
415 | /* Labels for Quadro fan speeds */ |
416 | static const char *const label_quadro_speeds[] = { | |
417 | "Fan 1 speed", | |
418 | "Fan 2 speed", | |
419 | "Fan 3 speed", | |
420 | "Fan 4 speed", | |
421 | "Flow speed [dL/h]" | |
422 | }; | |
423 | ||
2c552111 LA |
424 | /* Labels for Aquaero fan speeds */ |
425 | static const char *const label_aquaero_speeds[] = { | |
426 | "Fan 1 speed", | |
427 | "Fan 2 speed", | |
428 | "Fan 3 speed", | |
429 | "Fan 4 speed", | |
430 | "Flow sensor 1 [dL/h]", | |
431 | "Flow sensor 2 [dL/h]" | |
432 | }; | |
433 | ||
aed80bb9 AS |
434 | /* Labels for High Flow Next */ |
435 | static const char *const label_highflownext_temp_sensors[] = { | |
436 | "Coolant temp", | |
437 | "External sensor" | |
438 | }; | |
439 | ||
440 | static const char *const label_highflownext_fan_speed[] = { | |
441 | "Flow [dL/h]", | |
442 | "Water quality [%]", | |
443 | "Conductivity [nS/cm]", | |
444 | }; | |
445 | ||
446 | static const char *const label_highflownext_power[] = { | |
447 | "Dissipated power", | |
448 | }; | |
449 | ||
450 | static const char *const label_highflownext_voltage[] = { | |
451 | "+5V voltage", | |
452 | "+5V USB voltage" | |
453 | }; | |
454 | ||
b3d3be6c AS |
455 | /* Labels for Leakshield */ |
456 | static const char *const label_leakshield_temp_sensors[] = { | |
457 | "Temperature 1", | |
458 | "Temperature 2" | |
459 | }; | |
460 | ||
461 | static const char *const label_leakshield_fan_speed[] = { | |
462 | "Pressure [ubar]", | |
463 | "User-Provided Pump Speed", | |
464 | "User-Provided Flow [dL/h]", | |
465 | "Reservoir Volume [ml]", | |
466 | "Reservoir Filled [ml]", | |
467 | }; | |
468 | ||
19692f17 AS |
469 | /* Labels for Aquastream XT */ |
470 | static const char *const label_aquastreamxt_temp_sensors[] = { | |
471 | "Fan IC temp", | |
472 | "External sensor", | |
473 | "Coolant temp" | |
474 | }; | |
475 | ||
7505dab7 AS |
476 | /* Labels for Aquastream Ultimate */ |
477 | static const char *const label_aquastreamult_temp[] = { | |
478 | "Coolant temp", | |
479 | "External temp" | |
480 | }; | |
481 | ||
482 | static const char *const label_aquastreamult_speeds[] = { | |
483 | "Fan speed", | |
484 | "Pump speed", | |
485 | "Pressure [mbar]", | |
486 | "Flow speed [dL/h]" | |
487 | }; | |
488 | ||
489 | static const char *const label_aquastreamult_power[] = { | |
490 | "Fan power", | |
491 | "Pump power" | |
492 | }; | |
493 | ||
494 | static const char *const label_aquastreamult_voltages[] = { | |
495 | "Fan voltage", | |
496 | "Pump voltage" | |
497 | }; | |
498 | ||
499 | static const char *const label_aquastreamult_current[] = { | |
500 | "Fan current", | |
501 | "Pump current" | |
502 | }; | |
503 | ||
e0f6c370 AS |
504 | /* Labels for Poweradjust 3 */ |
505 | static const char *const label_poweradjust3_temp_sensors[] = { | |
506 | "External sensor" | |
507 | }; | |
508 | ||
ceaa2240 AS |
509 | /* Labels for Highflow */ |
510 | static const char *const label_highflow_temp[] = { | |
511 | "External temp", | |
512 | "Internal temp" | |
513 | }; | |
514 | ||
515 | static const char *const label_highflow_speeds[] = { | |
516 | "Flow speed [dL/h]" | |
517 | }; | |
518 | ||
249c7521 LA |
519 | struct aqc_fan_structure_offsets { |
520 | u8 voltage; | |
521 | u8 curr; | |
522 | u8 power; | |
523 | u8 speed; | |
524 | }; | |
525 | ||
2c552111 LA |
526 | /* Fan structure offsets for Aquaero */ |
527 | static struct aqc_fan_structure_offsets aqc_aquaero_fan_structure = { | |
528 | .voltage = AQUAERO_FAN_VOLTAGE_OFFSET, | |
529 | .curr = AQUAERO_FAN_CURRENT_OFFSET, | |
530 | .power = AQUAERO_FAN_POWER_OFFSET, | |
531 | .speed = AQUAERO_FAN_SPEED_OFFSET | |
532 | }; | |
533 | ||
7505dab7 AS |
534 | /* Fan structure offsets for Aquastream Ultimate */ |
535 | static struct aqc_fan_structure_offsets aqc_aquastreamult_fan_structure = { | |
536 | .voltage = AQUASTREAMULT_FAN_VOLTAGE_OFFSET, | |
537 | .curr = AQUASTREAMULT_FAN_CURRENT_OFFSET, | |
538 | .power = AQUASTREAMULT_FAN_POWER_OFFSET, | |
539 | .speed = AQUASTREAMULT_FAN_SPEED_OFFSET | |
540 | }; | |
541 | ||
542 | /* Fan structure offsets for all devices except those above */ | |
249c7521 LA |
543 | static struct aqc_fan_structure_offsets aqc_general_fan_structure = { |
544 | .voltage = AQC_FAN_VOLTAGE_OFFSET, | |
545 | .curr = AQC_FAN_CURRENT_OFFSET, | |
546 | .power = AQC_FAN_POWER_OFFSET, | |
547 | .speed = AQC_FAN_SPEED_OFFSET | |
548 | }; | |
549 | ||
2fd3eec1 | 550 | struct aqc_data { |
0e35f63f AS |
551 | struct hid_device *hdev; |
552 | struct device *hwmon_dev; | |
553 | struct dentry *debugfs; | |
752b9279 | 554 | struct mutex mutex; /* Used for locking access when reading and writing PWM values */ |
2fd3eec1 AS |
555 | enum kinds kind; |
556 | const char *name; | |
557 | ||
e0f6c370 | 558 | int status_report_id; /* Used for legacy devices, report is stored in buffer */ |
b29090ba LA |
559 | int ctrl_report_id; |
560 | int secondary_ctrl_report_id; | |
561 | int secondary_ctrl_report_size; | |
562 | u8 *secondary_ctrl_report; | |
e0f6c370 | 563 | |
56b930dc AS |
564 | ktime_t last_ctrl_report_op; |
565 | int ctrl_report_delay; /* Delay between two ctrl report operations, in ms */ | |
566 | ||
752b9279 AS |
567 | int buffer_size; |
568 | u8 *buffer; | |
569 | int checksum_start; | |
570 | int checksum_length; | |
571 | int checksum_offset; | |
572 | ||
654c9735 | 573 | int num_fans; |
1ed5036b | 574 | u16 *fan_sensor_offsets; |
654c9735 AS |
575 | u16 *fan_ctrl_offsets; |
576 | int num_temp_sensors; | |
577 | int temp_sensor_start_offset; | |
e2769f5e AS |
578 | int num_virtual_temp_sensors; |
579 | int virtual_temp_sensor_start_offset; | |
3d2e9f58 AS |
580 | int num_calc_virt_temp_sensors; |
581 | int calc_virt_temp_sensor_start_offset; | |
662d20b3 | 582 | u16 temp_ctrl_offset; |
654c9735 | 583 | u16 power_cycle_count_offset; |
a2ba7ee2 LA |
584 | int num_flow_sensors; |
585 | u8 flow_sensors_start_offset; | |
6ff838f2 | 586 | u8 flow_pulses_ctrl_offset; |
249c7521 | 587 | struct aqc_fan_structure_offsets *fan_structure; |
654c9735 | 588 | |
2fd3eec1 | 589 | /* General info, same across all devices */ |
ad2f0811 | 590 | u8 serial_number_start_offset; |
2fd3eec1 | 591 | u32 serial_number[2]; |
ad2f0811 | 592 | u8 firmware_version_offset; |
2fd3eec1 AS |
593 | u16 firmware_version; |
594 | ||
654c9735 | 595 | /* How many times the device was powered on, if available */ |
2fd3eec1 AS |
596 | u32 power_cycles; |
597 | ||
598 | /* Sensor values */ | |
3d2e9f58 | 599 | s32 temp_input[20]; /* Max 4 physical and 16 virtual or 8 physical and 12 virtual */ |
b3d3be6c AS |
600 | s32 speed_input[8]; |
601 | u32 speed_input_min[1]; | |
602 | u32 speed_input_target[1]; | |
603 | u32 speed_input_max[1]; | |
752b9279 AS |
604 | u32 power_input[8]; |
605 | u16 voltage_input[8]; | |
606 | u16 current_input[8]; | |
607 | ||
608 | /* Label values */ | |
609 | const char *const *temp_label; | |
e2769f5e | 610 | const char *const *virtual_temp_label; |
3d2e9f58 | 611 | const char *const *calc_virt_temp_label; /* For Aquaero */ |
752b9279 AS |
612 | const char *const *speed_label; |
613 | const char *const *power_label; | |
614 | const char *const *voltage_label; | |
615 | const char *const *current_label; | |
2fd3eec1 | 616 | |
0e35f63f AS |
617 | unsigned long updated; |
618 | }; | |
619 | ||
752b9279 AS |
620 | /* Converts from centi-percent */ |
621 | static int aqc_percent_to_pwm(u16 val) | |
622 | { | |
623 | return DIV_ROUND_CLOSEST(val * 255, 100 * 100); | |
624 | } | |
625 | ||
626 | /* Converts to centi-percent */ | |
627 | static int aqc_pwm_to_percent(long val) | |
628 | { | |
629 | if (val < 0 || val > 255) | |
630 | return -EINVAL; | |
631 | ||
632 | return DIV_ROUND_CLOSEST(val * 100 * 100, 255); | |
633 | } | |
634 | ||
19692f17 AS |
635 | /* Converts raw value for Aquastream XT pump speed to RPM */ |
636 | static int aqc_aquastreamxt_convert_pump_rpm(u16 val) | |
637 | { | |
638 | if (val > 0) | |
639 | return DIV_ROUND_CLOSEST(AQUASTREAMXT_PUMP_CONVERSION_CONST, val); | |
640 | return 0; | |
641 | } | |
642 | ||
643 | /* Converts raw value for Aquastream XT fan speed to RPM */ | |
644 | static int aqc_aquastreamxt_convert_fan_rpm(u16 val) | |
645 | { | |
646 | if (val > 0) | |
647 | return DIV_ROUND_CLOSEST(AQUASTREAMXT_FAN_CONVERSION_CONST, val); | |
648 | return 0; | |
649 | } | |
650 | ||
56b930dc AS |
651 | static void aqc_delay_ctrl_report(struct aqc_data *priv) |
652 | { | |
653 | /* | |
654 | * If previous read or write is too close to this one, delay the current operation | |
655 | * to give the device enough time to process the previous one. | |
656 | */ | |
657 | if (priv->ctrl_report_delay) { | |
658 | s64 delta = ktime_ms_delta(ktime_get(), priv->last_ctrl_report_op); | |
659 | ||
660 | if (delta < priv->ctrl_report_delay) | |
661 | msleep(priv->ctrl_report_delay - delta); | |
662 | } | |
663 | } | |
664 | ||
752b9279 AS |
665 | /* Expects the mutex to be locked */ |
666 | static int aqc_get_ctrl_data(struct aqc_data *priv) | |
667 | { | |
668 | int ret; | |
669 | ||
56b930dc AS |
670 | aqc_delay_ctrl_report(priv); |
671 | ||
752b9279 | 672 | memset(priv->buffer, 0x00, priv->buffer_size); |
b29090ba | 673 | ret = hid_hw_raw_request(priv->hdev, priv->ctrl_report_id, priv->buffer, priv->buffer_size, |
752b9279 AS |
674 | HID_FEATURE_REPORT, HID_REQ_GET_REPORT); |
675 | if (ret < 0) | |
676 | ret = -ENODATA; | |
677 | ||
56b930dc AS |
678 | priv->last_ctrl_report_op = ktime_get(); |
679 | ||
752b9279 AS |
680 | return ret; |
681 | } | |
682 | ||
683 | /* Expects the mutex to be locked */ | |
684 | static int aqc_send_ctrl_data(struct aqc_data *priv) | |
685 | { | |
686 | int ret; | |
687 | u16 checksum; | |
688 | ||
56b930dc AS |
689 | aqc_delay_ctrl_report(priv); |
690 | ||
6c83ccb1 LA |
691 | /* Checksum is not needed for Aquaero */ |
692 | if (priv->kind != aquaero) { | |
693 | /* Init and xorout value for CRC-16/USB is 0xffff */ | |
694 | checksum = crc16(0xffff, priv->buffer + priv->checksum_start, | |
695 | priv->checksum_length); | |
696 | checksum ^= 0xffff; | |
752b9279 | 697 | |
6c83ccb1 LA |
698 | /* Place the new checksum at the end of the report */ |
699 | put_unaligned_be16(checksum, priv->buffer + priv->checksum_offset); | |
700 | } | |
752b9279 AS |
701 | |
702 | /* Send the patched up report back to the device */ | |
b29090ba | 703 | ret = hid_hw_raw_request(priv->hdev, priv->ctrl_report_id, priv->buffer, priv->buffer_size, |
752b9279 AS |
704 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); |
705 | if (ret < 0) | |
56b930dc | 706 | goto record_access_and_ret; |
752b9279 AS |
707 | |
708 | /* The official software sends this report after every change, so do it here as well */ | |
b29090ba LA |
709 | ret = hid_hw_raw_request(priv->hdev, priv->secondary_ctrl_report_id, |
710 | priv->secondary_ctrl_report, priv->secondary_ctrl_report_size, | |
711 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | |
56b930dc AS |
712 | |
713 | record_access_and_ret: | |
714 | priv->last_ctrl_report_op = ktime_get(); | |
715 | ||
752b9279 AS |
716 | return ret; |
717 | } | |
718 | ||
662d20b3 | 719 | /* Refreshes the control buffer and stores value at offset in val */ |
d0450fc1 | 720 | static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val, int type) |
752b9279 AS |
721 | { |
722 | int ret; | |
723 | ||
724 | mutex_lock(&priv->mutex); | |
725 | ||
726 | ret = aqc_get_ctrl_data(priv); | |
727 | if (ret < 0) | |
728 | goto unlock_and_return; | |
729 | ||
d0450fc1 LA |
730 | switch (type) { |
731 | case AQC_BE16: | |
732 | *val = (s16)get_unaligned_be16(priv->buffer + offset); | |
733 | break; | |
734 | case AQC_8: | |
735 | *val = priv->buffer[offset]; | |
736 | break; | |
737 | default: | |
738 | ret = -EINVAL; | |
739 | } | |
752b9279 AS |
740 | |
741 | unlock_and_return: | |
742 | mutex_unlock(&priv->mutex); | |
743 | return ret; | |
744 | } | |
745 | ||
4d09d155 | 746 | static int aqc_set_ctrl_vals(struct aqc_data *priv, int *offsets, long *vals, int *types, int len) |
752b9279 | 747 | { |
4d09d155 | 748 | int ret, i; |
752b9279 AS |
749 | |
750 | mutex_lock(&priv->mutex); | |
751 | ||
752 | ret = aqc_get_ctrl_data(priv); | |
753 | if (ret < 0) | |
754 | goto unlock_and_return; | |
755 | ||
4d09d155 LA |
756 | for (i = 0; i < len; i++) { |
757 | switch (types[i]) { | |
758 | case AQC_BE16: | |
759 | put_unaligned_be16((s16)vals[i], priv->buffer + offsets[i]); | |
760 | break; | |
761 | case AQC_8: | |
762 | priv->buffer[offsets[i]] = (u8)vals[i]; | |
763 | break; | |
764 | default: | |
765 | ret = -EINVAL; | |
766 | } | |
d0450fc1 LA |
767 | } |
768 | ||
769 | if (ret < 0) | |
770 | goto unlock_and_return; | |
752b9279 AS |
771 | |
772 | ret = aqc_send_ctrl_data(priv); | |
773 | ||
774 | unlock_and_return: | |
775 | mutex_unlock(&priv->mutex); | |
776 | return ret; | |
777 | } | |
778 | ||
4d09d155 LA |
779 | static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val, int type) |
780 | { | |
781 | return aqc_set_ctrl_vals(priv, &offset, &val, &type, 1); | |
782 | } | |
783 | ||
752b9279 | 784 | static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) |
0e35f63f | 785 | { |
2fd3eec1 AS |
786 | const struct aqc_data *priv = data; |
787 | ||
788 | switch (type) { | |
789 | case hwmon_temp: | |
662d20b3 AS |
790 | if (channel < priv->num_temp_sensors) { |
791 | switch (attr) { | |
792 | case hwmon_temp_label: | |
793 | case hwmon_temp_input: | |
794 | return 0444; | |
795 | case hwmon_temp_offset: | |
796 | if (priv->temp_ctrl_offset != 0) | |
797 | return 0644; | |
798 | break; | |
799 | default: | |
800 | break; | |
801 | } | |
802 | } | |
803 | ||
3d2e9f58 AS |
804 | if (channel < |
805 | priv->num_temp_sensors + priv->num_virtual_temp_sensors + | |
806 | priv->num_calc_virt_temp_sensors) | |
662d20b3 AS |
807 | switch (attr) { |
808 | case hwmon_temp_label: | |
809 | case hwmon_temp_input: | |
810 | return 0444; | |
811 | default: | |
812 | break; | |
813 | } | |
2fd3eec1 | 814 | break; |
752b9279 | 815 | case hwmon_pwm: |
654c9735 | 816 | if (priv->fan_ctrl_offsets && channel < priv->num_fans) { |
752b9279 AS |
817 | switch (attr) { |
818 | case hwmon_pwm_input: | |
819 | return 0644; | |
820 | default: | |
821 | break; | |
822 | } | |
752b9279 AS |
823 | } |
824 | break; | |
2fd3eec1 | 825 | case hwmon_fan: |
6ff838f2 AS |
826 | switch (attr) { |
827 | case hwmon_fan_input: | |
828 | case hwmon_fan_label: | |
829 | switch (priv->kind) { | |
7505dab7 AS |
830 | case aquastreamult: |
831 | /* | |
832 | * Special case to support pump RPM, fan RPM, | |
833 | * pressure and flow sensor | |
834 | */ | |
835 | if (channel < 4) | |
836 | return 0444; | |
837 | break; | |
6ff838f2 AS |
838 | case highflownext: |
839 | /* Special case to support flow sensor, water quality | |
840 | * and conductivity | |
841 | */ | |
842 | if (channel < 3) | |
843 | return 0444; | |
844 | break; | |
b3d3be6c AS |
845 | case leakshield: |
846 | /* Special case for Leakshield sensors */ | |
847 | if (channel < 5) | |
848 | return 0444; | |
849 | break; | |
2c552111 | 850 | case aquaero: |
120584c7 | 851 | case octo: |
6ff838f2 | 852 | case quadro: |
ceaa2240 | 853 | case highflow: |
a2ba7ee2 LA |
854 | /* Special case to support flow sensors */ |
855 | if (channel < priv->num_fans + priv->num_flow_sensors) | |
6ff838f2 AS |
856 | return 0444; |
857 | break; | |
858 | default: | |
859 | if (channel < priv->num_fans) | |
860 | return 0444; | |
861 | break; | |
862 | } | |
aed80bb9 | 863 | break; |
6ff838f2 | 864 | case hwmon_fan_pulses: |
bf7b5a12 AS |
865 | /* Special case for Quadro/Octo flow sensor */ |
866 | if (channel == priv->num_fans) { | |
867 | switch (priv->kind) { | |
868 | case quadro: | |
869 | case octo: | |
870 | return 0644; | |
871 | default: | |
872 | break; | |
873 | } | |
874 | } | |
cdbe34da | 875 | break; |
b3d3be6c AS |
876 | case hwmon_fan_min: |
877 | case hwmon_fan_max: | |
878 | case hwmon_fan_target: | |
879 | /* Special case for Leakshield pressure sensor */ | |
880 | if (priv->kind == leakshield && channel == 0) | |
881 | return 0444; | |
882 | break; | |
cdbe34da | 883 | default: |
cdbe34da AS |
884 | break; |
885 | } | |
886 | break; | |
2fd3eec1 | 887 | case hwmon_power: |
aed80bb9 | 888 | switch (priv->kind) { |
7505dab7 AS |
889 | case aquastreamult: |
890 | /* Special case to support pump and fan power */ | |
891 | if (channel < 2) | |
892 | return 0444; | |
893 | break; | |
aed80bb9 AS |
894 | case highflownext: |
895 | /* Special case to support one power sensor */ | |
896 | if (channel == 0) | |
897 | return 0444; | |
898 | break; | |
19692f17 AS |
899 | case aquastreamxt: |
900 | break; | |
aed80bb9 AS |
901 | default: |
902 | if (channel < priv->num_fans) | |
903 | return 0444; | |
904 | break; | |
905 | } | |
906 | break; | |
2fd3eec1 | 907 | case hwmon_curr: |
7505dab7 AS |
908 | switch (priv->kind) { |
909 | case aquastreamult: | |
910 | /* Special case to support pump and fan current */ | |
911 | if (channel < 2) | |
912 | return 0444; | |
913 | break; | |
19692f17 AS |
914 | case aquastreamxt: |
915 | /* Special case to support pump current */ | |
916 | if (channel == 0) | |
917 | return 0444; | |
918 | break; | |
7505dab7 AS |
919 | default: |
920 | if (channel < priv->num_fans) | |
921 | return 0444; | |
922 | break; | |
923 | } | |
752b9279 AS |
924 | break; |
925 | case hwmon_in: | |
926 | switch (priv->kind) { | |
927 | case d5next: | |
f4caa262 AS |
928 | /* Special case to support +5V and +12V voltage sensors */ |
929 | if (channel < priv->num_fans + 2) | |
752b9279 AS |
930 | return 0444; |
931 | break; | |
7505dab7 | 932 | case aquastreamult: |
aed80bb9 AS |
933 | case highflownext: |
934 | /* Special case to support two voltage sensors */ | |
935 | if (channel < 2) | |
936 | return 0444; | |
937 | break; | |
2fd3eec1 | 938 | default: |
654c9735 AS |
939 | if (channel < priv->num_fans) |
940 | return 0444; | |
2fd3eec1 AS |
941 | break; |
942 | } | |
943 | break; | |
944 | default: | |
945 | break; | |
946 | } | |
947 | ||
948 | return 0; | |
0e35f63f AS |
949 | } |
950 | ||
e0f6c370 AS |
951 | /* Read device sensors by manually requesting the sensor report (legacy way) */ |
952 | static int aqc_legacy_read(struct aqc_data *priv) | |
953 | { | |
954 | int ret, i, sensor_value; | |
955 | ||
956 | mutex_lock(&priv->mutex); | |
957 | ||
958 | memset(priv->buffer, 0x00, priv->buffer_size); | |
959 | ret = hid_hw_raw_request(priv->hdev, priv->status_report_id, priv->buffer, | |
960 | priv->buffer_size, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); | |
961 | if (ret < 0) | |
962 | goto unlock_and_return; | |
963 | ||
964 | /* Temperature sensor readings */ | |
965 | for (i = 0; i < priv->num_temp_sensors; i++) { | |
966 | sensor_value = get_unaligned_le16(priv->buffer + priv->temp_sensor_start_offset + | |
967 | i * AQC_SENSOR_SIZE); | |
0f564130 AS |
968 | if (sensor_value == AQC_SENSOR_NA) |
969 | priv->temp_input[i] = -ENODATA; | |
970 | else | |
971 | priv->temp_input[i] = sensor_value * 10; | |
e0f6c370 AS |
972 | } |
973 | ||
19692f17 AS |
974 | /* Special-case sensor readings */ |
975 | switch (priv->kind) { | |
976 | case aquastreamxt: | |
977 | /* Info provided with every report */ | |
978 | priv->serial_number[0] = get_unaligned_le16(priv->buffer + | |
979 | priv->serial_number_start_offset); | |
980 | priv->firmware_version = | |
981 | get_unaligned_le16(priv->buffer + priv->firmware_version_offset); | |
982 | ||
983 | /* Read pump speed in RPM */ | |
984 | sensor_value = get_unaligned_le16(priv->buffer + priv->fan_sensor_offsets[0]); | |
985 | priv->speed_input[0] = aqc_aquastreamxt_convert_pump_rpm(sensor_value); | |
986 | ||
987 | /* Read fan speed in RPM, if available */ | |
988 | sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_FAN_STATUS_OFFSET); | |
989 | if (sensor_value == AQUASTREAMXT_FAN_STOPPED) { | |
990 | priv->speed_input[1] = 0; | |
991 | } else { | |
992 | sensor_value = | |
993 | get_unaligned_le16(priv->buffer + priv->fan_sensor_offsets[1]); | |
994 | priv->speed_input[1] = aqc_aquastreamxt_convert_fan_rpm(sensor_value); | |
995 | } | |
996 | ||
997 | /* Calculation derived from linear regression */ | |
998 | sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_CURR_OFFSET); | |
999 | priv->current_input[0] = DIV_ROUND_CLOSEST(sensor_value * 176, 100) - 52; | |
1000 | ||
1001 | sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_VOLTAGE_OFFSET); | |
1002 | priv->voltage_input[0] = DIV_ROUND_CLOSEST(sensor_value * 1000, 61); | |
1003 | ||
1004 | sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_FAN_VOLTAGE_OFFSET); | |
1005 | priv->voltage_input[1] = DIV_ROUND_CLOSEST(sensor_value * 1000, 63); | |
1006 | break; | |
ceaa2240 AS |
1007 | case highflow: |
1008 | /* Info provided with every report */ | |
1009 | priv->serial_number[0] = get_unaligned_le16(priv->buffer + | |
1010 | priv->serial_number_start_offset); | |
1011 | priv->firmware_version = | |
1012 | get_unaligned_le16(priv->buffer + priv->firmware_version_offset); | |
1013 | ||
1014 | /* Read flow speed */ | |
1015 | priv->speed_input[0] = get_unaligned_le16(priv->buffer + | |
1016 | priv->flow_sensors_start_offset); | |
1017 | break; | |
19692f17 AS |
1018 | default: |
1019 | break; | |
1020 | } | |
1021 | ||
e0f6c370 AS |
1022 | priv->updated = jiffies; |
1023 | ||
1024 | unlock_and_return: | |
1025 | mutex_unlock(&priv->mutex); | |
1026 | return ret; | |
1027 | } | |
1028 | ||
2fd3eec1 AS |
1029 | static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
1030 | int channel, long *val) | |
0e35f63f | 1031 | { |
752b9279 | 1032 | int ret; |
2fd3eec1 | 1033 | struct aqc_data *priv = dev_get_drvdata(dev); |
0e35f63f | 1034 | |
e0f6c370 AS |
1035 | if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL)) { |
1036 | if (priv->status_report_id != 0) { | |
1037 | /* Legacy devices require manual reads */ | |
1038 | ret = aqc_legacy_read(priv); | |
1039 | if (ret < 0) | |
1040 | return -ENODATA; | |
1041 | } else { | |
1042 | return -ENODATA; | |
1043 | } | |
1044 | } | |
0e35f63f AS |
1045 | |
1046 | switch (type) { | |
1047 | case hwmon_temp: | |
662d20b3 AS |
1048 | switch (attr) { |
1049 | case hwmon_temp_input: | |
1050 | if (priv->temp_input[channel] == -ENODATA) | |
1051 | return -ENODATA; | |
1052 | ||
1053 | *val = priv->temp_input[channel]; | |
1054 | break; | |
1055 | case hwmon_temp_offset: | |
1056 | ret = | |
1057 | aqc_get_ctrl_val(priv, priv->temp_ctrl_offset + | |
d0450fc1 | 1058 | channel * AQC_SENSOR_SIZE, val, AQC_BE16); |
662d20b3 AS |
1059 | if (ret < 0) |
1060 | return ret; | |
2fd3eec1 | 1061 | |
662d20b3 AS |
1062 | *val *= 10; |
1063 | break; | |
1064 | default: | |
1065 | break; | |
1066 | } | |
0e35f63f AS |
1067 | break; |
1068 | case hwmon_fan: | |
6ff838f2 AS |
1069 | switch (attr) { |
1070 | case hwmon_fan_input: | |
b3d3be6c AS |
1071 | if (priv->speed_input[channel] == -ENODATA) |
1072 | return -ENODATA; | |
1073 | ||
6ff838f2 AS |
1074 | *val = priv->speed_input[channel]; |
1075 | break; | |
b3d3be6c AS |
1076 | case hwmon_fan_min: |
1077 | *val = priv->speed_input_min[channel]; | |
1078 | break; | |
1079 | case hwmon_fan_max: | |
1080 | *val = priv->speed_input_max[channel]; | |
1081 | break; | |
1082 | case hwmon_fan_target: | |
1083 | *val = priv->speed_input_target[channel]; | |
1084 | break; | |
6ff838f2 | 1085 | case hwmon_fan_pulses: |
d0450fc1 LA |
1086 | ret = aqc_get_ctrl_val(priv, priv->flow_pulses_ctrl_offset, |
1087 | val, AQC_BE16); | |
6ff838f2 AS |
1088 | if (ret < 0) |
1089 | return ret; | |
1090 | break; | |
1091 | default: | |
1092 | break; | |
1093 | } | |
0e35f63f AS |
1094 | break; |
1095 | case hwmon_power: | |
1096 | *val = priv->power_input[channel]; | |
1097 | break; | |
752b9279 | 1098 | case hwmon_pwm: |
bd1e92f9 LA |
1099 | switch (priv->kind) { |
1100 | case aquaero: | |
1101 | ret = aqc_get_ctrl_val(priv, | |
1102 | AQUAERO_CTRL_PRESET_START + channel * AQUAERO_CTRL_PRESET_SIZE, | |
1103 | val, AQC_BE16); | |
1104 | if (ret < 0) | |
1105 | return ret; | |
1106 | *val = aqc_percent_to_pwm(*val); | |
1107 | break; | |
1108 | default: | |
d0450fc1 LA |
1109 | ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel], |
1110 | val, AQC_BE16); | |
752b9279 AS |
1111 | if (ret < 0) |
1112 | return ret; | |
1113 | ||
a746b368 | 1114 | *val = aqc_percent_to_pwm(*val); |
bd1e92f9 | 1115 | break; |
752b9279 AS |
1116 | } |
1117 | break; | |
0e35f63f AS |
1118 | case hwmon_in: |
1119 | *val = priv->voltage_input[channel]; | |
1120 | break; | |
1121 | case hwmon_curr: | |
1122 | *val = priv->current_input[channel]; | |
1123 | break; | |
1124 | default: | |
1125 | return -EOPNOTSUPP; | |
1126 | } | |
1127 | ||
1128 | return 0; | |
1129 | } | |
1130 | ||
2fd3eec1 AS |
1131 | static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
1132 | int channel, const char **str) | |
0e35f63f | 1133 | { |
2fd3eec1 AS |
1134 | struct aqc_data *priv = dev_get_drvdata(dev); |
1135 | ||
3d2e9f58 AS |
1136 | /* Number of sensors that are not calculated */ |
1137 | int num_non_calc_sensors = priv->num_temp_sensors + priv->num_virtual_temp_sensors; | |
1138 | ||
0e35f63f AS |
1139 | switch (type) { |
1140 | case hwmon_temp: | |
3d2e9f58 | 1141 | if (channel < priv->num_temp_sensors) { |
e2769f5e | 1142 | *str = priv->temp_label[channel]; |
3d2e9f58 AS |
1143 | } else { |
1144 | if (priv->kind == aquaero && channel >= num_non_calc_sensors) | |
1145 | *str = | |
1146 | priv->calc_virt_temp_label[channel - num_non_calc_sensors]; | |
1147 | else | |
1148 | *str = priv->virtual_temp_label[channel - priv->num_temp_sensors]; | |
1149 | } | |
0e35f63f AS |
1150 | break; |
1151 | case hwmon_fan: | |
752b9279 | 1152 | *str = priv->speed_label[channel]; |
0e35f63f AS |
1153 | break; |
1154 | case hwmon_power: | |
752b9279 | 1155 | *str = priv->power_label[channel]; |
0e35f63f AS |
1156 | break; |
1157 | case hwmon_in: | |
752b9279 | 1158 | *str = priv->voltage_label[channel]; |
0e35f63f AS |
1159 | break; |
1160 | case hwmon_curr: | |
752b9279 AS |
1161 | *str = priv->current_label[channel]; |
1162 | break; | |
1163 | default: | |
1164 | return -EOPNOTSUPP; | |
1165 | } | |
1166 | ||
1167 | return 0; | |
1168 | } | |
1169 | ||
1170 | static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, | |
1171 | long val) | |
1172 | { | |
1173 | int ret, pwm_value; | |
bd1e92f9 LA |
1174 | /* Arrays for setting multiple values at once in the control report */ |
1175 | int ctrl_values_offsets[4]; | |
1176 | long ctrl_values[4]; | |
1177 | int ctrl_values_types[4]; | |
752b9279 AS |
1178 | struct aqc_data *priv = dev_get_drvdata(dev); |
1179 | ||
1180 | switch (type) { | |
662d20b3 AS |
1181 | case hwmon_temp: |
1182 | switch (attr) { | |
1183 | case hwmon_temp_offset: | |
1184 | /* Limit temp offset to +/- 15K as in the official software */ | |
1185 | val = clamp_val(val, -15000, 15000) / 10; | |
1186 | ret = | |
1187 | aqc_set_ctrl_val(priv, priv->temp_ctrl_offset + | |
d0450fc1 | 1188 | channel * AQC_SENSOR_SIZE, val, AQC_BE16); |
662d20b3 AS |
1189 | if (ret < 0) |
1190 | return ret; | |
1191 | break; | |
1192 | default: | |
1193 | return -EOPNOTSUPP; | |
1194 | } | |
1195 | break; | |
6ff838f2 AS |
1196 | case hwmon_fan: |
1197 | switch (attr) { | |
1198 | case hwmon_fan_pulses: | |
1199 | val = clamp_val(val, 10, 1000); | |
d0450fc1 LA |
1200 | ret = aqc_set_ctrl_val(priv, priv->flow_pulses_ctrl_offset, |
1201 | val, AQC_BE16); | |
6ff838f2 AS |
1202 | if (ret < 0) |
1203 | return ret; | |
1204 | break; | |
1205 | default: | |
1206 | break; | |
1207 | } | |
1208 | break; | |
752b9279 AS |
1209 | case hwmon_pwm: |
1210 | switch (attr) { | |
1211 | case hwmon_pwm_input: | |
bd1e92f9 LA |
1212 | pwm_value = aqc_pwm_to_percent(val); |
1213 | if (pwm_value < 0) | |
1214 | return pwm_value; | |
752b9279 | 1215 | |
bd1e92f9 LA |
1216 | switch (priv->kind) { |
1217 | case aquaero: | |
1218 | /* Write pwm value to preset corresponding to the channel */ | |
1219 | ctrl_values_offsets[0] = AQUAERO_CTRL_PRESET_START + | |
1220 | channel * AQUAERO_CTRL_PRESET_SIZE; | |
1221 | ctrl_values[0] = pwm_value; | |
1222 | ctrl_values_types[0] = AQC_BE16; | |
1223 | ||
1224 | /* Write preset number in fan control source */ | |
1225 | ctrl_values_offsets[1] = priv->fan_ctrl_offsets[channel] + | |
1226 | AQUAERO_FAN_CTRL_SRC_OFFSET; | |
1227 | ctrl_values[1] = AQUAERO_CTRL_PRESET_ID + channel; | |
1228 | ctrl_values_types[1] = AQC_BE16; | |
1229 | ||
1230 | /* Set minimum power to 0 to allow the fan to turn off */ | |
1231 | ctrl_values_offsets[2] = priv->fan_ctrl_offsets[channel] + | |
1232 | AQUAERO_FAN_CTRL_MIN_PWR_OFFSET; | |
1233 | ctrl_values[2] = 0; | |
1234 | ctrl_values_types[2] = AQC_BE16; | |
1235 | ||
1236 | /* Set maximum power to 255 to allow the fan to reach max speed */ | |
1237 | ctrl_values_offsets[3] = priv->fan_ctrl_offsets[channel] + | |
1238 | AQUAERO_FAN_CTRL_MAX_PWR_OFFSET; | |
1239 | ctrl_values[3] = aqc_pwm_to_percent(255); | |
1240 | ctrl_values_types[3] = AQC_BE16; | |
1241 | ||
1242 | ret = aqc_set_ctrl_vals(priv, ctrl_values_offsets, ctrl_values, | |
1243 | ctrl_values_types, 4); | |
1244 | if (ret < 0) | |
1245 | return ret; | |
1246 | break; | |
1247 | default: | |
654c9735 | 1248 | ret = aqc_set_ctrl_val(priv, priv->fan_ctrl_offsets[channel], |
d0450fc1 | 1249 | pwm_value, AQC_BE16); |
752b9279 AS |
1250 | if (ret < 0) |
1251 | return ret; | |
bd1e92f9 | 1252 | break; |
752b9279 | 1253 | } |
2fd3eec1 AS |
1254 | break; |
1255 | default: | |
1256 | break; | |
1257 | } | |
0e35f63f AS |
1258 | break; |
1259 | default: | |
1260 | return -EOPNOTSUPP; | |
1261 | } | |
1262 | ||
1263 | return 0; | |
1264 | } | |
1265 | ||
2fd3eec1 AS |
1266 | static const struct hwmon_ops aqc_hwmon_ops = { |
1267 | .is_visible = aqc_is_visible, | |
1268 | .read = aqc_read, | |
1269 | .read_string = aqc_read_string, | |
752b9279 | 1270 | .write = aqc_write |
0e35f63f AS |
1271 | }; |
1272 | ||
832dc510 | 1273 | static const struct hwmon_channel_info * const aqc_info[] = { |
2fd3eec1 | 1274 | HWMON_CHANNEL_INFO(temp, |
662d20b3 AS |
1275 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, |
1276 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
1277 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
1278 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
866e630a LA |
1279 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, |
1280 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
1281 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
1282 | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | |
e2769f5e AS |
1283 | HWMON_T_INPUT | HWMON_T_LABEL, |
1284 | HWMON_T_INPUT | HWMON_T_LABEL, | |
1285 | HWMON_T_INPUT | HWMON_T_LABEL, | |
1286 | HWMON_T_INPUT | HWMON_T_LABEL, | |
1287 | HWMON_T_INPUT | HWMON_T_LABEL, | |
1288 | HWMON_T_INPUT | HWMON_T_LABEL, | |
1289 | HWMON_T_INPUT | HWMON_T_LABEL, | |
1290 | HWMON_T_INPUT | HWMON_T_LABEL, | |
2fd3eec1 AS |
1291 | HWMON_T_INPUT | HWMON_T_LABEL, |
1292 | HWMON_T_INPUT | HWMON_T_LABEL, | |
1293 | HWMON_T_INPUT | HWMON_T_LABEL, | |
1294 | HWMON_T_INPUT | HWMON_T_LABEL), | |
1295 | HWMON_CHANNEL_INFO(fan, | |
b3d3be6c AS |
1296 | HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | |
1297 | HWMON_F_TARGET, | |
752b9279 AS |
1298 | HWMON_F_INPUT | HWMON_F_LABEL, |
1299 | HWMON_F_INPUT | HWMON_F_LABEL, | |
1300 | HWMON_F_INPUT | HWMON_F_LABEL, | |
6ff838f2 | 1301 | HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_PULSES, |
752b9279 | 1302 | HWMON_F_INPUT | HWMON_F_LABEL, |
2fd3eec1 | 1303 | HWMON_F_INPUT | HWMON_F_LABEL, |
120584c7 | 1304 | HWMON_F_INPUT | HWMON_F_LABEL, |
bf7b5a12 | 1305 | HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_PULSES), |
2fd3eec1 | 1306 | HWMON_CHANNEL_INFO(power, |
752b9279 AS |
1307 | HWMON_P_INPUT | HWMON_P_LABEL, |
1308 | HWMON_P_INPUT | HWMON_P_LABEL, | |
1309 | HWMON_P_INPUT | HWMON_P_LABEL, | |
1310 | HWMON_P_INPUT | HWMON_P_LABEL, | |
1311 | HWMON_P_INPUT | HWMON_P_LABEL, | |
1312 | HWMON_P_INPUT | HWMON_P_LABEL, | |
2fd3eec1 AS |
1313 | HWMON_P_INPUT | HWMON_P_LABEL, |
1314 | HWMON_P_INPUT | HWMON_P_LABEL), | |
752b9279 AS |
1315 | HWMON_CHANNEL_INFO(pwm, |
1316 | HWMON_PWM_INPUT, | |
1317 | HWMON_PWM_INPUT, | |
1318 | HWMON_PWM_INPUT, | |
1319 | HWMON_PWM_INPUT, | |
1320 | HWMON_PWM_INPUT, | |
1321 | HWMON_PWM_INPUT, | |
1322 | HWMON_PWM_INPUT, | |
1323 | HWMON_PWM_INPUT), | |
2fd3eec1 | 1324 | HWMON_CHANNEL_INFO(in, |
752b9279 AS |
1325 | HWMON_I_INPUT | HWMON_I_LABEL, |
1326 | HWMON_I_INPUT | HWMON_I_LABEL, | |
1327 | HWMON_I_INPUT | HWMON_I_LABEL, | |
1328 | HWMON_I_INPUT | HWMON_I_LABEL, | |
1329 | HWMON_I_INPUT | HWMON_I_LABEL, | |
2fd3eec1 AS |
1330 | HWMON_I_INPUT | HWMON_I_LABEL, |
1331 | HWMON_I_INPUT | HWMON_I_LABEL, | |
0e35f63f | 1332 | HWMON_I_INPUT | HWMON_I_LABEL), |
2fd3eec1 | 1333 | HWMON_CHANNEL_INFO(curr, |
752b9279 AS |
1334 | HWMON_C_INPUT | HWMON_C_LABEL, |
1335 | HWMON_C_INPUT | HWMON_C_LABEL, | |
1336 | HWMON_C_INPUT | HWMON_C_LABEL, | |
1337 | HWMON_C_INPUT | HWMON_C_LABEL, | |
1338 | HWMON_C_INPUT | HWMON_C_LABEL, | |
1339 | HWMON_C_INPUT | HWMON_C_LABEL, | |
2fd3eec1 AS |
1340 | HWMON_C_INPUT | HWMON_C_LABEL, |
1341 | HWMON_C_INPUT | HWMON_C_LABEL), | |
0e35f63f AS |
1342 | NULL |
1343 | }; | |
1344 | ||
2fd3eec1 AS |
1345 | static const struct hwmon_chip_info aqc_chip_info = { |
1346 | .ops = &aqc_hwmon_ops, | |
1347 | .info = aqc_info, | |
0e35f63f AS |
1348 | }; |
1349 | ||
229b159c | 1350 | static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) |
0e35f63f | 1351 | { |
e2769f5e | 1352 | int i, j, sensor_value; |
2fd3eec1 | 1353 | struct aqc_data *priv; |
0e35f63f | 1354 | |
2fd3eec1 | 1355 | if (report->id != STATUS_REPORT_ID) |
0e35f63f AS |
1356 | return 0; |
1357 | ||
1358 | priv = hid_get_drvdata(hdev); | |
1359 | ||
1360 | /* Info provided with every report */ | |
ad2f0811 LA |
1361 | priv->serial_number[0] = get_unaligned_be16(data + priv->serial_number_start_offset); |
1362 | priv->serial_number[1] = get_unaligned_be16(data + priv->serial_number_start_offset + | |
1363 | SERIAL_PART_OFFSET); | |
1364 | priv->firmware_version = get_unaligned_be16(data + priv->firmware_version_offset); | |
0e35f63f | 1365 | |
e2769f5e | 1366 | /* Physical temperature sensor readings */ |
654c9735 AS |
1367 | for (i = 0; i < priv->num_temp_sensors; i++) { |
1368 | sensor_value = get_unaligned_be16(data + | |
1369 | priv->temp_sensor_start_offset + | |
8bcb02bd | 1370 | i * AQC_SENSOR_SIZE); |
fdbfd330 | 1371 | if (sensor_value == AQC_SENSOR_NA) |
654c9735 AS |
1372 | priv->temp_input[i] = -ENODATA; |
1373 | else | |
1374 | priv->temp_input[i] = sensor_value * 10; | |
1375 | } | |
0e35f63f | 1376 | |
e2769f5e AS |
1377 | /* Virtual temperature sensor readings */ |
1378 | for (j = 0; j < priv->num_virtual_temp_sensors; j++) { | |
1379 | sensor_value = get_unaligned_be16(data + | |
1380 | priv->virtual_temp_sensor_start_offset + | |
8bcb02bd | 1381 | j * AQC_SENSOR_SIZE); |
fdbfd330 | 1382 | if (sensor_value == AQC_SENSOR_NA) |
e2769f5e AS |
1383 | priv->temp_input[i] = -ENODATA; |
1384 | else | |
1385 | priv->temp_input[i] = sensor_value * 10; | |
1386 | i++; | |
1387 | } | |
1388 | ||
654c9735 AS |
1389 | /* Fan speed and related readings */ |
1390 | for (i = 0; i < priv->num_fans; i++) { | |
1391 | priv->speed_input[i] = | |
249c7521 LA |
1392 | get_unaligned_be16(data + priv->fan_sensor_offsets[i] + |
1393 | priv->fan_structure->speed); | |
654c9735 AS |
1394 | priv->power_input[i] = |
1395 | get_unaligned_be16(data + priv->fan_sensor_offsets[i] + | |
249c7521 | 1396 | priv->fan_structure->power) * 10000; |
654c9735 AS |
1397 | priv->voltage_input[i] = |
1398 | get_unaligned_be16(data + priv->fan_sensor_offsets[i] + | |
249c7521 | 1399 | priv->fan_structure->voltage) * 10; |
654c9735 | 1400 | priv->current_input[i] = |
249c7521 LA |
1401 | get_unaligned_be16(data + priv->fan_sensor_offsets[i] + |
1402 | priv->fan_structure->curr); | |
654c9735 | 1403 | } |
0e35f63f | 1404 | |
a2ba7ee2 LA |
1405 | /* Flow sensor readings */ |
1406 | for (j = 0; j < priv->num_flow_sensors; j++) { | |
1407 | priv->speed_input[i] = get_unaligned_be16(data + priv->flow_sensors_start_offset + | |
1408 | j * AQC_SENSOR_SIZE); | |
1409 | i++; | |
1410 | } | |
1411 | ||
654c9735 AS |
1412 | if (priv->power_cycle_count_offset != 0) |
1413 | priv->power_cycles = get_unaligned_be32(data + priv->power_cycle_count_offset); | |
0e35f63f | 1414 | |
654c9735 AS |
1415 | /* Special-case sensor readings */ |
1416 | switch (priv->kind) { | |
3d2e9f58 AS |
1417 | case aquaero: |
1418 | /* Read calculated virtual temp sensors */ | |
1419 | i = priv->num_temp_sensors + priv->num_virtual_temp_sensors; | |
1420 | for (j = 0; j < priv->num_calc_virt_temp_sensors; j++) { | |
1421 | sensor_value = get_unaligned_be16(data + | |
1422 | priv->calc_virt_temp_sensor_start_offset + | |
1423 | j * AQC_SENSOR_SIZE); | |
fdbfd330 | 1424 | if (sensor_value == AQC_SENSOR_NA) |
3d2e9f58 AS |
1425 | priv->temp_input[i] = -ENODATA; |
1426 | else | |
1427 | priv->temp_input[i] = sensor_value * 10; | |
1428 | i++; | |
1429 | } | |
1430 | break; | |
7505dab7 AS |
1431 | case aquastreamult: |
1432 | priv->speed_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_OFFSET); | |
1433 | priv->speed_input[2] = get_unaligned_be16(data + AQUASTREAMULT_PRESSURE_OFFSET); | |
1434 | priv->speed_input[3] = get_unaligned_be16(data + AQUASTREAMULT_FLOW_SENSOR_OFFSET); | |
1435 | ||
1436 | priv->power_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000; | |
1437 | ||
1438 | priv->voltage_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_VOLTAGE) * 10; | |
1439 | ||
1440 | priv->current_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_CURRENT); | |
1441 | break; | |
654c9735 | 1442 | case d5next: |
2fd3eec1 | 1443 | priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; |
f4caa262 | 1444 | priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10; |
752b9279 | 1445 | break; |
aed80bb9 AS |
1446 | case highflownext: |
1447 | /* If external temp sensor is not connected, its power reading is also N/A */ | |
1448 | if (priv->temp_input[1] == -ENODATA) | |
1449 | priv->power_input[0] = -ENODATA; | |
1450 | else | |
1451 | priv->power_input[0] = | |
1452 | get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000; | |
1453 | ||
1454 | priv->voltage_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE) * 10; | |
1455 | priv->voltage_input[1] = | |
1456 | get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE_USB) * 10; | |
1457 | ||
aed80bb9 AS |
1458 | priv->speed_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_WATER_QUALITY); |
1459 | priv->speed_input[2] = get_unaligned_be16(data + HIGHFLOWNEXT_CONDUCTIVITY); | |
1460 | break; | |
b3d3be6c AS |
1461 | case leakshield: |
1462 | priv->speed_input[0] = | |
1463 | ((s16)get_unaligned_be16(data + LEAKSHIELD_PRESSURE_ADJUSTED)) * 100; | |
1464 | priv->speed_input_min[0] = get_unaligned_be16(data + LEAKSHIELD_PRESSURE_MIN) * 100; | |
1465 | priv->speed_input_target[0] = | |
1466 | get_unaligned_be16(data + LEAKSHIELD_PRESSURE_TARGET) * 100; | |
1467 | priv->speed_input_max[0] = get_unaligned_be16(data + LEAKSHIELD_PRESSURE_MAX) * 100; | |
1468 | ||
1469 | priv->speed_input[1] = get_unaligned_be16(data + LEAKSHIELD_PUMP_RPM_IN); | |
1470 | if (priv->speed_input[1] == AQC_SENSOR_NA) | |
1471 | priv->speed_input[1] = -ENODATA; | |
1472 | ||
1473 | priv->speed_input[2] = get_unaligned_be16(data + LEAKSHIELD_FLOW_IN); | |
1474 | if (priv->speed_input[2] == AQC_SENSOR_NA) | |
1475 | priv->speed_input[2] = -ENODATA; | |
1476 | ||
1477 | priv->speed_input[3] = get_unaligned_be16(data + LEAKSHIELD_RESERVOIR_VOLUME); | |
1478 | priv->speed_input[4] = get_unaligned_be16(data + LEAKSHIELD_RESERVOIR_FILLED); | |
1479 | ||
1480 | /* Second temp sensor is not positioned after the first one, read it here */ | |
1481 | priv->temp_input[1] = get_unaligned_be16(data + LEAKSHIELD_TEMPERATURE_2) * 10; | |
1482 | break; | |
2fd3eec1 AS |
1483 | default: |
1484 | break; | |
1485 | } | |
0e35f63f AS |
1486 | |
1487 | priv->updated = jiffies; | |
1488 | ||
1489 | return 0; | |
1490 | } | |
1491 | ||
0e35f63f AS |
1492 | static int serial_number_show(struct seq_file *seqf, void *unused) |
1493 | { | |
2fd3eec1 | 1494 | struct aqc_data *priv = seqf->private; |
0e35f63f AS |
1495 | |
1496 | seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]); | |
1497 | ||
1498 | return 0; | |
1499 | } | |
1500 | DEFINE_SHOW_ATTRIBUTE(serial_number); | |
1501 | ||
1502 | static int firmware_version_show(struct seq_file *seqf, void *unused) | |
1503 | { | |
2fd3eec1 | 1504 | struct aqc_data *priv = seqf->private; |
0e35f63f AS |
1505 | |
1506 | seq_printf(seqf, "%u\n", priv->firmware_version); | |
1507 | ||
1508 | return 0; | |
1509 | } | |
1510 | DEFINE_SHOW_ATTRIBUTE(firmware_version); | |
1511 | ||
1512 | static int power_cycles_show(struct seq_file *seqf, void *unused) | |
1513 | { | |
2fd3eec1 | 1514 | struct aqc_data *priv = seqf->private; |
0e35f63f AS |
1515 | |
1516 | seq_printf(seqf, "%u\n", priv->power_cycles); | |
1517 | ||
1518 | return 0; | |
1519 | } | |
1520 | DEFINE_SHOW_ATTRIBUTE(power_cycles); | |
1521 | ||
2fd3eec1 | 1522 | static void aqc_debugfs_init(struct aqc_data *priv) |
0e35f63f | 1523 | { |
2fd3eec1 | 1524 | char name[64]; |
0e35f63f | 1525 | |
2fd3eec1 AS |
1526 | scnprintf(name, sizeof(name), "%s_%s-%s", "aquacomputer", priv->name, |
1527 | dev_name(&priv->hdev->dev)); | |
0e35f63f AS |
1528 | |
1529 | priv->debugfs = debugfs_create_dir(name, NULL); | |
2fd3eec1 | 1530 | |
e0f6c370 AS |
1531 | if (priv->serial_number_start_offset != 0) |
1532 | debugfs_create_file("serial_number", 0444, priv->debugfs, priv, | |
1533 | &serial_number_fops); | |
1534 | if (priv->firmware_version_offset != 0) | |
1535 | debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, | |
1536 | &firmware_version_fops); | |
654c9735 | 1537 | if (priv->power_cycle_count_offset != 0) |
2fd3eec1 | 1538 | debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops); |
0e35f63f AS |
1539 | } |
1540 | ||
2fd3eec1 | 1541 | static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) |
0e35f63f | 1542 | { |
2fd3eec1 | 1543 | struct aqc_data *priv; |
0e35f63f AS |
1544 | int ret; |
1545 | ||
1546 | priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); | |
1547 | if (!priv) | |
1548 | return -ENOMEM; | |
1549 | ||
1550 | priv->hdev = hdev; | |
1551 | hid_set_drvdata(hdev, priv); | |
1552 | ||
2fd3eec1 | 1553 | priv->updated = jiffies - STATUS_UPDATE_INTERVAL; |
0e35f63f AS |
1554 | |
1555 | ret = hid_parse(hdev); | |
1556 | if (ret) | |
1557 | return ret; | |
1558 | ||
1559 | ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); | |
1560 | if (ret) | |
1561 | return ret; | |
1562 | ||
1563 | ret = hid_hw_open(hdev); | |
1564 | if (ret) | |
1565 | goto fail_and_stop; | |
1566 | ||
2fd3eec1 | 1567 | switch (hdev->product) { |
2c552111 LA |
1568 | case USB_PRODUCT_ID_AQUAERO: |
1569 | /* | |
1570 | * Aquaero presents itself as three HID devices under the same product ID: | |
1571 | * "aquaero keyboard/mouse", "aquaero System Control" and "aquaero Device", | |
1572 | * which is the one we want to communicate with. Unlike most other Aquacomputer | |
1573 | * devices, Aquaero does not return meaningful data when explicitly requested | |
1574 | * using GET_FEATURE_REPORT. | |
1575 | * | |
1576 | * The difference between "aquaero Device" and the other two is in the collections | |
1577 | * they present. The two other devices have the type of the second element in | |
1578 | * their respective collections set to 1, while the real device has it set to 0. | |
1579 | */ | |
1580 | if (hdev->collection[1].type != 0) { | |
1581 | ret = -ENODEV; | |
1582 | goto fail_and_close; | |
1583 | } | |
1584 | ||
1585 | priv->kind = aquaero; | |
1586 | ||
1587 | priv->num_fans = AQUAERO_NUM_FANS; | |
1588 | priv->fan_sensor_offsets = aquaero_sensor_fan_offsets; | |
bd1e92f9 | 1589 | priv->fan_ctrl_offsets = aquaero_ctrl_fan_offsets; |
2c552111 LA |
1590 | |
1591 | priv->num_temp_sensors = AQUAERO_NUM_SENSORS; | |
1592 | priv->temp_sensor_start_offset = AQUAERO_SENSOR_START; | |
1593 | priv->num_virtual_temp_sensors = AQUAERO_NUM_VIRTUAL_SENSORS; | |
1594 | priv->virtual_temp_sensor_start_offset = AQUAERO_VIRTUAL_SENSOR_START; | |
3d2e9f58 AS |
1595 | priv->num_calc_virt_temp_sensors = AQUAERO_NUM_CALC_VIRTUAL_SENSORS; |
1596 | priv->calc_virt_temp_sensor_start_offset = AQUAERO_CALC_VIRTUAL_SENSOR_START; | |
2c552111 LA |
1597 | priv->num_flow_sensors = AQUAERO_NUM_FLOW_SENSORS; |
1598 | priv->flow_sensors_start_offset = AQUAERO_FLOW_SENSORS_START; | |
1599 | ||
6c83ccb1 | 1600 | priv->buffer_size = AQUAERO_CTRL_REPORT_SIZE; |
866e630a | 1601 | priv->temp_ctrl_offset = AQUAERO_TEMP_CTRL_OFFSET; |
56b930dc | 1602 | priv->ctrl_report_delay = CTRL_REPORT_DELAY; |
6c83ccb1 | 1603 | |
2c552111 LA |
1604 | priv->temp_label = label_temp_sensors; |
1605 | priv->virtual_temp_label = label_virtual_temp_sensors; | |
3d2e9f58 | 1606 | priv->calc_virt_temp_label = label_aquaero_calc_temp_sensors; |
2c552111 LA |
1607 | priv->speed_label = label_aquaero_speeds; |
1608 | priv->power_label = label_fan_power; | |
1609 | priv->voltage_label = label_fan_voltage; | |
1610 | priv->current_label = label_fan_current; | |
1611 | break; | |
2fd3eec1 AS |
1612 | case USB_PRODUCT_ID_D5NEXT: |
1613 | priv->kind = d5next; | |
752b9279 | 1614 | |
654c9735 AS |
1615 | priv->num_fans = D5NEXT_NUM_FANS; |
1616 | priv->fan_sensor_offsets = d5next_sensor_fan_offsets; | |
09e89309 | 1617 | priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets; |
d5d896b8 | 1618 | |
654c9735 AS |
1619 | priv->num_temp_sensors = D5NEXT_NUM_SENSORS; |
1620 | priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP; | |
e2769f5e AS |
1621 | priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS; |
1622 | priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START; | |
662d20b3 | 1623 | priv->temp_ctrl_offset = D5NEXT_TEMP_CTRL_OFFSET; |
654c9735 | 1624 | |
d5d896b8 | 1625 | priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE; |
56b930dc | 1626 | priv->ctrl_report_delay = CTRL_REPORT_DELAY; |
d5d896b8 AS |
1627 | |
1628 | priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; | |
1629 | ||
752b9279 | 1630 | priv->temp_label = label_d5next_temp; |
e2769f5e | 1631 | priv->virtual_temp_label = label_virtual_temp_sensors; |
752b9279 AS |
1632 | priv->speed_label = label_d5next_speeds; |
1633 | priv->power_label = label_d5next_power; | |
1634 | priv->voltage_label = label_d5next_voltages; | |
1635 | priv->current_label = label_d5next_current; | |
2fd3eec1 | 1636 | break; |
229b159c JD |
1637 | case USB_PRODUCT_ID_FARBWERK: |
1638 | priv->kind = farbwerk; | |
1639 | ||
654c9735 | 1640 | priv->num_fans = 0; |
d5d896b8 | 1641 | |
654c9735 AS |
1642 | priv->num_temp_sensors = FARBWERK_NUM_SENSORS; |
1643 | priv->temp_sensor_start_offset = FARBWERK_SENSOR_START; | |
d5d896b8 | 1644 | |
229b159c JD |
1645 | priv->temp_label = label_temp_sensors; |
1646 | break; | |
2fd3eec1 AS |
1647 | case USB_PRODUCT_ID_FARBWERK360: |
1648 | priv->kind = farbwerk360; | |
752b9279 | 1649 | |
654c9735 | 1650 | priv->num_fans = 0; |
d5d896b8 | 1651 | |
654c9735 AS |
1652 | priv->num_temp_sensors = FARBWERK360_NUM_SENSORS; |
1653 | priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START; | |
e2769f5e AS |
1654 | priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS; |
1655 | priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START; | |
662d20b3 | 1656 | priv->temp_ctrl_offset = FARBWERK360_TEMP_CTRL_OFFSET; |
d5d896b8 AS |
1657 | |
1658 | priv->buffer_size = FARBWERK360_CTRL_REPORT_SIZE; | |
1659 | ||
752b9279 | 1660 | priv->temp_label = label_temp_sensors; |
e2769f5e | 1661 | priv->virtual_temp_label = label_virtual_temp_sensors; |
752b9279 AS |
1662 | break; |
1663 | case USB_PRODUCT_ID_OCTO: | |
1664 | priv->kind = octo; | |
654c9735 AS |
1665 | |
1666 | priv->num_fans = OCTO_NUM_FANS; | |
1667 | priv->fan_sensor_offsets = octo_sensor_fan_offsets; | |
1668 | priv->fan_ctrl_offsets = octo_ctrl_fan_offsets; | |
d5d896b8 | 1669 | |
654c9735 AS |
1670 | priv->num_temp_sensors = OCTO_NUM_SENSORS; |
1671 | priv->temp_sensor_start_offset = OCTO_SENSOR_START; | |
e2769f5e AS |
1672 | priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS; |
1673 | priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START; | |
120584c7 AS |
1674 | priv->num_flow_sensors = OCTO_NUM_FLOW_SENSORS; |
1675 | priv->flow_sensors_start_offset = OCTO_FLOW_SENSOR_OFFSET; | |
1676 | ||
662d20b3 | 1677 | priv->temp_ctrl_offset = OCTO_TEMP_CTRL_OFFSET; |
752b9279 | 1678 | |
d5d896b8 | 1679 | priv->buffer_size = OCTO_CTRL_REPORT_SIZE; |
56b930dc | 1680 | priv->ctrl_report_delay = CTRL_REPORT_DELAY; |
d5d896b8 | 1681 | |
bf7b5a12 | 1682 | priv->flow_pulses_ctrl_offset = OCTO_FLOW_PULSES_CTRL_OFFSET; |
d5d896b8 AS |
1683 | priv->power_cycle_count_offset = OCTO_POWER_CYCLES; |
1684 | ||
752b9279 | 1685 | priv->temp_label = label_temp_sensors; |
e2769f5e | 1686 | priv->virtual_temp_label = label_virtual_temp_sensors; |
120584c7 | 1687 | priv->speed_label = label_octo_speeds; |
752b9279 AS |
1688 | priv->power_label = label_fan_power; |
1689 | priv->voltage_label = label_fan_voltage; | |
1690 | priv->current_label = label_fan_current; | |
2fd3eec1 | 1691 | break; |
cdbe34da AS |
1692 | case USB_PRODUCT_ID_QUADRO: |
1693 | priv->kind = quadro; | |
1694 | ||
1695 | priv->num_fans = QUADRO_NUM_FANS; | |
1696 | priv->fan_sensor_offsets = quadro_sensor_fan_offsets; | |
1697 | priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets; | |
d5d896b8 | 1698 | |
cdbe34da AS |
1699 | priv->num_temp_sensors = QUADRO_NUM_SENSORS; |
1700 | priv->temp_sensor_start_offset = QUADRO_SENSOR_START; | |
e2769f5e AS |
1701 | priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS; |
1702 | priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START; | |
a2ba7ee2 LA |
1703 | priv->num_flow_sensors = QUADRO_NUM_FLOW_SENSORS; |
1704 | priv->flow_sensors_start_offset = QUADRO_FLOW_SENSOR_OFFSET; | |
1705 | ||
d5d896b8 AS |
1706 | priv->temp_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET; |
1707 | ||
cdbe34da | 1708 | priv->buffer_size = QUADRO_CTRL_REPORT_SIZE; |
56b930dc | 1709 | priv->ctrl_report_delay = CTRL_REPORT_DELAY; |
d5d896b8 | 1710 | |
6ff838f2 | 1711 | priv->flow_pulses_ctrl_offset = QUADRO_FLOW_PULSES_CTRL_OFFSET; |
d5d896b8 | 1712 | priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; |
cdbe34da AS |
1713 | |
1714 | priv->temp_label = label_temp_sensors; | |
e2769f5e | 1715 | priv->virtual_temp_label = label_virtual_temp_sensors; |
cdbe34da AS |
1716 | priv->speed_label = label_quadro_speeds; |
1717 | priv->power_label = label_fan_power; | |
1718 | priv->voltage_label = label_fan_voltage; | |
1719 | priv->current_label = label_fan_current; | |
1720 | break; | |
aed80bb9 AS |
1721 | case USB_PRODUCT_ID_HIGHFLOWNEXT: |
1722 | priv->kind = highflownext; | |
1723 | ||
1724 | priv->num_fans = 0; | |
d5d896b8 | 1725 | |
aed80bb9 AS |
1726 | priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS; |
1727 | priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START; | |
a2ba7ee2 LA |
1728 | priv->num_flow_sensors = HIGHFLOWNEXT_NUM_FLOW_SENSORS; |
1729 | priv->flow_sensors_start_offset = HIGHFLOWNEXT_FLOW; | |
d5d896b8 | 1730 | |
aed80bb9 AS |
1731 | priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; |
1732 | ||
1733 | priv->temp_label = label_highflownext_temp_sensors; | |
1734 | priv->speed_label = label_highflownext_fan_speed; | |
1735 | priv->power_label = label_highflownext_power; | |
1736 | priv->voltage_label = label_highflownext_voltage; | |
1737 | break; | |
b3d3be6c AS |
1738 | case USB_PRODUCT_ID_LEAKSHIELD: |
1739 | /* | |
1740 | * Choose the right Leakshield device, because | |
1741 | * the other one acts as a keyboard | |
1742 | */ | |
1743 | if (hdev->type != 2) { | |
1744 | ret = -ENODEV; | |
1745 | goto fail_and_close; | |
1746 | } | |
1747 | ||
1748 | priv->kind = leakshield; | |
1749 | ||
1750 | priv->num_fans = 0; | |
1751 | priv->num_temp_sensors = LEAKSHIELD_NUM_SENSORS; | |
1752 | priv->temp_sensor_start_offset = LEAKSHIELD_TEMPERATURE_1; | |
1753 | ||
1754 | priv->temp_label = label_leakshield_temp_sensors; | |
1755 | priv->speed_label = label_leakshield_fan_speed; | |
1756 | break; | |
19692f17 AS |
1757 | case USB_PRODUCT_ID_AQUASTREAMXT: |
1758 | priv->kind = aquastreamxt; | |
1759 | ||
1760 | priv->num_fans = AQUASTREAMXT_NUM_FANS; | |
1761 | priv->fan_sensor_offsets = aquastreamxt_sensor_fan_offsets; | |
1762 | ||
1763 | priv->num_temp_sensors = AQUASTREAMXT_NUM_SENSORS; | |
1764 | priv->temp_sensor_start_offset = AQUASTREAMXT_SENSOR_START; | |
1765 | priv->buffer_size = AQUASTREAMXT_SENSOR_REPORT_SIZE; | |
1766 | ||
1767 | priv->temp_label = label_aquastreamxt_temp_sensors; | |
1768 | priv->speed_label = label_d5next_speeds; | |
1769 | priv->voltage_label = label_d5next_voltages; | |
1770 | priv->current_label = label_d5next_current; | |
1771 | break; | |
7505dab7 AS |
1772 | case USB_PRODUCT_ID_AQUASTREAMULT: |
1773 | priv->kind = aquastreamult; | |
1774 | ||
1775 | priv->num_fans = AQUASTREAMULT_NUM_FANS; | |
1776 | priv->fan_sensor_offsets = aquastreamult_sensor_fan_offsets; | |
1777 | ||
1778 | priv->num_temp_sensors = AQUASTREAMULT_NUM_SENSORS; | |
1779 | priv->temp_sensor_start_offset = AQUASTREAMULT_SENSOR_START; | |
1780 | ||
1781 | priv->temp_label = label_aquastreamult_temp; | |
1782 | priv->speed_label = label_aquastreamult_speeds; | |
1783 | priv->power_label = label_aquastreamult_power; | |
1784 | priv->voltage_label = label_aquastreamult_voltages; | |
1785 | priv->current_label = label_aquastreamult_current; | |
1786 | break; | |
e0f6c370 AS |
1787 | case USB_PRODUCT_ID_POWERADJUST3: |
1788 | priv->kind = poweradjust3; | |
1789 | ||
1790 | priv->num_fans = 0; | |
1791 | ||
1792 | priv->num_temp_sensors = POWERADJUST3_NUM_SENSORS; | |
1793 | priv->temp_sensor_start_offset = POWERADJUST3_SENSOR_START; | |
1794 | priv->buffer_size = POWERADJUST3_SENSOR_REPORT_SIZE; | |
1795 | ||
1796 | priv->temp_label = label_poweradjust3_temp_sensors; | |
1797 | break; | |
ceaa2240 AS |
1798 | case USB_PRODUCT_ID_HIGHFLOW: |
1799 | priv->kind = highflow; | |
1800 | ||
1801 | priv->num_fans = 0; | |
1802 | ||
1803 | priv->num_temp_sensors = HIGHFLOW_NUM_SENSORS; | |
1804 | priv->temp_sensor_start_offset = HIGHFLOW_SENSOR_START; | |
1805 | priv->num_flow_sensors = HIGHFLOW_NUM_FLOW_SENSORS; | |
1806 | priv->flow_sensors_start_offset = HIGHFLOW_FLOW_SENSOR_OFFSET; | |
1807 | priv->buffer_size = HIGHFLOW_SENSOR_REPORT_SIZE; | |
1808 | ||
1809 | priv->temp_label = label_highflow_temp; | |
1810 | priv->speed_label = label_highflow_speeds; | |
1811 | break; | |
2fd3eec1 AS |
1812 | default: |
1813 | break; | |
1814 | } | |
1815 | ||
2c552111 LA |
1816 | switch (priv->kind) { |
1817 | case aquaero: | |
1818 | priv->serial_number_start_offset = AQUAERO_SERIAL_START; | |
1819 | priv->firmware_version_offset = AQUAERO_FIRMWARE_VERSION; | |
1820 | ||
1821 | priv->fan_structure = &aqc_aquaero_fan_structure; | |
6c83ccb1 LA |
1822 | |
1823 | priv->ctrl_report_id = AQUAERO_CTRL_REPORT_ID; | |
1824 | priv->secondary_ctrl_report_id = AQUAERO_SECONDARY_CTRL_REPORT_ID; | |
1825 | priv->secondary_ctrl_report_size = AQUAERO_SECONDARY_CTRL_REPORT_SIZE; | |
1826 | priv->secondary_ctrl_report = aquaero_secondary_ctrl_report; | |
2c552111 | 1827 | break; |
e0f6c370 AS |
1828 | case poweradjust3: |
1829 | priv->status_report_id = POWERADJUST3_STATUS_REPORT_ID; | |
1830 | break; | |
19692f17 AS |
1831 | case aquastreamxt: |
1832 | priv->serial_number_start_offset = AQUASTREAMXT_SERIAL_START; | |
1833 | priv->firmware_version_offset = AQUASTREAMXT_FIRMWARE_VERSION; | |
1834 | ||
1835 | priv->status_report_id = AQUASTREAMXT_STATUS_REPORT_ID; | |
1836 | break; | |
ceaa2240 AS |
1837 | case highflow: |
1838 | priv->serial_number_start_offset = HIGHFLOW_SERIAL_START; | |
1839 | priv->firmware_version_offset = HIGHFLOW_FIRMWARE_VERSION; | |
1840 | ||
1841 | priv->status_report_id = HIGHFLOW_STATUS_REPORT_ID; | |
1842 | break; | |
2c552111 LA |
1843 | default: |
1844 | priv->serial_number_start_offset = AQC_SERIAL_START; | |
1845 | priv->firmware_version_offset = AQC_FIRMWARE_VERSION; | |
ad2f0811 | 1846 | |
b29090ba LA |
1847 | priv->ctrl_report_id = CTRL_REPORT_ID; |
1848 | priv->secondary_ctrl_report_id = SECONDARY_CTRL_REPORT_ID; | |
1849 | priv->secondary_ctrl_report_size = SECONDARY_CTRL_REPORT_SIZE; | |
1850 | priv->secondary_ctrl_report = secondary_ctrl_report; | |
1851 | ||
7505dab7 AS |
1852 | if (priv->kind == aquastreamult) |
1853 | priv->fan_structure = &aqc_aquastreamult_fan_structure; | |
1854 | else | |
1855 | priv->fan_structure = &aqc_general_fan_structure; | |
2c552111 LA |
1856 | break; |
1857 | } | |
249c7521 | 1858 | |
654c9735 AS |
1859 | if (priv->buffer_size != 0) { |
1860 | priv->checksum_start = 0x01; | |
1861 | priv->checksum_length = priv->buffer_size - 3; | |
1862 | priv->checksum_offset = priv->buffer_size - 2; | |
1863 | } | |
1864 | ||
2fd3eec1 AS |
1865 | priv->name = aqc_device_names[priv->kind]; |
1866 | ||
752b9279 | 1867 | priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL); |
8877ecb0 CJ |
1868 | if (!priv->buffer) { |
1869 | ret = -ENOMEM; | |
1870 | goto fail_and_close; | |
1871 | } | |
752b9279 AS |
1872 | |
1873 | mutex_init(&priv->mutex); | |
1874 | ||
2fd3eec1 AS |
1875 | priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv, |
1876 | &aqc_chip_info, NULL); | |
0e35f63f AS |
1877 | |
1878 | if (IS_ERR(priv->hwmon_dev)) { | |
1879 | ret = PTR_ERR(priv->hwmon_dev); | |
1880 | goto fail_and_close; | |
1881 | } | |
1882 | ||
2fd3eec1 | 1883 | aqc_debugfs_init(priv); |
0e35f63f AS |
1884 | |
1885 | return 0; | |
1886 | ||
1887 | fail_and_close: | |
1888 | hid_hw_close(hdev); | |
1889 | fail_and_stop: | |
1890 | hid_hw_stop(hdev); | |
1891 | return ret; | |
1892 | } | |
1893 | ||
2fd3eec1 | 1894 | static void aqc_remove(struct hid_device *hdev) |
0e35f63f | 1895 | { |
2fd3eec1 | 1896 | struct aqc_data *priv = hid_get_drvdata(hdev); |
0e35f63f AS |
1897 | |
1898 | debugfs_remove_recursive(priv->debugfs); | |
1899 | hwmon_device_unregister(priv->hwmon_dev); | |
1900 | ||
1901 | hid_hw_close(hdev); | |
1902 | hid_hw_stop(hdev); | |
1903 | } | |
1904 | ||
2fd3eec1 | 1905 | static const struct hid_device_id aqc_table[] = { |
2c552111 | 1906 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUAERO) }, |
2fd3eec1 | 1907 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) }, |
229b159c | 1908 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) }, |
2fd3eec1 | 1909 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) }, |
752b9279 | 1910 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) }, |
cdbe34da | 1911 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, |
aed80bb9 | 1912 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) }, |
b3d3be6c | 1913 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_LEAKSHIELD) }, |
19692f17 | 1914 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMXT) }, |
7505dab7 | 1915 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT) }, |
e0f6c370 | 1916 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) }, |
ceaa2240 | 1917 | { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOW) }, |
2fd3eec1 | 1918 | { } |
0e35f63f AS |
1919 | }; |
1920 | ||
2fd3eec1 | 1921 | MODULE_DEVICE_TABLE(hid, aqc_table); |
0e35f63f | 1922 | |
2fd3eec1 | 1923 | static struct hid_driver aqc_driver = { |
0e35f63f | 1924 | .name = DRIVER_NAME, |
2fd3eec1 AS |
1925 | .id_table = aqc_table, |
1926 | .probe = aqc_probe, | |
1927 | .remove = aqc_remove, | |
1928 | .raw_event = aqc_raw_event, | |
0e35f63f AS |
1929 | }; |
1930 | ||
2fd3eec1 | 1931 | static int __init aqc_init(void) |
0e35f63f | 1932 | { |
2fd3eec1 | 1933 | return hid_register_driver(&aqc_driver); |
0e35f63f AS |
1934 | } |
1935 | ||
2fd3eec1 | 1936 | static void __exit aqc_exit(void) |
0e35f63f | 1937 | { |
2fd3eec1 | 1938 | hid_unregister_driver(&aqc_driver); |
0e35f63f AS |
1939 | } |
1940 | ||
1941 | /* Request to initialize after the HID bus to ensure it's not being loaded before */ | |
2fd3eec1 AS |
1942 | late_initcall(aqc_init); |
1943 | module_exit(aqc_exit); | |
0e35f63f AS |
1944 | |
1945 | MODULE_LICENSE("GPL"); | |
1946 | MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>"); | |
229b159c | 1947 | MODULE_AUTHOR("Jack Doan <me@jackdoan.com>"); |
2fd3eec1 | 1948 | MODULE_DESCRIPTION("Hwmon driver for Aquacomputer devices"); |