Hi NTDEV Community
Just for your information, the following question/issue is mainly just for fun or sparetime.
I simply want to turn off and on the backlight of my monitor since I was planning to (mis)use the large glowing area as a small software controlled LED strobe light.
As the built in backlight isn’t made of a fluorescent light tube but rather a LED array (if referring to my notebook stickers), judged on the hardware my notebook should theoretically able to support quite fast turn off and turn on cycles.
Remember that LEDs can be pulsed up to MHz…
Obviously, for this little project I would’t need to run my display at MHz frequencies, however, I would appreciate to control the display on a say, 20ms granularity, which would be just little above the thread switching time.
I ended up trying it by leveraging a device driver since I couldn’t find any suitable high level API that lets me just turn off and turn on the backlight:
I tried to scrutinize the working principle of the Windows power settings and learned that I can alter the screen brightness within a short timespan but I couldn’t make the screen go off, entirely.
After further research, I found a small program called “nircmd” which finally let me turn off my screen. I tracked then down the used API command “SendMessageA(INVALID_HANDLE_VALUE, 0x112, 0xF170, 2)” until this command vanished in the deep, undocumented darkness of win32k.sys.
There were two major issues with this command:
1.) The transition of turning off the screen was incredibly long. (100ms?)
2.) I did’t find a complementary API letting me turn on the screen again.
Next, I developed a small driver which hooks the whole IRP_MJ_XXX array of a arbitrary driver. I hooked the dispatch table of several driver objects including \acpi, \nvlddmkm, \BasicRender, \monitor, \dxgkrnl and so on, all the time looking after a particular IRP being sent clearly corresponding with the turn off command issued.
I even wrote an additional API which let me send a keyboard turn-on-capslock-LED IOCTL to i8042prt.sys while at DISPATCH_LEVEL which enabled me to take up to 10ms exact time measurements. I used my smart phone to measure the time the “turn off!”-command was received by my patch driver until the screen actually went black.
Interestingly, as long as I didn’t hook nvlddmkm.sys, the screen went black way before the keyboard LED turned on! (1s NEGATIVE! delay)
When I hooked nvlddmkm.sys the delay between the screen beginning to darken and my capslock LED turning on was about 60ms.
By provoking a precisely timed BSOD I was able to seemingly get the missing piece of the puzzle:
ffffd00107708578 fffff802
0e20cab8 : 0000000000000050 ffffffff
fffffffe 0000000000000001 ffffd001
077087d0 : nt!KeBugCheckEx
ffffd00107708580 fffff802
0e0e8e78 : 0000000000000001 ffffe000
7afe8900 ffffd001077087d0 ffffe000
7932e000 : nt! ?? ::FNODOBFM::string'+0x29408 ffffd001
07708620 fffff8020e1dd42f : 00000000
c0000002 ffffffffff676980 ffffe000
79370000 ffffd001077087d0 : nt!MmAccessFault+0x758 ffffd001
077087d0 fffff8005e4014b5 : fffff800
5e402070 0000000000000004 00000000
00000065 0000000000000001 : nt!KiPageFault+0x12f ffffd001
07708960 fffff960001edb9d : ffffe000
7afb6910 ffffe0007dc8acc0 00000000
00000000 ffffe00079247470 : IRP_HookTst+0x14b5 ffffd001
077089d0 fffff96000265d6a : fffff901
400b1be0 0000000000000000 fffff901
400fb2a0 00000000c0000001 : win32k!GreDeviceIoControlEx+0xd9 ffffd001
07708a70 fffff960001d6689 : fffff901
44ee3010 0000000000000004 00000000
00000001 0000000000000000 : win32k!DrvSetMonitorPowerState+0xfe ffffd001
07708ad0 fffff960001d6b25 : 00000000
00000000 0000000000000001 ffffe000
0000003c fffff90144ee3010 : win32k!PowerOffMonitor+0xe1 ffffd001
07708b10 fffff960001d7879 : ffffe000
7fce56b0 0000000300000100 00000000
0000001f fffff9600023b8fc : win32k!xxxUserPowerEventCalloutWorker+0x211 ffffd001
07708bb0 fffff96000172f74 : ffffe000
770ac080 0000000000000000 00000000
00000000 0000000000000000 : win32k!xxxUserPowerCalloutWorker+0x89 ffffd001
07708c10 fffff8020e1de9b3 : ffffe000
770ac080 0000000000000002 00000000
00000020 ffffd00107708c40 : win32k!NtUserCallNoParam+0x44 ffffd001
07708c40 00007ffaa92a161a : 00007ffa
a92a17f3 0000000000000004 00000000
00000000 0000000000000000 : nt!KiSystemServiceCopyEnd+0x13 000000bb
f26bfc38 00007ffaa92a17f3 : 00000000
00000004 0000000000000000 00000000
00000000 0000000000000004 : winsrv!NtUserCallNoParam+0xa \<--here, my SendMessageA command vanished! 000000bb
f26bfc40 00007ffaabf54411 : 00000000
00000000 0000000000000000 00000000
00000000 0000000000000000 : winsrv!NotificationThread+0x1bc 000000bb
f26bff40 0000000000000000 : 00000000
00000000 0000000000000000 00000000
00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x25
Since I exactly knew the IOCTL requirements nvlddmkm had at this point, I sent the IOCTL “0x23200F” to the first device object obtained from nvlddmkm->DeviceObject using the following code:
PUCHAR pPuffer = ExAllocatePool(NonPagedPool, 8);
pPuffer[0] = 0x4;
PIRP pIrp = IoBuildDeviceIoControlRequest(0x23200F, pPhysicalDrive, pPuffer, 4, NULL, 0, TRUE, NULL, &ioStatusBlock);
if (!pIrp){
DbgPrint(“could not allocate irp”);
return STATUS_NO_MEMORY;
}
DbgPrint(“0x%lX”, IofCallDriver(pPhysicalDrive, pIrp));
After calling IofCallDriver the screen went black as expected. Writing 0x1 to pPuffer turned the screen back on.
Unfortunately, I saw in no time that the screen control was extremely slow.
The same was true if I issued the request to monitor.sys. It also worked but it was still insanely slow so no chance to construct a strobe light from screen.
Since I also intercepted IRP_MJ_POWER IRPs during my research I leveraged PoRequestPowerIrp to send a power IRP to monitor.sys which led to a WDF_VIOLATION BSOD. An issue to nvlddmkm using the same technique returned me a 0xC0000010, INVALID_DEVICE_REQUEST.
Finally, I build a IRP_MJ_INTERNAL_DEVICE_CONTROL IRP, overwrote the major and minor function codes with IRP_MJ_POWER as well as IRP_MN_SET_POWER and crafted the returned IRP so it looked to the monitor.sys driver like a power IRP. I planned to circumvent the power manager as well as the KMDF by doing so.
Unfortunately, my IRP seemed to look a little too much like a real power request, so it kept being processed by the power manager.
That again bluescreened my computer with a WDF_VIOLATION message.
So you can see, I tried my best and researched a lot; however, I didn’t succeed in rapidly controlling my notebook display’s backlight.
Now onto my questions:
-
What other possiblities do you know of (rapidly) controlling the screen backlight?
-
Should I reverse monitor.sys and try to call internal APIs of monitor.sys, thus directly controlling the backlight?
-
Should I try to use WRITE_PORT_UCHAR() to directly control the internal display port?
-
Are there drivers other than monitor.sys or the proprietary nvidia driver I should issue IOCTLs to?
-
Why does it take so long to switch the monitor of if directly called with a IOCTL as mentioned above??
-
Do you know a component responsible (lowest level, Bus driver or port driver or whatever) which directly controls my hardware?
-
Am I even suggested to completely ignore the backlight and just to leverage documented Win32 graphic functions to black and white paint a window area as large as my desktop?
Some more information: As always with me, I do neither bother how undocumented your recommendations are, nor if a particular dirty solution only will work with my current OS with the exactly defined build number.
Preferred OS would be Windows 8.1, x64, Build 9600
Hardware is a ASUS G60J notebook with 4GB RAM, a LED backlight and a NVIDIA GTX260M graphics solution.
I do not bother either if your solution generates large lags, high CPU loads, or hangs as long as the screen is flashing, because everything like these problems can be solved at a later time.
If you don’t see a particular rapid solution just tell everything, including slow solutions. This helps me to better understand the internals and maybe com up with a rapid solution on my own.
And if nothing helps I will go for writing real mode boot code turning the screen off and on, since after all, there MUST be some command(s) controlling the backlight…on Windows 8.1 it’s just too good hidden from me…
Any help really appreciated, thanks in advance!
Best Regards
Microwave
P.S. I have also analyzed what happens if I alter the screen brightness.
There are IOCTLs to monitor.sys/nvlddmkm.sys sent ranging from 0x0 to 0x64 according to 0…100% brightness setting. So no need to send 0x0, as 0x0 already is being sent but doens’t mean “screen off”.