| // SPDX-License-Identifier: GPL-2.0-or-later | 
 | /* | 
 |  * Reverse-engineered NZXT RGB & Fan Controller/Smart Device v2 driver. | 
 |  * | 
 |  * Copyright (c) 2021 Aleksandr Mezin | 
 |  */ | 
 |  | 
 | #include <linux/hid.h> | 
 | #include <linux/hwmon.h> | 
 | #include <linux/math.h> | 
 | #include <linux/module.h> | 
 | #include <linux/mutex.h> | 
 | #include <linux/spinlock.h> | 
 | #include <linux/wait.h> | 
 |  | 
 | #include <asm/byteorder.h> | 
 | #include <asm/unaligned.h> | 
 |  | 
 | /* | 
 |  * The device has only 3 fan channels/connectors. But all HID reports have | 
 |  * space reserved for up to 8 channels. | 
 |  */ | 
 | #define FAN_CHANNELS 3 | 
 | #define FAN_CHANNELS_MAX 8 | 
 |  | 
 | #define UPDATE_INTERVAL_DEFAULT_MS 1000 | 
 |  | 
 | /* These strings match labels on the device exactly */ | 
 | static const char *const fan_label[] = { | 
 | 	"FAN 1", | 
 | 	"FAN 2", | 
 | 	"FAN 3", | 
 | }; | 
 |  | 
 | static const char *const curr_label[] = { | 
 | 	"FAN 1 Current", | 
 | 	"FAN 2 Current", | 
 | 	"FAN 3 Current", | 
 | }; | 
 |  | 
 | static const char *const in_label[] = { | 
 | 	"FAN 1 Voltage", | 
 | 	"FAN 2 Voltage", | 
 | 	"FAN 3 Voltage", | 
 | }; | 
 |  | 
 | enum { | 
 | 	INPUT_REPORT_ID_FAN_CONFIG = 0x61, | 
 | 	INPUT_REPORT_ID_FAN_STATUS = 0x67, | 
 | }; | 
 |  | 
 | enum { | 
 | 	FAN_STATUS_REPORT_SPEED = 0x02, | 
 | 	FAN_STATUS_REPORT_VOLTAGE = 0x04, | 
 | }; | 
 |  | 
 | enum { | 
 | 	FAN_TYPE_NONE = 0, | 
 | 	FAN_TYPE_DC = 1, | 
 | 	FAN_TYPE_PWM = 2, | 
 | }; | 
 |  | 
 | struct unknown_static_data { | 
 | 	/* | 
 | 	 * Some configuration data? Stays the same after fan speed changes, | 
 | 	 * changes in fan configuration, reboots and driver reloads. | 
 | 	 * | 
 | 	 * The same data in multiple report types. | 
 | 	 * | 
 | 	 * Byte 12 seems to be the number of fan channels, but I am not sure. | 
 | 	 */ | 
 | 	u8 unknown1[14]; | 
 | } __packed; | 
 |  | 
 | /* | 
 |  * The device sends this input report in response to "detect fans" command: | 
 |  * a 2-byte output report { 0x60, 0x03 }. | 
 |  */ | 
 | struct fan_config_report { | 
 | 	/* report_id should be INPUT_REPORT_ID_FAN_CONFIG = 0x61 */ | 
 | 	u8 report_id; | 
 | 	/* Always 0x03 */ | 
 | 	u8 magic; | 
 | 	struct unknown_static_data unknown_data; | 
 | 	/* Fan type as detected by the device. See FAN_TYPE_* enum. */ | 
 | 	u8 fan_type[FAN_CHANNELS_MAX]; | 
 | } __packed; | 
 |  | 
 | /* | 
 |  * The device sends these reports at a fixed interval (update interval) - | 
 |  * one report with type = FAN_STATUS_REPORT_SPEED, and one report with type = | 
 |  * FAN_STATUS_REPORT_VOLTAGE per update interval. | 
 |  */ | 
 | struct fan_status_report { | 
 | 	/* report_id should be INPUT_REPORT_ID_STATUS = 0x67 */ | 
 | 	u8 report_id; | 
 | 	/* FAN_STATUS_REPORT_SPEED = 0x02 or FAN_STATUS_REPORT_VOLTAGE = 0x04 */ | 
 | 	u8 type; | 
 | 	struct unknown_static_data unknown_data; | 
 | 	/* Fan type as detected by the device. See FAN_TYPE_* enum. */ | 
 | 	u8 fan_type[FAN_CHANNELS_MAX]; | 
 |  | 
 | 	union { | 
 | 		/* When type == FAN_STATUS_REPORT_SPEED */ | 
 | 		struct { | 
 | 			/* | 
 | 			 * Fan speed, in RPM. Zero for channels without fans | 
 | 			 * connected. | 
 | 			 */ | 
 | 			__le16 fan_rpm[FAN_CHANNELS_MAX]; | 
 | 			/* | 
 | 			 * Fan duty cycle, in percent. Non-zero even for | 
 | 			 * channels without fans connected. | 
 | 			 */ | 
 | 			u8 duty_percent[FAN_CHANNELS_MAX]; | 
 | 			/* | 
 | 			 * Exactly the same values as duty_percent[], non-zero | 
 | 			 * for disconnected fans too. | 
 | 			 */ | 
 | 			u8 duty_percent_dup[FAN_CHANNELS_MAX]; | 
 | 			/* "Case Noise" in db */ | 
 | 			u8 noise_db; | 
 | 		} __packed fan_speed; | 
 | 		/* When type == FAN_STATUS_REPORT_VOLTAGE */ | 
 | 		struct { | 
 | 			/* | 
 | 			 * Voltage, in millivolts. Non-zero even when fan is | 
 | 			 * not connected. | 
 | 			 */ | 
 | 			__le16 fan_in[FAN_CHANNELS_MAX]; | 
 | 			/* | 
 | 			 * Current, in milliamperes. Near-zero when | 
 | 			 * disconnected. | 
 | 			 */ | 
 | 			__le16 fan_current[FAN_CHANNELS_MAX]; | 
 | 		} __packed fan_voltage; | 
 | 	} __packed; | 
 | } __packed; | 
 |  | 
 | #define OUTPUT_REPORT_SIZE 64 | 
 |  | 
 | enum { | 
 | 	OUTPUT_REPORT_ID_INIT_COMMAND = 0x60, | 
 | 	OUTPUT_REPORT_ID_SET_FAN_SPEED = 0x62, | 
 | }; | 
 |  | 
 | enum { | 
 | 	INIT_COMMAND_SET_UPDATE_INTERVAL = 0x02, | 
 | 	INIT_COMMAND_DETECT_FANS = 0x03, | 
 | }; | 
 |  | 
 | /* | 
 |  * This output report sets pwm duty cycle/target fan speed for one or more | 
 |  * channels. | 
 |  */ | 
 | struct set_fan_speed_report { | 
 | 	/* report_id should be OUTPUT_REPORT_ID_SET_FAN_SPEED = 0x62 */ | 
 | 	u8 report_id; | 
 | 	/* Should be 0x01 */ | 
 | 	u8 magic; | 
 | 	/* To change fan speed on i-th channel, set i-th bit here */ | 
 | 	u8 channel_bit_mask; | 
 | 	/* | 
 | 	 * Fan duty cycle/target speed in percent. For voltage-controlled fans, | 
 | 	 * the minimal voltage (duty_percent = 1) is about 9V. | 
 | 	 * Setting duty_percent to 0 (if the channel is selected in | 
 | 	 * channel_bit_mask) turns off the fan completely (regardless of the | 
 | 	 * control mode). | 
 | 	 */ | 
 | 	u8 duty_percent[FAN_CHANNELS_MAX]; | 
 | } __packed; | 
 |  | 
 | struct drvdata { | 
 | 	struct hid_device *hid; | 
 | 	struct device *hwmon; | 
 |  | 
 | 	u8 fan_duty_percent[FAN_CHANNELS]; | 
 | 	u16 fan_rpm[FAN_CHANNELS]; | 
 | 	bool pwm_status_received; | 
 |  | 
 | 	u16 fan_in[FAN_CHANNELS]; | 
 | 	u16 fan_curr[FAN_CHANNELS]; | 
 | 	bool voltage_status_received; | 
 |  | 
 | 	u8 fan_type[FAN_CHANNELS]; | 
 | 	bool fan_config_received; | 
 |  | 
 | 	/* | 
 | 	 * wq is used to wait for *_received flags to become true. | 
 | 	 * All accesses to *_received flags and fan_* arrays are performed with | 
 | 	 * wq.lock held. | 
 | 	 */ | 
 | 	wait_queue_head_t wq; | 
 | 	/* | 
 | 	 * mutex is used to: | 
 | 	 * 1) Prevent concurrent conflicting changes to update interval and pwm | 
 | 	 * values (after sending an output hid report, the corresponding field | 
 | 	 * in drvdata must be updated, and only then new output reports can be | 
 | 	 * sent). | 
 | 	 * 2) Synchronize access to output_buffer (well, the buffer is here, | 
 | 	 * because synchronization is necessary anyway - so why not get rid of | 
 | 	 * a kmalloc?). | 
 | 	 */ | 
 | 	struct mutex mutex; | 
 | 	long update_interval; | 
 | 	u8 output_buffer[OUTPUT_REPORT_SIZE]; | 
 | }; | 
 |  | 
 | static long scale_pwm_value(long val, long orig_max, long new_max) | 
 | { | 
 | 	if (val <= 0) | 
 | 		return 0; | 
 |  | 
 | 	/* | 
 | 	 * Positive values should not become zero: 0 completely turns off the | 
 | 	 * fan. | 
 | 	 */ | 
 | 	return max(1L, DIV_ROUND_CLOSEST(min(val, orig_max) * new_max, orig_max)); | 
 | } | 
 |  | 
 | static void handle_fan_config_report(struct drvdata *drvdata, void *data, int size) | 
 | { | 
 | 	struct fan_config_report *report = data; | 
 | 	int i; | 
 |  | 
 | 	if (size < sizeof(struct fan_config_report)) | 
 | 		return; | 
 |  | 
 | 	if (report->magic != 0x03) | 
 | 		return; | 
 |  | 
 | 	spin_lock(&drvdata->wq.lock); | 
 |  | 
 | 	for (i = 0; i < FAN_CHANNELS; i++) | 
 | 		drvdata->fan_type[i] = report->fan_type[i]; | 
 |  | 
 | 	drvdata->fan_config_received = true; | 
 | 	wake_up_all_locked(&drvdata->wq); | 
 | 	spin_unlock(&drvdata->wq.lock); | 
 | } | 
 |  | 
 | static void handle_fan_status_report(struct drvdata *drvdata, void *data, int size) | 
 | { | 
 | 	struct fan_status_report *report = data; | 
 | 	int i; | 
 |  | 
 | 	if (size < sizeof(struct fan_status_report)) | 
 | 		return; | 
 |  | 
 | 	spin_lock(&drvdata->wq.lock); | 
 |  | 
 | 	/* | 
 | 	 * The device sends INPUT_REPORT_ID_FAN_CONFIG = 0x61 report in response | 
 | 	 * to "detect fans" command. Only accept other data after getting 0x61, | 
 | 	 * to make sure that fan detection is complete. In particular, fan | 
 | 	 * detection resets pwm values. | 
 | 	 */ | 
 | 	if (!drvdata->fan_config_received) { | 
 | 		spin_unlock(&drvdata->wq.lock); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	for (i = 0; i < FAN_CHANNELS; i++) { | 
 | 		if (drvdata->fan_type[i] == report->fan_type[i]) | 
 | 			continue; | 
 |  | 
 | 		/* | 
 | 		 * This should not happen (if my expectations about the device | 
 | 		 * are correct). | 
 | 		 * | 
 | 		 * Even if the userspace sends fan detect command through | 
 | 		 * hidraw, fan config report should arrive first. | 
 | 		 */ | 
 | 		hid_warn_once(drvdata->hid, | 
 | 			      "Fan %d type changed unexpectedly from %d to %d", | 
 | 			      i, drvdata->fan_type[i], report->fan_type[i]); | 
 | 		drvdata->fan_type[i] = report->fan_type[i]; | 
 | 	} | 
 |  | 
 | 	switch (report->type) { | 
 | 	case FAN_STATUS_REPORT_SPEED: | 
 | 		for (i = 0; i < FAN_CHANNELS; i++) { | 
 | 			drvdata->fan_rpm[i] = | 
 | 				get_unaligned_le16(&report->fan_speed.fan_rpm[i]); | 
 | 			drvdata->fan_duty_percent[i] = | 
 | 				report->fan_speed.duty_percent[i]; | 
 | 		} | 
 |  | 
 | 		drvdata->pwm_status_received = true; | 
 | 		wake_up_all_locked(&drvdata->wq); | 
 | 		break; | 
 |  | 
 | 	case FAN_STATUS_REPORT_VOLTAGE: | 
 | 		for (i = 0; i < FAN_CHANNELS; i++) { | 
 | 			drvdata->fan_in[i] = | 
 | 				get_unaligned_le16(&report->fan_voltage.fan_in[i]); | 
 | 			drvdata->fan_curr[i] = | 
 | 				get_unaligned_le16(&report->fan_voltage.fan_current[i]); | 
 | 		} | 
 |  | 
 | 		drvdata->voltage_status_received = true; | 
 | 		wake_up_all_locked(&drvdata->wq); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	spin_unlock(&drvdata->wq.lock); | 
 | } | 
 |  | 
 | static umode_t nzxt_smart2_hwmon_is_visible(const void *data, | 
 | 					    enum hwmon_sensor_types type, | 
 | 					    u32 attr, int channel) | 
 | { | 
 | 	switch (type) { | 
 | 	case hwmon_pwm: | 
 | 		switch (attr) { | 
 | 		case hwmon_pwm_input: | 
 | 		case hwmon_pwm_enable: | 
 | 			return 0644; | 
 |  | 
 | 		default: | 
 | 			return 0444; | 
 | 		} | 
 |  | 
 | 	case hwmon_chip: | 
 | 		switch (attr) { | 
 | 		case hwmon_chip_update_interval: | 
 | 			return 0644; | 
 |  | 
 | 		default: | 
 | 			return 0444; | 
 | 		} | 
 |  | 
 | 	default: | 
 | 		return 0444; | 
 | 	} | 
 | } | 
 |  | 
 | static int nzxt_smart2_hwmon_read(struct device *dev, enum hwmon_sensor_types type, | 
 | 				  u32 attr, int channel, long *val) | 
 | { | 
 | 	struct drvdata *drvdata = dev_get_drvdata(dev); | 
 | 	int res = -EINVAL; | 
 |  | 
 | 	if (type == hwmon_chip) { | 
 | 		switch (attr) { | 
 | 		case hwmon_chip_update_interval: | 
 | 			*val = drvdata->update_interval; | 
 | 			return 0; | 
 |  | 
 | 		default: | 
 | 			return -EINVAL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	spin_lock_irq(&drvdata->wq.lock); | 
 |  | 
 | 	switch (type) { | 
 | 	case hwmon_pwm: | 
 | 		/* | 
 | 		 * fancontrol: | 
 | 		 * 1) remembers pwm* values when it starts | 
 | 		 * 2) needs pwm*_enable to be 1 on controlled fans | 
 | 		 * So make sure we have correct data before allowing pwm* reads. | 
 | 		 * Returning errors for pwm of fan speed read can even cause | 
 | 		 * fancontrol to shut down. So the wait is unavoidable. | 
 | 		 */ | 
 | 		switch (attr) { | 
 | 		case hwmon_pwm_enable: | 
 | 			res = wait_event_interruptible_locked_irq(drvdata->wq, | 
 | 								  drvdata->fan_config_received); | 
 | 			if (res) | 
 | 				goto unlock; | 
 |  | 
 | 			*val = drvdata->fan_type[channel] != FAN_TYPE_NONE; | 
 | 			break; | 
 |  | 
 | 		case hwmon_pwm_mode: | 
 | 			res = wait_event_interruptible_locked_irq(drvdata->wq, | 
 | 								  drvdata->fan_config_received); | 
 | 			if (res) | 
 | 				goto unlock; | 
 |  | 
 | 			*val = drvdata->fan_type[channel] == FAN_TYPE_PWM; | 
 | 			break; | 
 |  | 
 | 		case hwmon_pwm_input: | 
 | 			res = wait_event_interruptible_locked_irq(drvdata->wq, | 
 | 								  drvdata->pwm_status_received); | 
 | 			if (res) | 
 | 				goto unlock; | 
 |  | 
 | 			*val = scale_pwm_value(drvdata->fan_duty_percent[channel], | 
 | 					       100, 255); | 
 | 			break; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	case hwmon_fan: | 
 | 		/* | 
 | 		 * It's not strictly necessary to wait for *_received in the | 
 | 		 * remaining cases (fancontrol doesn't care about them). But I'm | 
 | 		 * doing it to have consistent behavior. | 
 | 		 */ | 
 | 		if (attr == hwmon_fan_input) { | 
 | 			res = wait_event_interruptible_locked_irq(drvdata->wq, | 
 | 								  drvdata->pwm_status_received); | 
 | 			if (res) | 
 | 				goto unlock; | 
 |  | 
 | 			*val = drvdata->fan_rpm[channel]; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	case hwmon_in: | 
 | 		if (attr == hwmon_in_input) { | 
 | 			res = wait_event_interruptible_locked_irq(drvdata->wq, | 
 | 								  drvdata->voltage_status_received); | 
 | 			if (res) | 
 | 				goto unlock; | 
 |  | 
 | 			*val = drvdata->fan_in[channel]; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	case hwmon_curr: | 
 | 		if (attr == hwmon_curr_input) { | 
 | 			res = wait_event_interruptible_locked_irq(drvdata->wq, | 
 | 								  drvdata->voltage_status_received); | 
 | 			if (res) | 
 | 				goto unlock; | 
 |  | 
 | 			*val = drvdata->fan_curr[channel]; | 
 | 		} | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | unlock: | 
 | 	spin_unlock_irq(&drvdata->wq.lock); | 
 | 	return res; | 
 | } | 
 |  | 
 | static int send_output_report(struct drvdata *drvdata, const void *data, | 
 | 			      size_t data_size) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	if (data_size > sizeof(drvdata->output_buffer)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	memcpy(drvdata->output_buffer, data, data_size); | 
 |  | 
 | 	if (data_size < sizeof(drvdata->output_buffer)) | 
 | 		memset(drvdata->output_buffer + data_size, 0, | 
 | 		       sizeof(drvdata->output_buffer) - data_size); | 
 |  | 
 | 	ret = hid_hw_output_report(drvdata->hid, drvdata->output_buffer, | 
 | 				   sizeof(drvdata->output_buffer)); | 
 | 	return ret < 0 ? ret : 0; | 
 | } | 
 |  | 
 | static int set_pwm(struct drvdata *drvdata, int channel, long val) | 
 | { | 
 | 	int ret; | 
 | 	u8 duty_percent = scale_pwm_value(val, 255, 100); | 
 |  | 
 | 	struct set_fan_speed_report report = { | 
 | 		.report_id = OUTPUT_REPORT_ID_SET_FAN_SPEED, | 
 | 		.magic = 1, | 
 | 		.channel_bit_mask = 1 << channel | 
 | 	}; | 
 |  | 
 | 	ret = mutex_lock_interruptible(&drvdata->mutex); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	report.duty_percent[channel] = duty_percent; | 
 | 	ret = send_output_report(drvdata, &report, sizeof(report)); | 
 | 	if (ret) | 
 | 		goto unlock; | 
 |  | 
 | 	/* | 
 | 	 * pwmconfig and fancontrol scripts expect pwm writes to take effect | 
 | 	 * immediately (i. e. read from pwm* sysfs should return the value | 
 | 	 * written into it). The device seems to always accept pwm values - even | 
 | 	 * when there is no fan connected - so update pwm status without waiting | 
 | 	 * for a report, to make pwmconfig and fancontrol happy. Worst case - | 
 | 	 * if the device didn't accept new pwm value for some reason (never seen | 
 | 	 * this in practice) - it will be reported incorrectly only until next | 
 | 	 * update. This avoids "fan stuck" messages from pwmconfig, and | 
 | 	 * fancontrol setting fan speed to 100% during shutdown. | 
 | 	 */ | 
 | 	spin_lock_bh(&drvdata->wq.lock); | 
 | 	drvdata->fan_duty_percent[channel] = duty_percent; | 
 | 	spin_unlock_bh(&drvdata->wq.lock); | 
 |  | 
 | unlock: | 
 | 	mutex_unlock(&drvdata->mutex); | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* | 
 |  * Workaround for fancontrol/pwmconfig trying to write to pwm*_enable even if it | 
 |  * already is 1 and read-only. Otherwise, fancontrol won't restore pwm on | 
 |  * shutdown properly. | 
 |  */ | 
 | static int set_pwm_enable(struct drvdata *drvdata, int channel, long val) | 
 | { | 
 | 	long expected_val; | 
 | 	int res; | 
 |  | 
 | 	spin_lock_irq(&drvdata->wq.lock); | 
 |  | 
 | 	res = wait_event_interruptible_locked_irq(drvdata->wq, | 
 | 						  drvdata->fan_config_received); | 
 | 	if (res) { | 
 | 		spin_unlock_irq(&drvdata->wq.lock); | 
 | 		return res; | 
 | 	} | 
 |  | 
 | 	expected_val = drvdata->fan_type[channel] != FAN_TYPE_NONE; | 
 |  | 
 | 	spin_unlock_irq(&drvdata->wq.lock); | 
 |  | 
 | 	return (val == expected_val) ? 0 : -EOPNOTSUPP; | 
 | } | 
 |  | 
 | /* | 
 |  * Control byte	| Actual update interval in seconds | 
 |  * 0xff		| 65.5 | 
 |  * 0xf7		| 63.46 | 
 |  * 0x7f		| 32.74 | 
 |  * 0x3f		| 16.36 | 
 |  * 0x1f		| 8.17 | 
 |  * 0x0f		| 4.07 | 
 |  * 0x07		| 2.02 | 
 |  * 0x03		| 1.00 | 
 |  * 0x02		| 0.744 | 
 |  * 0x01		| 0.488 | 
 |  * 0x00		| 0.25 | 
 |  */ | 
 | static u8 update_interval_to_control_byte(long interval) | 
 | { | 
 | 	if (interval <= 250) | 
 | 		return 0; | 
 |  | 
 | 	return clamp_val(1 + DIV_ROUND_CLOSEST(interval - 488, 256), 0, 255); | 
 | } | 
 |  | 
 | static long control_byte_to_update_interval(u8 control_byte) | 
 | { | 
 | 	if (control_byte == 0) | 
 | 		return 250; | 
 |  | 
 | 	return 488 + (control_byte - 1) * 256; | 
 | } | 
 |  | 
 | static int set_update_interval(struct drvdata *drvdata, long val) | 
 | { | 
 | 	u8 control = update_interval_to_control_byte(val); | 
 | 	u8 report[] = { | 
 | 		OUTPUT_REPORT_ID_INIT_COMMAND, | 
 | 		INIT_COMMAND_SET_UPDATE_INTERVAL, | 
 | 		0x01, | 
 | 		0xe8, | 
 | 		control, | 
 | 		0x01, | 
 | 		0xe8, | 
 | 		control, | 
 | 	}; | 
 | 	int ret; | 
 |  | 
 | 	ret = send_output_report(drvdata, report, sizeof(report)); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	drvdata->update_interval = control_byte_to_update_interval(control); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int init_device(struct drvdata *drvdata, long update_interval) | 
 | { | 
 | 	int ret; | 
 | 	static const u8 detect_fans_report[] = { | 
 | 		OUTPUT_REPORT_ID_INIT_COMMAND, | 
 | 		INIT_COMMAND_DETECT_FANS, | 
 | 	}; | 
 |  | 
 | 	ret = send_output_report(drvdata, detect_fans_report, | 
 | 				 sizeof(detect_fans_report)); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	return set_update_interval(drvdata, update_interval); | 
 | } | 
 |  | 
 | static int nzxt_smart2_hwmon_write(struct device *dev, | 
 | 				   enum hwmon_sensor_types type, u32 attr, | 
 | 				   int channel, long val) | 
 | { | 
 | 	struct drvdata *drvdata = dev_get_drvdata(dev); | 
 | 	int ret; | 
 |  | 
 | 	switch (type) { | 
 | 	case hwmon_pwm: | 
 | 		switch (attr) { | 
 | 		case hwmon_pwm_enable: | 
 | 			return set_pwm_enable(drvdata, channel, val); | 
 |  | 
 | 		case hwmon_pwm_input: | 
 | 			return set_pwm(drvdata, channel, val); | 
 |  | 
 | 		default: | 
 | 			return -EINVAL; | 
 | 		} | 
 |  | 
 | 	case hwmon_chip: | 
 | 		switch (attr) { | 
 | 		case hwmon_chip_update_interval: | 
 | 			ret = mutex_lock_interruptible(&drvdata->mutex); | 
 | 			if (ret) | 
 | 				return ret; | 
 |  | 
 | 			ret = set_update_interval(drvdata, val); | 
 |  | 
 | 			mutex_unlock(&drvdata->mutex); | 
 | 			return ret; | 
 |  | 
 | 		default: | 
 | 			return -EINVAL; | 
 | 		} | 
 |  | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 | } | 
 |  | 
 | static int nzxt_smart2_hwmon_read_string(struct device *dev, | 
 | 					 enum hwmon_sensor_types type, u32 attr, | 
 | 					 int channel, const char **str) | 
 | { | 
 | 	switch (type) { | 
 | 	case hwmon_fan: | 
 | 		*str = fan_label[channel]; | 
 | 		return 0; | 
 | 	case hwmon_curr: | 
 | 		*str = curr_label[channel]; | 
 | 		return 0; | 
 | 	case hwmon_in: | 
 | 		*str = in_label[channel]; | 
 | 		return 0; | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 | } | 
 |  | 
 | static const struct hwmon_ops nzxt_smart2_hwmon_ops = { | 
 | 	.is_visible = nzxt_smart2_hwmon_is_visible, | 
 | 	.read = nzxt_smart2_hwmon_read, | 
 | 	.read_string = nzxt_smart2_hwmon_read_string, | 
 | 	.write = nzxt_smart2_hwmon_write, | 
 | }; | 
 |  | 
 | static const struct hwmon_channel_info *nzxt_smart2_channel_info[] = { | 
 | 	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL, | 
 | 			   HWMON_F_INPUT | HWMON_F_LABEL), | 
 | 	HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_MODE | HWMON_PWM_ENABLE, | 
 | 			   HWMON_PWM_INPUT | HWMON_PWM_MODE | HWMON_PWM_ENABLE, | 
 | 			   HWMON_PWM_INPUT | HWMON_PWM_MODE | HWMON_PWM_ENABLE), | 
 | 	HWMON_CHANNEL_INFO(in, 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_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), | 
 | 	NULL | 
 | }; | 
 |  | 
 | static const struct hwmon_chip_info nzxt_smart2_chip_info = { | 
 | 	.ops = &nzxt_smart2_hwmon_ops, | 
 | 	.info = nzxt_smart2_channel_info, | 
 | }; | 
 |  | 
 | static int nzxt_smart2_hid_raw_event(struct hid_device *hdev, | 
 | 				     struct hid_report *report, u8 *data, int size) | 
 | { | 
 | 	struct drvdata *drvdata = hid_get_drvdata(hdev); | 
 | 	u8 report_id = *data; | 
 |  | 
 | 	switch (report_id) { | 
 | 	case INPUT_REPORT_ID_FAN_CONFIG: | 
 | 		handle_fan_config_report(drvdata, data, size); | 
 | 		break; | 
 |  | 
 | 	case INPUT_REPORT_ID_FAN_STATUS: | 
 | 		handle_fan_status_report(drvdata, data, size); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int __maybe_unused nzxt_smart2_hid_reset_resume(struct hid_device *hdev) | 
 | { | 
 | 	struct drvdata *drvdata = hid_get_drvdata(hdev); | 
 |  | 
 | 	/* | 
 | 	 * Userspace is still frozen (so no concurrent sysfs attribute access | 
 | 	 * is possible), but raw_event can already be called concurrently. | 
 | 	 */ | 
 | 	spin_lock_bh(&drvdata->wq.lock); | 
 | 	drvdata->fan_config_received = false; | 
 | 	drvdata->pwm_status_received = false; | 
 | 	drvdata->voltage_status_received = false; | 
 | 	spin_unlock_bh(&drvdata->wq.lock); | 
 |  | 
 | 	return init_device(drvdata, drvdata->update_interval); | 
 | } | 
 |  | 
 | static int nzxt_smart2_hid_probe(struct hid_device *hdev, | 
 | 				 const struct hid_device_id *id) | 
 | { | 
 | 	struct drvdata *drvdata; | 
 | 	int ret; | 
 |  | 
 | 	drvdata = devm_kzalloc(&hdev->dev, sizeof(struct drvdata), GFP_KERNEL); | 
 | 	if (!drvdata) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	drvdata->hid = hdev; | 
 | 	hid_set_drvdata(hdev, drvdata); | 
 |  | 
 | 	init_waitqueue_head(&drvdata->wq); | 
 |  | 
 | 	mutex_init(&drvdata->mutex); | 
 | 	devm_add_action(&hdev->dev, (void (*)(void *))mutex_destroy, | 
 | 			&drvdata->mutex); | 
 |  | 
 | 	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 out_hw_stop; | 
 |  | 
 | 	hid_device_io_start(hdev); | 
 |  | 
 | 	init_device(drvdata, UPDATE_INTERVAL_DEFAULT_MS); | 
 |  | 
 | 	drvdata->hwmon = | 
 | 		hwmon_device_register_with_info(&hdev->dev, "nzxtsmart2", drvdata, | 
 | 						&nzxt_smart2_chip_info, NULL); | 
 | 	if (IS_ERR(drvdata->hwmon)) { | 
 | 		ret = PTR_ERR(drvdata->hwmon); | 
 | 		goto out_hw_close; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | out_hw_close: | 
 | 	hid_hw_close(hdev); | 
 |  | 
 | out_hw_stop: | 
 | 	hid_hw_stop(hdev); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void nzxt_smart2_hid_remove(struct hid_device *hdev) | 
 | { | 
 | 	struct drvdata *drvdata = hid_get_drvdata(hdev); | 
 |  | 
 | 	hwmon_device_unregister(drvdata->hwmon); | 
 |  | 
 | 	hid_hw_close(hdev); | 
 | 	hid_hw_stop(hdev); | 
 | } | 
 |  | 
 | static const struct hid_device_id nzxt_smart2_hid_id_table[] = { | 
 | 	{ HID_USB_DEVICE(0x1e71, 0x2006) }, /* NZXT Smart Device V2 */ | 
 | 	{ HID_USB_DEVICE(0x1e71, 0x200d) }, /* NZXT Smart Device V2 */ | 
 | 	{ HID_USB_DEVICE(0x1e71, 0x2009) }, /* NZXT RGB & Fan Controller */ | 
 | 	{ HID_USB_DEVICE(0x1e71, 0x200e) }, /* NZXT RGB & Fan Controller */ | 
 | 	{ HID_USB_DEVICE(0x1e71, 0x2010) }, /* NZXT RGB & Fan Controller */ | 
 | 	{}, | 
 | }; | 
 |  | 
 | static struct hid_driver nzxt_smart2_hid_driver = { | 
 | 	.name = "nzxt-smart2", | 
 | 	.id_table = nzxt_smart2_hid_id_table, | 
 | 	.probe = nzxt_smart2_hid_probe, | 
 | 	.remove = nzxt_smart2_hid_remove, | 
 | 	.raw_event = nzxt_smart2_hid_raw_event, | 
 | #ifdef CONFIG_PM | 
 | 	.reset_resume = nzxt_smart2_hid_reset_resume, | 
 | #endif | 
 | }; | 
 |  | 
 | static int __init nzxt_smart2_init(void) | 
 | { | 
 | 	return hid_register_driver(&nzxt_smart2_hid_driver); | 
 | } | 
 |  | 
 | static void __exit nzxt_smart2_exit(void) | 
 | { | 
 | 	hid_unregister_driver(&nzxt_smart2_hid_driver); | 
 | } | 
 |  | 
 | MODULE_DEVICE_TABLE(hid, nzxt_smart2_hid_id_table); | 
 | MODULE_AUTHOR("Aleksandr Mezin <mezin.alexander@gmail.com>"); | 
 | MODULE_DESCRIPTION("Driver for NZXT RGB & Fan Controller/Smart Device V2"); | 
 | MODULE_LICENSE("GPL"); | 
 |  | 
 | /* | 
 |  * With module_init()/module_hid_driver() and the driver built into the kernel: | 
 |  * | 
 |  * Driver 'nzxt_smart2' was unable to register with bus_type 'hid' because the | 
 |  * bus was not initialized. | 
 |  */ | 
 | late_initcall(nzxt_smart2_init); | 
 | module_exit(nzxt_smart2_exit); |