Contributors: 5
Author Tokens Token Proportion Commits Commit Proportion
Alexander Mezin 2776 99.07% 4 50.00%
void0red 19 0.68% 1 12.50%
Herman Fries 4 0.14% 1 12.50%
Colin Ian King 2 0.07% 1 12.50%
Krzysztof Kozlowski 1 0.04% 1 12.50%
Total 2802 8


// 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 * const 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 void mutex_fini(void *lock)
{
	mutex_destroy(lock);
}

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);
	ret = devm_add_action_or_reset(&hdev->dev, mutex_fini, &drvdata->mutex);
	if (ret)
		return ret;

	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, 0x200f) }, /* 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 */
	{ HID_USB_DEVICE(0x1e71, 0x2011) }, /* NZXT RGB & Fan Controller (6 RGB) */
	{ HID_USB_DEVICE(0x1e71, 0x2019) }, /* NZXT RGB & Fan Controller (6 RGB) */
	{},
};

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);