// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Copyright (C) LibreHardwareMonitor and Contributors.
// All Rights Reserved.
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace LibreHardwareMonitor.Interop
{
internal static class NvidiaML
{
private const string LinuxDllName = "nvidia-ml";
private const string WindowsDllName = "nvml.dll";
private static readonly object SyncRoot = new object();
private static WindowsNvmlGetHandleDelegate _windowsNvmlDeviceGetHandleByIndex;
private static WindowsNvmlGetPowerUsageDelegate _windowsNvmlDeviceGetPowerUsage;
private static WindowsNvmlDeviceGetPcieThroughputDelegate _windowsNvmlDeviceGetPcieThroughputDelegate;
private static WindowsNvmlGetHandleByPciBusIdDelegate _windowsNvmlDeviceGetHandleByPciBusId;
private static WindowsNvmlDelegate _windowsNvmlInit;
private static WindowsNvmlDelegate _windowsNvmlShutdown;
private static IntPtr WindowsDll;
internal static bool IsAvailable { get; private set; }
internal static bool Initialize()
{
lock (SyncRoot)
{
if (IsAvailable)
{
return true;
}
if (Software.OperatingSystem.IsUnix)
{
try
{
IsAvailable = nvmlInit() == NvmlReturn.Success;
}
catch (DllNotFoundException)
{ }
catch (EntryPointNotFoundException)
{
try
{
IsAvailable = nvmlInitLegacy() == NvmlReturn.Success;
}
catch (EntryPointNotFoundException)
{ }
}
}
else if (IsNvmlCompatibleWindowsVersion())
{
// Attempt to load the Nvidia Management Library from the
// windows standard search order for applications. This will
// help installations that either have the library in
// %windir%/system32 or provide their own library
WindowsDll = Kernel32.LoadLibrary(WindowsDllName);
// If there is no dll in the path, then attempt to load it
// from program files
if (WindowsDll == IntPtr.Zero)
{
string programFilesDirectory = Environment.ExpandEnvironmentVariables("%ProgramW6432%");
string dllPath = Path.Combine(programFilesDirectory, @"NVIDIA Corporation\NVSMI", WindowsDllName);
WindowsDll = Kernel32.LoadLibrary(dllPath);
}
IsAvailable = (WindowsDll != IntPtr.Zero)
&& InitialiseDelegates()
&& (_windowsNvmlInit() == NvmlReturn.Success);
}
return IsAvailable;
}
}
private static bool IsNvmlCompatibleWindowsVersion()
{
return Software.OperatingSystem.Is64Bit && ((Environment.OSVersion.Version.Major > 6) || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 1));
}
private static bool InitialiseDelegates()
{
IntPtr nvmlInit = Kernel32.GetProcAddress(WindowsDll, "nvmlInit_v2");
if (nvmlInit != IntPtr.Zero)
{
_windowsNvmlInit = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlInit, typeof(WindowsNvmlDelegate));
}
else
{
nvmlInit = Kernel32.GetProcAddress(WindowsDll, "nvmlInit");
if (nvmlInit != IntPtr.Zero)
_windowsNvmlInit = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlInit, typeof(WindowsNvmlDelegate));
else
return false;
}
IntPtr nvmlShutdown = Kernel32.GetProcAddress(WindowsDll, "nvmlShutdown");
if (nvmlShutdown != IntPtr.Zero)
_windowsNvmlShutdown = (WindowsNvmlDelegate)Marshal.GetDelegateForFunctionPointer(nvmlShutdown, typeof(WindowsNvmlDelegate));
else
return false;
IntPtr nvmlGetHandle = Kernel32.GetProcAddress(WindowsDll, "nvmlDeviceGetHandleByIndex_v2");
if (nvmlGetHandle != IntPtr.Zero)
{
_windowsNvmlDeviceGetHandleByIndex = (WindowsNvmlGetHandleDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandle, typeof(WindowsNvmlGetHandleDelegate));
}
else
{
nvmlGetHandle = Kernel32.GetProcAddress(WindowsDll, "nvmlDeviceGetHandleByIndex");
if (nvmlGetHandle != IntPtr.Zero)
_windowsNvmlDeviceGetHandleByIndex = (WindowsNvmlGetHandleDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandle, typeof(WindowsNvmlGetHandleDelegate));
else
return false;
}
IntPtr nvmlGetPowerUsage = Kernel32.GetProcAddress(WindowsDll, "nvmlDeviceGetPowerUsage");
if (nvmlGetPowerUsage != IntPtr.Zero)
_windowsNvmlDeviceGetPowerUsage = (WindowsNvmlGetPowerUsageDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetPowerUsage, typeof(WindowsNvmlGetPowerUsageDelegate));
else
return false;
IntPtr nvmlGetPcieThroughput = Kernel32.GetProcAddress(WindowsDll, "nvmlDeviceGetPcieThroughput");
if (nvmlGetPcieThroughput != IntPtr.Zero)
_windowsNvmlDeviceGetPcieThroughputDelegate = (WindowsNvmlDeviceGetPcieThroughputDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetPcieThroughput, typeof(WindowsNvmlDeviceGetPcieThroughputDelegate));
else
return false;
IntPtr nvmlGetHandlePciBus = Kernel32.GetProcAddress(WindowsDll, "nvmlDeviceGetHandleByPciBusId_v2");
if (nvmlGetHandlePciBus != IntPtr.Zero)
_windowsNvmlDeviceGetHandleByPciBusId = (WindowsNvmlGetHandleByPciBusIdDelegate)Marshal.GetDelegateForFunctionPointer(nvmlGetHandlePciBus, typeof(WindowsNvmlGetHandleByPciBusIdDelegate));
else
return false;
return true;
}
internal static void Close()
{
lock (SyncRoot)
{
if (IsAvailable)
{
if (Software.OperatingSystem.IsUnix)
{
nvmlShutdown();
}
else if (WindowsDll != IntPtr.Zero)
{
_windowsNvmlShutdown();
Kernel32.FreeLibrary(WindowsDll);
}
IsAvailable = false;
}
}
}
internal static NvmlDevice? NvmlDeviceGetHandleByIndex(int index)
{
if (IsAvailable)
{
NvmlDevice nvmlDevice;
if (Software.OperatingSystem.IsUnix)
{
try
{
if (nvmlDeviceGetHandleByIndex(index, out nvmlDevice) == NvmlReturn.Success)
return nvmlDevice;
}
catch (EntryPointNotFoundException)
{
if (nvmlDeviceGetHandleByIndexLegacy(index, out nvmlDevice) == NvmlReturn.Success)
return nvmlDevice;
}
}
else if (_windowsNvmlDeviceGetHandleByIndex(index, out nvmlDevice) == NvmlReturn.Success)
{
return nvmlDevice;
}
}
return null;
}
internal static NvmlDevice? NvmlDeviceGetHandleByPciBusId(string pciBusId)
{
if (IsAvailable)
{
NvmlDevice nvmlDevice;
if (Software.OperatingSystem.IsUnix)
{
if (nvmlDeviceGetHandleByPciBusId(pciBusId, out nvmlDevice) == NvmlReturn.Success)
return nvmlDevice;
}
else if (_windowsNvmlDeviceGetHandleByPciBusId(pciBusId, out nvmlDevice) == NvmlReturn.Success)
{
return nvmlDevice;
}
}
return null;
}
internal static int? NvmlDeviceGetPowerUsage(NvmlDevice nvmlDevice)
{
if (IsAvailable)
{
int powerUsage;
if (Software.OperatingSystem.IsUnix)
{
if (nvmlDeviceGetPowerUsage(nvmlDevice, out powerUsage) == NvmlReturn.Success)
return powerUsage;
}
else if (_windowsNvmlDeviceGetPowerUsage(nvmlDevice, out powerUsage) == NvmlReturn.Success)
{
return powerUsage;
}
}
return null;
}
internal static uint? NvmlDeviceGetPcieThroughput(NvmlDevice nvmlDevice, NvmlPcieUtilCounter counter)
{
if (IsAvailable)
{
uint pcieThroughput;
if (Software.OperatingSystem.IsUnix)
{
if (nvmlDeviceGetPcieThroughput(nvmlDevice, counter, out pcieThroughput) == NvmlReturn.Success)
return pcieThroughput;
}
else if (_windowsNvmlDeviceGetPcieThroughputDelegate(nvmlDevice, counter, out pcieThroughput) == NvmlReturn.Success)
{
return pcieThroughput;
}
}
return null;
}
[DllImport(LinuxDllName, EntryPoint = "nvmlInit_v2", ExactSpelling = true)]
private static extern NvmlReturn nvmlInit();
[DllImport(LinuxDllName, EntryPoint = "nvmlInit", ExactSpelling = true)]
private static extern NvmlReturn nvmlInitLegacy();
[DllImport(LinuxDllName, EntryPoint = "nvmlShutdown", ExactSpelling = true)]
private static extern NvmlReturn nvmlShutdown();
[DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByIndex_v2", ExactSpelling = true)]
private static extern NvmlReturn nvmlDeviceGetHandleByIndex(int index, out NvmlDevice device);
[DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByPciBusId_v2", ExactSpelling = true)]
private static extern NvmlReturn nvmlDeviceGetHandleByPciBusId([MarshalAs(UnmanagedType.LPStr)] string pciBusId, out NvmlDevice device);
[DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetHandleByIndex", ExactSpelling = true)]
private static extern NvmlReturn nvmlDeviceGetHandleByIndexLegacy(int index, out NvmlDevice device);
[DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPowerUsage", ExactSpelling = true)]
private static extern NvmlReturn nvmlDeviceGetPowerUsage(NvmlDevice device, out int power);
[DllImport(LinuxDllName, EntryPoint = "nvmlDeviceGetPcieThroughput", ExactSpelling = true)]
private static extern NvmlReturn nvmlDeviceGetPcieThroughput(NvmlDevice device, NvmlPcieUtilCounter counter, out uint value);
private delegate NvmlReturn WindowsNvmlDelegate();
private delegate NvmlReturn WindowsNvmlGetHandleDelegate(int index, out NvmlDevice device);
private delegate NvmlReturn WindowsNvmlGetHandleByPciBusIdDelegate([MarshalAs(UnmanagedType.LPStr)] string pciBusId, out NvmlDevice device);
private delegate NvmlReturn WindowsNvmlGetPowerUsageDelegate(NvmlDevice device, out int power);
private delegate NvmlReturn WindowsNvmlDeviceGetPcieThroughputDelegate(NvmlDevice device, NvmlPcieUtilCounter counter, out uint value);
internal enum NvmlPcieUtilCounter
{
TxBytes = 0,
RxBytes = 1
}
[StructLayout(LayoutKind.Sequential)]
internal struct NvmlDevice
{
public IntPtr Handle;
}
internal enum NvmlReturn
{
///
/// The operation was successful
///
Success = 0,
///
/// NvidiaML was not first initialized with nvmlInit()
///
Uninitialized = 1,
///
/// A supplied argument is invalid
///
InvalidArgument = 2,
///
/// The requested operation is not available on target device
///
NotSupported = 3,
///
/// The current user does not have permission for operation
///
NoPermission = 4,
///
/// A query to find an object was unsuccessful
///
NotFound = 6,
///
/// An input argument is not large enough
///
InsufficientSize = 7,
///
/// A device's external power cables are not properly attached
///
InsufficientPower = 8,
///
/// NVIDIA driver is not loaded
///
DriverNotLoaded = 9,
///
/// User provided timeout passed
///
TimeOut = 10,
///
/// NVIDIA Kernel detected an interrupt issue with a GPU
///
IRQIssue = 11,
///
/// NvidiaML Shared Library couldn't be found or loaded
///
LibraryNotFound = 12,
///
/// Local version of NvidiaML doesn't implement this function
///
FunctionNotFound = 13,
///
/// infoROM is corrupted
///
CorruptedInfoRom = 14,
///
/// The GPU has fallen off the bus or has otherwise become inaccessible
///
GpuIsLost = 15,
///
/// The GPU requires a reset before it can be used again
///
ResetRequired = 16,
///
/// The GPU control device has been blocked by the operating system/cgroups
///
OperatingSystem = 17,
///
/// RM detects a driver/library version mismatch
///
LibRmVersionMismatch = 18,
///
/// An operation cannot be performed because the GPU is currently in use
///
InUse = 19,
///
/// An internal driver error occurred
///
Unknown = 999
}
}
}