Thanks for the work and for sharing, tmplinshi. I really needed a library like this. BTW, the last "Ptr" on the
DllCall for WriteIoPortByte should be removed.
If anyone happens to have a(n X230) ThinkPad, here's a script using WinRing0 that makes the microphone button LED flash when caps lock is active. I've tested it on my laptop, and while it didn't cause it to blow up, I do have to state that this should be tried at your own risk. If you want to run the script without compiling it, put the DLLs from
MinHook into the same folder as the script.
Code: Select all
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
ListLines, Off
SetBatchLines, -1
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir, %A_ScriptDir%
#KeyHistory 0
#InstallKeybdHook
#UseHook On
#Persistent
#SingleInstance force
; Registers of the embedded controller
global EC_DATAPORT := 0x62
global EC_CTRLPORT := 0x66
;Embedded controller status register bits
global EC_STAT_OBF := 0x01 ; Output buffer full
global EC_STAT_IBF := 0x02 ; Input buffer full
global EC_STAT_CMD := 0x08 ; Last write was a command write (0=data)
; Embedded controller commands
; (write to EC_CTRLPORT to initiate read/write operation)
global EC_CTRLPORT_READ := 0x80
global EC_CTRLPORT_WRITE := 0x81
global EC_CTRLPORT_QUERY := 0x84
; If running uncompiled, the line "static _Init := WinRing0.Init()" must be changed to "static _Init := 0"
#Include Class_WinRing0.ahk
if not A_IsAdmin
{
Run *RunAs "%A_ScriptFullPath%"
ExitApp
}
if (!A_IsCompiled)
{
FindFirstFileHook(lpFileName, lpFindFileData)
{
global FindFirstFileOrigPtr
if (lpFileName)
{
strEncoding := A_PtrSize == 8 ? "UTF-16" : "CP0"
SplitPath, % StrGet(lpFileName, strEncoding), driverName
if (driverName ~= "WinRing0(x64)?\.sys")
StrPut(A_ScriptDir . "\" . driverName, lpFileName, 260, strEncoding) ; fingers crossed I don't get a pointer to something located in .text or something < MAX_PATH during the time this hook is active, WinRing0 is initialised and the hook is removed...
}
return DllCall(FindFirstFileOrigPtr, "Ptr", lpFileName, "Ptr", lpFindFileData, "Ptr")
}
minhookDll := "MinHook." . (A_PtrSize == 8 ? "x64" : "x86") . ".dll"
minhookModule := DllCall("LoadLibrary", "Str", minhookDll, "Ptr")
if (!minhookModule || DllCall(minhookDll . "\MH_Initialize") != 0)
ExitApp 1
FindFirstFileHookPtr := RegisterCallback("FindFirstFileHook", "Fast")
if (DllCall(minhookDll . "\MH_CreateHookApi", "WStr", "kernel32", "AStr", "FindFirstFile" . (A_PtrSize == 8 ? "W" : "A"), "Ptr", FindFirstFileHookPtr, "Ptr*", FindFirstFileOrigPtr) != 0)
ExitApp 1
if (DllCall(minhookDll . "\MH_EnableHook", "Ptr", MH_ALL_HOOKS := 0) != 0)
ExitApp 1
}
if (!WinRing0.dll)
WinRing0._Init := WinRing0.Init()
if (!A_IsCompiled) {
DllCall(minhookDll . "\MH_DisableHook", "Ptr", MH_ALL_HOOKS), DllCall(minhookDll . "\MH_Uninitialize")
DllCall("FreeLibrary", "Ptr", minhookModule), minhookModule := 0
DllCall("GlobalFree", "Ptr", FindFirstFileHookPtr, "Ptr"), FindFirstFileHookPtr := 0
}
OnExit("AtExit")
; Thanks to https://gitlab.com/valinet/thinkpad-leds-control
SetMicrophoneLedState(off:=false)
{
ec_offset := 0x0C
led := 0x0E
state := (off || !GetKeyState("CapsLock", "T")) ? 0x00 : 0xC0
WriteByteToEC(ec_offset, (led | state))
}
; Thanks to http://tp4xfancontrol.cvs.sourceforge.net/viewvc/tp4xfancontrol/tp4xfancontrol/source/portio.cpp?view=markup for the EC writing code
waitportstatus(bits, wantedstate)
{
port := EC_CTRLPORT
timeout := 1000
time_ := 0
tick := 10
ret := false
while (time_ < timeout) {
data := DllCall(WinRing0.dll . "\ReadIoPortByte", "UShort", port, "UChar")
if (wantedstate == (data & bits)) {
ret := true
break
}
DllCall("Sleep", "UInt", tick)
time_ := time_ + tick
}
return ret
}
writeport(port, data)
{
WinRing0.WriteIoPortByte(port, data)
return true
}
WriteByteToEC(offset, data)
{
; wait for IBF and OBF to clear
ret := waitportstatus(EC_STAT_IBF| EC_STAT_OBF, 0)
if (!ret)
return false
; tell 'em we want to "WRITE"
ret := writeport(EC_CTRLPORT, EC_CTRLPORT_WRITE)
if (!ret)
return false
; wait for IBF to clear (command byte removed from EC's input queue)
ret := waitportstatus(EC_STAT_IBF, 0)
if (!ret)
return false
; tell 'em where we want to write to
ret := writeport(EC_DATAPORT, offset)
if (!ret)
return false
; wait for IBF to clear (address byte removed from EC's input queue)
ret := waitportstatus(EC_STAT_IBF, 0)
if (!ret)
return false
; tell 'em what we want to write there
ret := writeport(EC_DATAPORT, data)
if (!ret)
return false
; wait for IBF to clear (data byte removed from EC's input queue)
ret := waitportstatus(EC_STAT_IBF, 0)
return ret
}
AtExit(ExitReason, ExitCode)
{
if ExitReason not in Shutdown
{
if (WinRing0.dll && WinRing0.GetDllStatus() == "No error")
SetMicrophoneLedState(true)
}
WinRing0 := ""
}
~CapsLock::SetTimer, SetMicrophoneLedState, -100