Added support for XNVCTRL on Linux to add support for --gpu-temp-retain for NVidia GPU
authorjsteube <jens.steube@gmail.com>
Sat, 18 Jun 2016 08:59:58 +0000 (10:59 +0200)
committerjsteube <jens.steube@gmail.com>
Sat, 18 Jun 2016 08:59:58 +0000 (10:59 +0200)
docs/changes.txt
include/common.h
include/ext_xnvctrl.h [new file with mode: 0644]
include/shared.h
include/types.h
src/Makefile
src/ext_xnvctrl.c [new file with mode: 0644]
src/hashcat.c
src/shared.c

index 068593a..a842ae1 100644 (file)
@@ -137,3 +137,4 @@ It combines all features of all hashcat projects in one project.
 - Disabled retain support by default, you can reactive it using --gpu-temp-retain
 - Completely get rid of HAVE_ADL, HAVE_NVML and HAVE_NVAPI in sources
 - Replaced NVAPI with NVML on windows
+- Added support for XNVCTRL on Linux to add support for --gpu-temp-retain for NVidia GPU
index 52562cb..be7ea98 100644 (file)
@@ -56,6 +56,7 @@ typedef void *OCL_LIB;
 typedef void *ADL_LIB;
 typedef void *NVAPI_LIB;
 typedef void *NVML_LIB;
+typedef void *XNVCTRL_LIB;
 #ifdef OSX
 #define __stdcall
 #endif
@@ -90,6 +91,7 @@ typedef HINSTANCE OCL_LIB;
 typedef HINSTANCE ADL_LIB;
 typedef HINSTANCE NVAPI_LIB;
 typedef HINSTANCE NVML_LIB;
+typedef HINSTANCE XNVCTRL_LIB;
 #endif
 
 #define mkdir(name,mode) mkdir (name)
