From 006289c784ebebcfea14ca8ce2db63d3c40831ba Mon Sep 17 00:00:00 2001 From: Adam Honse Date: Wed, 30 Jan 2019 22:00:41 -0600 Subject: [PATCH] Initial commit --- OpenAuraSDK.sln | 31 ++ OpenAuraSDK/AuraController.cpp | 40 ++ OpenAuraSDK/AuraController.h | 58 +++ OpenAuraSDK/OpenAuraSDK.cpp | Bin 0 -> 16978 bytes OpenAuraSDK/OpenAuraSDK.h | 10 + OpenAuraSDK/OpenAuraSDK.vcxproj | 186 +++++++ OpenAuraSDK/OpenAuraSDK.vcxproj.filters | 57 +++ OpenAuraSDK/dllmain.cpp | Bin 0 -> 906 bytes OpenAuraSDK/i2c_smbus.cpp | 121 +++++ OpenAuraSDK/i2c_smbus.h | 67 +++ OpenAuraSDK/i2c_smbus_i801.cpp | 478 ++++++++++++++++++ OpenAuraSDK/i2c_smbus_i801.h | 87 ++++ OpenAuraSDK/i2c_smbus_piix4.cpp | 168 ++++++ OpenAuraSDK/i2c_smbus_piix4.h | 48 ++ dependencies/inpout32_1501/Win32/inpout32.dll | Bin 0 -> 98304 bytes dependencies/inpout32_1501/Win32/inpout32.h | 32 ++ dependencies/inpout32_1501/Win32/inpout32.lib | Bin 0 -> 5116 bytes 17 files changed, 1383 insertions(+) create mode 100644 OpenAuraSDK.sln create mode 100644 OpenAuraSDK/AuraController.cpp create mode 100644 OpenAuraSDK/AuraController.h create mode 100644 OpenAuraSDK/OpenAuraSDK.cpp create mode 100644 OpenAuraSDK/OpenAuraSDK.h create mode 100644 OpenAuraSDK/OpenAuraSDK.vcxproj create mode 100644 OpenAuraSDK/OpenAuraSDK.vcxproj.filters create mode 100644 OpenAuraSDK/dllmain.cpp create mode 100644 OpenAuraSDK/i2c_smbus.cpp create mode 100644 OpenAuraSDK/i2c_smbus.h create mode 100644 OpenAuraSDK/i2c_smbus_i801.cpp create mode 100644 OpenAuraSDK/i2c_smbus_i801.h create mode 100644 OpenAuraSDK/i2c_smbus_piix4.cpp create mode 100644 OpenAuraSDK/i2c_smbus_piix4.h create mode 100644 dependencies/inpout32_1501/Win32/inpout32.dll create mode 100644 dependencies/inpout32_1501/Win32/inpout32.h create mode 100644 dependencies/inpout32_1501/Win32/inpout32.lib diff --git a/OpenAuraSDK.sln b/OpenAuraSDK.sln new file mode 100644 index 00000000..efc9459c --- /dev/null +++ b/OpenAuraSDK.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenAuraSDK", "OpenAuraSDK\OpenAuraSDK.vcxproj", "{6D22BFF3-C1DF-407A-8816-05D63919A991}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6D22BFF3-C1DF-407A-8816-05D63919A991}.Debug|x64.ActiveCfg = Debug|x64 + {6D22BFF3-C1DF-407A-8816-05D63919A991}.Debug|x64.Build.0 = Debug|x64 + {6D22BFF3-C1DF-407A-8816-05D63919A991}.Debug|x86.ActiveCfg = Debug|Win32 + {6D22BFF3-C1DF-407A-8816-05D63919A991}.Debug|x86.Build.0 = Debug|Win32 + {6D22BFF3-C1DF-407A-8816-05D63919A991}.Release|x64.ActiveCfg = Release|x64 + {6D22BFF3-C1DF-407A-8816-05D63919A991}.Release|x64.Build.0 = Release|x64 + {6D22BFF3-C1DF-407A-8816-05D63919A991}.Release|x86.ActiveCfg = Release|Win32 + {6D22BFF3-C1DF-407A-8816-05D63919A991}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A2BB3E13-D506-413D-900E-F2B1323B3BB3} + EndGlobalSection +EndGlobal diff --git a/OpenAuraSDK/AuraController.cpp b/OpenAuraSDK/AuraController.cpp new file mode 100644 index 00000000..8319df4a --- /dev/null +++ b/OpenAuraSDK/AuraController.cpp @@ -0,0 +1,40 @@ +/*-----------------------------------------*\ +| AuraController.h | +| | +| Driver for ASUS Aura RGB lighting | +| controller | +| | +| Adam Honse (CalcProgrammer1) 8/19/2018 | +\*-----------------------------------------*/ + +#include "AuraController.h" + +unsigned char AuraController::AuraRegisterRead(aura_register reg) +{ + //Write Aura register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Read Aura value + return(bus->i2c_smbus_read_byte_data(dev, 0x81)); + +} + +void AuraController::AuraRegisterWrite(aura_register reg, unsigned char val) +{ + //Write Aura register + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write Aura value + bus->i2c_smbus_write_byte_data(dev, 0x01, val); + +} + +void AuraController::AuraRegisterWriteBlock(aura_register reg, unsigned char * data, unsigned char sz) +{ + //Write Aura register (0x8000 for colors) + bus->i2c_smbus_write_word_data(dev, 0x00, ((reg << 8) & 0xFF00) | ((reg >> 8) & 0x00FF)); + + //Write Aura block data + bus->i2c_smbus_write_block_data(dev, 0x03, 15, data); + +} \ No newline at end of file diff --git a/OpenAuraSDK/AuraController.h b/OpenAuraSDK/AuraController.h new file mode 100644 index 00000000..626efcee --- /dev/null +++ b/OpenAuraSDK/AuraController.h @@ -0,0 +1,58 @@ +/*-----------------------------------------*\ +| AuraController.h | +| | +| Definitions and types for ASUS Aura RGB | +| lighting controller | +| | +| Adam Honse (CalcProgrammer1) 8/19/2018 | +\*-----------------------------------------*/ + +#include "i2c_smbus.h" + +#pragma once + +typedef unsigned char aura_dev_id; +typedef unsigned short aura_register; + +#define AURA_APPLY_VAL 0x01 /* Value for Apply Changes Register */ + +enum +{ + AURA_REG_COLORS_DIRECT = 0x8000, /* Colors for Direct Mode 15 bytes */ + AURA_REG_COLORS_EFFECT = 0x8010, /* Colors for Internal Effects 15 bytes */ + AURA_REG_DIRECT = 0x8020, /* "Direct Access" Selection Register */ + AURA_REG_MODE = 0x8021, /* AURA Mode Selection Register */ + AURA_REG_APPLY = 0x80A0 /* AURA Apply Changes Register */ +}; + +enum +{ + AURA_MODE_OFF = 0, /* OFF mode */ + AURA_MODE_STATIC = 1, /* Static color mode */ + AURA_MODE_BREATHING = 2, /* Breathing effect mode */ + AURA_MODE_FLASHING = 3, /* Flashing effect mode */ + AURA_MODE_SPECTRUM_CYCLE = 4, /* Spectrum Cycle mode */ + AURA_MODE_RAINBOW = 5, /* Rainbow effect mode */ + AURA_MODE_SPECTRUM_CYCLE_BREATHING = 6, /* Rainbow Breathing effect mode */ + AURA_MODE_CHASE_FADE = 7, /* Chase with Fade effect mode */ + AURA_MODE_SPECTRUM_CYCLE_CHASE_FADE = 8, /* Chase with Fade, Rainbow effect mode */ + AURA_MODE_CHASE = 9, /* Chase effect mode */ + AURA_MODE_SPECTRUM_CYCLE_CHASE = 10, /* Chase with Rainbow effect mode */ + AURA_MODE_SPECTRUM_CYCLE_WAVE = 11, /* Wave effect mode */ + AURA_MODE_CHASE_RAINBOW_PULSE = 12, /* Chase with Rainbow Pulse effect mode*/ + AURA_MODE_RANDOM_FLICKER = 13, /* Random flicker effect mode */ +}; + +class AuraController +{ +public: + unsigned char AuraRegisterRead(aura_register reg); + void AuraRegisterWrite(aura_register reg, unsigned char val); + void AuraRegisterWriteBlock(aura_register reg, unsigned char * data, unsigned char sz); + i2c_smbus_interface * bus; + aura_dev_id dev; + +private: + + +}; \ No newline at end of file diff --git a/OpenAuraSDK/OpenAuraSDK.cpp b/OpenAuraSDK/OpenAuraSDK.cpp new file mode 100644 index 0000000000000000000000000000000000000000..711a178eda765eea38e58d22d1643d2bdcf08d57 GIT binary patch literal 16978 zcmezWPoF`bL4m=ap@1QkA&0-?)=!H2HYYRWFqAT+fbCUc z$Yd~LNM?vm^QoJVOCPCPOAe1%nA0dNUa;7z`K;3Fw4{ zsWUh%N*Ib5@)>d%au`w>iU{b$6)U*?Xv0v(kO~fYP<+^dLjn|f#SA43DGXK&Rw(i| z@c72&HU$PP20I1?hGZ=6E@rTXn_b3`&yWd@cV~tohE#AYrov4IrJ6Eu?4~kkz(t+F zvE{;0#E=PgQ4vE3LnSy3DlnuViRv&YfYVYjLncEyIOT!T7bp#uFeor2F(fh+Go&(r zR3$=FZag@3lrh9Z-2_T2Wel2N*H(i`1qKMjOg$hydEiuB#E{02$dC+9XIfyJK&cDj zXNYbExLauY-!C>~&Xiy6upG8sx3k{L1>6c{v6{0wm`OeLzDL3sq^V+96J z1|tS%hIj@Sh9Cw{hA;+Kh9HJ`hF}I?1}BD4hG2$xh5!an22X|v1``G=6o11^hU6Fp z23rONaLAT3C}7D^$T>uVK@%G9Ff(AZ27?9xy`a>j3C*RN47v<#>V*p^7l2Z9DMJoJ36Ast z%DW}tT$Be5Sy+hIg3}LjIKy%qB)&j30x0$1E9o%92WAE+Eg+{w2pcu-k=26yQU*;a z5H>_Dviq=?S$Yfw;BqH}As$?IBr{|)XfVLchNW$UZ$b4B$Rv<0pxm4SuB{NY9H=DE zh36Vwh7yK+aM=#39U-L)NGC`=s4fK6rXV*egG(q_O{&1)%iz!8!r;mf&)~=q$`Hcf z4=tYzz@d&?bs$41gC~PCgEv^cApzAv46Y203@%``3JgXBRJ$>RGWan#Lsc7tRfF7) zDC{`m-Sg_=@TXk zO2M$236^$Ya>Wce42j^nCY~XYAq8CGVy2rkaM_X%u8Hy(G#FrQ4AgimVW?mzVNe3Q zMTwyt%7WCf`3$8DCGc_*6lO4&6oBhBQ2mkyHW?@EYzfUrITsBRBtNM!)ECJ-qElGj1) z5Kv1Ol$*5}^cj%j5~Ld$BlIFt7bHA0859__859^`X%?R;5cdRtTZl>Ewg;$ArLnrNRS(dbM4^HZ-`bI!f&W7SjvO>7*t0X z5Qtm+^L2d+G!H3#Yiz{Ycm8T1%HW>hhNMms>E4eC>4 zwog!f1ak|f3qa!oi3~*ypivQ!eL3J!G*EjH?GKO}DD{Uiw4FVW=?dfqO8o)q zHw|=rfZRZ-KR`Xdf%XT;4V3x=)OQ_de}LRTsXsuy?}7FQ$PJYG12ldx(Eb3qfl_~f zMnDGIA0RhS>JP_3Q0{`(TZkQ*rV2dLdV(Eb3qfl_~f+RX#)50D!u^#`ckJkb6Cxq(uDfZELi?GKO} zDD?-Z-8|6#0J(uue}LM}1MLrx8z}V$sNFo!{s6fF<_}Wa?kNnJ;58wj6+ED}CGGrz zI?@iZ3m1m@1zjFrn-@0!57R?Cf56rTz-+=z!~B6QPS9tUu&=UIWk^9<;R2cKf|-hL zFS>~?C^72`U3~;v=?u{cGFt)7boA6ftX(kE6&S)862WVjQ^D(CAT}v5V6zK7#)!2G z)nA}+gY-N>>x@9Y!)8|sc$F_i9ca!JvN8(hIv9;V1d(kr1pAJVO@=sEWe{(Z5m7c7 zQNbo-qHHpzf=wnw*6bD+KyST z(bIO!x{kKC8!;Gw_XL5`feDs92h>P$uw9F=^yLOlZ3ufodu-503P8SgVIZ{97SbEy zWGDvb8_;fU1qRTbCeW^I(2k5~=x(4`=Oj>X)3 zrXY7<_Cjdy23TqV*$>*m30X}I69uiSb_CaoP7MAG;TSzX+Plw@A%G!(!G|FdTs9$G sh!Brw2xD+$@WJXfNWMZ&_n`e1pqyI7kjJ3F5W*0|5X#^REfZ=P0DlH`761SM literal 0 HcmV?d00001 diff --git a/OpenAuraSDK/OpenAuraSDK.h b/OpenAuraSDK/OpenAuraSDK.h new file mode 100644 index 00000000..3f8cb34b --- /dev/null +++ b/OpenAuraSDK/OpenAuraSDK.h @@ -0,0 +1,10 @@ +#pragma once + +typedef unsigned int AuraBusDriverType; + +enum +{ + I2C_DRIVER_SMBUS_PIIX4, + I2C_DRIVER_SMBUS_I801, + NUM_I2C_DRIVERS +}; \ No newline at end of file diff --git a/OpenAuraSDK/OpenAuraSDK.vcxproj b/OpenAuraSDK/OpenAuraSDK.vcxproj new file mode 100644 index 00000000..351f9daa --- /dev/null +++ b/OpenAuraSDK/OpenAuraSDK.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {6D22BFF3-C1DF-407A-8816-05D63919A991} + Win32Proj + OpenAuraSDK + 10.0.17134.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + .exe + + + true + .exe + + + false + .exe + + + false + .exe + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;OPENAURASDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + ..\dependencies\inpout32_1501\Win32;%(AdditionalIncludeDirectories) + true + + + + + Windows + true + ..\dependencies\inpout32_1501\Win32;%(AdditionalLibraryDirectories) + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;OPENAURASDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + + + + + Windows + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;OPENAURASDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + ..\dependencies\inpout32_1501\Win32;%(AdditionalIncludeDirectories) + true + + + + + Windows + true + true + true + ..\dependencies\inpout32_1501\Win32;%(AdditionalLibraryDirectories) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;OPENAURASDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS + true + + + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenAuraSDK/OpenAuraSDK.vcxproj.filters b/OpenAuraSDK/OpenAuraSDK.vcxproj.filters new file mode 100644 index 00000000..d7d7b34d --- /dev/null +++ b/OpenAuraSDK/OpenAuraSDK.vcxproj.filters @@ -0,0 +1,57 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/OpenAuraSDK/dllmain.cpp b/OpenAuraSDK/dllmain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44c1a0c401f1b6e0d2e4572add2e869d0005f854 GIT binary patch literal 906 zcmezWPoF`bL4hHKA%`J{A(tVMA(J7GL60Gsp@5-)L4m=FL4m=AA(bHwES}0x%%H$f z!jQp`%AmlI%8p7CQNp0Wkj9YDP=ut;g~5lxhe3fM5o~4-LncEq zLn1>7LncE$*e+fME(T?=?qr4>hEj$Uu<1$+7T1#}L90#1IK~FUT*xP`_v}C@^?1_%ir2xG;n=_%OIKC@^F&_%h@( zq%f2+Z`#d%(unha4um;X2@VrV9)@E z2_e5g%z>GJY91(Vi@`AniT8Mh0EQq2e+Fj;SB7AQV1{@GM}`oF5C%sEX9f=jD-^e2 z>J4G=U I2C_SMBUS_BLOCK_MAX) + { + length = I2C_SMBUS_BLOCK_MAX; + } + data.block[0] = length; + memcpy(&data.block[1], values, length); + return i2c_smbus_xfer(addr, I2C_SMBUS_WRITE, command, I2C_SMBUS_BLOCK_DATA, &data); +} + +s32 i2c_smbus_interface::i2c_smbus_read_i2c_block_data(u8 addr, u8 command, u8 length, u8 *values) +{ + i2c_smbus_data data; + if (length > I2C_SMBUS_BLOCK_MAX) + { + length = I2C_SMBUS_BLOCK_MAX; + } + data.block[0] = length; + if (i2c_smbus_xfer(addr, I2C_SMBUS_READ, command, I2C_SMBUS_I2C_BLOCK_DATA, &data)) + { + return -1; + } + else + { + memcpy(values, &data.block[1], data.block[0]); + return data.block[0]; + } +} + +s32 i2c_smbus_interface::i2c_smbus_write_i2c_block_data(u8 addr, u8 command, u8 length, const u8 *values) +{ + i2c_smbus_data data; + if (length > I2C_SMBUS_BLOCK_MAX) + { + length = I2C_SMBUS_BLOCK_MAX; + } + data.block[0] = length; + memcpy(&data.block[1], values, length); + return i2c_smbus_xfer(addr, I2C_SMBUS_WRITE, command, I2C_SMBUS_I2C_BLOCK_DATA, &data); +} \ No newline at end of file diff --git a/OpenAuraSDK/i2c_smbus.h b/OpenAuraSDK/i2c_smbus.h new file mode 100644 index 00000000..e1d5e016 --- /dev/null +++ b/OpenAuraSDK/i2c_smbus.h @@ -0,0 +1,67 @@ +/*-----------------------------------------*\ +| i2c_smbus.h | +| | +| Definitions and types for SMBUS drivers | +| | +| Adam Honse (CalcProgrammer1) 8/8/2018 | +| Portions based on Linux source code | +| GNU GPL v2 | +\*-----------------------------------------*/ + +#include +#include +#include +#include "inpout32.h" + +typedef UINT8 u8; +typedef UINT16 u16; +typedef UINT32 uint32_t; +typedef INT32 s32; + +#pragma comment(lib, "inpout32.lib") +#pragma once + +//Data for SMBus Messages +#define I2C_SMBUS_BLOCK_MAX 32 + +union i2c_smbus_data +{ + u8 byte; + u16 word; + u8 block[I2C_SMBUS_BLOCK_MAX + 2]; +}; + +// i2c_smbus_xfer read or write markers +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +// SMBus transaction types (size parameter in the above functions) +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_PROC_CALL 4 +#define I2C_SMBUS_BLOCK_DATA 5 +#define I2C_SMBUS_I2C_BLOCK_BROKEN 6 +#define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ +#define I2C_SMBUS_I2C_BLOCK_DATA 8 + +class i2c_smbus_interface +{ +public: + //Functions derived from i2c-core.c + s32 i2c_smbus_write_quick(u8 addr, u8 value); + s32 i2c_smbus_read_byte(u8 addr); + s32 i2c_smbus_write_byte(u8 addr, u8 value); + s32 i2c_smbus_read_byte_data(u8 addr, u8 command); + s32 i2c_smbus_write_byte_data(u8 addr, u8 command, u8 value); + s32 i2c_smbus_read_word_data(u8 addr, u8 command); + s32 i2c_smbus_write_word_data(u8 addr, u8 command, u16 value); + s32 i2c_smbus_read_block_data(u8 addr, u8 command, u8 *values); + s32 i2c_smbus_write_block_data(u8 addr, u8 command, u8 length, const u8 *values); + s32 i2c_smbus_read_i2c_block_data(u8 addr, u8 command, u8 length, u8 *values); + s32 i2c_smbus_write_i2c_block_data(u8 addr, u8 command, u8 length, const u8 *values); + + //Virtual function to be implemented by the driver + virtual s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data) = 0; +}; \ No newline at end of file diff --git a/OpenAuraSDK/i2c_smbus_i801.cpp b/OpenAuraSDK/i2c_smbus_i801.cpp new file mode 100644 index 00000000..267fc6bf --- /dev/null +++ b/OpenAuraSDK/i2c_smbus_i801.cpp @@ -0,0 +1,478 @@ +/*-----------------------------------------*\ +| i2c_smbus_i801.cpp | +| | +| i801 SMBUS driver for Windows | +| | +| Adam Honse (CalcProgrammer1) 1/29/2019 | +| Portions based on Linux source code | +| GNU GPL v2 | +\*-----------------------------------------*/ + +#include "i2c_smbus_i801.h" + +/* Return negative errno on error. */ +s32 i2c_smbus_i801::i801_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data) +{ + int hwpec = 0; + int block = 0; + int ret = 0, xact = 0; + //struct i801_priv *priv = i2c_get_adapdata(adap); + + //mutex_lock(&priv->acpi_lock); + //if (priv->acpi_reserved) { + // mutex_unlock(&priv->acpi_lock); + // return -EBUSY; + //} + + //pm_runtime_get_sync(&priv->pci_dev->dev); + + //hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) + // && size != I2C_SMBUS_QUICK + // && size != I2C_SMBUS_I2C_BLOCK_DATA; + + switch (size) + { + case I2C_SMBUS_QUICK: + Out32(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01)); + xact = I801_QUICK; + break; + case I2C_SMBUS_BYTE: + Out32(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + Out32(SMBHSTCMD, command); + xact = I801_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + Out32(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01)); + Out32(SMBHSTCMD, command); + if (read_write == I2C_SMBUS_WRITE) + Out32(SMBHSTDAT0, data->byte); + xact = I801_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + Out32(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01)); + Out32(SMBHSTCMD, command); + if (read_write == I2C_SMBUS_WRITE) + { + Out32(SMBHSTDAT0, data->word & 0xff); + Out32(SMBHSTDAT1, (data->word & 0xff00) >> 8); + } + xact = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + Out32(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01)); + Out32(SMBHSTCMD, command); + block = 1; + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + /* + * NB: page 240 of ICH5 datasheet shows that the R/#W + * bit should be cleared here, even when reading. + * However if SPD Write Disable is set (Lynx Point and later), + * the read will fail if we don't set the R/#W bit. + */ + Out32(SMBHSTADD, ((addr & 0x7f) << 1) | (read_write & 0x01)); + + if (read_write == I2C_SMBUS_READ) + { + /* NB: page 240 of ICH5 datasheet also shows + * that DATA1 is the cmd field when reading */ + Out32(SMBHSTDAT1, command); + } + else + { + Out32(SMBHSTCMD, command); + } + block = 1; + break; + default: + ret = -EOPNOTSUPP; + goto out; + } + + //if (hwpec) /* enable/disable hardware PEC */ + // outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv)); + //else + // outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), + // SMBAUXCTL(priv)); + + if (block) + ret = i801_block_transaction(data, read_write, size, hwpec); + else + ret = i801_transaction(xact); + + /* Some BIOSes don't like it when PEC is enabled at reboot or resume + time, so we forcibly disable it after every transaction. Turn off + E32B for the same reason. */ + //if (hwpec || block) + // outb_p(inb_p(SMBAUXCTL(priv)) & + // ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); + + if (block) + goto out; + if (ret) + goto out; + if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) + goto out; + + switch (xact & 0x7f) + { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + case I801_BYTE_DATA: + data->byte = Inp32(SMBHSTDAT0); + break; + case I801_WORD_DATA: + data->word = Inp32(SMBHSTDAT0) + (Inp32(SMBHSTDAT1) << 8); + break; + } + +out: + //pm_runtime_mark_last_busy(&priv->pci_dev->dev); + //pm_runtime_put_autosuspend(&priv->pci_dev->dev); + //mutex_unlock(&priv->acpi_lock); + return ret; +} + +/* Block transaction function */ +int i2c_smbus_i801::i801_block_transaction(i2c_smbus_data *data, char read_write, int command, int hwpec) +{ + int result = 0; + unsigned char hostc; + + //if (command == I2C_SMBUS_I2C_BLOCK_DATA) + //{ + // if (read_write == I2C_SMBUS_WRITE) + // { + // /* set I2C_EN bit in configuration register */ + // pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc); + // pci_write_config_byte(priv->pci_dev, SMBHSTCFG, + // hostc | SMBHSTCFG_I2C_EN); + // } + // else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) { + // dev_err(&priv->pci_dev->dev, + // "I2C block read is unsupported!\n"); + // return -EOPNOTSUPP; + // } + //} + + if (read_write == I2C_SMBUS_WRITE || command == I2C_SMBUS_I2C_BLOCK_DATA) + { + if (data->block[0] < 1) + data->block[0] = 1; + if (data->block[0] > I2C_SMBUS_BLOCK_MAX) + data->block[0] = I2C_SMBUS_BLOCK_MAX; + } + else + { + data->block[0] = 32; /* max for SMBus block reads */ + } + + /* Experience has shown that the block buffer can only be used for + SMBus (not I2C) block transactions, even though the datasheet + doesn't mention this limitation. */ + //if ((priv->features & FEATURE_BLOCK_BUFFER) + // && command != I2C_SMBUS_I2C_BLOCK_DATA + // && i801_set_block_buffer_mode(priv) == 0) + // result = i801_block_transaction_by_block(priv, data, + // read_write, hwpec); + //else + result = i801_block_transaction_byte_by_byte(data, read_write, command, hwpec); + + //if (command == I2C_SMBUS_I2C_BLOCK_DATA + // && read_write == I2C_SMBUS_WRITE) { + // /* restore saved configuration register value */ + // pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc); + //} + return result; +} + +/* +* For "byte-by-byte" block transactions: +* I2C write uses cmd=I801_BLOCK_DATA, I2C_EN=1 +* I2C read uses cmd=I801_I2C_BLOCK_DATA +*/ +int i2c_smbus_i801::i801_block_transaction_byte_by_byte(i2c_smbus_data *data, char read_write, int command, int hwpec) +{ + int i, len; + int smbcmd; + int status; + int result; + + result = i801_check_pre(); + if (result < 0) + return result; + + len = data->block[0]; + + if (read_write == I2C_SMBUS_WRITE) + { + Out32(SMBHSTDAT0, len); + Out32(SMBBLKDAT, data->block[1]); + } + + if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_READ) + smbcmd = I801_I2C_BLOCK_DATA; + else + smbcmd = I801_BLOCK_DATA; + + //if (priv->features & FEATURE_IRQ) { + // priv->is_read = (read_write == I2C_SMBUS_READ); + // if (len == 1 && priv->is_read) + // smbcmd |= SMBHSTCNT_LAST_BYTE; + // priv->cmd = smbcmd | SMBHSTCNT_INTREN; + // priv->len = len; + // priv->count = 0; + // priv->data = &data->block[1]; + // + // outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv)); + // result = wait_event_timeout(priv->waitq, + // (status = priv->status), + // adap->timeout); + // if (!result) { + // status = -ETIMEDOUT; + // dev_warn(&priv->pci_dev->dev, + // "Timeout waiting for interrupt!\n"); + // } + // priv->status = 0; + // return i801_check_post(priv, status); + //} + + for (i = 1; i <= len; i++) + { + if (i == len && read_write == I2C_SMBUS_READ) + smbcmd |= SMBHSTCNT_LAST_BYTE; + Out32(SMBHSTCNT, smbcmd); + + if (i == 1) + Out32(SMBHSTCNT, Inp32(SMBHSTCNT) | SMBHSTCNT_START); + + status = i801_wait_byte_done(); + if (status) + goto exit; + + if (i == 1 && read_write == I2C_SMBUS_READ && command != I2C_SMBUS_I2C_BLOCK_DATA) + { + len = Inp32(SMBHSTDAT0); + if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) + { + /* Recover */ + while (Inp32(SMBHSTSTS) & SMBHSTSTS_HOST_BUSY) + Out32(SMBHSTSTS, SMBHSTSTS_BYTE_DONE); + Out32(SMBHSTSTS, SMBHSTSTS_INTR); + return -EPROTO; + } + data->block[0] = len; + } + + /* Retrieve/store value in SMBBLKDAT */ + if (read_write == I2C_SMBUS_READ) + data->block[i] = Inp32(SMBBLKDAT); + if (read_write == I2C_SMBUS_WRITE && i + 1 <= len) + Out32(SMBBLKDAT, data->block[i + 1]); + + /* signals SMBBLKDAT ready */ + Out32(SMBHSTSTS, SMBHSTSTS_BYTE_DONE); + } + + status = i801_wait_intr(); +exit: + return i801_check_post(status); +} + +/* +* Convert the status register to an error code, and clear it. +* Note that status only contains the bits we want to clear, not the +* actual register value. +*/ +int i2c_smbus_i801::i801_check_post(int status) +{ + int result = 0; + + /* + * If the SMBus is still busy, we give up + * Note: This timeout condition only happens when using polling + * transactions. For interrupt operation, NAK/timeout is indicated by + * DEV_ERR. + */ + if (status < 0) + { + /* try to stop the current command */ + Out32(SMBHSTCNT, Inp32(SMBHSTCNT) | SMBHSTCNT_KILL); + //usleep_range(1000, 2000); + Out32(SMBHSTCNT, Inp32(SMBHSTCNT) & (~SMBHSTCNT_KILL)); + + Out32(SMBHSTSTS, STATUS_FLAGS); + return -ETIMEDOUT; + } + + if (status & SMBHSTSTS_FAILED) + { + result = -EIO; + } + + if (status & SMBHSTSTS_DEV_ERR) + { + /* + * This may be a PEC error, check and clear it. + * + * AUXSTS is handled differently from HSTSTS. + * For HSTSTS, i801_isr() or i801_wait_intr() + * has already cleared the error bits in hardware, + * and we are passed a copy of the original value + * in "status". + * For AUXSTS, the hardware register is left + * for us to handle here. + * This is asymmetric, slightly iffy, but safe, + * since all this code is serialized and the CRCE + * bit is harmless as long as it's cleared before + * the next operation. + */ + //if ((priv->features & FEATURE_SMBUS_PEC) && + // (inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE)) { + // outb_p(SMBAUXSTS_CRCE, SMBAUXSTS(priv)); + // result = -EBADMSG; + // dev_dbg(&priv->pci_dev->dev, "PEC error\n"); + //} + //else { + result = -ENXIO; + // dev_dbg(&priv->pci_dev->dev, "No response\n"); + //} + } + + if (status & SMBHSTSTS_BUS_ERR) + { + result = -EAGAIN; + } + + /* Clear status flags except BYTE_DONE, to be cleared by caller */ + Out32(SMBHSTSTS, status); + + return result; +} + +/* Make sure the SMBus host is ready to start transmitting. +Return 0 if it is, -EBUSY if it is not. */ +int i2c_smbus_i801::i801_check_pre() +{ + int status; + + status = Inp32(SMBHSTSTS); + if (status & SMBHSTSTS_HOST_BUSY) + { + return -EBUSY; + } + + status &= STATUS_FLAGS; + if (status) + { + Out32(SMBHSTSTS, status); + status = Inp32(SMBHSTSTS) & STATUS_FLAGS; + if (status) + { + return -EBUSY; + } + } + + /* + * Clear CRC status if needed. + * During normal operation, i801_check_post() takes care + * of it after every operation. We do it here only in case + * the hardware was already in this state when the driver + * started. + */ + //if (priv->features & FEATURE_SMBUS_PEC) { + // status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE; + // if (status) { + // dev_dbg(&priv->pci_dev->dev, + // "Clearing aux status flags (%02x)\n", status); + // outb_p(status, SMBAUXSTS(priv)); + // status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE; + // if (status) { + // dev_err(&priv->pci_dev->dev, + // "Failed clearing aux status flags (%02x)\n", + // status); + // return -EBUSY; + // } + // } + //} + + return 0; +} + +int i2c_smbus_i801::i801_transaction(int xact) +{ + int status; + int result; + + result = i801_check_pre(); + if (result < 0) + return result; + + //if (priv->features & FEATURE_IRQ) + //{ + // outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START, + // SMBHSTCNT(priv)); + // result = wait_event_timeout(priv->waitq, + // (status = priv->status), + // adap->timeout); + // if (!result) { + // status = -ETIMEDOUT; + // dev_warn(&priv->pci_dev->dev, + // "Timeout waiting for interrupt!\n"); + // } + // priv->status = 0; + // return i801_check_post(priv, status); + //} + + /* the current contents of SMBHSTCNT can be overwritten, since PEC, + * SMBSCMD are passed in xact */ + Out32(SMBHSTCNT, xact | SMBHSTCNT_START); + + status = i801_wait_intr(); + return i801_check_post(status); +} + +/* Wait for either BYTE_DONE or an error flag being set */ +int i2c_smbus_i801::i801_wait_byte_done() +{ + int timeout = 0; + int status; + + /* We will always wait for a fraction of a second! */ + do + { + //usleep_range(250, 500); + status = Inp32(SMBHSTSTS); + } while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) && (timeout++ < MAX_RETRIES)); + + if (timeout > MAX_RETRIES) + { + return -ETIMEDOUT; + } + return status & STATUS_ERROR_FLAGS; +} + +/* Wait for BUSY being cleared and either INTR or an error flag being set */ +int i2c_smbus_i801::i801_wait_intr() +{ + int timeout = 0; + int status; + + /* We will always wait for a fraction of a second! */ + do + { + //usleep_range(250, 500); + status = Inp32(SMBHSTSTS); + } while (((status & SMBHSTSTS_HOST_BUSY) || !(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) && (timeout++ < MAX_RETRIES)); + + if (timeout > MAX_RETRIES) + { + return -ETIMEDOUT; + } + return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR); +} + +s32 i2c_smbus_i801::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data) +{ + return i801_access(addr, read_write, command, size, data); +} \ No newline at end of file diff --git a/OpenAuraSDK/i2c_smbus_i801.h b/OpenAuraSDK/i2c_smbus_i801.h new file mode 100644 index 00000000..aeda780c --- /dev/null +++ b/OpenAuraSDK/i2c_smbus_i801.h @@ -0,0 +1,87 @@ +/*-----------------------------------------*\ +| i2c_smbus_i801.h | +| | +| i801 SMBUS driver for Windows | +| | +| Adam Honse (CalcProgrammer1) 1/29/2019 | +| Portions based on Linux source code | +| GNU GPL v2 | +\*-----------------------------------------*/ + +#include "i2c_smbus.h" + +#pragma once + +/* BIT shifting macro */ +#define BIT(x) ( 1 << x ) + +/* I801 SMBus address offsets */ +#define SMBHSTSTS (0 + i801_smba) +#define SMBHSTCNT (2 + i801_smba) +#define SMBHSTCMD (3 + i801_smba) +#define SMBHSTADD (4 + i801_smba) +#define SMBHSTDAT0 (5 + i801_smba) +#define SMBHSTDAT1 (6 + i801_smba) +#define SMBBLKDAT (7 + i801_smba) +#define SMBPEC (8 + i801_smba) /* ICH3 and later */ +#define SMBAUXSTS (12 + i801_smba) /* ICH4 and later */ +#define SMBAUXCTL (13 + i801_smba) /* ICH4 and later */ +#define SMBSLVSTS (16 + i801_smba) /* ICH3 and later */ +#define SMBSLVCMD (17 + i801_smba) /* ICH3 and later */ +#define SMBNTFDADD (20 + i801_smba) /* ICH3 and later */ + +/* Other settings */ +#define MAX_RETRIES 400 + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_PROC_CALL 0x10 /* unimplemented */ +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */ + +/* I801 Host Control register bits */ +#define SMBHSTCNT_INTREN BIT(0) +#define SMBHSTCNT_KILL BIT(1) +#define SMBHSTCNT_LAST_BYTE BIT(5) +#define SMBHSTCNT_START BIT(6) +#define SMBHSTCNT_PEC_EN BIT(7) /* ICH3 and later */ + +/* I801 Hosts Status register bits */ +#define SMBHSTSTS_BYTE_DONE BIT(7) +#define SMBHSTSTS_INUSE_STS BIT(6) +#define SMBHSTSTS_SMBALERT_STS BIT(5) +#define SMBHSTSTS_FAILED BIT(4) +#define SMBHSTSTS_BUS_ERR BIT(3) +#define SMBHSTSTS_DEV_ERR BIT(2) +#define SMBHSTSTS_INTR BIT(1) +#define SMBHSTSTS_HOST_BUSY BIT(0) + +/* Host Notify Status register bits */ +#define SMBSLVSTS_HST_NTFY_STS BIT(0) + +/* Host Notify Command register bits */ +#define SMBSLVCMD_HST_NTFY_INTREN BIT(0) + +#define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR) +#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR | STATUS_ERROR_FLAGS) + +class i2c_smbus_i801 : public i2c_smbus_interface +{ +public: + u16 i801_smba = 0xF000; + +private: + s32 i801_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data); + int i801_block_transaction(i2c_smbus_data *data, char read_write, int command, int hwpec); + int i801_block_transaction_byte_by_byte(i2c_smbus_data *data, char read_write, int command, int hwpec); + int i801_check_post(int status); + int i801_check_pre(); + int i801_transaction(int xact); + int i801_wait_byte_done(); + int i801_wait_intr(); + s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data); + +}; \ No newline at end of file diff --git a/OpenAuraSDK/i2c_smbus_piix4.cpp b/OpenAuraSDK/i2c_smbus_piix4.cpp new file mode 100644 index 00000000..b762a5a8 --- /dev/null +++ b/OpenAuraSDK/i2c_smbus_piix4.cpp @@ -0,0 +1,168 @@ +/*-----------------------------------------*\ +| i2c_smbus_piix4.cpp | +| | +| PIIX4 SMBUS driver for Windows | +| | +| Adam Honse (CalcProgrammer1) 8/8/2018 | +| Portions based on Linux source code | +| GNU GPL v2 | +\*-----------------------------------------*/ + +#include "i2c_smbus_piix4.h" + +//Logic adapted from piix4_transaction() in i2c-piix4.c +int i2c_smbus_piix4::piix4_transaction() +{ + int result = 0; + int temp; + int timeout = 0; + + /* Make sure the SMBus host is ready to start transmitting */ + temp = Inp32(SMBHSTSTS); + + if (temp != 0x00) + { + Out32(SMBHSTSTS, temp); + + temp = Inp32(SMBHSTSTS); + + if (temp != 0x00) + { + return -EBUSY; + } + } + + /* start the transaction by setting bit 6 */ + temp = Inp32(SMBHSTCNT); + Out32(SMBHSTCNT, temp | 0x040); + + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + temp = 0; + while ((++timeout < MAX_TIMEOUT) && temp <= 1) + { + temp = Inp32(SMBHSTSTS); + } + + /* If the SMBus is still busy, we give up */ + if (timeout == MAX_TIMEOUT) + { + result = -ETIMEDOUT; + } + + if (temp & 0x10) + { + result = -EIO; + } + + if (temp & 0x08) + { + result = -EIO; + } + + if (temp & 0x04) + { + result = -ENXIO; + } + + temp = Inp32(SMBHSTSTS); + if (temp != 0x00) + { + Out32(SMBHSTSTS, temp); + } + + return result; +} + +//Logic adapted from piix4_access() in i2c-piix4.c +s32 i2c_smbus_piix4::piix4_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data) +{ + int i, len, status; + + switch (size) + { + case I2C_SMBUS_QUICK: + Out32(SMBHSTADD, (addr << 1) | read_write); + size = PIIX4_QUICK; + break; + case I2C_SMBUS_BYTE_DATA: + Out32(SMBHSTADD, (addr << 1) | read_write); + Out32(SMBHSTCMD, command); + if (read_write == I2C_SMBUS_WRITE) + { + Out32(SMBHSTDAT0, data->byte); + } + size = PIIX4_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + Out32(SMBHSTADD, (addr << 1) | read_write); + Out32(SMBHSTCMD, command); + if (read_write == I2C_SMBUS_WRITE) + { + Out32(SMBHSTDAT0, data->word & 0xFF); + Out32(SMBHSTDAT1, (data->word & 0xFF00) >> 8); + } + size = PIIX4_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + Out32(SMBHSTADD, (addr << 1) | read_write); + Out32(SMBHSTCMD, command); + if (read_write == I2C_SMBUS_WRITE) + { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + { + return -EINVAL; + } + Out32(SMBHSTDAT0, len); + Inp32(SMBHSTCNT); + for (i = 1; i <= len; i++) + { + Out32(SMBBLKDAT, data->block[i]); + } + } + size = PIIX4_BLOCK_DATA; + break; + default: + return -EOPNOTSUPP; + } + + Out32(SMBHSTCNT, (size & 0x1C)); + + status = piix4_transaction(); + + if (status) + return status; + + if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) + return 0; + + switch (size) + { + case PIIX4_BYTE: + case PIIX4_BYTE_DATA: + data->byte = (u8)Inp32(SMBHSTDAT0); + break; + case PIIX4_WORD_DATA: + data->word = Inp32(SMBHSTDAT0) + (Inp32(SMBHSTDAT1) << 8); + break; + case PIIX4_BLOCK_DATA: + data->block[0] = (u8)Inp32(SMBHSTDAT0); + if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX) + { + return -EPROTO; + } + Inp32(SMBHSTCNT); + for (i = 1; i <= data->block[0]; i++) + { + data->block[i] = (u8)Inp32(SMBBLKDAT); + } + break; + } + + return 0; +} + +s32 i2c_smbus_piix4::i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data) +{ + return piix4_access(addr, read_write, command, size, data); +} \ No newline at end of file diff --git a/OpenAuraSDK/i2c_smbus_piix4.h b/OpenAuraSDK/i2c_smbus_piix4.h new file mode 100644 index 00000000..262e0c58 --- /dev/null +++ b/OpenAuraSDK/i2c_smbus_piix4.h @@ -0,0 +1,48 @@ +/*-----------------------------------------*\ +| i2c_smbus_piix4.h | +| | +| Definitions and types for PIIX4 SMBUS | +| driver | +| | +| Adam Honse (CalcProgrammer1) 8/8/2018 | +| Portions based on Linux source code | +| GNU GPL v2 | +\*-----------------------------------------*/ + +#include "i2c_smbus.h" + +#pragma once + +// PIIX4 SMBus address offsets +#define SMBHSTSTS (0 + piix4_smba) +#define SMBHSLVSTS (1 + piix4_smba) +#define SMBHSTCNT (2 + piix4_smba) +#define SMBHSTCMD (3 + piix4_smba) +#define SMBHSTADD (4 + piix4_smba) +#define SMBHSTDAT0 (5 + piix4_smba) +#define SMBHSTDAT1 (6 + piix4_smba) +#define SMBBLKDAT (7 + piix4_smba) +#define SMBSLVCNT (8 + piix4_smba) +#define SMBSHDWCMD (9 + piix4_smba) +#define SMBSLVEVT (0xA + piix4_smba) +#define SMBSLVDAT (0xC + piix4_smba) + +#define MAX_TIMEOUT 5000 + +// PIIX4 constants +#define PIIX4_QUICK 0x00 +#define PIIX4_BYTE 0x04 +#define PIIX4_BYTE_DATA 0x08 +#define PIIX4_WORD_DATA 0x0C +#define PIIX4_BLOCK_DATA 0x14 + +class i2c_smbus_piix4 : public i2c_smbus_interface +{ +public: + u16 piix4_smba = 0x0B00; + +private: + int piix4_transaction(); + s32 piix4_access(u16 addr, char read_write, u8 command, int size, i2c_smbus_data *data); + s32 i2c_smbus_xfer(u8 addr, char read_write, u8 command, int size, i2c_smbus_data* data); +}; \ No newline at end of file diff --git a/dependencies/inpout32_1501/Win32/inpout32.dll b/dependencies/inpout32_1501/Win32/inpout32.dll new file mode 100644 index 0000000000000000000000000000000000000000..8889280046028c78b7a379a7e284399b6e1ab0c4 GIT binary patch literal 98304 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 z

