diff --git a/CMakeLists.txt b/CMakeLists.txt
index 638db439a6..02d69f9d6c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -145,6 +145,7 @@ createBuildInfo()
 
 add_subdirectory(libdevcore)
 add_subdirectory(libethash)
+add_subdirectory(libhwmon)
 if (ETHASHCL)
 	add_subdirectory(libethash-cl)
 endif ()
diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h
index e525a86842..0cb97a7417 100644
--- a/ethminer/MinerAux.h
+++ b/ethminer/MinerAux.h
@@ -244,6 +244,10 @@ class MinerCLI
 		{
 			m_report_stratum_hashrate = true;
 		}
+		else if ((arg == "-HWMON") && i + 1 < argc)
+		{
+			m_show_hwmonitors = true;
+		}
 
 #endif
 #if API_CORE
@@ -572,6 +576,7 @@ class MinerCLI
 			<< "        1: eth-proxy compatible: dwarfpool, f2pool, nanopool (required for hashrate reporting to work with nanopool)" << endl
 			<< "        2: EthereumStratum/1.0.0: nicehash" << endl
 			<< "    -RH, --report-hashrate Report current hashrate to pool (please only enable on pools supporting this)" << endl
+			<< "    -HWMON Displays gpu temp and fan percent." << endl
 			<< "    -SE, --stratum-email <s> Email address used in eth-proxy (optional)" << endl
 			<< "    --farm-recheck <n>  Leave n ms between checks for changed work (default: 500). When using stratum, use a high value (i.e. 2000) to get more stable hashrate output" << endl
 #endif