diff --git a/include/ext_xnvctrl.h b/include/ext_xnvctrl.h
new file mode 100644 (file)
index 0000000..f608555
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * Authors.....: Jens Steube <jens.steube@gmail.com>
+ * License.....: MIT
+ */
+
+#ifndef EXT_XNVCTRL_H
+#define EXT_XNVCTRL_H
+
+#if defined(HAVE_HWMON)
+
+#include <common.h>
+
+/**
+ * Stuff from X11/Xlib.h
+ */
+
+typedef void *(*XOPENDISPLAY)  (char *);
+typedef int   (*XCLOSEDISPLAY) (void *);
+
+/**
+ * Declarations from NVCtrl.h
+ */
+
+#define NV_CTRL_TARGET_TYPE_GPU            1
+#define NV_CTRL_TARGET_TYPE_COOLER         5 /* e.g., fan */
+
+#define NV_CTRL_GPU_COOLER_MANUAL_CONTROL                       319 /* RW-G */
+#define NV_CTRL_GPU_COOLER_MANUAL_CONTROL_FALSE                   0
+#define NV_CTRL_GPU_COOLER_MANUAL_CONTROL_TRUE                    1
+
+#define NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL                    417 /* R--C */
+#define NV_CTRL_THERMAL_COOLER_LEVEL                            320 /* RW-C */
+
+/*
+ * NV_CTRL_GPU_CORE_THRESHOLD reflects the temperature at which the
+ * GPU is throttled to prevent overheating.
+ */
+
+#define NV_CTRL_GPU_CORE_THRESHOLD                              61  /* R--G */
+
+/**
+ * hashcat stuff from here
+ */
+
+typedef int HM_ADAPTER_XNVCTRL;
+
+#include <shared.h>
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+#define XNVCTRL_API_CALL __stdcall
+#else
+#define XNVCTRL_API_CALL
+#endif
+
+typedef int  (*XNVCTRL_API_CALL XNVCTRLQUERYTARGETATTRIBUTE) (void *, int, int, unsigned int, unsigned int, int *);
+typedef void (*XNVCTRL_API_CALL XNVCTRLSETTARGETATTRIBUTE)   (void *, int, int, unsigned int, unsigned int, int);
+
+typedef struct
+{
+  void *dpy;
+
+  XNVCTRL_LIB lib_x11;
+  XNVCTRL_LIB lib_xnvctrl;
+
+  XOPENDISPLAY  XOpenDisplay;
+  XCLOSEDISPLAY XCloseDisplay;
+
+  XNVCTRLQUERYTARGETATTRIBUTE XNVCTRLQueryTargetAttribute;
+  XNVCTRLSETTARGETATTRIBUTE   XNVCTRLSetTargetAttribute;
+
+} hm_xnvctrl_lib_t;
+
+#define XNVCTRL_PTR hm_xnvctrl_lib_t
+
+int  xnvctrl_init         (XNVCTRL_PTR *xnvctrl);
+void xnvctrl_close        (XNVCTRL_PTR *xnvctrl);
+
+int  hm_XNVCTRL_XOpenDisplay  (XNVCTRL_PTR *xnvctrl);
+void hm_XNVCTRL_XCloseDisplay (XNVCTRL_PTR *xnvctrl);
+
+int get_core_threshold    (XNVCTRL_PTR *xnvctrl, int gpu, int *val);
+
+int get_fan_control       (XNVCTRL_PTR *xnvctrl, int gpu, int *val);
+int set_fan_control       (XNVCTRL_PTR *xnvctrl, int gpu, int  val);
+
+int get_fan_speed_current (XNVCTRL_PTR *xnvctrl, int gpu, int *val);
+int get_fan_speed_target  (XNVCTRL_PTR *xnvctrl, int gpu, int *val);
+int set_fan_speed_target  (XNVCTRL_PTR *xnvctrl, int gpu, int  val);
+
+#endif // HAVE_HWMON
+
+#endif // EXT_XNVCTRL_H
index 0203117..34824d3 100644 (file)
@@ -117,6 +117,7 @@ static inline int  CPU_ISSET (int num, cpu_set_t *cs) { return (cs->count & (1 <
 #include "ext_ADL.h"
 #include "ext_nvapi.h"
 #include "ext_nvml.h"
+#include "ext_xnvctrl.h"
 
 /**
  * shared stuff
@@ -1464,6 +1465,7 @@ int hm_get_memoryspeed_with_device_id        (const uint device_id);
 int hm_get_corespeed_with_device_id          (const uint device_id);
 int hm_get_throttle_with_device_id           (const uint device_id);
 int hm_set_fanspeed_with_device_id_adl       (const uint device_id, const int fanspeed, const int fanpolicy);
+int hm_set_fanspeed_with_device_id_xnvctrl   (const uint device_id, const int fanspeed);
 
 void hm_device_val_to_str (char *target_buf, int max_buf_size, char *suffix, int value);
 #endif // HAVE_HWMON
index c8f8bf6..8578347 100644 (file)
@@ -1099,9 +1099,10 @@ typedef struct __hc_device_param hc_device_param_t;
 #ifdef HAVE_HWMON
 typedef struct
 {
-  HM_ADAPTER_ADL   adl;
-  HM_ADAPTER_NVML  nvml;
-  HM_ADAPTER_NVAPI nvapi;
+  HM_ADAPTER_ADL     adl;
+  HM_ADAPTER_NVML    nvml;
+  HM_ADAPTER_NVAPI   nvapi;
+  HM_ADAPTER_XNVCTRL xnvctrl;
 
   int od_version;
 
@@ -1177,6 +1178,7 @@ typedef struct
   void      *hm_adl;
   void      *hm_nvml;
   void      *hm_nvapi;
+  void      *hm_xnvctrl;
   hm_attrs_t hm_device[DEVICES_MAX];
   #endif
 
index 182bdfa..cab2beb 100644 (file)
@@ -156,17 +156,18 @@ ifeq ($(UNAME),Linux)
 NATIVE_OBJS              += obj/ext_ADL.NATIVE.o
 NATIVE_OBJS              += obj/ext_nvapi.NATIVE.o
 NATIVE_OBJS              += obj/ext_nvml.NATIVE.o
+NATIVE_OBJS              += obj/ext_xnvctrl.NATIVE.o
 endif
 
-LINUX_32_OBJS            := obj/ext_OpenCL.LINUX.32.o obj/shared.LINUX.32.o obj/rp_kernel_on_cpu.LINUX.32.o obj/ext_ADL.LINUX.32.o obj/ext_nvml.LINUX.32.o obj/ext_nvapi.LINUX.32.o
-LINUX_64_OBJS            := obj/ext_OpenCL.LINUX.64.o obj/shared.LINUX.64.o obj/rp_kernel_on_cpu.LINUX.64.o obj/ext_ADL.LINUX.64.o obj/ext_nvml.LINUX.64.o obj/ext_nvapi.LINUX.64.o
+LINUX_32_OBJS            := obj/ext_OpenCL.LINUX.32.o obj/shared.LINUX.32.o obj/rp_kernel_on_cpu.LINUX.32.o obj/ext_ADL.LINUX.32.o obj/ext_nvml.LINUX.32.o obj/ext_nvapi.LINUX.32.o obj/ext_xnvctrl.LINUX.32.o
+LINUX_64_OBJS            := obj/ext_OpenCL.LINUX.64.o obj/shared.LINUX.64.o obj/rp_kernel_on_cpu.LINUX.64.o obj/ext_ADL.LINUX.64.o obj/ext_nvml.LINUX.64.o obj/ext_nvapi.LINUX.64.o obj/ext_xnvctrl.LINUX.64.o
 
 ## may need to adjust according to your mingw distribution
 CRT_GLOB_32              := /usr/i686-w64-mingw32/lib/CRT_glob.o
 CRT_GLOB_64              := /usr/x86_64-w64-mingw32/lib/CRT_glob.o
 
-WIN_32_OBJS              := obj/ext_OpenCL.WIN.32.o   obj/shared.WIN.32.o   obj/rp_kernel_on_cpu.WIN.32.o   obj/ext_ADL.WIN.32.o   obj/ext_nvml.WIN.32.o   obj/ext_nvapi.WIN.32.o   $(CRT_GLOB_32)
-WIN_64_OBJS              := obj/ext_OpenCL.WIN.64.o   obj/shared.WIN.64.o   obj/rp_kernel_on_cpu.WIN.64.o   obj/ext_ADL.WIN.64.o   obj/ext_nvml.WIN.64.o   obj/ext_nvapi.WIN.64.o   $(CRT_GLOB_64)
+WIN_32_OBJS              := obj/ext_OpenCL.WIN.32.o   obj/shared.WIN.32.o   obj/rp_kernel_on_cpu.WIN.32.o   obj/ext_ADL.WIN.32.o   obj/ext_nvml.WIN.32.o   obj/ext_nvapi.WIN.32.o   obj/ext_xnvctrl.WIN.32.o   $(CRT_GLOB_32)
+WIN_64_OBJS              := obj/ext_OpenCL.WIN.64.o   obj/shared.WIN.64.o   obj/rp_kernel_on_cpu.WIN.64.o   obj/ext_ADL.WIN.64.o   obj/ext_nvml.WIN.64.o   obj/ext_nvapi.WIN.64.o   obj/ext_xnvctrl.WIN.64.o   $(CRT_GLOB_64)
 
 ##
 ## Targets: Global
diff --git a/src/ext_xnvctrl.c b/src/ext_xnvctrl.c
new file mode 100644 (file)
index 0000000..7c97e79
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * Authors.....: Jens Steube <jens.steube@gmail.com>
+ * License.....: MIT
+ */
+
+#include <ext_xnvctrl.h>
+
+int xnvctrl_init (XNVCTRL_PTR *xnvctrl)
+{
+  if (!xnvctrl) return (-1);
+
+  memset (xnvctrl, 0, sizeof (XNVCTRL_PTR));
+
+  #ifdef _WIN
+
+  // unsupport platform?
+  return (-1);
+
+  #elif _POSIX
+
+  xnvctrl->lib_x11 = dlopen ("libX11.so", RTLD_LAZY);
+
+  if (xnvctrl->lib_x11 == NULL)
+  {
+    if (data.quiet == 0) log_info ("WARNING: load X11 library failed, proceed without X11 HWMon enabled.");
+
+    return -1;
+  }
+
+  xnvctrl->lib_xnvctrl = dlopen ("libXNVCtrl.so", RTLD_LAZY);
+
+  if (xnvctrl->lib_xnvctrl == NULL)
+  {
+    xnvctrl->lib_xnvctrl = dlopen ("libXNVCtrl.so.0", RTLD_LAZY);
+
+    if (xnvctrl->lib_xnvctrl == NULL)
+    {
+      if (data.quiet == 0) log_info ("WARNING: load XNVCTRL library failed, proceed without XNVCTRL HWMon enabled.");
+
+      return -1;
+    }
+  }
+
+  xnvctrl->XOpenDisplay  = dlsym (xnvctrl->lib_x11, "XOpenDisplay");
+  xnvctrl->XCloseDisplay = dlsym (xnvctrl->lib_x11, "XCloseDisplay");
+
+  xnvctrl->XNVCTRLQueryTargetAttribute = dlsym (xnvctrl->lib_xnvctrl, "XNVCTRLQueryTargetAttribute");
+  xnvctrl->XNVCTRLSetTargetAttribute   = dlsym (xnvctrl->lib_xnvctrl, "XNVCTRLSetTargetAttribute");
+
+  #endif
+
+  // not using HC_LOAD_FUNC() here, because we're using 2 libraries and therefore have 2 different variable names for them
+
+  return 0;
+}
+
+void xnvctrl_close (XNVCTRL_PTR *xnvctrl)
+{
+  if (xnvctrl)
+  {
+    #if _POSIX
+
+    if (xnvctrl->lib_x11)
+    {
+      dlclose (xnvctrl->lib_x11);
+    }
+
+    if (xnvctrl->lib_xnvctrl)
+    {
+      dlclose (xnvctrl->lib_xnvctrl);
+    }
+
+    #endif
+
+    myfree (xnvctrl);
+  }
+}
+
+int hm_XNVCTRL_XOpenDisplay (XNVCTRL_PTR *xnvctrl)
+{
+  void *dpy = xnvctrl->XOpenDisplay (NULL);
+
+  if (dpy == NULL)
+  {
+    return -1;
+  }
+
+  xnvctrl->dpy = dpy;
+
+  return 0;
+}
+
+void hm_XNVCTRL_XCloseDisplay (XNVCTRL_PTR *xnvctrl)
+{
+  xnvctrl->XCloseDisplay (xnvctrl->dpy);
+}
+
+int get_fan_control (XNVCTRL_PTR *xnvctrl, int gpu, int *val)
+{
+  int rc = xnvctrl->XNVCTRLQueryTargetAttribute (xnvctrl->dpy, NV_CTRL_TARGET_TYPE_GPU, gpu, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL, val);
+
+  if (!rc) return -1;
+
+  return 0;
+}
+
+int set_fan_control (XNVCTRL_PTR *xnvctrl, int gpu, int val)
+{
+  xnvctrl->XNVCTRLSetTargetAttribute (xnvctrl->dpy, NV_CTRL_TARGET_TYPE_GPU, gpu, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL, val);
+
+  int cur;
+
+  int rc = get_fan_control (xnvctrl, gpu, &cur);
+
+  if (rc == -1) return -1;
+
+  if (cur != val) return -1;
+
+  return 0;
+}
+
+int get_core_threshold (XNVCTRL_PTR *xnvctrl, int gpu, int *val)
+{
+  int rc = xnvctrl->XNVCTRLQueryTargetAttribute (xnvctrl->dpy, NV_CTRL_TARGET_TYPE_GPU, gpu, 0, NV_CTRL_GPU_CORE_THRESHOLD, val);
+
+  if (!rc) return -1;
+
+  return 0;
+}
+
+int get_fan_speed_current (XNVCTRL_PTR *xnvctrl, int gpu, int *val)
+{
+  int rc = xnvctrl->XNVCTRLQueryTargetAttribute (xnvctrl->dpy, NV_CTRL_TARGET_TYPE_COOLER, gpu, 0, NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL, val);
+
+  if (!rc) return -1;
+
+  return 0;
+}
+
+int get_fan_speed_target (XNVCTRL_PTR *xnvctrl, int gpu, int *val)
+{
+  int rc = xnvctrl->XNVCTRLQueryTargetAttribute (xnvctrl->dpy, NV_CTRL_TARGET_TYPE_COOLER, gpu, 0, NV_CTRL_THERMAL_COOLER_LEVEL, val);
+
+  if (!rc) return -1;
+
+  return 0;
+}
+
+int set_fan_speed_target (XNVCTRL_PTR *xnvctrl, int gpu, int val)
+{
+  xnvctrl->XNVCTRLSetTargetAttribute (xnvctrl->dpy, NV_CTRL_TARGET_TYPE_COOLER, gpu, 0, NV_CTRL_THERMAL_COOLER_LEVEL, val);
+
+  int cur;
+
+  int rc = get_fan_speed_target (xnvctrl, gpu, &cur);
+
+  if (rc == -1) return -1;
+
+  if (cur != val) return -1;
+
+  return 0;
+}
index 0619fce..dec2065 100644 (file)
@@ -4235,7 +4235,9 @@ static void *thread_monitor (void *p)
                   }
                   else if (device_param->device_vendor_id == VENDOR_ID_NV)
                   {
-
+                    #ifdef _POSIX
+                    hm_set_fanspeed_with_device_id_xnvctrl (device_id, fan_speed_new);
+                    #endif
                   }
 
                   fan_speed_chgd[device_id] = 1;
@@ -13651,9 +13653,10 @@ int main (int argc, char **argv)
      * OpenCL devices: simply push all devices from all platforms into the same device array
      */
 
-    int need_adl   = 0;
-    int need_nvapi = 0;
-    int need_nvml  = 0;
+    int need_adl     = 0;
+    int need_nvapi   = 0;
+    int need_nvml    = 0;
+    int need_xnvctrl = 0;
 
     hc_device_param_t *devices_param = (hc_device_param_t *) mycalloc (DEVICES_MAX, sizeof (hc_device_param_t));
 
@@ -14063,6 +14066,10 @@ int main (int argc, char **argv)
           {
             need_nvml = 1;
 
+            #ifdef _POSIX
+            need_xnvctrl = 1;
+            #endif
+
             #ifdef _WIN
             need_nvapi = 1;
             #endif
@@ -14363,19 +14370,22 @@ int main (int argc, char **argv)
      */
 
     #ifdef HAVE_HWMON
-    hm_attrs_t hm_adapters_adl[DEVICES_MAX]   = { { 0 } };
-    hm_attrs_t hm_adapters_nvapi[DEVICES_MAX] = { { 0 } };
-    hm_attrs_t hm_adapters_nvml[DEVICES_MAX]  = { { 0 } };
+    hm_attrs_t hm_adapters_adl[DEVICES_MAX]     = { { 0 } };
+    hm_attrs_t hm_adapters_nvapi[DEVICES_MAX]   = { { 0 } };
+    hm_attrs_t hm_adapters_nvml[DEVICES_MAX]    = { { 0 } };
+    hm_attrs_t hm_adapters_xnvctrl[DEVICES_MAX] = { { 0 } };
 
     if (gpu_temp_disable == 0)
     {
-      ADL_PTR   *adl   = (ADL_PTR *)   mymalloc (sizeof (ADL_PTR));
-      NVAPI_PTR *nvapi = (NVAPI_PTR *) mymalloc (sizeof (NVAPI_PTR));
-      NVML_PTR  *nvml  = (NVML_PTR *)  mymalloc (sizeof (NVML_PTR));
+      ADL_PTR     *adl     = (ADL_PTR *)     mymalloc (sizeof (ADL_PTR));
+      NVAPI_PTR   *nvapi   = (NVAPI_PTR *)   mymalloc (sizeof (NVAPI_PTR));
+      NVML_PTR    *nvml    = (NVML_PTR *)    mymalloc (sizeof (NVML_PTR));
+      XNVCTRL_PTR *xnvctrl = (XNVCTRL_PTR *) mymalloc (sizeof (XNVCTRL_PTR));
 
-      data.hm_adl   = NULL;
-      data.hm_nvapi = NULL;
-      data.hm_nvml  = NULL;
+      data.hm_adl     = NULL;
+      data.hm_nvapi   = NULL;
+      data.hm_nvml    = NULL;
+      data.hm_xnvctrl = NULL;
 
       if ((need_nvml == 1) && (nvml_init (nvml) == 0))
       {
@@ -14432,6 +14442,30 @@ int main (int argc, char **argv)
         }
       }
 
+      if ((need_xnvctrl == 1) && (xnvctrl_init (xnvctrl) == 0))
+      {
+        data.hm_xnvctrl = xnvctrl;
+      }
+
+      if (data.hm_xnvctrl)
+      {
+        if (hm_XNVCTRL_XOpenDisplay (data.hm_xnvctrl) == 0)
+        {
+          for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
+          {
+            hc_device_param_t *device_param = &data.devices_param[device_id];
+
+            if ((device_param->device_type & CL_DEVICE_TYPE_GPU) == 0) continue;
+
+            hm_adapters_xnvctrl[device_id].xnvctrl = device_id;
+
+            int speed = 0;
+
+            if (get_fan_speed_current (data.hm_xnvctrl, device_id, &speed) == 0) hm_adapters_xnvctrl[device_id].fan_get_supported = 1;
+          }
+        }
+      }
+
       if ((need_adl == 1) && (adl_init (adl) == 0))
       {
         data.hm_adl = adl;
@@ -14478,7 +14512,7 @@ int main (int argc, char **argv)
         }
       }
 
-      if (data.hm_adl == NULL && data.hm_nvml == NULL)
+      if (data.hm_adl == NULL && data.hm_nvml == NULL && data.hm_xnvctrl)
       {
         gpu_temp_disable = 1;
       }
@@ -14567,7 +14601,7 @@ int main (int argc, char **argv)
        */
 
       #ifdef HAVE_HWMON
-      if (gpu_temp_disable == 0 && data.hm_adl == NULL && data.hm_nvml == NULL)
+      if (gpu_temp_disable == 0 && data.hm_adl == NULL && data.hm_nvml == NULL && data.hm_xnvctrl == NULL)
       {
         log_info ("Watchdog: Hardware Monitoring Interface not found on your system");
       }
@@ -14617,6 +14651,7 @@ int main (int argc, char **argv)
           data.hm_device[device_id].adl               = hm_adapters_adl[platform_devices_id].adl;
           data.hm_device[device_id].nvapi             = 0;
           data.hm_device[device_id].nvml              = 0;
+          data.hm_device[device_id].xnvctrl           = 0;
           data.hm_device[device_id].od_version        = hm_adapters_adl[platform_devices_id].od_version;
           data.hm_device[device_id].fan_get_supported = hm_adapters_adl[platform_devices_id].fan_get_supported;
           data.hm_device[device_id].fan_set_supported = hm_adapters_adl[platform_devices_id].fan_set_supported;
@@ -14627,6 +14662,7 @@ int main (int argc, char **argv)
           data.hm_device[device_id].adl               = 0;
           data.hm_device[device_id].nvapi             = hm_adapters_nvapi[platform_devices_id].nvapi;
           data.hm_device[device_id].nvml              = hm_adapters_nvml[platform_devices_id].nvml;
+          data.hm_device[device_id].xnvctrl           = hm_adapters_xnvctrl[platform_devices_id].xnvctrl;
           data.hm_device[device_id].od_version        = 0;
           data.hm_device[device_id].fan_get_supported = hm_adapters_nvml[platform_devices_id].fan_get_supported;
           data.hm_device[device_id].fan_set_supported = 0;
@@ -16144,7 +16180,9 @@ int main (int argc, char **argv)
               }
               else if (device_param->device_vendor_id == VENDOR_ID_NV)
               {
-
+                #ifdef _POSIX
+                rc = hm_set_fanspeed_with_device_id_xnvctrl (device_id, fanspeed);
+                #endif
               }
 
               if (rc == 0)
@@ -18344,7 +18382,9 @@ int main (int argc, char **argv)
               }
               else if (device_param->device_vendor_id == VENDOR_ID_NV)
               {
-
+                #ifdef _POSIX
+                rc = set_fan_control (data.hm_xnvctrl, data.hm_device[device_id].xnvctrl, NV_CTRL_GPU_COOLER_MANUAL_CONTROL_FALSE);
+                #endif
               }
 
               if (rc == -1) log_info ("WARNING: Failed to restore default fan speed and policy for device #%", device_id + 1);