Xc!MPM0FXuVy^_rH_}e2iG+%QK*=rWAD8Do^l>^V>lam!PdpP*3x>f))USjH5U_c!_$q^B)iEV?|8e#{z=?mp*xMpOJwf_(dk@v>?|9!2vJ2K7m3?!m{>BsfYFV zQa0mnZk?q(@HhyAH~T=z6=J3Wx|!V|Gyj)9cwq}ROa$8feW3>$KB#@bP-@uvt(4IX zVJN664Nmp{ML~_$?jNs(x=VSwV>z-|UNF1@g>rZ8lh*(It)Suo8do}DFZP3mz*`US zw_f`9|No0?EC2tutbI_LgE$1KV1_tB&e{l3RFVOStJj5KmxN(Bp!I)g z7$k?3n1sCmt!oFJFeBgm??KtwuorG1i(jO!L}Z2+3qV7UFM?M7|NpWEG>7uPRP2Sz zO3-j8WX)B;i{C3iLn)En$6sFpHwFd%mvVp>Pnj*#V&3j=rvn1-!C~CyU|50`TY{gjcJjMecv$mu{^n==*8k$-OF81=V_zPXWnlPz z96bCWdhjJv^CRZwpBzP6pO^kunfj!bL104n@m(O<=9er*Jk1Z7nNR#zY5h^dAn;$M z_X|k=ze;Zfh~}te5HP7_5QwN{5KsmkU(ep<#@OM`*yYA_@Fi1+J5%#9=H}yI)6c+7 z@AYJWs|BlKY(B=+e4Gj7hGlS76Ci4uk1>K=c$^U=+X0vD^<=nR%OG%~mOC*qC+okVCr%V5fZkN6{oi6=vx?TD{bh`9^ z=yvJ*(&^IwrQ4rAW#3PWf0(~V-S$3 z1D$Bg5f^{*@Psb54)$(W1RLUes7S9T14tes*5w8Y4yeq8-V(z!B5ghT@G z-_J|`{8yRUP|qOH{r~&T|0=x(^$Y^No(!-!`3Z>=ZBU%F)Pds!qKFvN+?bUwcDOS)zXONfdvNG&LNXfUHmDjzT)kszeh-d?4kVQmU}iyl(B%d)vBRCQ z`5klfdr-{sH-V0{LDJFd$zW2?AYfC^AmCEZAmCHaAP`c|AP`f}AdpheAW#nSnMB?`dB($`dLup z2ITGT{}bvN1ZLDT2&|}Q5ZD7U8x|jK%pLB`&=80Cx5J$o;tQxK$Zv2lNIXGh&YU?D z92VXkq9OoV%?P@BCNwA@Ffb#d0e)@;ctx%8kxmyCj+9PS(0G2gi;6*a1V^`zijJ{l zcL7iHzkemx-61L_&Ay6ng(9PZH!P8vL!cck!ymzD9nd60)3Qz|*}Y#-Qbr-5wmqCtk451)Z>v zApsh}C=mExAo9WvTyF5hg4Kkm2y`;-0Ojbzp`fA1Zq5INGXD!D{ugSz03FWIe1s?V z5DP#`OAZVHC3-B5bkOdsz%XXl*W7I@j^MX$C zDh}=j?RM+_*lPnmC!w1iayS9=v2PA6j3uU^R=NRfx>HvBbcujx35RE?aB$X_*BrrF zUtco^XMOuGDg!!Om%W*X?Z5GX*P<_GF8==?avuMSg^U0H?*yID1r9?O6_(~7|4PMQ zbS(z&GkuW{SJ`~xKXh}X669EgVkyiGkjw``&MH-T;RQ0*;Dy0r$ZE|No#PWkAOabbp6>5pvuBbgsbowq+bgsaY1+i>;4A)m-c! z5MK&BNeMo#?ILJ{Vr1)qQs&OsC;#0zjzjlW7=We}VxN@Ac7qIu?SupeX0M9{=$tdq z_C(NZ<>_7j{~tEK-CfI(#r2{bv||N4T94_=UKd7?KG4K0bj%i#*g@N!K<6%)3V=2y z_%++QykijfSIYXLUy^}gA&3K7H_MjB`~Uyzg0NoS58$2q9H7h6G{9S!O4FNdL3)`= zZ$s+Q<{uTM7dpY_g@t9Y_PTxv$YShu

D-FRHQ;JdO|E^vnx3{OA9Y#bLdnAA$lv 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