2021-10-31 06:30:58 +03:00
// 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 9 V .
* 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 ;
2022-01-09 22:45:58 +03:00
static const u8 detect_fans_report [ ] = {
2021-10-31 06:30:58 +03:00
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 ,
} ;
2023-04-06 23:35:29 +03:00
static const struct hwmon_channel_info * const nzxt_smart2_channel_info [ ] = {
2021-10-31 06:30:58 +03:00
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 ;
}
2021-12-28 04:48:13 +03:00
static int __maybe_unused nzxt_smart2_hid_reset_resume ( struct hid_device * hdev )
2021-10-31 06:30:58 +03:00
{
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 ) ;
}
2023-02-27 12:15:34 +03:00
static void mutex_fini ( void * lock )
{
mutex_destroy ( lock ) ;
}
2021-10-31 06:30:58 +03:00
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 ) ;
2023-02-27 12:15:34 +03:00
ret = devm_add_action_or_reset ( & hdev - > dev , mutex_fini , & drvdata - > mutex ) ;
if ( ret )
return ret ;
2021-10-31 06:30:58 +03:00
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 */
2022-09-18 14:55:06 +03:00
{ HID_USB_DEVICE ( 0x1e71 , 0x200f ) } , /* NZXT Smart Device V2 */
2021-10-31 06:30:58 +03:00
{ 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 */
2023-02-19 13:59:19 +03:00
{ HID_USB_DEVICE ( 0x1e71 , 0x2011 ) } , /* NZXT RGB & Fan Controller (6 RGB) */
{ HID_USB_DEVICE ( 0x1e71 , 0x2019 ) } , /* NZXT RGB & Fan Controller (6 RGB) */
2021-10-31 06:30:58 +03:00
{ } ,
} ;
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 ) ;