@@ -18442,6 +18482,24 @@ int main (int argc, char **argv)
         data.hm_nvml = NULL;
       }
 
+      if (data.hm_nvapi)
+      {
+        hm_NvAPI_Unload (data.hm_nvapi);
+
+        nvapi_close (data.hm_nvapi);
+
+        data.hm_nvapi = NULL;
+      }
+
+      if (data.hm_xnvctrl)
+      {
+        hm_XNVCTRL_XCloseDisplay (data.hm_xnvctrl);
+
+        xnvctrl_close (data.hm_xnvctrl);
+
+        data.hm_xnvctrl = NULL;
+      }
+
       if (data.hm_adl)
       {
         hm_ADL_Main_Control_Destroy (data.hm_adl);
index ac99216..43aded4 100644 (file)
@@ -3195,7 +3195,12 @@ int hm_get_fanpolicy_with_device_id (const uint device_id)
     if (data.devices_param[device_id].device_vendor_id == VENDOR_ID_NV)
     {
       #if defined(LINUX)
-      return 0;
+      if (data.hm_xnvctrl)
+      {
+        if (set_fan_control (data.hm_xnvctrl, data.hm_device[device_id].xnvctrl, NV_CTRL_GPU_COOLER_MANUAL_CONTROL_TRUE) != 0) return -1;
+
+        return 1;
+      }
       #endif
 
       #if defined(WIN)
@@ -3404,6 +3409,21 @@ int hm_get_throttle_with_device_id (const uint device_id)
   return -1;
 }
 
+int hm_set_fanspeed_with_device_id_xnvctrl (const uint device_id, const int fanspeed)
+{
+  if (data.hm_device[device_id].fan_set_supported == 1)
+  {
+    if (data.hm_xnvctrl)
+    {
+      if (set_fan_speed_target (data.hm_xnvctrl, data.hm_device[device_id].xnvctrl, fanspeed) != 0) return -1;
+
+      return 0;
+    }
+  }
+
+  return -1;
+}
+
 int hm_set_fanspeed_with_device_id_adl (const uint device_id, const int fanspeed, const int fanpolicy)
 {
   if (data.hm_device[device_id].fan_set_supported == 1)