Files
OpenRGB/i2c_smbus/Windows/i2c_smbus_pawnio.cpp
Adam Honse 043c3c8a9b Split out detection system from ResourceManager into DetectionManager
* Split detection system out into its own class, DetectionManager
    * Clean up ResourceManger's many callbacks into just two, one for detection and one general purpose
2026-02-07 01:50:52 -06:00

356 lines
13 KiB
C++

/*---------------------------------------------------------*\
| i2c_smbus_pawnio.cpp |
| |
| PawnIO SMBUS driver for Windows |
| |
| Stephen Horvath (Steve-Tech) 04 May 2025 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-or-later |
\*---------------------------------------------------------*/
#include <string>
#include "DetectionManager.h"
#include "i2c_smbus_pawnio.h"
#include "LogManager.h"
#include "PawnIOLib.h"
#include "ResourceManager.h"
#include "SettingsManager.h"
#include "wmi.h"
std::unordered_map<std::string, int> i2c_smbus_pawnio::using_handle;
i2c_smbus_pawnio::i2c_smbus_pawnio(HANDLE handle, std::string name)
{
/*-----------------------------------------------------*\
| Store bus information |
| TODO: Remove name field once all drivers use the same |
| ioctl names |
\*-----------------------------------------------------*/
this->handle = handle;
this->name = name;
/*-----------------------------------------------------*\
| Get driver settings |
\*-----------------------------------------------------*/
json drivers_settings = ResourceManager::get()->GetSettingsManager()->GetSettings("Drivers");
/*-----------------------------------------------------*\
| Get bus information |
\*-----------------------------------------------------*/
const SIZE_T in_size = 1;
ULONG64 in[in_size] = {0};
const SIZE_T out_size = 3;
ULONG64 out[out_size];
SIZE_T return_size;
HRESULT status;
status = pawnio_execute(handle, "ioctl_identity", in, in_size, out, out_size, &return_size);
if(!status)
{
this->pci_vendor = (out[2] & 0x000000000000FFFF);
this->pci_device = (out[2] & 0x00000000FFFF0000) >> 16;
this->pci_subsystem_vendor = (out[2] & 0x0000FFFF0000FFFF) >> 32;
this->pci_subsystem_device = (out[2] & 0xFFFF000000000000) >> 48;
char name_str[9];
name_str[0] = (out[0] & 0x00000000000000FF);
name_str[1] = (out[0] & 0x000000000000FF00) >> 8;
name_str[2] = (out[0] & 0x0000000000FF0000) >> 16;
name_str[3] = (out[0] & 0x00000000FF000000) >> 24;
name_str[4] = (out[0] & 0x000000FF00000000) >> 32;
name_str[5] = (out[0] & 0x0000FF0000000000) >> 40;
name_str[6] = (out[0] & 0x00FF000000000000) >> 48;
name_str[7] = (out[0] & 0xFF00000000000000) >> 56;
name_str[8] = 0;
strncpy( this->device_name, name_str, 512 );
}
/*-----------------------------------------------------*\
| Get shared SMBus access setting |
\*-----------------------------------------------------*/
bool shared_smbus_access = true;
if(drivers_settings.contains("shared_smbus_access"))
{
shared_smbus_access = drivers_settings["shared_smbus_access"].get<bool>();
}
/*-----------------------------------------------------*\
| Create global SMBus mutex if enabled |
\*-----------------------------------------------------*/
if(shared_smbus_access)
{
global_smbus_access_handle = CreateMutexA(NULL, FALSE, GLOBAL_SMBUS_MUTEX_NAME);
}
using_handle[name]++;
}
i2c_smbus_pawnio::~i2c_smbus_pawnio()
{
/*-----------------------------------------------------*\
| Close global SMBus mutex |
\*-----------------------------------------------------*/
if(global_smbus_access_handle != NULL)
{
CloseHandle(global_smbus_access_handle);
}
/*-----------------------------------------------------*\
| TODO: find a way to do this without name field |
\*-----------------------------------------------------*/
if(--using_handle[name] == 0 && pawnio_close(handle))
{
LOG_ERROR("PawnIO failed to close");
}
}
s32 i2c_smbus_pawnio::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data)
{
/*-----------------------------------------------------*\
| Lock SMBus mutex |
\*-----------------------------------------------------*/
if(global_smbus_access_handle != NULL)
{
WaitForSingleObject(global_smbus_access_handle, INFINITE);
}
/*-----------------------------------------------------*\
| Pack input data |
\*-----------------------------------------------------*/
const SIZE_T in_size = 9;
ULONG64 in[in_size];
const SIZE_T out_size = 5;
ULONG64 out[out_size];
SIZE_T return_size;
HRESULT status;
in[0] = addr;
in[1] = read_write;
in[2] = command;
in[3] = size;
if(data != NULL)
{
memcpy( &in[4], data, sizeof(i2c_smbus_data));
}
/*-----------------------------------------------------*\
| Perform SMBus transfer |
\*-----------------------------------------------------*/
status = pawnio_execute(handle, "ioctl_smbus_xfer", in, in_size, out, out_size, &return_size);
/*-----------------------------------------------------*\
| Unpack output data |
\*-----------------------------------------------------*/
if(data != NULL)
{
memcpy( data, &out[0], sizeof(i2c_smbus_data));
}
/*-----------------------------------------------------*\
| Unlock SMBus mutex |
\*-----------------------------------------------------*/
if(global_smbus_access_handle != NULL)
{
ReleaseMutex(global_smbus_access_handle);
}
return(status);
}
s32 i2c_smbus_pawnio::i2c_xfer(u8 /*addr*/, char /*read_write*/, int* /*size*/, u8* /*data*/)
{
return(-1);
}
HRESULT i2c_smbus_pawnio::start_pawnio(std::string filename, PHANDLE phandle)
{
char exePath[MAX_PATH];
HANDLE handle;
HRESULT status;
LOG_INFO("Start PawnIO: %s", filename.c_str());
/*-----------------------------------------------------*\
| Get the path of the executable |
\*-----------------------------------------------------*/
if(!GetModuleFileNameA(NULL, exePath, MAX_PATH))
{
LOG_ERROR("Failed to get executable path, PawnIO initialization aborted");
return E_FAIL;
}
/*-----------------------------------------------------*\
| Construct the path to `filename` in the executable's |
| directory |
\*-----------------------------------------------------*/
std::filesystem::path exeDir = std::filesystem::path(exePath).parent_path();
std::filesystem::path filePath = exeDir / filename;
/*-----------------------------------------------------*\
| Check if the file exists |
\*-----------------------------------------------------*/
if(!std::filesystem::exists(filePath))
{
LOG_ERROR("Failed to find %s in the executable's directory, PawnIO initialization aborted", filename.c_str());
return(E_FAIL);
}
/*-----------------------------------------------------*\
| Open the file |
\*-----------------------------------------------------*/
std::ifstream file(filePath, std::ios::binary);
if(!file.is_open())
{
LOG_ERROR("Failed to open %s, PawnIO initialization aborted", filename.c_str());
return(E_FAIL);
}
/*-----------------------------------------------------*\
| Read the contents of the file into a vector of bytes |
\*-----------------------------------------------------*/
std::vector<char> blob((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
/*-----------------------------------------------------*\
| Close the file |
\*-----------------------------------------------------*/
file.close();
/*-----------------------------------------------------*\
| Open PawnIO driver |
\*-----------------------------------------------------*/
status = pawnio_open(phandle);
if(status)
{
if(status == E_ACCESSDENIED)
{
LOG_ERROR("Permission Denied, PawnIO initialization aborted");
}
else
{
LOG_ERROR("Could not open PawnIO, PawnIO initialization aborted");
}
return(status);
}
handle = *phandle;
/*-----------------------------------------------------*\
| Load the file into PawnIO |
\*-----------------------------------------------------*/
status = pawnio_load(handle, reinterpret_cast<const UCHAR*>(blob.data()), blob.size());
/*-----------------------------------------------------*\
| Check if the load was successful |
\*-----------------------------------------------------*/
if(status)
{
pawnio_close(handle);
if (status == HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED))
{
LOG_INFO("PawnIO module initialization aborted (unsupported)");
}
else
{
LOG_ERROR("PawnIO module initialization aborted (code=%ld)", status);
}
return(status);
}
/*-----------------------------------------------------*\
| Log a message and return OK if PawnIO successfully |
| opened |
\*-----------------------------------------------------*/
LOG_INFO("PawnIO initialized successully");
return(S_OK);
}
s32 piix4_port_sel(HANDLE pawnio_handle, s32 port)
{
const SIZE_T in_size = 1;
ULONG64 in[in_size] = {(ULONG64)port};
const SIZE_T out_size = 1;
ULONG64 out[out_size];
SIZE_T return_size;
HRESULT status;
/*-----------------------------------------------------*\
| Execute PIIX4 port_sel ioctl |
\*-----------------------------------------------------*/
status = pawnio_execute(pawnio_handle, "ioctl_piix4_port_sel", in, in_size, out, 1, &return_size);
return(status ? -EIO : 0);
}
bool i2c_smbus_pawnio_detect()
{
ULONG dll_version;
if(pawnio_version(&dll_version))
{
LOG_INFO("PawnIO is not loaded, PawnIO I2C bus detection aborted");
return(false);
}
i2c_smbus_interface * bus;
HANDLE pawnio_handle;
/*-----------------------------------------------------*\
| Try to load Intel (i801) SMBus driver |
\*-----------------------------------------------------*/
if(i2c_smbus_pawnio::start_pawnio("SmbusI801.bin", &pawnio_handle) == S_OK)
{
bus = new i2c_smbus_pawnio(pawnio_handle, "i801");
DetectionManager::get()->RegisterI2CBus(bus);
}
/*-----------------------------------------------------*\
| Try to load AMD (PIIX4) SMBus driver - primary bus |
\*-----------------------------------------------------*/
if(i2c_smbus_pawnio::start_pawnio("SmbusPIIX4.bin", &pawnio_handle) == S_OK)
{
/*-------------------------------------------------*\
| Select port 0 |
\*-------------------------------------------------*/
piix4_port_sel(pawnio_handle, 0);
bus = new i2c_smbus_pawnio(pawnio_handle, "piix4");
DetectionManager::get()->RegisterI2CBus(bus);
}
/*-----------------------------------------------------*\
| Try to load AMD (PIIX4 SMBus driver - secondary bus |
\*-----------------------------------------------------*/
if(i2c_smbus_pawnio::start_pawnio("SmbusPIIX4.bin", &pawnio_handle) == S_OK)
{
/*--------------------------------------------------*\
| Select port 1 |
\*-------------------------------------------------*/
piix4_port_sel(pawnio_handle, 1);
bus = new i2c_smbus_pawnio(pawnio_handle, "piix4");
DetectionManager::get()->RegisterI2CBus(bus);
}
/*-----------------------------------------------------*\
| Try to load Nuvoton (NCT6793) SMBus driver |
\*-----------------------------------------------------*/
if(i2c_smbus_pawnio::start_pawnio("SmbusNCT6793.bin", &pawnio_handle) == S_OK)
{
bus = new i2c_smbus_pawnio(pawnio_handle, "NCT6793");
DetectionManager::get()->RegisterI2CBus(bus);
}
return(true);
}
REGISTER_I2C_BUS_DETECTOR(i2c_smbus_pawnio_detect);