Where it all began
Letâs start with our problem statement. We have 1 (one) laptop. A new, gamer laptop. With some RGB-backlight on its keyboard. It looks like this:

Picture taken from lenovo.com
Thereâs also a program installed on this laptop. Thatâs the thing that controls our backlight.
One problem â the program runs under Windows, and we want everything to work on our favourite Linux. Want LEDs to flash and those pretty colours to blink on and off and such. A natural question arises, can we do all that without reverse-engineering and writing our own drivers?
A natural answer arises, no. Letâs open IDA and get cracking.
Step 1 â Digging in the code
There are three triggers that make the backlight glow. In order of ascending difficulty:
1. Big buff gamer program called Lenovo Nerve Center, which has a big buff setup menu just for this backlight.
2. Hotkey combination Fn+Space â possibly managed by the aforementioned program, too.
3. BIOS. While the laptop is loading, the backlight blinks red for a brief moment. Maybe we can use that?
I had to try all 3. But there was some success only along the first one, so letâs talk about the first one here.
We open the folder with our program:

Notice here! Thereâs a DLL with a very interesting name â LedSettingsPlugin.dll. Could it be...? Letâs open it in IDA Pro and see for ourselves.

Look at the right half of the screen here â so, so much debug information! Letâs use it. Some strings look like function names here. I wonder whyâ¦

Oh, these are function names. Convenient! Letâs call some things by their own names and then look at the function list again. To name things in IDA you can use the hotkey N, or just right-click the thing you want to name.

Looking at functions, we see something called Y720LedSetHelper::SetLEDStatusEx. Looks like what we need! It forms some kind of a string, and then gives it to something called CHidDevHelper::HidRequestsByPath. The interesting part we should be looking at is var_38, lovingly highlighted by IDA.
Var_34 is interesting, too. It goes right after var_38 â in assembler traditions, variables are stored in reverse order under the RSP. Var_34 in IDA is just the name of the constant â -34h.
Starting from var_38, our program puts a zero, then style, colour, the number three and the block â part of the keyboard that will gain this colour. (Later on, the experiments will show that the number three is actually the brightness. Adding a control on that will make our driver even better than the official one!)
Now, let us take a deep dive into HidRequestsByPath and try to figure out what do we need to send and where.

Look, two functions. HidD_GetFeature and HidD_SetFeature.
They arenât tracked in the file⦠but are very well tracked in the official Microsoft documentation â here and here.
Well, we can pat ourselves on the back now. This is rock bottom, thereâs nowhere to dig deeper. Linux has those two functions â we just go back, and call them with the same arguments. ...Right?
Step 2 â launching the prototype...?
Not exactly. Letâs go back to Linux and open up lsusb. Weâll find our keyboard there, and weâll send something to it.

Integrated Technology Express, Inc. is the most interesting one here. Letâs use the popular tool /dev/hidraw. We can find the proper one just by looking at the corresponding/sys/class/hidraw/hidraw*/device/uevent.

This one. All the digits match â that means /dev/hidraw0 is our device. But letâs try to send something⦠and nothing happens! Why. At this point, burnout kicks in. Maybe itâs not for mere mortals, this reverse engineering thing?
But letâs keep trying. Were the author more good at this, he would have reversed Nerve Center some more and come up with h a solution⦠But we have no brain. Letâs reboot to Windows, thereâs an idea.
Thereâs this thing in Windows â Device Manager, they call it. Has a lot of functions â but weâre interested in one. It allows you to disable devices. Simple and authorative.
So letâs just disable all the devices until our LED stops blinking!

This did it. If we peek at its Hardware ID â weâll see what it is, that mysterious LED device.

Look â itâs him.

The device that has been pestering my dmesg for several years.
[Why wasnât it shown in lsusb? Itâs not USB at all, of course! The backlight uses a protocol called I2C HID â it allows the manufacturer to
Sidestep 2.5 â letâs make a commit
This search for the correct device has been heavily abridged. In the original telling, I had to open my setup almost all the way to the kernel before I realised why nothing worked. Also, I did not enjoy this dmesg log showing itself to me every time I unlock my computer. Since weâre here â why not go and write a short commit?
We need to find two things â the place where I2C HID devices are handled, and the place where their quirks are stored. Here. Letâs not think too hard and look at the error: incomplete report. Letâs make it complete.

Add a new quirk, letâs call it I2C_HID_QUIRK_BAD_INPUT_SIZE or something like that. To keep the theming.

Also letâs add our device to the list of quirked. What weâve done so far:
1. Looked up âi2c hid linux kernelâ in a search engine. Clicked the 4th result in DuckDuckGo.
2. Wrote three (!) words in English â BAD_INPUT_SIZE
3. Added one to BIT(4). Result â BIT(5).
4. Added a number to hid-ids.h â the ID of our device (not illustrated, but itâs similar in difficulty)
Weâre programmers, letâs do some programming now.
See the line:
ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8;
And then it complains that ret_size is not what it ones.
Whenever our quirk is active, letâs do this but backwards.

Send the patch to the mailing list⦠(test it beforehand!). To be honest, getting into that mailing list was trickier than the actual patch. Itâs not simple by any means.

Thatâs it!
Step 3 â driver!
At this point I remembered â I was trying to write a driver. Decided I wonât commit that to kernel yet â questions began arising, at this point you really have to think some. If anyone reading this is good at kernel and can consult me, Iâd be glad to talk.
Letâs open python. (Used to be bash, but you change your mind very quickly with that sort of language). Letâs use the popular tool /dev/hidraw.
/dev/hidraw0, /dev/hidraw1 â how do those files work, anyways? For simple I/O you can use them as any other, and it seems that is would just work. And GetFeature and SetFeature â well, thatâs a different story entirely.
StackOverflow (or someone else, I canât remember) told me I would have to look into something called ioctl. Itâs a special way of working with uncommon files like HID devices, terminals and similar disgusting things.
It seems simple on the surface â you give it an open file handle (not Python-level, OS-level! Read more on OS handles here if you'd like to know.), some number and a buffer â and then it does something with that buffer and gives it back to you. Iâm not trying to be witty here, itâs just that itâs almost fully implementation-dependent. Hereâs an example: 0xC0114806. Whatâs that? Thatâs SetFeature. Thatâs also
(6 << 29) | (ord('H') << 8) | (0x06 << 0) | (0x11 << 16).
The first 6 means the file will be open both for reading and for writing. We only write, but the docs said 6 that means we put 6. Ord(âHâ) stands for HID. Sometimes it stands for other things, depending on the file. But now itâs HID.
0x06 is the command itself. Sixth command of HID is SetFeature, which we need. And the last part? The bufferâs size.
All that remains is putting it together into an ioctl call, and you get a driver. It works..
Afterword from the author
Hope the article was an interesting read. Even a useful read, possibly. Some parts were omitted or shortened for brevity â a more observant reader might notice that the driver can read the keyboardâs current state, and the set_status function has a second ioctl call that wasnât even mentioned in the text. Hardware development and the Linux kernel are big things, and one tale wonât cover it all. And in the end of the day, maybe the article isnât about this. Maybe itâs just about how doing something small and good, for open-source or for yourself, is not hard at all. You just need to find a weekâs worth of free evenings, some tea and a wish to dig around in code.