@@ -813,7 +818,7 @@ class MinerCLI
 				});
 				for (unsigned i = 0; !completed; ++i)
 				{
-					auto mp = f.miningProgress();
+					auto mp = f.miningProgress(m_show_hwmonitors);
 					if (current)
 					{
 						minelog << mp << f.getSolutionStats() << f.farmLaunchedFormatted();
@@ -962,7 +967,7 @@ class MinerCLI
 
 			while (client.isRunning())
 			{
-				auto mp = f.miningProgress();
+				auto mp = f.miningProgress(m_show_hwmonitors);
 				if (client.isConnected())
 				{
 					if (client.current())
@@ -1011,7 +1016,7 @@ class MinerCLI
 
 			while (client.isRunning())
 			{
-				auto mp = f.miningProgress();
+				auto mp = f.miningProgress(m_show_hwmonitors);
 				if (client.isConnected())
 				{
 					if (client.current())
@@ -1084,6 +1089,7 @@ class MinerCLI
 	unsigned m_defaultStratumFarmRecheckPeriod = 2000;
 	bool m_farmRecheckSet = false;
 	int m_worktimeout = 180;
+	bool m_show_hwmonitors = false;
 #if API_CORE
 	int m_api_port = 0;
 #endif	
diff --git a/libapicore/ApiServer.cpp b/libapicore/ApiServer.cpp
index 56286fb884..aa3587efb8 100644
--- a/libapicore/ApiServer.cpp
+++ b/libapicore/ApiServer.cpp
@@ -17,7 +17,7 @@ void ApiServer::getMinerStat1(const Json::Value& request, Json::Value& response)
 	auto runningTime = std::chrono::duration_cast<std::chrono::minutes>(steady_clock::now() - this->m_farm.farmLaunched());
 	
 	SolutionStats s = this->m_farm.getSolutionStats();
-	WorkingProgress p = this->m_farm.miningProgress();
+	WorkingProgress p = this->m_farm.miningProgress(true);
 	
 	ostringstream totalMhEth; 
 	ostringstream totalMhDcr; 
@@ -37,7 +37,14 @@ void ApiServer::getMinerStat1(const Json::Value& request, Json::Value& response)
 	{
 		detailedMhEth << std::fixed << std::setprecision(0) << (p.minerRate(i) / 1000.0f) << (((numGpus -1) > gpuIndex) ? ";" : "");
 		detailedMhDcr << "off" << (((numGpus -1) > gpuIndex) ? ";" : ""); // DualMining not supported
-		tempAndFans << "50;50" <<(((numGpus -1) > gpuIndex) ? ";" : ""); // Fetching Temp and Fans not supported
+		gpuIndex++;
+	}
+
+	gpuIndex = 0;
+	numGpus = p.minerMonitors.size();
+	for (auto const& i : p.minerMonitors)
+	{
+		tempAndFans << i.tempC << ";" << i.fanP << (((numGpus - 1) > gpuIndex) ? "; " : ""); // Fetching Temp and Fans
 		gpuIndex++;
 	}
 
diff --git a/libethash-cl/CLMiner.cpp b/libethash-cl/CLMiner.cpp
index acb1559c6c..839b02b1b4 100644
--- a/libethash-cl/CLMiner.cpp
+++ b/libethash-cl/CLMiner.cpp
@@ -340,6 +340,28 @@ bool CLMiner::configureGPU(
 	return false;
 }
 
+HwMonitor CLMiner::hwmon()
+{
+	HwMonitor hw;
+	unsigned int tempC = 0, fanpcnt = 0;
+	if (nvmlh) {
+		wrap_nvml_get_tempC(nvmlh, index, &tempC);
+		wrap_nvml_get_fanpcnt(nvmlh, index, &fanpcnt);
+	}
+	if (adlh) {
+		wrap_adl_get_tempC(adlh, index, &tempC);
+		wrap_adl_get_fanpcnt(adlh, index, &fanpcnt);
+	}
+#if defined(__linux)
+	if (sysfsh) {
+		wrap_amdsysfs_get_tempC(sysfsh, index, &tempC);
+		wrap_amdsysfs_get_fanpcnt(sysfsh, index, &fanpcnt);
+	}
+#endif
+	hw.tempC = tempC;
+	hw.fanP = fanpcnt;
+	return hw;
+}
 
 bool CLMiner::init(const h256& seed)
 {
@@ -362,10 +384,15 @@ bool CLMiner::init(const h256& seed)
 		if (platformName == "NVIDIA CUDA")
 		{
 			platformId = OPENCL_PLATFORM_NVIDIA;
+			nvmlh = wrap_nvml_create();
 		}
 		else if (platformName == "AMD Accelerated Parallel Processing")
 		{
 			platformId = OPENCL_PLATFORM_AMD;
+			adlh = wrap_adl_create();
+#if defined(__linux)
+			sysfsh = wrap_amdsysfs_create();
+#endif
 		}
 		else if (platformName == "Clover")
 		{
diff --git a/libethash-cl/CLMiner.h b/libethash-cl/CLMiner.h
index 8c6ab30538..27064a947b 100644
--- a/libethash-cl/CLMiner.h
+++ b/libethash-cl/CLMiner.h
@@ -8,6 +8,11 @@
 #include <libdevcore/Worker.h>
 #include <libethcore/EthashAux.h>
 #include <libethcore/Miner.h>
+#include <libhwmon/wrapnvml.h>
+#include <libhwmon/wrapadl.h>
+#if defined(__linux)
+#include <libhwmon/wrapamdsysfs.h>
+#endif
 
 #define CL_USE_DEPRECATED_OPENCL_1_2_APIS true
 #define CL_HPP_ENABLE_EXCEPTIONS true
@@ -68,7 +73,7 @@ class CLMiner: public Miner
 			s_devices[i] = _devices[i];
 		}
 	}
-
+	HwMonitor hwmon() override;
 protected:
 	void kickOff() override;
 	void pause() override;
@@ -100,6 +105,11 @@ class CLMiner: public Miner
 	/// The initial global work size for the searches
 	static unsigned s_initialGlobalWorkSize;
 
+	wrap_nvml_handle *nvmlh = NULL;
+	wrap_adl_handle *adlh = NULL;
+#if defined(__linux)
+	wrap_amdsysfs_handle *sysfsh = NULL;
+#endif
 };
 
 }
diff --git a/libethash-cl/CMakeLists.txt b/libethash-cl/CMakeLists.txt
index 91f974d3c4..55e58e9215 100644
--- a/libethash-cl/CMakeLists.txt
+++ b/libethash-cl/CMakeLists.txt
@@ -30,5 +30,5 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
 include_directories(..)
 
 add_library(ethash-cl ${SOURCES})
-target_link_libraries(ethash-cl PUBLIC ethcore ethash)
+target_link_libraries(ethash-cl PUBLIC ethcore ethash hwmon)
 target_link_libraries(ethash-cl PRIVATE OpenCL::OpenCL)
diff --git a/libethash-cuda/CMakeLists.txt b/libethash-cuda/CMakeLists.txt
index 0e42953f70..3fd6d91b1f 100644
--- a/libethash-cuda/CMakeLists.txt
+++ b/libethash-cuda/CMakeLists.txt
@@ -25,7 +25,7 @@ file(GLOB sources "*.cpp" "*.cu")
 file(GLOB headers "*.h" "*.cuh")
 
 cuda_add_library(ethash-cuda STATIC ${sources} ${headers})
-target_link_libraries(ethash-cuda ethcore ethash)
+target_link_libraries(ethash-cuda ethcore ethash hwmon)
 target_include_directories(ethash-cuda PUBLIC ${CUDA_INCLUDE_DIRS})
 target_include_directories(ethash-cuda PRIVATE .. ${CMAKE_CURRENT_BINARY_DIR})
 
diff --git a/libethash-cuda/CUDAMiner.cpp b/libethash-cuda/CUDAMiner.cpp
index f870615999..d32c695238 100644
--- a/libethash-cuda/CUDAMiner.cpp
+++ b/libethash-cuda/CUDAMiner.cpp
@@ -213,6 +213,15 @@ void CUDAMiner::listDevices()
 	return ethash_cuda_miner::listDevices();
 }
 
+HwMonitor CUDAMiner::hwmon()
+{
+	HwMonitor hw;
+	if (m_miner) {
+		hw = m_miner->hwmon();
+	}
+	return hw;
+}
+
 bool CUDAMiner::configureGPU(
 	unsigned _blockSize,
 	unsigned _gridSize,
diff --git a/libethash-cuda/CUDAMiner.h b/libethash-cuda/CUDAMiner.h
index 82226cc2a3..690a3ad47f 100644
--- a/libethash-cuda/CUDAMiner.h
+++ b/libethash-cuda/CUDAMiner.h
@@ -69,10 +69,10 @@ class EthashCUDAHook;
 				s_devices[i] = _devices[i];
 			}
 		}
+		HwMonitor hwmon() override;
 	protected:
 		void kickOff() override;
 		void pause() override;
-
 	private:
 		void workLoop() override;
 		void report(uint64_t _nonce);
diff --git a/libethash-cuda/ethash_cuda_miner.cpp b/libethash-cuda/ethash_cuda_miner.cpp
index d2d7fe45b2..ea2a0c6aed 100644
--- a/libethash-cuda/ethash_cuda_miner.cpp
+++ b/libethash-cuda/ethash_cuda_miner.cpp
@@ -211,14 +211,16 @@ bool ethash_cuda_miner::init(ethash_light_t _light, uint8_t const* _lightData, u
 			return false;
 
 		// use selected device
-		int device_num = std::min<int>((int)_deviceId, device_count - 1);
+		m_device_num = std::min<int>((int)_deviceId, device_count - 1);
+
+		nvmlh = wrap_nvml_create();
 
 		cudaDeviceProp device_props;
-		CUDA_SAFE_CALL(cudaGetDeviceProperties(&device_props, device_num));
+		CUDA_SAFE_CALL(cudaGetDeviceProperties(&device_props, m_device_num));
 
 		cudalog << "Using device: " << device_props.name << " (Compute " + to_string(device_props.major) + "." + to_string(device_props.minor) + ")";
 
-		CUDA_SAFE_CALL(cudaSetDevice(device_num));
+		CUDA_SAFE_CALL(cudaSetDevice(m_device_num));
 		CUDA_SAFE_CALL(cudaDeviceReset());
 		CUDA_SAFE_CALL(cudaSetDeviceFlags(s_scheduleFlag));
 		CUDA_SAFE_CALL(cudaDeviceSetCacheConfig(cudaFuncCachePreferL1));
@@ -260,13 +262,13 @@ bool ethash_cuda_miner::init(ethash_light_t _light, uint8_t const* _lightData, u
 
 		if (!*hostDAG)
 		{
-			cudalog << "Generating DAG for GPU #" << device_num;
-			ethash_generate_dag(dagSize, s_gridSize, s_blockSize, m_streams[0], device_num);
+			cudalog << "Generating DAG for GPU #" << m_device_num;
+			ethash_generate_dag(dagSize, s_gridSize, s_blockSize, m_streams[0], m_device_num);
 
 			if (_cpyToHost)
 			{
 				uint8_t* memoryDAG = new uint8_t[dagSize];
-				cudalog << "Copying DAG from GPU #" << device_num << " to host";
+				cudalog << "Copying DAG from GPU #" << m_device_num << " to host";
 				CUDA_SAFE_CALL(cudaMemcpy(reinterpret_cast<void*>(memoryDAG), dag, dagSize, cudaMemcpyDeviceToHost));
 
 				*hostDAG = (void*)memoryDAG;
@@ -274,7 +276,7 @@ bool ethash_cuda_miner::init(ethash_light_t _light, uint8_t const* _lightData, u
 		}
 		else
 		{
-			cudalog << "Copying DAG from host to GPU #" << device_num;
+			cudalog << "Copying DAG from host to GPU #" << m_device_num;
 			const void* hdag = (const void*)(*hostDAG);
 			CUDA_SAFE_CALL(cudaMemcpy(reinterpret_cast<void*>(dag), hdag, dagSize, cudaMemcpyHostToDevice));
 		}
@@ -359,3 +361,16 @@ void ethash_cuda_miner::search(uint8_t const* header, uint64_t target, search_ho
 	}
 }
 
+dev::eth::HwMonitor ethash_cuda_miner::hwmon()
+{
+	dev::eth::HwMonitor hw;
+	if (nvmlh) {
+		unsigned int tempC = 0, fanpcnt = 0;
+		wrap_nvml_get_tempC(nvmlh, nvmlh->cuda_nvml_device_id[m_device_num], &tempC);
+		wrap_nvml_get_fanpcnt(nvmlh, nvmlh->cuda_nvml_device_id[m_device_num], &fanpcnt);
+		hw.tempC = tempC;
+		hw.fanP = fanpcnt;
+	}
+	return hw;
+}
+
diff --git a/libethash-cuda/ethash_cuda_miner.h b/libethash-cuda/ethash_cuda_miner.h
index 65134cff43..9086f05c63 100644
--- a/libethash-cuda/ethash_cuda_miner.h
+++ b/libethash-cuda/ethash_cuda_miner.h
@@ -5,6 +5,8 @@
 #include <time.h>
 #include <functional>
 #include <libethash/ethash.h>
+#include <libethcore/Miner.h>
+#include <libhwmon/wrapnvml.h>
 #include "ethash_cuda_miner_kernel.h"
 
 class ethash_cuda_miner
@@ -39,6 +41,7 @@ class ethash_cuda_miner
 
 	void finish();
 	void search(uint8_t const* header, uint64_t target, search_hook& hook, bool _ethStratum, uint64_t _startN);
+	dev::eth::HwMonitor hwmon();
 
 	/* -- default values -- */
 	/// Default value of the block size. Also known as workgroup size.
@@ -54,8 +57,8 @@ class ethash_cuda_miner
 	uint64_t m_current_nonce;
 	uint64_t m_starting_nonce;
 	uint64_t m_current_index;
-
 	uint32_t m_sharedBytes;
+	int m_device_num;
 
 	volatile uint32_t ** m_search_buf;
 	cudaStream_t  * m_streams;
@@ -70,4 +73,6 @@ class ethash_cuda_miner
 	static unsigned s_scheduleFlag;
 
 	static unsigned m_parallelHash;
+
+	wrap_nvml_handle *nvmlh = NULL;
 };
diff --git a/libethcore/Farm.h b/libethcore/Farm.h
index 28c9bc014c..8c5af85c80 100644
--- a/libethcore/Farm.h
+++ b/libethcore/Farm.h
@@ -217,7 +217,7 @@ class Farm: public FarmFace
 	 * @brief Get information on the progress of mining this work package.
 	 * @return The progress with mining so far.
 	 */
-	WorkingProgress const& miningProgress() const
+	WorkingProgress const& miningProgress(bool hwmon = false) const
 	{
 		WorkingProgress p;
 		p.ms = 0;
@@ -225,8 +225,9 @@ class Farm: public FarmFace
 		{
 			Guard l2(x_minerWork);
 			for (auto const& i : m_miners) {
-				(void) i; // unused
 				p.minersHashes.push_back(0);
+				if (hwmon)
+					p.minerMonitors.push_back(i->hwmon());
 			}
 		}
 
@@ -327,6 +328,8 @@ class Farm: public FarmFace
 	mutable Mutex x_progress;
 	mutable WorkingProgress m_progress;
 
+	mutable Mutex x_hwmons;
+
 	SolutionFound m_onSolutionFound;
 	MinerRestart m_onMinerRestart;
 
diff --git a/libethcore/Miner.h b/libethcore/Miner.h
index e2942b0d63..70b63955ee 100644
--- a/libethcore/Miner.h
+++ b/libethcore/Miner.h
@@ -64,6 +64,17 @@ enum class MinerType
 	CUDA
 };
 
+struct HwMonitor
+{
+	int tempC = 0;
+	int fanP = 0;
+};
+
+inline std::ostream& operator<<(std::ostream& os, HwMonitor _hw)
+{
+	return os << _hw.tempC << "C " << _hw.fanP << "%";
+}
+
 /// Describes the progress of a mining operation.
 struct WorkingProgress
 {
@@ -72,6 +83,7 @@ struct WorkingProgress
 	uint64_t rate() const { return ms == 0 ? 0 : hashes * 1000 / ms; }
 
 	std::vector<uint64_t> minersHashes;
+	std::vector<HwMonitor> minerMonitors;
 	uint64_t minerRate(const uint64_t hashCount) const { return ms == 0 ? 0 : hashCount * 1000 / ms; }
 };
 
@@ -85,7 +97,10 @@ inline std::ostream& operator<<(std::ostream& _out, WorkingProgress _p)
 	for (size_t i = 0; i < _p.minersHashes.size(); ++i)
 	{
 		mh = _p.minerRate(_p.minersHashes[i]) / 1000000.0f;
-		_out << "gpu/" << i << " " << EthTeal << std::fixed << std::setw(5) << std::setprecision(2) << mh << EthReset << "  ";
+		_out << "gpu/" << i << " " << EthTeal << std::fixed << std::setw(5) << std::setprecision(2) << mh << EthReset;
+		if (_p.minerMonitors.size() == _p.minersHashes.size())
+			_out << " " << EthTeal << _p.minerMonitors[i] << EthReset;
+		_out << "  ";
 	}
 
 	return _out;
@@ -174,6 +189,8 @@ class Miner: public Worker
 
 	void resetHashCount() { m_hashCount = 0; }
 
+	virtual HwMonitor hwmon() = 0;
+
 protected:
 
 	/**
diff --git a/libhwmon/CMakeLists.txt b/libhwmon/CMakeLists.txt
new file mode 100644
index 0000000000..58d8106471
--- /dev/null
+++ b/libhwmon/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(SOURCES
+    wraphelper.h
+    wrapnvml.h wrapnvml.cpp
+    wrapadl.h wrapadl.cpp
+    wrapamdsysfs.h wrapamdsysfs.cpp
+)
+
+add_library(hwmon ${SOURCES})
+target_link_libraries(hwmon devcore)
+target_include_directories(hwmon PRIVATE ..)
+
+if (ETHASHCUDA)
+	find_package(CUDA REQUIRED)
+	target_include_directories(hwmon PUBLIC ${CUDA_INCLUDE_DIRS})
+endif()
\ No newline at end of file
diff --git a/libhwmon/wrapadl.cpp b/libhwmon/wrapadl.cpp
new file mode 100644
index 0000000000..42e6744c4d
--- /dev/null
+++ b/libhwmon/wrapadl.cpp
@@ -0,0 +1,193 @@
+/*
+* Wrapper for ADL, inspired by wrapnvml from John E. Stone
+*
+* By Philipp Andreas - github@smurfy.de
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "wraphelper.h"
+#include "wrapadl.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void* ADL_API_CALL ADL_Main_Memory_Alloc(int iSize)
+{
+	void* lpBuffer = malloc(iSize);
+	return lpBuffer;
+}
+
+wrap_adl_handle * wrap_adl_create()
+{
+	wrap_adl_handle *adlh = NULL;
+
+#if defined(_WIN64)
+	/* 64-bit Windows */
+#define  libatiadlxx "atiadlxx.dll"
+#elif defined(_WIN32) || defined(_MSC_VER)
+	/* 32-bit Windows */
+#define  libatiadlxx "atiadlxx.dll"
+#elif defined(__linux) && (defined(__i386__) || defined(__ARM_ARCH_7A__))
+	/* 32-bit linux assumed */
+#define  libatiadlxx "libatiadlxx.so"
+#elif defined(__linux)
+	/* 64-bit linux assumed */
+#define  libatiadlxx "libatiadlxx.so"
+#else
+#define  libatiadlxx ""
+#warning "Unrecognized platform: need ADL DLL path for this platform..."
+return NULL;
+#endif
+
+#if WIN32
+	char tmp[512];
+	ExpandEnvironmentStringsA(libatiadlxx, tmp, sizeof(tmp));
+#else
+	char tmp[512] = libatiadlxx;
+#endif
+
+	void *adl_dll = wrap_dlopen(tmp);
+	if (adl_dll == NULL)
+		return NULL;
+
+	adlh = (wrap_adl_handle *)calloc(1, sizeof(wrap_adl_handle));
+
+	adlh->adl_dll = adl_dll;
+
+	adlh->adlMainControlCreate = (wrap_adlReturn_t(*)(ADL_MAIN_MALLOC_CALLBACK, int))
+		wrap_dlsym(adlh->adl_dll, "ADL_Main_Control_Create");
+	adlh->adlAdapterNumberOfAdapters = (wrap_adlReturn_t(*)(int *))
+		wrap_dlsym(adlh->adl_dll, "ADL_Adapter_NumberOfAdapters_Get");
+	adlh->adlAdapterAdapterInfoGet = (wrap_adlReturn_t(*)(LPAdapterInfo, int))
+		wrap_dlsym(adlh->adl_dll, "ADL_Adapter_AdapterInfo_Get");
+	adlh->adlAdapterAdapterIdGet = (wrap_adlReturn_t(*)(int, int*))
+		wrap_dlsym(adlh->adl_dll, "ADL_Adapter_ID_Get");
+	adlh->adlOverdrive5TemperatureGet = (wrap_adlReturn_t(*)(int, int, ADLTemperature*))
+		wrap_dlsym(adlh->adl_dll, "ADL_Overdrive5_Temperature_Get");
+	adlh->adlOverdrive5FanSpeedGet = (wrap_adlReturn_t(*)(int, int, ADLFanSpeedValue*))
+		wrap_dlsym(adlh->adl_dll, "ADL_Overdrive5_FanSpeed_Get");
+	adlh->adlMainControlRefresh = (wrap_adlReturn_t(*)(void))
+		wrap_dlsym(adlh->adl_dll, "ADL_Main_Control_Refresh");
+	adlh->adlMainControlDestory = (wrap_adlReturn_t(*)(void))
+		wrap_dlsym(adlh->adl_dll, "ADL_Main_Control_Destroy");
+
+	if (adlh->adlMainControlCreate == NULL ||
+		adlh->adlMainControlDestory == NULL ||
+		adlh->adlMainControlRefresh == NULL ||
+		adlh->adlAdapterNumberOfAdapters == NULL ||
+		adlh->adlAdapterAdapterInfoGet == NULL ||
+		adlh->adlAdapterAdapterIdGet == NULL ||
+		adlh->adlOverdrive5TemperatureGet == NULL ||
+		adlh->adlOverdrive5FanSpeedGet == NULL
+		) {
+#if 0
+		printf("Failed to obtain all required ADL function pointers\n");
+#endif
+		wrap_dlclose(adlh->adl_dll);
+		free(adlh);
+		return NULL;
+	}
+
+	adlh->adlMainControlCreate(ADL_Main_Memory_Alloc, 1); 
+	adlh->adlMainControlRefresh();
+
+	int logicalGpuCount = 0;
+	adlh->adlAdapterNumberOfAdapters(&logicalGpuCount);
+
+	adlh->phys_logi_device_id = (int*)calloc(logicalGpuCount, sizeof(int));
+	
+	adlh->adl_gpucount = 0;
+	int last_adapter = 0;
+	if (logicalGpuCount > 0) {
+		adlh->devs = (LPAdapterInfo)malloc(sizeof(AdapterInfo) * logicalGpuCount);
+		memset(adlh->devs, '\0', sizeof(AdapterInfo) * logicalGpuCount);
+
+		adlh->devs->iSize = sizeof(adlh->devs);
+
+		int res = adlh->adlAdapterAdapterInfoGet(adlh->devs, sizeof(AdapterInfo) * logicalGpuCount);
+
+		for (int i = 0; i < logicalGpuCount; i++) {
+			int adapterIndex = adlh->devs[i].iAdapterIndex;
+			int adapterID = 0;
+
+			res = adlh->adlAdapterAdapterIdGet(adapterIndex, &adapterID);
+
+			if (res != WRAPADL_OK) {
+				continue;
+			}
+
+			adlh->phys_logi_device_id[adlh->adl_gpucount] = adapterIndex;
+
+			if (adapterID == last_adapter) {
+				continue;
+			}
+			last_adapter = adapterID;
+			adlh->adl_gpucount++;
+		}
+	}
+
+	return adlh;
+}
+
+int wrap_adl_destory(wrap_adl_handle *adlh)
+{
+	adlh->adlMainControlDestory();
+	wrap_dlclose(adlh->adl_dll);
+	free(adlh);
+	return 0;
+}
+
+int wrap_adl_get_gpucount(wrap_adl_handle *adlh, int *gpucount)
+{
+	*gpucount = adlh->adl_gpucount;
+	return 0;
+}
+
+int wrap_adl_get_gpu_name(wrap_adl_handle *adlh, int gpuindex, char *namebuf, int bufsize)
+{
+	if (gpuindex < 0 || gpuindex >= adlh->adl_gpucount)
+		return -1;
+
+	memcpy(namebuf, adlh->devs[adlh->phys_logi_device_id[gpuindex]].strAdapterName, bufsize);
+	return 0;
+}
+
+
+int wrap_adl_get_tempC(wrap_adl_handle *adlh, int gpuindex, unsigned int *tempC)
+{
+	wrap_adlReturn_t rc;
+	if (gpuindex < 0 || gpuindex >= adlh->adl_gpucount)
+		return -1;
+
+	ADLTemperature *temperature = new ADLTemperature();
+	rc = adlh->adlOverdrive5TemperatureGet(adlh->phys_logi_device_id[gpuindex], 0, temperature);
+	if (rc != WRAPADL_OK) {
+		return -1;
+	}
+	*tempC = unsigned(temperature->iTemperature / 1000);
+	free(temperature);
+	return 0;
+}
+
+int wrap_adl_get_fanpcnt(wrap_adl_handle *adlh, int gpuindex, unsigned int *fanpcnt)
+{
+	wrap_adlReturn_t rc;
+	if (gpuindex < 0 || gpuindex >= adlh->adl_gpucount)
+		return -1;
+
+	ADLFanSpeedValue *fan = new ADLFanSpeedValue();
+	fan->iSpeedType = 1;
+	rc = adlh->adlOverdrive5FanSpeedGet(adlh->phys_logi_device_id[gpuindex], 0, fan);
+	if (rc != WRAPADL_OK) {
+		return -1;
+	}
+	*fanpcnt = unsigned(fan->iFanSpeed);
+	free(fan);
+	return 0;
+}
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/libhwmon/wrapadl.h b/libhwmon/wrapadl.h
new file mode 100644
index 0000000000..c49f3ce4ea
--- /dev/null
+++ b/libhwmon/wrapadl.h
@@ -0,0 +1,140 @@
+/*
+* Wrapper for ADL, inspired by wrapnvml from John E. Stone
+* 
+* By Philipp Andreas - github@smurfy.de
+*/
+
+#ifndef _WRAPADL_H_
+#define _WRAPADL_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef enum wrap_adlReturn_enum {
+	WRAPADL_OK= 0
+} wrap_adlReturn_t;
+
+// Some ADL defines and structs from adl sdk
+#if defined (__MSC_VER)
+#define ADL_API_CALL __cdecl
+#elif defined (_WIN32) || defined (__WIN32__)
+#define ADL_API_CALL __stdcall
+#else
+#define ADL_API_CALL
+#endif
+
+typedef void* (ADL_API_CALL *ADL_MAIN_MALLOC_CALLBACK)(int);
+
+#define ADL_MAX_PATH                                    256
+typedef struct AdapterInfo
+{
+	/// \ALL_STRUCT_MEM
+
+	/// Size of the structure.
+	int iSize;
+	/// The ADL index handle. One GPU may be associated with one or two index handles
+	int iAdapterIndex;
+	/// The unique device ID associated with this adapter.
+	char strUDID[ADL_MAX_PATH];
+	/// The BUS number associated with this adapter.
+	int iBusNumber;
+	/// The driver number associated with this adapter.
+	int iDeviceNumber;
+	/// The function number.
+	int iFunctionNumber;
+	/// The vendor ID associated with this adapter.
+	int iVendorID;
+	/// Adapter name.
+	char strAdapterName[ADL_MAX_PATH];
+	/// Display name. For example, "\\Display0" for Windows or ":0:0" for Linux.
+	char strDisplayName[ADL_MAX_PATH];
+	/// Present or not; 1 if present and 0 if not present.It the logical adapter is present, the display name such as \\.\Display1 can be found from OS
+	int iPresent;
+	// @}
+
+#if defined (_WIN32) || defined (_WIN64)
+	/// \WIN_STRUCT_MEM
+
+	/// Exist or not; 1 is exist and 0 is not present.
+	int iExist;
+	/// Driver registry path.
+	char strDriverPath[ADL_MAX_PATH];
+	/// Driver registry path Ext for.
+	char strDriverPathExt[ADL_MAX_PATH];
+	/// PNP string from Windows.
+	char strPNPString[ADL_MAX_PATH];
+	/// It is generated from EnumDisplayDevices.
+	int iOSDisplayIndex;
+	// @}
+#endif /* (_WIN32) || (_WIN64) */
+
+#if defined (LINUX)
+	/// \LNX_STRUCT_MEM
+
+	/// Internal X screen number from GPUMapInfo (DEPRICATED use XScreenInfo)
+	int iXScreenNum;
+	/// Internal driver index from GPUMapInfo
+	int iDrvIndex;
+	/// \deprecated Internal x config file screen identifier name. Use XScreenInfo instead.
+	char strXScreenConfigName[ADL_MAX_PATH];
+
+	// @}
+#endif /* (LINUX) */
+} AdapterInfo, *LPAdapterInfo;
+
+typedef struct ADLTemperature
+{
+	/// Must be set to the size of the structure
+	int iSize;
+	/// Temperature in millidegrees Celsius.
+	int iTemperature;
+} ADLTemperature;
+
+typedef struct ADLFanSpeedValue
+{
+	/// Must be set to the size of the structure
+	int iSize;
+	/// Possible valies: \ref ADL_DL_FANCTRL_SPEED_TYPE_PERCENT or \ref ADL_DL_FANCTRL_SPEED_TYPE_RPM
+	int iSpeedType;
+	/// Fan speed value
+	int iFanSpeed;
+	/// The only flag for now is: \ref ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED
+	int iFlags;
+} ADLFanSpeedValue;
+
+/*
+* Handle to hold the function pointers for the entry points we need,
+* and the shared library itself.
+*/
+typedef struct {
+	void *adl_dll;
+	int adl_gpucount;
+	int *phys_logi_device_id;
+	LPAdapterInfo devs;
+	wrap_adlReturn_t(*adlMainControlCreate)(ADL_MAIN_MALLOC_CALLBACK, int);
+	wrap_adlReturn_t(*adlAdapterNumberOfAdapters)(int *);
+	wrap_adlReturn_t(*adlAdapterAdapterInfoGet)(LPAdapterInfo, int);
+	wrap_adlReturn_t(*adlAdapterAdapterIdGet)(int, int*);
+	wrap_adlReturn_t(*adlOverdrive5TemperatureGet)(int, int, ADLTemperature*);
+	wrap_adlReturn_t(*adlOverdrive5FanSpeedGet)(int, int, ADLFanSpeedValue*);
+	wrap_adlReturn_t(*adlMainControlRefresh)(void);
+	wrap_adlReturn_t(*adlMainControlDestory)(void);
+} wrap_adl_handle;
+
+wrap_adl_handle * wrap_adl_create();
+int wrap_adl_destory(wrap_adl_handle *adlh);
+
+int wrap_adl_get_gpucount(wrap_adl_handle *adlh, int *gpucount);
+
+int wrap_adl_get_gpu_name(wrap_adl_handle *adlh, int gpuindex, char *namebuf, int bufsize);
+
+int wrap_adl_get_tempC(wrap_adl_handle *adlh, int gpuindex, unsigned int *tempC);
+
+int wrap_adl_get_fanpcnt(wrap_adl_handle *adlh, int gpuindex, unsigned int *fanpcnt);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/libhwmon/wrapamdsysfs.cpp b/libhwmon/wrapamdsysfs.cpp
new file mode 100644
index 0000000000..707c29b331
--- /dev/null
+++ b/libhwmon/wrapamdsysfs.cpp
@@ -0,0 +1,209 @@
+/*
+* Wrapper for AMD SysFS on linux, using adapted code from amdcovc by matszpk
+*
+* By Philipp Andreas - github@smurfy.de
+*/
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string>
+#include <cstring>
+#include <climits>
+#include <limits>
+#include <cstdint>
+#include <algorithm>
+#include <fstream>
+#include <sys/types.h>
+#if defined(__linux)
+#include <dirent.h>
+#endif
+#include "wrapamdsysfs.h"
+
+static bool getFileContentValue(const char* filename, unsigned int& value)
+{
+	value = 0;
+	std::ifstream ifs(filename, std::ios::binary);
+	std::string line;
+	std::getline(ifs, line);
+	char* p = (char*)line.c_str();
+	char* p2;
+	errno = 0;
+	value = strtoul(p, &p2, 0);
+	if (errno != 0)
+		return false;
+	return (p != p2);
+}
+
+
+wrap_amdsysfs_handle * wrap_amdsysfs_create()
+{
+	wrap_amdsysfs_handle *sysfsh = NULL;
+
+#if defined(__linux)
+	sysfsh = (wrap_amdsysfs_handle *)calloc(1, sizeof(wrap_amdsysfs_handle));
+
+	DIR* dirp = opendir("/sys/class/drm");
+	if (dirp == nullptr)
+		return NULL;
+
+	unsigned int gpucount = 0;
+	struct dirent* dire;
+	while ((dire = readdir(dirp)) != nullptr)
+	{
+		if (::strncmp(dire->d_name, "card", 4) != 0)
+			continue; // is not card directory
+		const char* p;
+		for (p = dire->d_name + 4; ::isdigit(*p); p++);
+		if (*p != 0)
+			continue; // is not card directory
+		unsigned int v = ::strtoul(dire->d_name + 4, nullptr, 10);
+		gpucount = std::max(gpucount, v + 1);
+	}
+	if (errno != 0)
+	{
+		closedir(dirp);
+		return NULL;
+	}
+	closedir(dirp);
+
+	sysfsh->card_sysfs_device_id = (int*)calloc(gpucount, sizeof(int));
+	sysfsh->sysfs_hwmon_id = (int*)calloc(gpucount, sizeof(int));
+
+	// filter AMD GPU cards and create mappings
+	char dbuf[120];
+	int cardIndex = 0;
+	for (unsigned int i = 0; i < gpucount; i++)
+	{
+		sysfsh->card_sysfs_device_id[cardIndex] = -1;
+		sysfsh->sysfs_hwmon_id[cardIndex] = -1;
+
+		snprintf(dbuf, 120, "/sys/class/drm/card%u/device/vendor", i);
+		unsigned int vendorId = 0;
+		if (!getFileContentValue(dbuf, vendorId))
+			continue;
+		if (vendorId != 4098) // if not AMD
+			continue;
+
+		sysfsh->card_sysfs_device_id[cardIndex] = i;
+		cardIndex++;
+	}
+
+	// Number of AMD cards found we do not care about non AMD cards
+	sysfsh->sysfs_gpucount = cardIndex;
+
+	// Get hwmon directory index
+	for (int i = 0; i < sysfsh->sysfs_gpucount; i++)
+	{
+		int sysfsIdx = sysfsh->card_sysfs_device_id[i];
+
+		// Should not happen
+		if (sysfsIdx < 0) {
+			free(sysfsh);
+			return NULL;
+		}
+
+		// search hwmon
+		errno = 0;
+		snprintf(dbuf, 120, "/sys/class/drm/card%u/device/hwmon", sysfsIdx);
+		DIR* dirp = opendir(dbuf);
+		if (dirp == nullptr) {
+			free(sysfsh);
+			return NULL;
+		}
+		errno = 0;
+		struct dirent* dire;
+		unsigned int hwmonIndex = UINT_MAX;
+		while ((dire = readdir(dirp)) != nullptr)
+		{
+			if (::strncmp(dire->d_name, "hwmon", 5) != 0)
+				continue; // is not hwmon directory
+			const char* p;
+			for (p = dire->d_name + 5; ::isdigit(*p); p++);
+			if (*p != 0)
+				continue; // is not hwmon directory
+			errno = 0;
+			unsigned int v = ::strtoul(dire->d_name + 5, nullptr, 10);
+			hwmonIndex = std::min(hwmonIndex, v);
+		}
+		if (errno != 0)
+		{
+			closedir(dirp);
+			free(sysfsh);
+			return NULL;
+		}
+		closedir(dirp);
+		if (hwmonIndex == UINT_MAX) {
+			free(sysfsh);
+			return NULL;
+		}
+
+		sysfsh->sysfs_hwmon_id[i] = hwmonIndex;
+	}
+#endif
+
+	return sysfsh;
+}
+int wrap_amdsysfs_destory(wrap_amdsysfs_handle *sysfsh)
+{
+	free(sysfsh);
+	return 0;
+}
+
+int wrap_amdsysfs_get_gpucount(wrap_amdsysfs_handle *sysfsh, int *gpucount)
+{
+	*gpucount = sysfsh->sysfs_gpucount;
+	return 0;
+}
+
+int wrap_amdsysfs_get_tempC(wrap_amdsysfs_handle *sysfsh, int index, unsigned int *tempC)
+{
+	int gpuindex = sysfsh->card_sysfs_device_id[index];
+	if (gpuindex < 0 || index >= sysfsh->sysfs_gpucount)
+		return -1;
+
+	int hwmonindex = sysfsh->sysfs_hwmon_id[index];
+	if (hwmonindex < 0)
+		return -1;
+
+	char dbuf[120];
+	snprintf(dbuf, 120, "/sys/class/drm/card%u/device/hwmon/hwmon%u/temp1_input",
+		gpuindex, hwmonindex);
+
+	unsigned int temp = 0;
+	getFileContentValue(dbuf, temp);
+
+	if (temp > 0)
+		*tempC = temp / 1000;
+
+	return 0;
+}
+
+int wrap_amdsysfs_get_fanpcnt(wrap_amdsysfs_handle *sysfsh, int index, unsigned int *fanpcnt)
+{
+	int gpuindex = sysfsh->card_sysfs_device_id[index];
+	if (gpuindex < 0 || index >= sysfsh->sysfs_gpucount)
+		return -1;
+
+	int hwmonindex = sysfsh->sysfs_hwmon_id[index];
+	if (hwmonindex < 0)
+		return -1;
+
+	unsigned int pwm = 0, pwmMax = 255, pwmMin = 0;
+
+	char dbuf[120];
+	snprintf(dbuf, 120, "/sys/class/drm/card%u/device/hwmon/hwmon%u/pwm1",
+		gpuindex, hwmonindex);
+	getFileContentValue(dbuf, pwm);
+
+	snprintf(dbuf, 120, "/sys/class/drm/card%u/device/hwmon/hwmon%u/pwm1_max",
+		gpuindex, hwmonindex);
+	getFileContentValue(dbuf, pwmMax);
+
+	snprintf(dbuf, 120, "/sys/class/drm/card%u/device/hwmon/hwmon%u/pwm1_min",
+		gpuindex, hwmonindex);
+	getFileContentValue(dbuf, pwmMin);
+
+	*fanpcnt = double(pwm - pwmMin) / double(pwmMax - pwmMin) * 100.0;
+	return 0;
+}
\ No newline at end of file
diff --git a/libhwmon/wrapamdsysfs.h b/libhwmon/wrapamdsysfs.h
new file mode 100644
index 0000000000..05c637d031
--- /dev/null
+++ b/libhwmon/wrapamdsysfs.h
@@ -0,0 +1,25 @@
+/*
+* Wrapper for AMD SysFS on linux, using adapted code from amdcovc by matszpk
+*
+* By Philipp Andreas - github@smurfy.de
+*/
+
+#ifndef _WRAPAMDSYSFS_H_
+#define _WRAPAMDSYSFS_H_
+
+typedef struct {
+	int sysfs_gpucount;
+	int *card_sysfs_device_id;  /* map cardidx to filesystem card idx */
+	int *sysfs_hwmon_id;        /* filesystem card idx to filesystem hwmon idx */
+} wrap_amdsysfs_handle;
+
+wrap_amdsysfs_handle * wrap_amdsysfs_create();
+int wrap_amdsysfs_destory(wrap_amdsysfs_handle *sysfsh);
+
+int wrap_amdsysfs_get_gpucount(wrap_amdsysfs_handle *sysfsh, int *gpucount);
+
+int wrap_amdsysfs_get_tempC(wrap_amdsysfs_handle *sysfsh, int index, unsigned int *tempC);
+
+int wrap_amdsysfs_get_fanpcnt(wrap_amdsysfs_handle *sysfsh, int index, unsigned int *fanpcnt);
+
+#endif
\ No newline at end of file
diff --git a/libhwmon/wraphelper.h b/libhwmon/wraphelper.h
new file mode 100644
index 0000000000..5c627f7e6b
--- /dev/null
+++ b/libhwmon/wraphelper.h
@@ -0,0 +1,28 @@
+/*
+* Wrappers to emulate dlopen() on other systems like Windows
+*/
+#if defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64)
+#include <windows.h>
+static void *wrap_dlopen(const char *filename) {
+	return (void *)LoadLibrary(filename);
+}
+static void *wrap_dlsym(void *h, const char *sym) {
+	return (void *)GetProcAddress((HINSTANCE)h, sym);
+}
+static int wrap_dlclose(void *h) {
+	/* FreeLibrary returns nonzero on success */
+	return (!FreeLibrary((HINSTANCE)h));
+}
+#else
+/* assume we can use dlopen itself... */
+#include <dlfcn.h>
+static void *wrap_dlopen(const char *filename) {
+	return dlopen(filename, RTLD_NOW);
+}
+static void *wrap_dlsym(void *h, const char *sym) {
+	return dlsym(h, sym);
+}
+static int wrap_dlclose(void *h) {
+	return dlclose(h);
+}
+#endif
\ No newline at end of file
diff --git a/libhwmon/wrapnvml.cpp b/libhwmon/wrapnvml.cpp
new file mode 100644
index 0000000000..757e8739de
--- /dev/null
+++ b/libhwmon/wrapnvml.cpp
@@ -0,0 +1,261 @@
+/*
+ * A trivial little dlopen()-based wrapper library for the
+ * NVIDIA NVML library, to allow runtime discovery of NVML on an
+ * arbitrary system.  This is all very hackish and simple-minded, but
+ * it serves my immediate needs in the short term until NVIDIA provides
+ * a static NVML wrapper library themselves, hopefully in
+ * CUDA 6.5 or maybe sometime shortly after.
+ *
+ * This trivial code is made available under the "new" 3-clause BSD license,
+ * and/or any of the GPL licenses you prefer.
+ * Feel free to use the code and modify as you see fit.
+ *
+ * John E. Stone - john.stone@gmail.com
+ *
+ * Modified to work with ethminer by
+ * 
+ * Philipp Andreas - github@smurfy.de
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "wraphelper.h"
+#include "wrapnvml.h"
+#if ETH_ETHASHCUDA
+#include "cuda_runtime.h"
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+wrap_nvml_handle * wrap_nvml_create() {
+  int i=0;
+  wrap_nvml_handle *nvmlh = NULL;
+
+  /* 
+   * We use hard-coded library installation locations for the time being...
+   * No idea where or if libnvidia-ml.so is installed on MacOS X, a 
+   * deep scouring of the filesystem on one of the Mac CUDA build boxes
+   * I used turned up nothing, so for now it's not going to work on OSX.
+   */
+#if defined(_WIN64)
+  /* 64-bit Windows */
+#define  libnvidia_ml "%PROGRAMFILES%/NVIDIA Corporation/NVSMI/nvml.dll"
+#elif defined(_WIN32) || defined(_MSC_VER)
+  /* 32-bit Windows */
+#define  libnvidia_ml "%PROGRAMFILES%/NVIDIA Corporation/NVSMI/nvml.dll"
+#elif defined(__linux) && (defined(__i386__) || defined(__ARM_ARCH_7A__))
+  /* 32-bit linux assumed */
+#define  libnvidia_ml "libnvidia-ml.so"
+#elif defined(__linux)
+  /* 64-bit linux assumed */
+#define  libnvidia_ml "libnvidia-ml.so"
+#else
+#define  libnvidia_ml ""
+#warning "Unrecognized platform: need NVML DLL path for this platform..."
+return NULL;
+#endif
+
+#if WIN32
+  char tmp[512];
+  ExpandEnvironmentStringsA(libnvidia_ml, tmp, sizeof(tmp)); 
+#else
+  char tmp[512] = libnvidia_ml;
+#endif
+
+  void *nvml_dll = wrap_dlopen(tmp);
+  if (nvml_dll == NULL)
+    return NULL;
+
+  nvmlh = (wrap_nvml_handle *) calloc(1, sizeof(wrap_nvml_handle));
+
+  nvmlh->nvml_dll = nvml_dll;  
+
+  nvmlh->nvmlInit = (wrap_nvmlReturn_t (*)(void)) 
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlInit");
+  nvmlh->nvmlDeviceGetCount = (wrap_nvmlReturn_t (*)(int *)) 
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetCount_v2");
+  nvmlh->nvmlDeviceGetHandleByIndex = (wrap_nvmlReturn_t (*)(int, wrap_nvmlDevice_t *)) 
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetHandleByIndex_v2");
+  nvmlh->nvmlDeviceGetPciInfo = (wrap_nvmlReturn_t (*)(wrap_nvmlDevice_t, wrap_nvmlPciInfo_t *)) 
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetPciInfo");
+  nvmlh->nvmlDeviceGetName = (wrap_nvmlReturn_t (*)(wrap_nvmlDevice_t, char *, int))
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetName");
+  nvmlh->nvmlDeviceGetTemperature = (wrap_nvmlReturn_t (*)(wrap_nvmlDevice_t, int, unsigned int *))
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetTemperature");
+  nvmlh->nvmlDeviceGetFanSpeed = (wrap_nvmlReturn_t (*)(wrap_nvmlDevice_t, unsigned int *))
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetFanSpeed");
+  nvmlh->nvmlDeviceGetPowerUsage = (wrap_nvmlReturn_t (*)(wrap_nvmlDevice_t, unsigned int *))
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetPowerUsage");
+  nvmlh->nvmlShutdown = (wrap_nvmlReturn_t (*)()) 
+    wrap_dlsym(nvmlh->nvml_dll, "nvmlShutdown");
+
+  if (nvmlh->nvmlInit == NULL || 
+      nvmlh->nvmlShutdown == NULL ||
+      nvmlh->nvmlDeviceGetCount == NULL ||
+      nvmlh->nvmlDeviceGetHandleByIndex == NULL || 
+      nvmlh->nvmlDeviceGetPciInfo == NULL ||
+      nvmlh->nvmlDeviceGetName == NULL ||
+      nvmlh->nvmlDeviceGetTemperature == NULL ||
+      nvmlh->nvmlDeviceGetFanSpeed == NULL ||
+      nvmlh->nvmlDeviceGetPowerUsage == NULL
+      ) {
+#if 0
+    printf("Failed to obtain all required NVML function pointers\n");
+#endif
+    wrap_dlclose(nvmlh->nvml_dll);
+    free(nvmlh);
+    return NULL;
+  }
+
+  nvmlh->nvmlInit();
+  nvmlh->nvmlDeviceGetCount(&nvmlh->nvml_gpucount);
+
+#if ETH_ETHASHCUDA
+  /* Query CUDA device count, in case it doesn't agree with NVML, since  */
+  /* CUDA will only report GPUs with compute capability greater than 1.0 */ 
+  if (cudaGetDeviceCount(&nvmlh->cuda_gpucount) != cudaSuccess) {
+#if 0
+    printf("Failed to query CUDA device count!\n");
+#endif
+    wrap_dlclose(nvmlh->nvml_dll);
+    free(nvmlh);
+    return NULL;
+  }
+#endif
+  nvmlh->devs = (wrap_nvmlDevice_t *) calloc(nvmlh->nvml_gpucount, sizeof(wrap_nvmlDevice_t));
+
+#if ETH_ETHASHCUDA
+  nvmlh->nvml_pci_domain_id = (unsigned int*) calloc(nvmlh->nvml_gpucount, sizeof(unsigned int));
+  nvmlh->nvml_pci_bus_id = (unsigned int*) calloc(nvmlh->nvml_gpucount, sizeof(unsigned int));
+  nvmlh->nvml_pci_device_id = (unsigned int*) calloc(nvmlh->nvml_gpucount, sizeof(unsigned int));
+  nvmlh->nvml_cuda_device_id = (int*) calloc(nvmlh->nvml_gpucount, sizeof(int));
+  nvmlh->cuda_nvml_device_id = (int*) calloc(nvmlh->cuda_gpucount, sizeof(int));
+
+  /* Obtain GPU device handles we're going to need repeatedly... */
+  for (i=0; i<nvmlh->nvml_gpucount; i++) {
+    nvmlh->nvmlDeviceGetHandleByIndex(i, &nvmlh->devs[i]);
+  } 
+
+  /* Query PCI info for each NVML device, and build table for mapping of */
+  /* CUDA device IDs to NVML device IDs and vice versa                   */
+  for (i=0; i<nvmlh->nvml_gpucount; i++) {
+    wrap_nvmlPciInfo_t pciinfo;
+    nvmlh->nvmlDeviceGetPciInfo(nvmlh->devs[i], &pciinfo);
+    nvmlh->nvml_pci_domain_id[i] = pciinfo.domain;
+    nvmlh->nvml_pci_bus_id[i]    = pciinfo.bus;
+    nvmlh->nvml_pci_device_id[i] = pciinfo.device;
+  }
+
+  /* build mapping of NVML device IDs to CUDA IDs */
+  for (i=0; i<nvmlh->nvml_gpucount; i++) {
+    nvmlh->nvml_cuda_device_id[i] = -1;
+  } 
+  for (i=0; i<nvmlh->cuda_gpucount; i++) {
+    cudaDeviceProp props;
+    nvmlh->cuda_nvml_device_id[i] = -1;
+
+    if (cudaGetDeviceProperties(&props, i) == cudaSuccess) {
+      int j;
+      for (j=0; j<nvmlh->nvml_gpucount; j++) {
+        if ((nvmlh->nvml_pci_domain_id[j] == props.pciDomainID) &&
+            (nvmlh->nvml_pci_bus_id[j]    == props.pciBusID) &&
+            (nvmlh->nvml_pci_device_id[j] == props.pciDeviceID)) {
+#if 0
+          printf("CUDA GPU[%d] matches NVML GPU[%d]\n", i, j);
+#endif
+          nvmlh->nvml_cuda_device_id[j] = i;
+          nvmlh->cuda_nvml_device_id[i] = j;
+        }
+      }
+    }
+  }
+
+#endif
+
+  return nvmlh;
+}
+
+
+int wrap_nvml_destroy(wrap_nvml_handle *nvmlh) {
+  nvmlh->nvmlShutdown();
+
+  wrap_dlclose(nvmlh->nvml_dll);
+  free(nvmlh);
+  return 0;
+}
+
+
+int wrap_nvml_get_gpucount(wrap_nvml_handle *nvmlh, int *gpucount) {
+  *gpucount = nvmlh->nvml_gpucount;
+  return 0; 
+}
+
+int wrap_cuda_get_gpucount(wrap_nvml_handle *nvmlh, int *gpucount) {
+  *gpucount = nvmlh->cuda_gpucount;
+  return 0; 
+}
+
+int wrap_nvml_get_gpu_name(wrap_nvml_handle *nvmlh,
+                           int gpuindex,
+                           char *namebuf,
+                           int bufsize) {
+  if (gpuindex < 0 || gpuindex >= nvmlh->nvml_gpucount)
+    return -1;
+
+  if (nvmlh->nvmlDeviceGetName(nvmlh->devs[gpuindex], namebuf, bufsize) != WRAPNVML_SUCCESS)
+    return -1; 
+
+  return 0;
+}
+
+
+int wrap_nvml_get_tempC(wrap_nvml_handle *nvmlh,
+                        int gpuindex, unsigned int *tempC) {
+  wrap_nvmlReturn_t rc;
+  if (gpuindex < 0 || gpuindex >= nvmlh->nvml_gpucount)
+    return -1;
+
+  rc = nvmlh->nvmlDeviceGetTemperature(nvmlh->devs[gpuindex], 0u /* NVML_TEMPERATURE_GPU */, tempC);
+  if (rc != WRAPNVML_SUCCESS) {
+    return -1; 
+  }
+
+  return 0;
+}
+
+
+int wrap_nvml_get_fanpcnt(wrap_nvml_handle *nvmlh,
+                          int gpuindex, unsigned int *fanpcnt) {
+  wrap_nvmlReturn_t rc;
+  if (gpuindex < 0 || gpuindex >= nvmlh->nvml_gpucount)
+    return -1;
+
+  rc = nvmlh->nvmlDeviceGetFanSpeed(nvmlh->devs[gpuindex], fanpcnt);
+  if (rc != WRAPNVML_SUCCESS) {
+    return -1; 
+  }
+
+  return 0;
+}
+
+
+int wrap_nvml_get_power_usage(wrap_nvml_handle *nvmlh,
+                              int gpuindex,
+                              unsigned int *milliwatts) {
+  if (gpuindex < 0 || gpuindex >= nvmlh->nvml_gpucount)
+    return -1;
+
+  if (nvmlh->nvmlDeviceGetPowerUsage(nvmlh->devs[gpuindex], milliwatts) != WRAPNVML_SUCCESS)
+    return -1; 
+
+  return 0;
+}
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+
diff --git a/libhwmon/wrapnvml.h b/libhwmon/wrapnvml.h
new file mode 100644
index 0000000000..cf818d70ea
--- /dev/null
+++ b/libhwmon/wrapnvml.h
@@ -0,0 +1,127 @@
+/*
+ * A trivial little dlopen()-based wrapper library for the 
+ * NVIDIA NVML library, to allow runtime discovery of NVML on an
+ * arbitrary system.  This is all very hackish and simple-minded, but
+ * it serves my immediate needs in the short term until NVIDIA provides 
+ * a static NVML wrapper library themselves, hopefully in 
+ * CUDA 6.5 or maybe sometime shortly after. 
+ *
+ * This trivial code is made available under the "new" 3-clause BSD license,
+ * and/or any of the GPL licenses you prefer.
+ * Feel free to use the code and modify as you see fit.
+ *
+ * John E. Stone - john.stone@gmail.com
+ *
+ */
+
+#ifndef _WRAPNVML_H_
+#define _WRAPNVML_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* 
+ * Ugly hacks to avoid dependencies on the real nvml.h until it starts
+ * getting included with the CUDA toolkit or a GDK that's got a known 
+ * install location, etc.
+ */
+typedef enum wrap_nvmlReturn_enum {
+  WRAPNVML_SUCCESS = 0
+} wrap_nvmlReturn_t;
+
+typedef void * wrap_nvmlDevice_t;
+
+/* our own version of the PCI info struct */
+typedef struct {
+  char bus_id_str[16];             /* string form of bus info */
+  unsigned int domain;
+  unsigned int bus;
+  unsigned int device;
+  unsigned int pci_device_id;      /* combined device and vendor id */
+  unsigned int pci_subsystem_id;
+  unsigned int res0;               /* NVML internal use only */
+  unsigned int res1;
+  unsigned int res2;
+  unsigned int res3;
+} wrap_nvmlPciInfo_t;
+
+
+/* 
+ * Handle to hold the function pointers for the entry points we need,
+ * and the shared library itself.
+ */
+typedef struct {
+  void *nvml_dll;
+  int nvml_gpucount;
+  int cuda_gpucount;
+  unsigned int *nvml_pci_domain_id;
+  unsigned int *nvml_pci_bus_id;
+  unsigned int *nvml_pci_device_id;
+  int *nvml_cuda_device_id;          /* map NVML dev to CUDA dev */
+  int *cuda_nvml_device_id;          /* map CUDA dev to NVML dev */
+  wrap_nvmlDevice_t *devs;
+  wrap_nvmlReturn_t (*nvmlInit)(void);
+  wrap_nvmlReturn_t (*nvmlDeviceGetCount)(int *);
+  wrap_nvmlReturn_t (*nvmlDeviceGetHandleByIndex)(int, wrap_nvmlDevice_t *);
+  wrap_nvmlReturn_t (*nvmlDeviceGetPciInfo)(wrap_nvmlDevice_t, wrap_nvmlPciInfo_t *);
+  wrap_nvmlReturn_t (*nvmlDeviceGetName)(wrap_nvmlDevice_t, char *, int);
+  wrap_nvmlReturn_t (*nvmlDeviceGetTemperature)(wrap_nvmlDevice_t, int, unsigned int *);
+  wrap_nvmlReturn_t (*nvmlDeviceGetFanSpeed)(wrap_nvmlDevice_t, unsigned int *);
+  wrap_nvmlReturn_t (*nvmlDeviceGetPowerUsage)(wrap_nvmlDevice_t, unsigned int *);
+  wrap_nvmlReturn_t (*nvmlShutdown)(void);
+} wrap_nvml_handle;
+
+
+wrap_nvml_handle * wrap_nvml_create();
+int wrap_nvml_destroy(wrap_nvml_handle *nvmlh);
+
+/*
+ * Query the number of GPUs seen by NVML
+ */
+int wrap_nvml_get_gpucount(wrap_nvml_handle *nvmlh, int *gpucount);
+
+/*
+ * Query the number of GPUs seen by CUDA 
+ */
+int wrap_cuda_get_gpucount(wrap_nvml_handle *nvmlh, int *gpucount);
+
+
+/*
+ * query the name of the GPU model from the CUDA device ID
+ *
+ */
+int wrap_nvml_get_gpu_name(wrap_nvml_handle *nvmlh,
+                           int gpuindex, 
+                           char *namebuf,
+                           int bufsize);
+
+/* 
+ * Query the current GPU temperature (Celsius), from the CUDA device ID
+ */
+int wrap_nvml_get_tempC(wrap_nvml_handle *nvmlh,
+                        int gpuindex, unsigned int *tempC);
+
+/* 
+ * Query the current GPU fan speed (percent) from the CUDA device ID
+ */
+int wrap_nvml_get_fanpcnt(wrap_nvml_handle *nvmlh,
+                          int gpuindex, unsigned int *fanpcnt);
+
+/* 
+ * Query the current GPU power usage in millwatts from the CUDA device ID
+ *
+ * This feature is only available on recent GPU generations and may be
+ * limited in some cases only to Tesla series GPUs.
+ * If the query is run on an unsupported GPU, this routine will return -1.
+ */
+int wrap_nvml_get_power_usage(wrap_nvml_handle *nvmlh,
+                              int gpuindex,
+                              unsigned int *milliwatts);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif