123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832 |
- // 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, 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 <[email protected]>");
- 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);
|