| // SPDX-License-Identifier: GPL-2.0+ | 
 | /* | 
 |  * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, | 
 |  * Quadro, High Flow Next, Aquaero, Aquastream Ultimate, Leakshield, | 
 |  * High Flow USB/MPS Flow family) | 
 |  * | 
 |  * Aquacomputer devices send HID reports (with ID 0x01) every second to report | 
 |  * sensor values, except for devices that communicate through the | 
 |  * legacy way (currently, Poweradjust 3 and High Flow USB/MPS Flow family). | 
 |  * | 
 |  * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com> | 
 |  * Copyright 2022 Jack Doan <me@jackdoan.com> | 
 |  */ | 
 |  | 
 | #include <linux/crc16.h> | 
 | #include <linux/debugfs.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/hid.h> | 
 | #include <linux/hwmon.h> | 
 | #include <linux/jiffies.h> | 
 | #include <linux/ktime.h> | 
 | #include <linux/module.h> | 
 | #include <linux/mutex.h> | 
 | #include <linux/seq_file.h> | 
 | #include <linux/unaligned.h> | 
 |  | 
 | #define USB_VENDOR_ID_AQUACOMPUTER	0x0c70 | 
 | #define USB_PRODUCT_ID_AQUAERO		0xf001 | 
 | #define USB_PRODUCT_ID_FARBWERK		0xf00a | 
 | #define USB_PRODUCT_ID_QUADRO		0xf00d | 
 | #define USB_PRODUCT_ID_D5NEXT		0xf00e | 
 | #define USB_PRODUCT_ID_FARBWERK360	0xf010 | 
 | #define USB_PRODUCT_ID_OCTO		0xf011 | 
 | #define USB_PRODUCT_ID_HIGHFLOWNEXT	0xf012 | 
 | #define USB_PRODUCT_ID_LEAKSHIELD	0xf014 | 
 | #define USB_PRODUCT_ID_AQUASTREAMXT	0xf0b6 | 
 | #define USB_PRODUCT_ID_AQUASTREAMULT	0xf00b | 
 | #define USB_PRODUCT_ID_POWERADJUST3	0xf0bd | 
 | #define USB_PRODUCT_ID_HIGHFLOW		0xf003 | 
 |  | 
 | enum kinds { | 
 | 	d5next, farbwerk, farbwerk360, octo, quadro, | 
 | 	highflownext, aquaero, poweradjust3, aquastreamult, | 
 | 	aquastreamxt, leakshield, highflow | 
 | }; | 
 |  | 
 | static const char *const aqc_device_names[] = { | 
 | 	[d5next] = "d5next", | 
 | 	[farbwerk] = "farbwerk", | 
 | 	[farbwerk360] = "farbwerk360", | 
 | 	[octo] = "octo", | 
 | 	[quadro] = "quadro", | 
 | 	[highflownext] = "highflownext", | 
 | 	[leakshield] = "leakshield", | 
 | 	[aquastreamxt] = "aquastreamxt", | 
 | 	[aquaero] = "aquaero", | 
 | 	[aquastreamult] = "aquastreamultimate", | 
 | 	[poweradjust3] = "poweradjust3", | 
 | 	[highflow] = "highflow" /* Covers MPS Flow devices */ | 
 | }; | 
 |  | 
 | #define DRIVER_NAME			"aquacomputer_d5next" | 
 |  | 
 | #define STATUS_REPORT_ID		0x01 | 
 | #define STATUS_UPDATE_INTERVAL		(2 * HZ)	/* In seconds */ | 
 | #define SERIAL_PART_OFFSET		2 | 
 |  | 
 | #define CTRL_REPORT_ID			0x03 | 
 | #define AQUAERO_CTRL_REPORT_ID		0x0b | 
 |  | 
 | #define CTRL_REPORT_DELAY		200	/* ms */ | 
 |  | 
 | /* The HID report that the official software always sends | 
 |  * after writing values, currently same for all devices | 
 |  */ | 
 | #define SECONDARY_CTRL_REPORT_ID	0x02 | 
 | #define SECONDARY_CTRL_REPORT_SIZE	0x0B | 
 |  | 
 | static u8 secondary_ctrl_report[] = { | 
 | 	0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6 | 
 | }; | 
 |  | 
 | /* Secondary HID report values for Aquaero */ | 
 | #define AQUAERO_SECONDARY_CTRL_REPORT_ID	0x06 | 
 | #define AQUAERO_SECONDARY_CTRL_REPORT_SIZE	0x07 | 
 |  | 
 | static u8 aquaero_secondary_ctrl_report[] = { | 
 | 	0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00 | 
 | }; | 
 |  | 
 | /* Report IDs for legacy devices */ | 
 | #define AQUASTREAMXT_STATUS_REPORT_ID	0x04 | 
 |  | 
 | #define POWERADJUST3_STATUS_REPORT_ID	0x03 | 
 |  | 
 | #define HIGHFLOW_STATUS_REPORT_ID	0x02 | 
 |  | 
 | /* Data types for reading and writing control reports */ | 
 | #define AQC_8		0 | 
 | #define AQC_BE16	1 | 
 |  | 
 | /* Info, sensor sizes and offsets for most Aquacomputer devices */ | 
 | #define AQC_SERIAL_START		0x3 | 
 | #define AQC_FIRMWARE_VERSION		0xD | 
 |  | 
 | #define AQC_SENSOR_SIZE			0x02 | 
 | #define AQC_SENSOR_NA			0x7FFF | 
 | #define AQC_FAN_PERCENT_OFFSET		0x00 | 
 | #define AQC_FAN_VOLTAGE_OFFSET		0x02 | 
 | #define AQC_FAN_CURRENT_OFFSET		0x04 | 
 | #define AQC_FAN_POWER_OFFSET		0x06 | 
 | #define AQC_FAN_SPEED_OFFSET		0x08 | 
 |  | 
 | /* Specs of the Aquaero fan controllers */ | 
 | #define AQUAERO_SERIAL_START			0x07 | 
 | #define AQUAERO_FIRMWARE_VERSION		0x0B | 
 | #define AQUAERO_NUM_FANS			4 | 
 | #define AQUAERO_NUM_SENSORS			8 | 
 | #define AQUAERO_NUM_VIRTUAL_SENSORS		8 | 
 | #define AQUAERO_NUM_CALC_VIRTUAL_SENSORS	4 | 
 | #define AQUAERO_NUM_FLOW_SENSORS		2 | 
 | #define AQUAERO_CTRL_REPORT_SIZE		0xa93 | 
 | #define AQUAERO_CTRL_PRESET_ID			0x5c | 
 | #define AQUAERO_CTRL_PRESET_SIZE		0x02 | 
 | #define AQUAERO_CTRL_PRESET_START		0x55c | 
 |  | 
 | /* Sensor report offsets for Aquaero fan controllers */ | 
 | #define AQUAERO_SENSOR_START			0x65 | 
 | #define AQUAERO_VIRTUAL_SENSOR_START		0x85 | 
 | #define AQUAERO_CALC_VIRTUAL_SENSOR_START	0x95 | 
 | #define AQUAERO_FLOW_SENSORS_START		0xF9 | 
 | #define AQUAERO_FAN_VOLTAGE_OFFSET		0x04 | 
 | #define AQUAERO_FAN_CURRENT_OFFSET		0x06 | 
 | #define AQUAERO_FAN_POWER_OFFSET		0x08 | 
 | #define AQUAERO_FAN_SPEED_OFFSET		0x00 | 
 | static u16 aquaero_sensor_fan_offsets[] = { 0x167, 0x173, 0x17f, 0x18B }; | 
 |  | 
 | /* Control report offsets for the Aquaero fan controllers */ | 
 | #define AQUAERO_TEMP_CTRL_OFFSET	0xdb | 
 | #define AQUAERO_FAN_CTRL_MIN_PWR_OFFSET	0x04 | 
 | #define AQUAERO_FAN_CTRL_MAX_PWR_OFFSET	0x06 | 
 | #define AQUAERO_FAN_CTRL_SRC_OFFSET	0x10 | 
 | static u16 aquaero_ctrl_fan_offsets[] = { 0x20c, 0x220, 0x234, 0x248 }; | 
 |  | 
 | /* Specs of the D5 Next pump */ | 
 | #define D5NEXT_NUM_FANS			2 | 
 | #define D5NEXT_NUM_SENSORS		1 | 
 | #define D5NEXT_NUM_VIRTUAL_SENSORS	8 | 
 | #define D5NEXT_CTRL_REPORT_SIZE		0x329 | 
 |  | 
 | /* Sensor report offsets for the D5 Next pump */ | 
 | #define D5NEXT_POWER_CYCLES		0x18 | 
 | #define D5NEXT_COOLANT_TEMP		0x57 | 
 | #define D5NEXT_PUMP_OFFSET		0x6c | 
 | #define D5NEXT_FAN_OFFSET		0x5f | 
 | #define D5NEXT_5V_VOLTAGE		0x39 | 
 | #define D5NEXT_12V_VOLTAGE		0x37 | 
 | #define D5NEXT_VIRTUAL_SENSORS_START	0x3f | 
 | static u16 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; | 
 |  | 
 | /* Control report offsets for the D5 Next pump */ | 
 | #define D5NEXT_TEMP_CTRL_OFFSET		0x2D	/* Temperature sensor offsets location */ | 
 | static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };	/* Pump and fan speed (from 0-100%) */ | 
 |  | 
 | /* Specs of the Aquastream Ultimate pump */ | 
 | /* Pump does not follow the standard structure, so only consider the fan */ | 
 | #define AQUASTREAMULT_NUM_FANS		1 | 
 | #define AQUASTREAMULT_NUM_SENSORS	2 | 
 |  | 
 | /* Sensor report offsets for the Aquastream Ultimate pump */ | 
 | #define AQUASTREAMULT_SENSOR_START		0x2D | 
 | #define AQUASTREAMULT_PUMP_OFFSET		0x51 | 
 | #define AQUASTREAMULT_PUMP_VOLTAGE		0x3D | 
 | #define AQUASTREAMULT_PUMP_CURRENT		0x53 | 
 | #define AQUASTREAMULT_PUMP_POWER		0x55 | 
 | #define AQUASTREAMULT_FAN_OFFSET		0x41 | 
 | #define AQUASTREAMULT_PRESSURE_OFFSET		0x57 | 
 | #define AQUASTREAMULT_FLOW_SENSOR_OFFSET	0x37 | 
 | #define AQUASTREAMULT_FAN_VOLTAGE_OFFSET	0x02 | 
 | #define AQUASTREAMULT_FAN_CURRENT_OFFSET	0x00 | 
 | #define AQUASTREAMULT_FAN_POWER_OFFSET		0x04 | 
 | #define AQUASTREAMULT_FAN_SPEED_OFFSET		0x06 | 
 | static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET }; | 
 |  | 
 | /* Spec and sensor report offset for the Farbwerk RGB controller */ | 
 | #define FARBWERK_NUM_SENSORS		4 | 
 | #define FARBWERK_SENSOR_START		0x2f | 
 |  | 
 | /* Specs of the Farbwerk 360 RGB controller */ | 
 | #define FARBWERK360_NUM_SENSORS			4 | 
 | #define FARBWERK360_NUM_VIRTUAL_SENSORS		16 | 
 | #define FARBWERK360_CTRL_REPORT_SIZE		0x682 | 
 |  | 
 | /* Sensor report offsets for the Farbwerk 360 */ | 
 | #define FARBWERK360_SENSOR_START		0x32 | 
 | #define FARBWERK360_VIRTUAL_SENSORS_START	0x3a | 
 |  | 
 | /* Control report offsets for the Farbwerk 360 */ | 
 | #define FARBWERK360_TEMP_CTRL_OFFSET		0x8 | 
 |  | 
 | /* Specs of the Octo fan controller */ | 
 | #define OCTO_NUM_FANS			8 | 
 | #define OCTO_NUM_SENSORS		4 | 
 | #define OCTO_NUM_VIRTUAL_SENSORS	16 | 
 | #define OCTO_NUM_FLOW_SENSORS		1 | 
 | #define OCTO_CTRL_REPORT_SIZE		0x65F | 
 |  | 
 | /* Sensor report offsets for the Octo */ | 
 | #define OCTO_POWER_CYCLES		0x18 | 
 | #define OCTO_SENSOR_START		0x3D | 
 | #define OCTO_VIRTUAL_SENSORS_START	0x45 | 
 | #define OCTO_FLOW_SENSOR_OFFSET		0x7B | 
 | static u16 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; | 
 |  | 
 | /* Control report offsets for the Octo */ | 
 | #define OCTO_TEMP_CTRL_OFFSET		0xA | 
 | #define OCTO_FLOW_PULSES_CTRL_OFFSET	0x6 | 
 | /* Fan speed offsets (0-100%) */ | 
 | static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE }; | 
 |  | 
 | /* Specs of Quadro fan controller */ | 
 | #define QUADRO_NUM_FANS			4 | 
 | #define QUADRO_NUM_SENSORS		4 | 
 | #define QUADRO_NUM_VIRTUAL_SENSORS	16 | 
 | #define QUADRO_NUM_FLOW_SENSORS		1 | 
 | #define QUADRO_CTRL_REPORT_SIZE		0x3c1 | 
 |  | 
 | /* Sensor report offsets for the Quadro */ | 
 | #define QUADRO_POWER_CYCLES		0x18 | 
 | #define QUADRO_SENSOR_START		0x34 | 
 | #define QUADRO_VIRTUAL_SENSORS_START	0x3c | 
 | #define QUADRO_FLOW_SENSOR_OFFSET	0x6e | 
 | static u16 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; | 
 |  | 
 | /* Control report offsets for the Quadro */ | 
 | #define QUADRO_TEMP_CTRL_OFFSET		0xA | 
 | #define QUADRO_FLOW_PULSES_CTRL_OFFSET	0x6 | 
 | static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed offsets (0-100%) */ | 
 |  | 
 | /* Specs of High Flow Next flow sensor */ | 
 | #define HIGHFLOWNEXT_NUM_SENSORS	2 | 
 | #define HIGHFLOWNEXT_NUM_FLOW_SENSORS	1 | 
 |  | 
 | /* Sensor report offsets for the High Flow Next */ | 
 | #define HIGHFLOWNEXT_SENSOR_START	85 | 
 | #define HIGHFLOWNEXT_FLOW		81 | 
 | #define HIGHFLOWNEXT_WATER_QUALITY	89 | 
 | #define HIGHFLOWNEXT_POWER		91 | 
 | #define HIGHFLOWNEXT_CONDUCTIVITY	95 | 
 | #define HIGHFLOWNEXT_5V_VOLTAGE		97 | 
 | #define HIGHFLOWNEXT_5V_VOLTAGE_USB	99 | 
 |  | 
 | /* Specs of the Leakshield */ | 
 | #define LEAKSHIELD_NUM_SENSORS		2 | 
 |  | 
 | /* Sensor report offsets for Leakshield */ | 
 | #define LEAKSHIELD_PRESSURE_ADJUSTED	285 | 
 | #define LEAKSHIELD_TEMPERATURE_1	265 | 
 | #define LEAKSHIELD_TEMPERATURE_2	287 | 
 | #define LEAKSHIELD_PRESSURE_MIN		291 | 
 | #define LEAKSHIELD_PRESSURE_TARGET	293 | 
 | #define LEAKSHIELD_PRESSURE_MAX		295 | 
 | #define LEAKSHIELD_PUMP_RPM_IN		101 | 
 | #define LEAKSHIELD_FLOW_IN		111 | 
 | #define LEAKSHIELD_RESERVOIR_VOLUME	313 | 
 | #define LEAKSHIELD_RESERVOIR_FILLED	311 | 
 |  | 
 | /* Specs of the Aquastream XT pump */ | 
 | #define AQUASTREAMXT_SERIAL_START		0x3a | 
 | #define AQUASTREAMXT_FIRMWARE_VERSION		0x32 | 
 | #define AQUASTREAMXT_NUM_FANS			2 | 
 | #define AQUASTREAMXT_NUM_SENSORS		3 | 
 | #define AQUASTREAMXT_FAN_STOPPED		0x4 | 
 | #define AQUASTREAMXT_PUMP_CONVERSION_CONST	45000000 | 
 | #define AQUASTREAMXT_FAN_CONVERSION_CONST	5646000 | 
 | #define AQUASTREAMXT_SENSOR_REPORT_SIZE		0x42 | 
 |  | 
 | /* Sensor report offsets and info for Aquastream XT */ | 
 | #define AQUASTREAMXT_SENSOR_START		0xd | 
 | #define AQUASTREAMXT_FAN_VOLTAGE_OFFSET		0x7 | 
 | #define AQUASTREAMXT_FAN_STATUS_OFFSET		0x1d | 
 | #define AQUASTREAMXT_PUMP_VOLTAGE_OFFSET	0x9 | 
 | #define AQUASTREAMXT_PUMP_CURR_OFFSET		0xb | 
 | static u16 aquastreamxt_sensor_fan_offsets[] = { 0x13, 0x1b }; | 
 |  | 
 | /* Specs of the Poweradjust 3 */ | 
 | #define POWERADJUST3_NUM_SENSORS	1 | 
 | #define POWERADJUST3_SENSOR_REPORT_SIZE	0x32 | 
 |  | 
 | /* Sensor report offsets for the Poweradjust 3 */ | 
 | #define POWERADJUST3_SENSOR_START	0x03 | 
 |  | 
 | /* Specs of the High Flow USB */ | 
 | #define HIGHFLOW_NUM_SENSORS		2 | 
 | #define HIGHFLOW_NUM_FLOW_SENSORS	1 | 
 | #define HIGHFLOW_SENSOR_REPORT_SIZE	0x76 | 
 |  | 
 | /* Sensor report offsets for the High Flow USB */ | 
 | #define HIGHFLOW_FIRMWARE_VERSION	0x3 | 
 | #define HIGHFLOW_SERIAL_START		0x9 | 
 | #define HIGHFLOW_FLOW_SENSOR_OFFSET	0x23 | 
 | #define HIGHFLOW_SENSOR_START		0x2b | 
 |  | 
 | /* Labels for D5 Next */ | 
 | static const char *const label_d5next_temp[] = { | 
 | 	"Coolant temp" | 
 | }; | 
 |  | 
 | static const char *const label_d5next_speeds[] = { | 
 | 	"Pump speed", | 
 | 	"Fan speed" | 
 | }; | 
 |  | 
 | static const char *const label_d5next_power[] = { | 
 | 	"Pump power", | 
 | 	"Fan power" | 
 | }; | 
 |  | 
 | static const char *const label_d5next_voltages[] = { | 
 | 	"Pump voltage", | 
 | 	"Fan voltage", | 
 | 	"+5V voltage", | 
 | 	"+12V voltage" | 
 | }; | 
 |  | 
 | static const char *const label_d5next_current[] = { | 
 | 	"Pump current", | 
 | 	"Fan current" | 
 | }; | 
 |  | 
 | /* Labels for Aquaero, Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */ | 
 | static const char *const label_temp_sensors[] = { | 
 | 	"Sensor 1", | 
 | 	"Sensor 2", | 
 | 	"Sensor 3", | 
 | 	"Sensor 4", | 
 | 	"Sensor 5", | 
 | 	"Sensor 6", | 
 | 	"Sensor 7", | 
 | 	"Sensor 8" | 
 | }; | 
 |  | 
 | static const char *const label_virtual_temp_sensors[] = { | 
 | 	"Virtual sensor 1", | 
 | 	"Virtual sensor 2", | 
 | 	"Virtual sensor 3", | 
 | 	"Virtual sensor 4", | 
 | 	"Virtual sensor 5", | 
 | 	"Virtual sensor 6", | 
 | 	"Virtual sensor 7", | 
 | 	"Virtual sensor 8", | 
 | 	"Virtual sensor 9", | 
 | 	"Virtual sensor 10", | 
 | 	"Virtual sensor 11", | 
 | 	"Virtual sensor 12", | 
 | 	"Virtual sensor 13", | 
 | 	"Virtual sensor 14", | 
 | 	"Virtual sensor 15", | 
 | 	"Virtual sensor 16", | 
 | }; | 
 |  | 
 | static const char *const label_aquaero_calc_temp_sensors[] = { | 
 | 	"Calc. virtual sensor 1", | 
 | 	"Calc. virtual sensor 2", | 
 | 	"Calc. virtual sensor 3", | 
 | 	"Calc. virtual sensor 4" | 
 | }; | 
 |  | 
 | static const char *const label_fan_power[] = { | 
 | 	"Fan 1 power", | 
 | 	"Fan 2 power", | 
 | 	"Fan 3 power", | 
 | 	"Fan 4 power", | 
 | 	"Fan 5 power", | 
 | 	"Fan 6 power", | 
 | 	"Fan 7 power", | 
 | 	"Fan 8 power" | 
 | }; | 
 |  | 
 | static const char *const label_fan_voltage[] = { | 
 | 	"Fan 1 voltage", | 
 | 	"Fan 2 voltage", | 
 | 	"Fan 3 voltage", | 
 | 	"Fan 4 voltage", | 
 | 	"Fan 5 voltage", | 
 | 	"Fan 6 voltage", | 
 | 	"Fan 7 voltage", | 
 | 	"Fan 8 voltage" | 
 | }; | 
 |  | 
 | static const char *const label_fan_current[] = { | 
 | 	"Fan 1 current", | 
 | 	"Fan 2 current", | 
 | 	"Fan 3 current", | 
 | 	"Fan 4 current", | 
 | 	"Fan 5 current", | 
 | 	"Fan 6 current", | 
 | 	"Fan 7 current", | 
 | 	"Fan 8 current" | 
 | }; | 
 |  | 
 | /* Labels for Octo fan speeds */ | 
 | static const char *const label_octo_speeds[] = { | 
 | 	"Fan 1 speed", | 
 | 	"Fan 2 speed", | 
 | 	"Fan 3 speed", | 
 | 	"Fan 4 speed", | 
 | 	"Fan 5 speed", | 
 | 	"Fan 6 speed", | 
 | 	"Fan 7 speed", | 
 | 	"Fan 8 speed", | 
 | 	"Flow speed [dL/h]", | 
 | }; | 
 |  | 
 | /* Labels for Quadro fan speeds */ | 
 | static const char *const label_quadro_speeds[] = { | 
 | 	"Fan 1 speed", | 
 | 	"Fan 2 speed", | 
 | 	"Fan 3 speed", | 
 | 	"Fan 4 speed", | 
 | 	"Flow speed [dL/h]" | 
 | }; | 
 |  | 
 | /* Labels for Aquaero fan speeds */ | 
 | static const char *const label_aquaero_speeds[] = { | 
 | 	"Fan 1 speed", | 
 | 	"Fan 2 speed", | 
 | 	"Fan 3 speed", | 
 | 	"Fan 4 speed", | 
 | 	"Flow sensor 1 [dL/h]", | 
 | 	"Flow sensor 2 [dL/h]" | 
 | }; | 
 |  | 
 | /* Labels for High Flow Next */ | 
 | static const char *const label_highflownext_temp_sensors[] = { | 
 | 	"Coolant temp", | 
 | 	"External sensor" | 
 | }; | 
 |  | 
 | static const char *const label_highflownext_fan_speed[] = { | 
 | 	"Flow [dL/h]", | 
 | 	"Water quality [%]", | 
 | 	"Conductivity [nS/cm]", | 
 | }; | 
 |  | 
 | static const char *const label_highflownext_power[] = { | 
 | 	"Dissipated power", | 
 | }; | 
 |  | 
 | static const char *const label_highflownext_voltage[] = { | 
 | 	"+5V voltage", | 
 | 	"+5V USB voltage" | 
 | }; | 
 |  | 
 | /* Labels for Leakshield */ | 
 | static const char *const label_leakshield_temp_sensors[] = { | 
 | 	"Temperature 1", | 
 | 	"Temperature 2" | 
 | }; | 
 |  | 
 | static const char *const label_leakshield_fan_speed[] = { | 
 | 	"Pressure [ubar]", | 
 | 	"User-Provided Pump Speed", | 
 | 	"User-Provided Flow [dL/h]", | 
 | 	"Reservoir Volume [ml]", | 
 | 	"Reservoir Filled [ml]", | 
 | }; | 
 |  | 
 | /* Labels for Aquastream XT */ | 
 | static const char *const label_aquastreamxt_temp_sensors[] = { | 
 | 	"Fan IC temp", | 
 | 	"External sensor", | 
 | 	"Coolant temp" | 
 | }; | 
 |  | 
 | /* Labels for Aquastream Ultimate */ | 
 | static const char *const label_aquastreamult_temp[] = { | 
 | 	"Coolant temp", | 
 | 	"External temp" | 
 | }; | 
 |  | 
 | static const char *const label_aquastreamult_speeds[] = { | 
 | 	"Fan speed", | 
 | 	"Pump speed", | 
 | 	"Pressure [mbar]", | 
 | 	"Flow speed [dL/h]" | 
 | }; | 
 |  | 
 | static const char *const label_aquastreamult_power[] = { | 
 | 	"Fan power", | 
 | 	"Pump power" | 
 | }; | 
 |  | 
 | static const char *const label_aquastreamult_voltages[] = { | 
 | 	"Fan voltage", | 
 | 	"Pump voltage" | 
 | }; | 
 |  | 
 | static const char *const label_aquastreamult_current[] = { | 
 | 	"Fan current", | 
 | 	"Pump current" | 
 | }; | 
 |  | 
 | /* Labels for Poweradjust 3 */ | 
 | static const char *const label_poweradjust3_temp_sensors[] = { | 
 | 	"External sensor" | 
 | }; | 
 |  | 
 | /* Labels for Highflow */ | 
 | static const char *const label_highflow_temp[] = { | 
 | 	"External temp", | 
 | 	"Internal temp" | 
 | }; | 
 |  | 
 | static const char *const label_highflow_speeds[] = { | 
 | 	"Flow speed [dL/h]" | 
 | }; | 
 |  | 
 | struct aqc_fan_structure_offsets { | 
 | 	u8 voltage; | 
 | 	u8 curr; | 
 | 	u8 power; | 
 | 	u8 speed; | 
 | }; | 
 |  | 
 | /* Fan structure offsets for Aquaero */ | 
 | static struct aqc_fan_structure_offsets aqc_aquaero_fan_structure = { | 
 | 	.voltage = AQUAERO_FAN_VOLTAGE_OFFSET, | 
 | 	.curr = AQUAERO_FAN_CURRENT_OFFSET, | 
 | 	.power = AQUAERO_FAN_POWER_OFFSET, | 
 | 	.speed = AQUAERO_FAN_SPEED_OFFSET | 
 | }; | 
 |  | 
 | /* Fan structure offsets for Aquastream Ultimate */ | 
 | static struct aqc_fan_structure_offsets aqc_aquastreamult_fan_structure = { | 
 | 	.voltage = AQUASTREAMULT_FAN_VOLTAGE_OFFSET, | 
 | 	.curr = AQUASTREAMULT_FAN_CURRENT_OFFSET, | 
 | 	.power = AQUASTREAMULT_FAN_POWER_OFFSET, | 
 | 	.speed = AQUASTREAMULT_FAN_SPEED_OFFSET | 
 | }; | 
 |  | 
 | /* Fan structure offsets for all devices except those above */ | 
 | static struct aqc_fan_structure_offsets aqc_general_fan_structure = { | 
 | 	.voltage = AQC_FAN_VOLTAGE_OFFSET, | 
 | 	.curr = AQC_FAN_CURRENT_OFFSET, | 
 | 	.power = AQC_FAN_POWER_OFFSET, | 
 | 	.speed = AQC_FAN_SPEED_OFFSET | 
 | }; | 
 |  | 
 | struct aqc_data { | 
 | 	struct hid_device *hdev; | 
 | 	struct device *hwmon_dev; | 
 | 	struct dentry *debugfs; | 
 | 	struct mutex mutex;	/* Used for locking access when reading and writing PWM values */ | 
 | 	enum kinds kind; | 
 | 	const char *name; | 
 |  | 
 | 	int status_report_id;	/* Used for legacy devices, report is stored in buffer */ | 
 | 	int ctrl_report_id; | 
 | 	int secondary_ctrl_report_id; | 
 | 	int secondary_ctrl_report_size; | 
 | 	u8 *secondary_ctrl_report; | 
 |  | 
 | 	ktime_t last_ctrl_report_op; | 
 | 	int ctrl_report_delay;	/* Delay between two ctrl report operations, in ms */ | 
 |  | 
 | 	int buffer_size; | 
 | 	u8 *buffer; | 
 | 	int checksum_start; | 
 | 	int checksum_length; | 
 | 	int checksum_offset; | 
 |  | 
 | 	int num_fans; | 
 | 	u16 *fan_sensor_offsets; | 
 | 	u16 *fan_ctrl_offsets; | 
 | 	int num_temp_sensors; | 
 | 	int temp_sensor_start_offset; | 
 | 	int num_virtual_temp_sensors; | 
 | 	int virtual_temp_sensor_start_offset; | 
 | 	int num_calc_virt_temp_sensors; | 
 | 	int calc_virt_temp_sensor_start_offset; | 
 | 	u16 temp_ctrl_offset; | 
 | 	u16 power_cycle_count_offset; | 
 | 	int num_flow_sensors; | 
 | 	u8 flow_sensors_start_offset; | 
 | 	u8 flow_pulses_ctrl_offset; | 
 | 	struct aqc_fan_structure_offsets *fan_structure; | 
 |  | 
 | 	/* General info, same across all devices */ | 
 | 	u8 serial_number_start_offset; | 
 | 	u32 serial_number[2]; | 
 | 	u8 firmware_version_offset; | 
 | 	u16 firmware_version; | 
 |  | 
 | 	/* How many times the device was powered on, if available */ | 
 | 	u32 power_cycles; | 
 |  | 
 | 	/* Sensor values */ | 
 | 	s32 temp_input[20];	/* Max 4 physical and 16 virtual or 8 physical and 12 virtual */ | 
 | 	s32 speed_input[9]; | 
 | 	u32 speed_input_min[1]; | 
 | 	u32 speed_input_target[1]; | 
 | 	u32 speed_input_max[1]; | 
 | 	u32 power_input[8]; | 
 | 	u16 voltage_input[8]; | 
 | 	u16 current_input[8]; | 
 |  | 
 | 	/* Label values */ | 
 | 	const char *const *temp_label; | 
 | 	const char *const *virtual_temp_label; | 
 | 	const char *const *calc_virt_temp_label;	/* For Aquaero */ | 
 | 	const char *const *speed_label; | 
 | 	const char *const *power_label; | 
 | 	const char *const *voltage_label; | 
 | 	const char *const *current_label; | 
 |  | 
 | 	unsigned long updated; | 
 | }; | 
 |  | 
 | /* Converts from centi-percent */ | 
 | static int aqc_percent_to_pwm(u16 val) | 
 | { | 
 | 	return DIV_ROUND_CLOSEST(val * 255, 100 * 100); | 
 | } | 
 |  | 
 | /* Converts to centi-percent */ | 
 | static int aqc_pwm_to_percent(long val) | 
 | { | 
 | 	if (val < 0 || val > 255) | 
 | 		return -EINVAL; | 
 |  | 
 | 	return DIV_ROUND_CLOSEST(val * 100 * 100, 255); | 
 | } | 
 |  | 
 | /* Converts raw value for Aquastream XT pump speed to RPM */ | 
 | static int aqc_aquastreamxt_convert_pump_rpm(u16 val) | 
 | { | 
 | 	if (val > 0) | 
 | 		return DIV_ROUND_CLOSEST(AQUASTREAMXT_PUMP_CONVERSION_CONST, val); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Converts raw value for Aquastream XT fan speed to RPM */ | 
 | static int aqc_aquastreamxt_convert_fan_rpm(u16 val) | 
 | { | 
 | 	if (val > 0) | 
 | 		return DIV_ROUND_CLOSEST(AQUASTREAMXT_FAN_CONVERSION_CONST, val); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void aqc_delay_ctrl_report(struct aqc_data *priv) | 
 | { | 
 | 	/* | 
 | 	 * If previous read or write is too close to this one, delay the current operation | 
 | 	 * to give the device enough time to process the previous one. | 
 | 	 */ | 
 | 	if (priv->ctrl_report_delay) { | 
 | 		s64 delta = ktime_ms_delta(ktime_get(), priv->last_ctrl_report_op); | 
 |  | 
 | 		if (delta < priv->ctrl_report_delay) | 
 | 			msleep(priv->ctrl_report_delay - delta); | 
 | 	} | 
 | } | 
 |  | 
 | /* Expects the mutex to be locked */ | 
 | static int aqc_get_ctrl_data(struct aqc_data *priv) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	aqc_delay_ctrl_report(priv); | 
 |  | 
 | 	memset(priv->buffer, 0x00, priv->buffer_size); | 
 | 	ret = hid_hw_raw_request(priv->hdev, priv->ctrl_report_id, priv->buffer, priv->buffer_size, | 
 | 				 HID_FEATURE_REPORT, HID_REQ_GET_REPORT); | 
 | 	if (ret < 0) | 
 | 		ret = -ENODATA; | 
 |  | 
 | 	priv->last_ctrl_report_op = ktime_get(); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* Expects the mutex to be locked */ | 
 | static int aqc_send_ctrl_data(struct aqc_data *priv) | 
 | { | 
 | 	int ret; | 
 | 	u16 checksum; | 
 |  | 
 | 	aqc_delay_ctrl_report(priv); | 
 |  | 
 | 	/* Checksum is not needed for Aquaero */ | 
 | 	if (priv->kind != aquaero) { | 
 | 		/* Init and xorout value for CRC-16/USB is 0xffff */ | 
 | 		checksum = crc16(0xffff, priv->buffer + priv->checksum_start, | 
 | 				 priv->checksum_length); | 
 | 		checksum ^= 0xffff; | 
 |  | 
 | 		/* Place the new checksum at the end of the report */ | 
 | 		put_unaligned_be16(checksum, priv->buffer + priv->checksum_offset); | 
 | 	} | 
 |  | 
 | 	/* Send the patched up report back to the device */ | 
 | 	ret = hid_hw_raw_request(priv->hdev, priv->ctrl_report_id, priv->buffer, priv->buffer_size, | 
 | 				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | 
 | 	if (ret < 0) | 
 | 		goto record_access_and_ret; | 
 |  | 
 | 	/* The official software sends this report after every change, so do it here as well */ | 
 | 	ret = hid_hw_raw_request(priv->hdev, priv->secondary_ctrl_report_id, | 
 | 				 priv->secondary_ctrl_report, priv->secondary_ctrl_report_size, | 
 | 				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT); | 
 |  | 
 | record_access_and_ret: | 
 | 	priv->last_ctrl_report_op = ktime_get(); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* Refreshes the control buffer and stores value at offset in val */ | 
 | static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val, int type) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	mutex_lock(&priv->mutex); | 
 |  | 
 | 	ret = aqc_get_ctrl_data(priv); | 
 | 	if (ret < 0) | 
 | 		goto unlock_and_return; | 
 |  | 
 | 	switch (type) { | 
 | 	case AQC_BE16: | 
 | 		*val = (s16)get_unaligned_be16(priv->buffer + offset); | 
 | 		break; | 
 | 	case AQC_8: | 
 | 		*val = priv->buffer[offset]; | 
 | 		break; | 
 | 	default: | 
 | 		ret = -EINVAL; | 
 | 	} | 
 |  | 
 | unlock_and_return: | 
 | 	mutex_unlock(&priv->mutex); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int aqc_set_ctrl_vals(struct aqc_data *priv, int *offsets, long *vals, int *types, int len) | 
 | { | 
 | 	int ret, i; | 
 |  | 
 | 	mutex_lock(&priv->mutex); | 
 |  | 
 | 	ret = aqc_get_ctrl_data(priv); | 
 | 	if (ret < 0) | 
 | 		goto unlock_and_return; | 
 |  | 
 | 	for (i = 0; i < len; i++) { | 
 | 		switch (types[i]) { | 
 | 		case AQC_BE16: | 
 | 			put_unaligned_be16((s16)vals[i], priv->buffer + offsets[i]); | 
 | 			break; | 
 | 		case AQC_8: | 
 | 			priv->buffer[offsets[i]] = (u8)vals[i]; | 
 | 			break; | 
 | 		default: | 
 | 			ret = -EINVAL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (ret < 0) | 
 | 		goto unlock_and_return; | 
 |  | 
 | 	ret = aqc_send_ctrl_data(priv); | 
 |  | 
 | unlock_and_return: | 
 | 	mutex_unlock(&priv->mutex); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val, int type) | 
 | { | 
 | 	return aqc_set_ctrl_vals(priv, &offset, &val, &type, 1); | 
 | } | 
 |  | 
 | static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) | 
 | { | 
 | 	const struct aqc_data *priv = data; | 
 |  | 
 | 	switch (type) { | 
 | 	case hwmon_temp: | 
 | 		if (channel < priv->num_temp_sensors) { | 
 | 			switch (attr) { | 
 | 			case hwmon_temp_label: | 
 | 			case hwmon_temp_input: | 
 | 				return 0444; | 
 | 			case hwmon_temp_offset: | 
 | 				if (priv->temp_ctrl_offset != 0) | 
 | 					return 0644; | 
 | 				break; | 
 | 			default: | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (channel < | 
 | 		    priv->num_temp_sensors + priv->num_virtual_temp_sensors + | 
 | 		    priv->num_calc_virt_temp_sensors) | 
 | 			switch (attr) { | 
 | 			case hwmon_temp_label: | 
 | 			case hwmon_temp_input: | 
 | 				return 0444; | 
 | 			default: | 
 | 				break; | 
 | 			} | 
 | 		break; | 
 | 	case hwmon_pwm: | 
 | 		if (priv->fan_ctrl_offsets && channel < priv->num_fans) { | 
 | 			switch (attr) { | 
 | 			case hwmon_pwm_input: | 
 | 				return 0644; | 
 | 			default: | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_fan: | 
 | 		switch (attr) { | 
 | 		case hwmon_fan_input: | 
 | 		case hwmon_fan_label: | 
 | 			switch (priv->kind) { | 
 | 			case aquastreamult: | 
 | 				/* | 
 | 				 * Special case to support pump RPM, fan RPM, | 
 | 				 * pressure and flow sensor | 
 | 				 */ | 
 | 				if (channel < 4) | 
 | 					return 0444; | 
 | 				break; | 
 | 			case highflownext: | 
 | 				/* Special case to support flow sensor, water quality | 
 | 				 * and conductivity | 
 | 				 */ | 
 | 				if (channel < 3) | 
 | 					return 0444; | 
 | 				break; | 
 | 			case leakshield: | 
 | 				/* Special case for Leakshield sensors */ | 
 | 				if (channel < 5) | 
 | 					return 0444; | 
 | 				break; | 
 | 			case aquaero: | 
 | 			case octo: | 
 | 			case quadro: | 
 | 			case highflow: | 
 | 				/* Special case to support flow sensors */ | 
 | 				if (channel < priv->num_fans + priv->num_flow_sensors) | 
 | 					return 0444; | 
 | 				break; | 
 | 			default: | 
 | 				if (channel < priv->num_fans) | 
 | 					return 0444; | 
 | 				break; | 
 | 			} | 
 | 			break; | 
 | 		case hwmon_fan_pulses: | 
 | 			/* Special case for Quadro/Octo flow sensor */ | 
 | 			if (channel == priv->num_fans) { | 
 | 				switch (priv->kind) { | 
 | 				case quadro: | 
 | 				case octo: | 
 | 					return 0644; | 
 | 				default: | 
 | 					break; | 
 | 				} | 
 | 			} | 
 | 			break; | 
 | 		case hwmon_fan_min: | 
 | 		case hwmon_fan_max: | 
 | 		case hwmon_fan_target: | 
 | 			/* Special case for Leakshield pressure sensor */ | 
 | 			if (priv->kind == leakshield && channel == 0) | 
 | 				return 0444; | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_power: | 
 | 		switch (priv->kind) { | 
 | 		case aquastreamult: | 
 | 			/* Special case to support pump and fan power */ | 
 | 			if (channel < 2) | 
 | 				return 0444; | 
 | 			break; | 
 | 		case highflownext: | 
 | 			/* Special case to support one power sensor */ | 
 | 			if (channel == 0) | 
 | 				return 0444; | 
 | 			break; | 
 | 		case aquastreamxt: | 
 | 			break; | 
 | 		default: | 
 | 			if (channel < priv->num_fans) | 
 | 				return 0444; | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_curr: | 
 | 		switch (priv->kind) { | 
 | 		case aquastreamult: | 
 | 			/* Special case to support pump and fan current */ | 
 | 			if (channel < 2) | 
 | 				return 0444; | 
 | 			break; | 
 | 		case aquastreamxt: | 
 | 			/* Special case to support pump current */ | 
 | 			if (channel == 0) | 
 | 				return 0444; | 
 | 			break; | 
 | 		default: | 
 | 			if (channel < priv->num_fans) | 
 | 				return 0444; | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_in: | 
 | 		switch (priv->kind) { | 
 | 		case d5next: | 
 | 			/* Special case to support +5V and +12V voltage sensors */ | 
 | 			if (channel < priv->num_fans + 2) | 
 | 				return 0444; | 
 | 			break; | 
 | 		case aquastreamult: | 
 | 		case highflownext: | 
 | 			/* Special case to support two voltage sensors */ | 
 | 			if (channel < 2) | 
 | 				return 0444; | 
 | 			break; | 
 | 		default: | 
 | 			if (channel < priv->num_fans) | 
 | 				return 0444; | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Read device sensors by manually requesting the sensor report (legacy way) */ | 
 | static int aqc_legacy_read(struct aqc_data *priv) | 
 | { | 
 | 	int ret, i, sensor_value; | 
 |  | 
 | 	mutex_lock(&priv->mutex); | 
 |  | 
 | 	memset(priv->buffer, 0x00, priv->buffer_size); | 
 | 	ret = hid_hw_raw_request(priv->hdev, priv->status_report_id, priv->buffer, | 
 | 				 priv->buffer_size, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); | 
 | 	if (ret < 0) | 
 | 		goto unlock_and_return; | 
 |  | 
 | 	/* Temperature sensor readings */ | 
 | 	for (i = 0; i < priv->num_temp_sensors; i++) { | 
 | 		sensor_value = get_unaligned_le16(priv->buffer + priv->temp_sensor_start_offset + | 
 | 						  i * AQC_SENSOR_SIZE); | 
 | 		if (sensor_value == AQC_SENSOR_NA) | 
 | 			priv->temp_input[i] = -ENODATA; | 
 | 		else | 
 | 			priv->temp_input[i] = sensor_value * 10; | 
 | 	} | 
 |  | 
 | 	/* Special-case sensor readings */ | 
 | 	switch (priv->kind) { | 
 | 	case aquastreamxt: | 
 | 		/* Info provided with every report */ | 
 | 		priv->serial_number[0] = get_unaligned_le16(priv->buffer + | 
 | 							    priv->serial_number_start_offset); | 
 | 		priv->firmware_version = | 
 | 			get_unaligned_le16(priv->buffer + priv->firmware_version_offset); | 
 |  | 
 | 		/* Read pump speed in RPM */ | 
 | 		sensor_value = get_unaligned_le16(priv->buffer + priv->fan_sensor_offsets[0]); | 
 | 		priv->speed_input[0] = aqc_aquastreamxt_convert_pump_rpm(sensor_value); | 
 |  | 
 | 		/* Read fan speed in RPM, if available */ | 
 | 		sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_FAN_STATUS_OFFSET); | 
 | 		if (sensor_value == AQUASTREAMXT_FAN_STOPPED) { | 
 | 			priv->speed_input[1] = 0; | 
 | 		} else { | 
 | 			sensor_value = | 
 | 				get_unaligned_le16(priv->buffer + priv->fan_sensor_offsets[1]); | 
 | 			priv->speed_input[1] = aqc_aquastreamxt_convert_fan_rpm(sensor_value); | 
 | 		} | 
 |  | 
 | 		/* Calculation derived from linear regression */ | 
 | 		sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_CURR_OFFSET); | 
 | 		priv->current_input[0] = DIV_ROUND_CLOSEST(sensor_value * 176, 100) - 52; | 
 |  | 
 | 		sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_VOLTAGE_OFFSET); | 
 | 		priv->voltage_input[0] = DIV_ROUND_CLOSEST(sensor_value * 1000, 61); | 
 |  | 
 | 		sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_FAN_VOLTAGE_OFFSET); | 
 | 		priv->voltage_input[1] = DIV_ROUND_CLOSEST(sensor_value * 1000, 63); | 
 | 		break; | 
 | 	case highflow: | 
 | 		/* Info provided with every report */ | 
 | 		priv->serial_number[0] = get_unaligned_le16(priv->buffer + | 
 | 							    priv->serial_number_start_offset); | 
 | 		priv->firmware_version = | 
 | 		    get_unaligned_le16(priv->buffer + priv->firmware_version_offset); | 
 |  | 
 | 		/* Read flow speed */ | 
 | 		priv->speed_input[0] = get_unaligned_le16(priv->buffer + | 
 | 							  priv->flow_sensors_start_offset); | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	priv->updated = jiffies; | 
 |  | 
 | unlock_and_return: | 
 | 	mutex_unlock(&priv->mutex); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, | 
 | 		    int channel, long *val) | 
 | { | 
 | 	int ret; | 
 | 	struct aqc_data *priv = dev_get_drvdata(dev); | 
 |  | 
 | 	if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL)) { | 
 | 		if (priv->status_report_id != 0) { | 
 | 			/* Legacy devices require manual reads */ | 
 | 			ret = aqc_legacy_read(priv); | 
 | 			if (ret < 0) | 
 | 				return -ENODATA; | 
 | 		} else { | 
 | 			return -ENODATA; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	switch (type) { | 
 | 	case hwmon_temp: | 
 | 		switch (attr) { | 
 | 		case hwmon_temp_input: | 
 | 			if (priv->temp_input[channel] == -ENODATA) | 
 | 				return -ENODATA; | 
 |  | 
 | 			*val = priv->temp_input[channel]; | 
 | 			break; | 
 | 		case hwmon_temp_offset: | 
 | 			ret = | 
 | 			    aqc_get_ctrl_val(priv, priv->temp_ctrl_offset + | 
 | 					     channel * AQC_SENSOR_SIZE, val, AQC_BE16); | 
 | 			if (ret < 0) | 
 | 				return ret; | 
 |  | 
 | 			*val *= 10; | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_fan: | 
 | 		switch (attr) { | 
 | 		case hwmon_fan_input: | 
 | 			if (priv->speed_input[channel] == -ENODATA) | 
 | 				return -ENODATA; | 
 |  | 
 | 			*val = priv->speed_input[channel]; | 
 | 			break; | 
 | 		case hwmon_fan_min: | 
 | 			*val = priv->speed_input_min[channel]; | 
 | 			break; | 
 | 		case hwmon_fan_max: | 
 | 			*val = priv->speed_input_max[channel]; | 
 | 			break; | 
 | 		case hwmon_fan_target: | 
 | 			*val = priv->speed_input_target[channel]; | 
 | 			break; | 
 | 		case hwmon_fan_pulses: | 
 | 			ret = aqc_get_ctrl_val(priv, priv->flow_pulses_ctrl_offset, | 
 | 					       val, AQC_BE16); | 
 | 			if (ret < 0) | 
 | 				return ret; | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_power: | 
 | 		*val = priv->power_input[channel]; | 
 | 		break; | 
 | 	case hwmon_pwm: | 
 | 		switch (priv->kind) { | 
 | 		case aquaero: | 
 | 			ret = aqc_get_ctrl_val(priv, | 
 | 				AQUAERO_CTRL_PRESET_START + channel * AQUAERO_CTRL_PRESET_SIZE, | 
 | 				val, AQC_BE16); | 
 | 			if (ret < 0) | 
 | 				return ret; | 
 | 			*val = aqc_percent_to_pwm(*val); | 
 | 			break; | 
 | 		default: | 
 | 			ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel], | 
 | 					       val, AQC_BE16); | 
 | 			if (ret < 0) | 
 | 				return ret; | 
 |  | 
 | 			*val = aqc_percent_to_pwm(*val); | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_in: | 
 | 		*val = priv->voltage_input[channel]; | 
 | 		break; | 
 | 	case hwmon_curr: | 
 | 		*val = priv->current_input[channel]; | 
 | 		break; | 
 | 	default: | 
 | 		return -EOPNOTSUPP; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, | 
 | 			   int channel, const char **str) | 
 | { | 
 | 	struct aqc_data *priv = dev_get_drvdata(dev); | 
 |  | 
 | 	/* Number of sensors that are not calculated */ | 
 | 	int num_non_calc_sensors = priv->num_temp_sensors + priv->num_virtual_temp_sensors; | 
 |  | 
 | 	switch (type) { | 
 | 	case hwmon_temp: | 
 | 		if (channel < priv->num_temp_sensors) { | 
 | 			*str = priv->temp_label[channel]; | 
 | 		} else { | 
 | 			if (priv->kind == aquaero && channel >= num_non_calc_sensors) | 
 | 				*str = | 
 | 				    priv->calc_virt_temp_label[channel - num_non_calc_sensors]; | 
 | 			else | 
 | 				*str = priv->virtual_temp_label[channel - priv->num_temp_sensors]; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_fan: | 
 | 		*str = priv->speed_label[channel]; | 
 | 		break; | 
 | 	case hwmon_power: | 
 | 		*str = priv->power_label[channel]; | 
 | 		break; | 
 | 	case hwmon_in: | 
 | 		*str = priv->voltage_label[channel]; | 
 | 		break; | 
 | 	case hwmon_curr: | 
 | 		*str = priv->current_label[channel]; | 
 | 		break; | 
 | 	default: | 
 | 		return -EOPNOTSUPP; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, | 
 | 		     long val) | 
 | { | 
 | 	int ret, pwm_value; | 
 | 	/* Arrays for setting multiple values at once in the control report */ | 
 | 	int ctrl_values_offsets[4]; | 
 | 	long ctrl_values[4]; | 
 | 	int ctrl_values_types[4]; | 
 | 	struct aqc_data *priv = dev_get_drvdata(dev); | 
 |  | 
 | 	switch (type) { | 
 | 	case hwmon_temp: | 
 | 		switch (attr) { | 
 | 		case hwmon_temp_offset: | 
 | 			/* Limit temp offset to +/- 15K as in the official software */ | 
 | 			val = clamp_val(val, -15000, 15000) / 10; | 
 | 			ret = | 
 | 			    aqc_set_ctrl_val(priv, priv->temp_ctrl_offset + | 
 | 					     channel * AQC_SENSOR_SIZE, val, AQC_BE16); | 
 | 			if (ret < 0) | 
 | 				return ret; | 
 | 			break; | 
 | 		default: | 
 | 			return -EOPNOTSUPP; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_fan: | 
 | 		switch (attr) { | 
 | 		case hwmon_fan_pulses: | 
 | 			val = clamp_val(val, 10, 1000); | 
 | 			ret = aqc_set_ctrl_val(priv, priv->flow_pulses_ctrl_offset, | 
 | 					       val, AQC_BE16); | 
 | 			if (ret < 0) | 
 | 				return ret; | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	case hwmon_pwm: | 
 | 		switch (attr) { | 
 | 		case hwmon_pwm_input: | 
 | 			pwm_value = aqc_pwm_to_percent(val); | 
 | 			if (pwm_value < 0) | 
 | 				return pwm_value; | 
 |  | 
 | 			switch (priv->kind) { | 
 | 			case aquaero: | 
 | 				/* Write pwm value to preset corresponding to the channel */ | 
 | 				ctrl_values_offsets[0] = AQUAERO_CTRL_PRESET_START + | 
 | 				    channel * AQUAERO_CTRL_PRESET_SIZE; | 
 | 				ctrl_values[0] = pwm_value; | 
 | 				ctrl_values_types[0] = AQC_BE16; | 
 |  | 
 | 				/* Write preset number in fan control source */ | 
 | 				ctrl_values_offsets[1] = priv->fan_ctrl_offsets[channel] + | 
 | 				    AQUAERO_FAN_CTRL_SRC_OFFSET; | 
 | 				ctrl_values[1] = AQUAERO_CTRL_PRESET_ID + channel; | 
 | 				ctrl_values_types[1] = AQC_BE16; | 
 |  | 
 | 				/* Set minimum power to 0 to allow the fan to turn off */ | 
 | 				ctrl_values_offsets[2] = priv->fan_ctrl_offsets[channel] + | 
 | 				    AQUAERO_FAN_CTRL_MIN_PWR_OFFSET; | 
 | 				ctrl_values[2] = 0; | 
 | 				ctrl_values_types[2] = AQC_BE16; | 
 |  | 
 | 				/* Set maximum power to 255 to allow the fan to reach max speed */ | 
 | 				ctrl_values_offsets[3] = priv->fan_ctrl_offsets[channel] + | 
 | 				    AQUAERO_FAN_CTRL_MAX_PWR_OFFSET; | 
 | 				ctrl_values[3] = aqc_pwm_to_percent(255); | 
 | 				ctrl_values_types[3] = AQC_BE16; | 
 |  | 
 | 				ret = aqc_set_ctrl_vals(priv, ctrl_values_offsets, ctrl_values, | 
 | 							ctrl_values_types, 4); | 
 | 				if (ret < 0) | 
 | 					return ret; | 
 | 				break; | 
 | 			default: | 
 | 				ret = aqc_set_ctrl_val(priv, priv->fan_ctrl_offsets[channel], | 
 | 						       pwm_value, AQC_BE16); | 
 | 				if (ret < 0) | 
 | 					return ret; | 
 | 				break; | 
 | 			} | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 | 	default: | 
 | 		return -EOPNOTSUPP; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct hwmon_ops aqc_hwmon_ops = { | 
 | 	.is_visible = aqc_is_visible, | 
 | 	.read = aqc_read, | 
 | 	.read_string = aqc_read_string, | 
 | 	.write = aqc_write | 
 | }; | 
 |  | 
 | static const struct hwmon_channel_info * const aqc_info[] = { | 
 | 	HWMON_CHANNEL_INFO(temp, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL, | 
 | 			   HWMON_T_INPUT | HWMON_T_LABEL), | 
 | 	HWMON_CHANNEL_INFO(fan, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | | 
 | 			   HWMON_F_TARGET, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_PULSES, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_PULSES), | 
 | 	HWMON_CHANNEL_INFO(power, | 
 | 			   HWMON_P_INPUT | HWMON_P_LABEL, | 
 | 			   HWMON_P_INPUT | HWMON_P_LABEL, | 
 | 			   HWMON_P_INPUT | HWMON_P_LABEL, | 
 | 			   HWMON_P_INPUT | HWMON_P_LABEL, | 
 | 			   HWMON_P_INPUT | HWMON_P_LABEL, | 
 | 			   HWMON_P_INPUT | HWMON_P_LABEL, | 
 | 			   HWMON_P_INPUT | HWMON_P_LABEL, | 
 | 			   HWMON_P_INPUT | HWMON_P_LABEL), | 
 | 	HWMON_CHANNEL_INFO(pwm, | 
 | 			   HWMON_PWM_INPUT, | 
 | 			   HWMON_PWM_INPUT, | 
 | 			   HWMON_PWM_INPUT, | 
 | 			   HWMON_PWM_INPUT, | 
 | 			   HWMON_PWM_INPUT, | 
 | 			   HWMON_PWM_INPUT, | 
 | 			   HWMON_PWM_INPUT, | 
 | 			   HWMON_PWM_INPUT), | 
 | 	HWMON_CHANNEL_INFO(in, | 
 | 			   HWMON_I_INPUT | HWMON_I_LABEL, | 
 | 			   HWMON_I_INPUT | HWMON_I_LABEL, | 
 | 			   HWMON_I_INPUT | HWMON_I_LABEL, | 
 | 			   HWMON_I_INPUT | HWMON_I_LABEL, | 
 | 			   HWMON_I_INPUT | HWMON_I_LABEL, | 
 | 			   HWMON_I_INPUT | HWMON_I_LABEL, | 
 | 			   HWMON_I_INPUT | HWMON_I_LABEL, | 
 | 			   HWMON_I_INPUT | HWMON_I_LABEL), | 
 | 	HWMON_CHANNEL_INFO(curr, | 
 | 			   HWMON_C_INPUT | HWMON_C_LABEL, | 
 | 			   HWMON_C_INPUT | HWMON_C_LABEL, | 
 | 			   HWMON_C_INPUT | HWMON_C_LABEL, | 
 | 			   HWMON_C_INPUT | HWMON_C_LABEL, | 
 | 			   HWMON_C_INPUT | HWMON_C_LABEL, | 
 | 			   HWMON_C_INPUT | HWMON_C_LABEL, | 
 | 			   HWMON_C_INPUT | HWMON_C_LABEL, | 
 | 			   HWMON_C_INPUT | HWMON_C_LABEL), | 
 | 	NULL | 
 | }; | 
 |  | 
 | static const struct hwmon_chip_info aqc_chip_info = { | 
 | 	.ops = &aqc_hwmon_ops, | 
 | 	.info = aqc_info, | 
 | }; | 
 |  | 
 | static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) | 
 | { | 
 | 	int i, j, sensor_value; | 
 | 	struct aqc_data *priv; | 
 |  | 
 | 	if (report->id != STATUS_REPORT_ID) | 
 | 		return 0; | 
 |  | 
 | 	priv = hid_get_drvdata(hdev); | 
 |  | 
 | 	/* Info provided with every report */ | 
 | 	priv->serial_number[0] = get_unaligned_be16(data + priv->serial_number_start_offset); | 
 | 	priv->serial_number[1] = get_unaligned_be16(data + priv->serial_number_start_offset + | 
 | 						    SERIAL_PART_OFFSET); | 
 | 	priv->firmware_version = get_unaligned_be16(data + priv->firmware_version_offset); | 
 |  | 
 | 	/* Physical temperature sensor readings */ | 
 | 	for (i = 0; i < priv->num_temp_sensors; i++) { | 
 | 		sensor_value = get_unaligned_be16(data + | 
 | 						  priv->temp_sensor_start_offset + | 
 | 						  i * AQC_SENSOR_SIZE); | 
 | 		if (sensor_value == AQC_SENSOR_NA) | 
 | 			priv->temp_input[i] = -ENODATA; | 
 | 		else | 
 | 			priv->temp_input[i] = sensor_value * 10; | 
 | 	} | 
 |  | 
 | 	/* Virtual temperature sensor readings */ | 
 | 	for (j = 0; j < priv->num_virtual_temp_sensors; j++) { | 
 | 		sensor_value = get_unaligned_be16(data + | 
 | 						  priv->virtual_temp_sensor_start_offset + | 
 | 						  j * AQC_SENSOR_SIZE); | 
 | 		if (sensor_value == AQC_SENSOR_NA) | 
 | 			priv->temp_input[i] = -ENODATA; | 
 | 		else | 
 | 			priv->temp_input[i] = sensor_value * 10; | 
 | 		i++; | 
 | 	} | 
 |  | 
 | 	/* Fan speed and related readings */ | 
 | 	for (i = 0; i < priv->num_fans; i++) { | 
 | 		priv->speed_input[i] = | 
 | 		    get_unaligned_be16(data + priv->fan_sensor_offsets[i] + | 
 | 				       priv->fan_structure->speed); | 
 | 		priv->power_input[i] = | 
 | 		    get_unaligned_be16(data + priv->fan_sensor_offsets[i] + | 
 | 				       priv->fan_structure->power) * 10000; | 
 | 		priv->voltage_input[i] = | 
 | 		    get_unaligned_be16(data + priv->fan_sensor_offsets[i] + | 
 | 				       priv->fan_structure->voltage) * 10; | 
 | 		priv->current_input[i] = | 
 | 		    get_unaligned_be16(data + priv->fan_sensor_offsets[i] + | 
 | 				       priv->fan_structure->curr); | 
 | 	} | 
 |  | 
 | 	/* Flow sensor readings */ | 
 | 	for (j = 0; j < priv->num_flow_sensors; j++) { | 
 | 		priv->speed_input[i] = get_unaligned_be16(data + priv->flow_sensors_start_offset + | 
 | 							  j * AQC_SENSOR_SIZE); | 
 | 		i++; | 
 | 	} | 
 |  | 
 | 	if (priv->power_cycle_count_offset != 0) | 
 | 		priv->power_cycles = get_unaligned_be32(data + priv->power_cycle_count_offset); | 
 |  | 
 | 	/* Special-case sensor readings */ | 
 | 	switch (priv->kind) { | 
 | 	case aquaero: | 
 | 		/* Read calculated virtual temp sensors */ | 
 | 		i = priv->num_temp_sensors + priv->num_virtual_temp_sensors; | 
 | 		for (j = 0; j < priv->num_calc_virt_temp_sensors; j++) { | 
 | 			sensor_value = get_unaligned_be16(data + | 
 | 					priv->calc_virt_temp_sensor_start_offset + | 
 | 					j * AQC_SENSOR_SIZE); | 
 | 			if (sensor_value == AQC_SENSOR_NA) | 
 | 				priv->temp_input[i] = -ENODATA; | 
 | 			else | 
 | 				priv->temp_input[i] = sensor_value * 10; | 
 | 			i++; | 
 | 		} | 
 | 		break; | 
 | 	case aquastreamult: | 
 | 		priv->speed_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_OFFSET); | 
 | 		priv->speed_input[2] = get_unaligned_be16(data + AQUASTREAMULT_PRESSURE_OFFSET); | 
 | 		priv->speed_input[3] = get_unaligned_be16(data + AQUASTREAMULT_FLOW_SENSOR_OFFSET); | 
 |  | 
 | 		priv->power_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000; | 
 |  | 
 | 		priv->voltage_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_VOLTAGE) * 10; | 
 |  | 
 | 		priv->current_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_CURRENT); | 
 | 		break; | 
 | 	case d5next: | 
 | 		priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; | 
 | 		priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10; | 
 | 		break; | 
 | 	case highflownext: | 
 | 		/* If external temp sensor is not connected, its power reading is also N/A */ | 
 | 		if (priv->temp_input[1] == -ENODATA) | 
 | 			priv->power_input[0] = -ENODATA; | 
 | 		else | 
 | 			priv->power_input[0] = | 
 | 			    get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000; | 
 |  | 
 | 		priv->voltage_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE) * 10; | 
 | 		priv->voltage_input[1] = | 
 | 		    get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE_USB) * 10; | 
 |  | 
 | 		priv->speed_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_WATER_QUALITY); | 
 | 		priv->speed_input[2] = get_unaligned_be16(data + HIGHFLOWNEXT_CONDUCTIVITY); | 
 | 		break; | 
 | 	case leakshield: | 
 | 		priv->speed_input[0] = | 
 | 		    ((s16)get_unaligned_be16(data + LEAKSHIELD_PRESSURE_ADJUSTED)) * 100; | 
 | 		priv->speed_input_min[0] = get_unaligned_be16(data + LEAKSHIELD_PRESSURE_MIN) * 100; | 
 | 		priv->speed_input_target[0] = | 
 | 		    get_unaligned_be16(data + LEAKSHIELD_PRESSURE_TARGET) * 100; | 
 | 		priv->speed_input_max[0] = get_unaligned_be16(data + LEAKSHIELD_PRESSURE_MAX) * 100; | 
 |  | 
 | 		priv->speed_input[1] = get_unaligned_be16(data + LEAKSHIELD_PUMP_RPM_IN); | 
 | 		if (priv->speed_input[1] == AQC_SENSOR_NA) | 
 | 			priv->speed_input[1] = -ENODATA; | 
 |  | 
 | 		priv->speed_input[2] = get_unaligned_be16(data + LEAKSHIELD_FLOW_IN); | 
 | 		if (priv->speed_input[2] == AQC_SENSOR_NA) | 
 | 			priv->speed_input[2] = -ENODATA; | 
 |  | 
 | 		priv->speed_input[3] = get_unaligned_be16(data + LEAKSHIELD_RESERVOIR_VOLUME); | 
 | 		priv->speed_input[4] = get_unaligned_be16(data + LEAKSHIELD_RESERVOIR_FILLED); | 
 |  | 
 | 		/* Second temp sensor is not positioned after the first one, read it here */ | 
 | 		priv->temp_input[1] = get_unaligned_be16(data + LEAKSHIELD_TEMPERATURE_2) * 10; | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	priv->updated = jiffies; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int serial_number_show(struct seq_file *seqf, void *unused) | 
 | { | 
 | 	struct aqc_data *priv = seqf->private; | 
 |  | 
 | 	seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]); | 
 |  | 
 | 	return 0; | 
 | } | 
 | DEFINE_SHOW_ATTRIBUTE(serial_number); | 
 |  | 
 | static int firmware_version_show(struct seq_file *seqf, void *unused) | 
 | { | 
 | 	struct aqc_data *priv = seqf->private; | 
 |  | 
 | 	seq_printf(seqf, "%u\n", priv->firmware_version); | 
 |  | 
 | 	return 0; | 
 | } | 
 | DEFINE_SHOW_ATTRIBUTE(firmware_version); | 
 |  | 
 | static int power_cycles_show(struct seq_file *seqf, void *unused) | 
 | { | 
 | 	struct aqc_data *priv = seqf->private; | 
 |  | 
 | 	seq_printf(seqf, "%u\n", priv->power_cycles); | 
 |  | 
 | 	return 0; | 
 | } | 
 | DEFINE_SHOW_ATTRIBUTE(power_cycles); | 
 |  | 
 | static void aqc_debugfs_init(struct aqc_data *priv) | 
 | { | 
 | 	char name[64]; | 
 |  | 
 | 	scnprintf(name, sizeof(name), "%s_%s-%s", "aquacomputer", priv->name, | 
 | 		  dev_name(&priv->hdev->dev)); | 
 |  | 
 | 	priv->debugfs = debugfs_create_dir(name, NULL); | 
 |  | 
 | 	if (priv->serial_number_start_offset != 0) | 
 | 		debugfs_create_file("serial_number", 0444, priv->debugfs, priv, | 
 | 				    &serial_number_fops); | 
 | 	if (priv->firmware_version_offset != 0) | 
 | 		debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, | 
 | 				    &firmware_version_fops); | 
 | 	if (priv->power_cycle_count_offset != 0) | 
 | 		debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops); | 
 | } | 
 |  | 
 | static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) | 
 | { | 
 | 	struct aqc_data *priv; | 
 | 	int ret; | 
 |  | 
 | 	priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); | 
 | 	if (!priv) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	priv->hdev = hdev; | 
 | 	hid_set_drvdata(hdev, priv); | 
 |  | 
 | 	priv->updated = jiffies - STATUS_UPDATE_INTERVAL; | 
 |  | 
 | 	ret = hid_parse(hdev); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	ret = hid_hw_open(hdev); | 
 | 	if (ret) | 
 | 		goto fail_and_stop; | 
 |  | 
 | 	switch (hdev->product) { | 
 | 	case USB_PRODUCT_ID_AQUAERO: | 
 | 		/* | 
 | 		 * Aquaero presents itself as three HID devices under the same product ID: | 
 | 		 * "aquaero keyboard/mouse", "aquaero System Control" and "aquaero Device", | 
 | 		 * which is the one we want to communicate with. Unlike most other Aquacomputer | 
 | 		 * devices, Aquaero does not return meaningful data when explicitly requested | 
 | 		 * using GET_FEATURE_REPORT. | 
 | 		 * | 
 | 		 * The difference between "aquaero Device" and the other two is in the collections | 
 | 		 * they present. The two other devices have the type of the second element in | 
 | 		 * their respective collections set to 1, while the real device has it set to 0. | 
 | 		 */ | 
 | 		if (hdev->collection[1].type != 0) { | 
 | 			ret = -ENODEV; | 
 | 			goto fail_and_close; | 
 | 		} | 
 |  | 
 | 		priv->kind = aquaero; | 
 |  | 
 | 		priv->num_fans = AQUAERO_NUM_FANS; | 
 | 		priv->fan_sensor_offsets = aquaero_sensor_fan_offsets; | 
 | 		priv->fan_ctrl_offsets = aquaero_ctrl_fan_offsets; | 
 |  | 
 | 		priv->num_temp_sensors = AQUAERO_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = AQUAERO_SENSOR_START; | 
 | 		priv->num_virtual_temp_sensors = AQUAERO_NUM_VIRTUAL_SENSORS; | 
 | 		priv->virtual_temp_sensor_start_offset = AQUAERO_VIRTUAL_SENSOR_START; | 
 | 		priv->num_calc_virt_temp_sensors = AQUAERO_NUM_CALC_VIRTUAL_SENSORS; | 
 | 		priv->calc_virt_temp_sensor_start_offset = AQUAERO_CALC_VIRTUAL_SENSOR_START; | 
 | 		priv->num_flow_sensors = AQUAERO_NUM_FLOW_SENSORS; | 
 | 		priv->flow_sensors_start_offset = AQUAERO_FLOW_SENSORS_START; | 
 |  | 
 | 		priv->buffer_size = AQUAERO_CTRL_REPORT_SIZE; | 
 | 		priv->temp_ctrl_offset = AQUAERO_TEMP_CTRL_OFFSET; | 
 | 		priv->ctrl_report_delay = CTRL_REPORT_DELAY; | 
 |  | 
 | 		priv->temp_label = label_temp_sensors; | 
 | 		priv->virtual_temp_label = label_virtual_temp_sensors; | 
 | 		priv->calc_virt_temp_label = label_aquaero_calc_temp_sensors; | 
 | 		priv->speed_label = label_aquaero_speeds; | 
 | 		priv->power_label = label_fan_power; | 
 | 		priv->voltage_label = label_fan_voltage; | 
 | 		priv->current_label = label_fan_current; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_D5NEXT: | 
 | 		priv->kind = d5next; | 
 |  | 
 | 		priv->num_fans = D5NEXT_NUM_FANS; | 
 | 		priv->fan_sensor_offsets = d5next_sensor_fan_offsets; | 
 | 		priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets; | 
 |  | 
 | 		priv->num_temp_sensors = D5NEXT_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP; | 
 | 		priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS; | 
 | 		priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START; | 
 | 		priv->temp_ctrl_offset = D5NEXT_TEMP_CTRL_OFFSET; | 
 |  | 
 | 		priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE; | 
 | 		priv->ctrl_report_delay = CTRL_REPORT_DELAY; | 
 |  | 
 | 		priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; | 
 |  | 
 | 		priv->temp_label = label_d5next_temp; | 
 | 		priv->virtual_temp_label = label_virtual_temp_sensors; | 
 | 		priv->speed_label = label_d5next_speeds; | 
 | 		priv->power_label = label_d5next_power; | 
 | 		priv->voltage_label = label_d5next_voltages; | 
 | 		priv->current_label = label_d5next_current; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_FARBWERK: | 
 | 		priv->kind = farbwerk; | 
 |  | 
 | 		priv->num_fans = 0; | 
 |  | 
 | 		priv->num_temp_sensors = FARBWERK_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = FARBWERK_SENSOR_START; | 
 |  | 
 | 		priv->temp_label = label_temp_sensors; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_FARBWERK360: | 
 | 		priv->kind = farbwerk360; | 
 |  | 
 | 		priv->num_fans = 0; | 
 |  | 
 | 		priv->num_temp_sensors = FARBWERK360_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START; | 
 | 		priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS; | 
 | 		priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START; | 
 | 		priv->temp_ctrl_offset = FARBWERK360_TEMP_CTRL_OFFSET; | 
 |  | 
 | 		priv->buffer_size = FARBWERK360_CTRL_REPORT_SIZE; | 
 |  | 
 | 		priv->temp_label = label_temp_sensors; | 
 | 		priv->virtual_temp_label = label_virtual_temp_sensors; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_OCTO: | 
 | 		priv->kind = octo; | 
 |  | 
 | 		priv->num_fans = OCTO_NUM_FANS; | 
 | 		priv->fan_sensor_offsets = octo_sensor_fan_offsets; | 
 | 		priv->fan_ctrl_offsets = octo_ctrl_fan_offsets; | 
 |  | 
 | 		priv->num_temp_sensors = OCTO_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = OCTO_SENSOR_START; | 
 | 		priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS; | 
 | 		priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START; | 
 | 		priv->num_flow_sensors = OCTO_NUM_FLOW_SENSORS; | 
 | 		priv->flow_sensors_start_offset = OCTO_FLOW_SENSOR_OFFSET; | 
 |  | 
 | 		priv->temp_ctrl_offset = OCTO_TEMP_CTRL_OFFSET; | 
 |  | 
 | 		priv->buffer_size = OCTO_CTRL_REPORT_SIZE; | 
 | 		priv->ctrl_report_delay = CTRL_REPORT_DELAY; | 
 |  | 
 | 		priv->flow_pulses_ctrl_offset = OCTO_FLOW_PULSES_CTRL_OFFSET; | 
 | 		priv->power_cycle_count_offset = OCTO_POWER_CYCLES; | 
 |  | 
 | 		priv->temp_label = label_temp_sensors; | 
 | 		priv->virtual_temp_label = label_virtual_temp_sensors; | 
 | 		priv->speed_label = label_octo_speeds; | 
 | 		priv->power_label = label_fan_power; | 
 | 		priv->voltage_label = label_fan_voltage; | 
 | 		priv->current_label = label_fan_current; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_QUADRO: | 
 | 		priv->kind = quadro; | 
 |  | 
 | 		priv->num_fans = QUADRO_NUM_FANS; | 
 | 		priv->fan_sensor_offsets = quadro_sensor_fan_offsets; | 
 | 		priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets; | 
 |  | 
 | 		priv->num_temp_sensors = QUADRO_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = QUADRO_SENSOR_START; | 
 | 		priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS; | 
 | 		priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START; | 
 | 		priv->num_flow_sensors = QUADRO_NUM_FLOW_SENSORS; | 
 | 		priv->flow_sensors_start_offset = QUADRO_FLOW_SENSOR_OFFSET; | 
 |  | 
 | 		priv->temp_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET; | 
 |  | 
 | 		priv->buffer_size = QUADRO_CTRL_REPORT_SIZE; | 
 | 		priv->ctrl_report_delay = CTRL_REPORT_DELAY; | 
 |  | 
 | 		priv->flow_pulses_ctrl_offset = QUADRO_FLOW_PULSES_CTRL_OFFSET; | 
 | 		priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; | 
 |  | 
 | 		priv->temp_label = label_temp_sensors; | 
 | 		priv->virtual_temp_label = label_virtual_temp_sensors; | 
 | 		priv->speed_label = label_quadro_speeds; | 
 | 		priv->power_label = label_fan_power; | 
 | 		priv->voltage_label = label_fan_voltage; | 
 | 		priv->current_label = label_fan_current; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_HIGHFLOWNEXT: | 
 | 		priv->kind = highflownext; | 
 |  | 
 | 		priv->num_fans = 0; | 
 |  | 
 | 		priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START; | 
 | 		priv->num_flow_sensors = HIGHFLOWNEXT_NUM_FLOW_SENSORS; | 
 | 		priv->flow_sensors_start_offset = HIGHFLOWNEXT_FLOW; | 
 |  | 
 | 		priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; | 
 |  | 
 | 		priv->temp_label = label_highflownext_temp_sensors; | 
 | 		priv->speed_label = label_highflownext_fan_speed; | 
 | 		priv->power_label = label_highflownext_power; | 
 | 		priv->voltage_label = label_highflownext_voltage; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_LEAKSHIELD: | 
 | 		/* | 
 | 		 * Choose the right Leakshield device, because | 
 | 		 * the other one acts as a keyboard | 
 | 		 */ | 
 | 		if (hdev->type != 2) { | 
 | 			ret = -ENODEV; | 
 | 			goto fail_and_close; | 
 | 		} | 
 |  | 
 | 		priv->kind = leakshield; | 
 |  | 
 | 		priv->num_fans = 0; | 
 | 		priv->num_temp_sensors = LEAKSHIELD_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = LEAKSHIELD_TEMPERATURE_1; | 
 |  | 
 | 		priv->temp_label = label_leakshield_temp_sensors; | 
 | 		priv->speed_label = label_leakshield_fan_speed; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_AQUASTREAMXT: | 
 | 		priv->kind = aquastreamxt; | 
 |  | 
 | 		priv->num_fans = AQUASTREAMXT_NUM_FANS; | 
 | 		priv->fan_sensor_offsets = aquastreamxt_sensor_fan_offsets; | 
 |  | 
 | 		priv->num_temp_sensors = AQUASTREAMXT_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = AQUASTREAMXT_SENSOR_START; | 
 | 		priv->buffer_size = AQUASTREAMXT_SENSOR_REPORT_SIZE; | 
 |  | 
 | 		priv->temp_label = label_aquastreamxt_temp_sensors; | 
 | 		priv->speed_label = label_d5next_speeds; | 
 | 		priv->voltage_label = label_d5next_voltages; | 
 | 		priv->current_label = label_d5next_current; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_AQUASTREAMULT: | 
 | 		priv->kind = aquastreamult; | 
 |  | 
 | 		priv->num_fans = AQUASTREAMULT_NUM_FANS; | 
 | 		priv->fan_sensor_offsets = aquastreamult_sensor_fan_offsets; | 
 |  | 
 | 		priv->num_temp_sensors = AQUASTREAMULT_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = AQUASTREAMULT_SENSOR_START; | 
 |  | 
 | 		priv->temp_label = label_aquastreamult_temp; | 
 | 		priv->speed_label = label_aquastreamult_speeds; | 
 | 		priv->power_label = label_aquastreamult_power; | 
 | 		priv->voltage_label = label_aquastreamult_voltages; | 
 | 		priv->current_label = label_aquastreamult_current; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_POWERADJUST3: | 
 | 		priv->kind = poweradjust3; | 
 |  | 
 | 		priv->num_fans = 0; | 
 |  | 
 | 		priv->num_temp_sensors = POWERADJUST3_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = POWERADJUST3_SENSOR_START; | 
 | 		priv->buffer_size = POWERADJUST3_SENSOR_REPORT_SIZE; | 
 |  | 
 | 		priv->temp_label = label_poweradjust3_temp_sensors; | 
 | 		break; | 
 | 	case USB_PRODUCT_ID_HIGHFLOW: | 
 | 		priv->kind = highflow; | 
 |  | 
 | 		priv->num_fans = 0; | 
 |  | 
 | 		priv->num_temp_sensors = HIGHFLOW_NUM_SENSORS; | 
 | 		priv->temp_sensor_start_offset = HIGHFLOW_SENSOR_START; | 
 | 		priv->num_flow_sensors = HIGHFLOW_NUM_FLOW_SENSORS; | 
 | 		priv->flow_sensors_start_offset = HIGHFLOW_FLOW_SENSOR_OFFSET; | 
 | 		priv->buffer_size = HIGHFLOW_SENSOR_REPORT_SIZE; | 
 |  | 
 | 		priv->temp_label = label_highflow_temp; | 
 | 		priv->speed_label = label_highflow_speeds; | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	switch (priv->kind) { | 
 | 	case aquaero: | 
 | 		priv->serial_number_start_offset = AQUAERO_SERIAL_START; | 
 | 		priv->firmware_version_offset = AQUAERO_FIRMWARE_VERSION; | 
 |  | 
 | 		priv->fan_structure = &aqc_aquaero_fan_structure; | 
 |  | 
 | 		priv->ctrl_report_id = AQUAERO_CTRL_REPORT_ID; | 
 | 		priv->secondary_ctrl_report_id = AQUAERO_SECONDARY_CTRL_REPORT_ID; | 
 | 		priv->secondary_ctrl_report_size = AQUAERO_SECONDARY_CTRL_REPORT_SIZE; | 
 | 		priv->secondary_ctrl_report = aquaero_secondary_ctrl_report; | 
 | 		break; | 
 | 	case poweradjust3: | 
 | 		priv->status_report_id = POWERADJUST3_STATUS_REPORT_ID; | 
 | 		break; | 
 | 	case aquastreamxt: | 
 | 		priv->serial_number_start_offset = AQUASTREAMXT_SERIAL_START; | 
 | 		priv->firmware_version_offset = AQUASTREAMXT_FIRMWARE_VERSION; | 
 |  | 
 | 		priv->status_report_id = AQUASTREAMXT_STATUS_REPORT_ID; | 
 | 		break; | 
 | 	case highflow: | 
 | 		priv->serial_number_start_offset = HIGHFLOW_SERIAL_START; | 
 | 		priv->firmware_version_offset = HIGHFLOW_FIRMWARE_VERSION; | 
 |  | 
 | 		priv->status_report_id = HIGHFLOW_STATUS_REPORT_ID; | 
 | 		break; | 
 | 	default: | 
 | 		priv->serial_number_start_offset = AQC_SERIAL_START; | 
 | 		priv->firmware_version_offset = AQC_FIRMWARE_VERSION; | 
 |  | 
 | 		priv->ctrl_report_id = CTRL_REPORT_ID; | 
 | 		priv->secondary_ctrl_report_id = SECONDARY_CTRL_REPORT_ID; | 
 | 		priv->secondary_ctrl_report_size = SECONDARY_CTRL_REPORT_SIZE; | 
 | 		priv->secondary_ctrl_report = secondary_ctrl_report; | 
 |  | 
 | 		if (priv->kind == aquastreamult) | 
 | 			priv->fan_structure = &aqc_aquastreamult_fan_structure; | 
 | 		else | 
 | 			priv->fan_structure = &aqc_general_fan_structure; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if (priv->buffer_size != 0) { | 
 | 		priv->checksum_start = 0x01; | 
 | 		priv->checksum_length = priv->buffer_size - 3; | 
 | 		priv->checksum_offset = priv->buffer_size - 2; | 
 | 	} | 
 |  | 
 | 	priv->name = aqc_device_names[priv->kind]; | 
 |  | 
 | 	priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL); | 
 | 	if (!priv->buffer) { | 
 | 		ret = -ENOMEM; | 
 | 		goto fail_and_close; | 
 | 	} | 
 |  | 
 | 	mutex_init(&priv->mutex); | 
 |  | 
 | 	priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv, | 
 | 							  &aqc_chip_info, NULL); | 
 |  | 
 | 	if (IS_ERR(priv->hwmon_dev)) { | 
 | 		ret = PTR_ERR(priv->hwmon_dev); | 
 | 		goto fail_and_close; | 
 | 	} | 
 |  | 
 | 	aqc_debugfs_init(priv); | 
 |  | 
 | 	return 0; | 
 |  | 
 | fail_and_close: | 
 | 	hid_hw_close(hdev); | 
 | fail_and_stop: | 
 | 	hid_hw_stop(hdev); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void aqc_remove(struct hid_device *hdev) | 
 | { | 
 | 	struct aqc_data *priv = hid_get_drvdata(hdev); | 
 |  | 
 | 	debugfs_remove_recursive(priv->debugfs); | 
 | 	hwmon_device_unregister(priv->hwmon_dev); | 
 |  | 
 | 	hid_hw_close(hdev); | 
 | 	hid_hw_stop(hdev); | 
 | } | 
 |  | 
 | static const struct hid_device_id aqc_table[] = { | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUAERO) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_LEAKSHIELD) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMXT) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) }, | 
 | 	{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOW) }, | 
 | 	{ } | 
 | }; | 
 |  | 
 | MODULE_DEVICE_TABLE(hid, aqc_table); | 
 |  | 
 | static struct hid_driver aqc_driver = { | 
 | 	.name = DRIVER_NAME, | 
 | 	.id_table = aqc_table, | 
 | 	.probe = aqc_probe, | 
 | 	.remove = aqc_remove, | 
 | 	.raw_event = aqc_raw_event, | 
 | }; | 
 |  | 
 | static int __init aqc_init(void) | 
 | { | 
 | 	return hid_register_driver(&aqc_driver); | 
 | } | 
 |  | 
 | static void __exit aqc_exit(void) | 
 | { | 
 | 	hid_unregister_driver(&aqc_driver); | 
 | } | 
 |  | 
 | /* Request to initialize after the HID bus to ensure it's not being loaded before */ | 
 | late_initcall(aqc_init); | 
 | module_exit(aqc_exit); | 
 |  | 
 | MODULE_LICENSE("GPL"); | 
 | MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>"); | 
 | MODULE_AUTHOR("Jack Doan <me@jackdoan.com>"); | 
 | MODULE_DESCRIPTION("Hwmon driver for Aquacomputer devices"); |