gpio-keys: an intro to kernel dev

My first foray into Linux kernel development

20 Jan 2024

Recently laid off from my job, and I suddenly had a lot of time left before me. Why not use that time to pursue my personal projects?

I started off by trying to mainline my touchkeys. I got this idea from toonis who mainlined cypress-sf a while back.

Even though touchkeys are relatively simple, there were a lot of moving parts. I don’t want to get too deep into it currently, as I’m planning to make another blog post about it, but making your own driver, modifying the dts, and reading downstream files was quite challenging for a beginner like me.

I don’t exactly remember how I discovered gpio-keys, but I think it was when I was referring to qcom-apq8064-asus-nexus7-flo.dts where I first saw it. It seemed like a simpler form of the touchkeys since I didn’t have to modify any drivers, so why not go ahead and add it for my device?

Looking back I’m glad I did this first. Otherwise the hiccups I encountered (described later) would’ve confused me even more.

downstream to mainline

Before we start, here’s my patch to mainline.

By playing around with the touchkeys, I learned that the board-<codename>.c file is basically the old way of doing a dts file. For me, it was board-express.c and here was the main part that I was looking at.

Now how does that translate to my patch?

inline comments for gpio-keys

gpio-keys means that this node is for the touchscreen. Just kidding. The node name is pretty self explanatory.

/ {
    gpio-keys {

compatible tells us that “gpio-keys” is the driver that we want to select

		compatible = "gpio-keys";

I believe the value of pinctrl-names can be arbitrary, but most dts files I found left them as “default”. pinctrl-0 should refer to the gpios in the gpio controller (inline comments for this below)

		pinctrl-names = "default";
		pinctrl-0 = <&gpio_keys_pin_a>;

Each subnode has a pattern:

For the Home button we add wakeup-event-action and wakeup-source to use it to wake the phone from suspend

Downstream, it’s defined here and I got the GPIO numbers from here. I think it’s pretty simple to match them up with the definitions in the DTS.

Here’s the documentation to learn more. You can also look at similar DTS files to your own to see how to approach it

		key-home {
			label = "Home";
			gpios = <&msmgpio 40 GPIO_ACTIVE_LOW>;
			debounce-interval = <5>;
			linux,code = <KEY_HOMEPAGE>;
			wakeup-event-action = <EV_ACT_ASSERTED>;
			wakeup-source;
		};

		key-volume-up {
			label = "Volume Up";
			gpios = <&msmgpio 50 GPIO_ACTIVE_LOW>;
			debounce-interval = <5>;
			linux,code = <KEY_VOLUMEUP>;
		};

		key-volume-down {
			label = "Volume Down";
			gpios = <&msmgpio 81 GPIO_ACTIVE_LOW>;
			debounce-interval = <5>;
			linux,code = <KEY_VOLUMEDOWN>;
		};
	};
 };

inline comments for gpio controller

msmgpio is the GPIO controller. My phone has a couple of interrupt controllers like this, so how do I verify this? You can do cat /proc/interrupts and see what controller is being used. For me it was the following:

Note: Mainline can be labeled differently, but this should help you start searching for the controller downstream. I got lucky in this case.

&msmgpio {

For this to make sense, you have to look at my downstream code

    gpio_keys_pin_a: gpio-keys-active-state {
		pins = "gpio40", "gpio50", "gpio81";
		function = "gpio";
		bias-disable;
		drive-strength = <8>;
	};
};

configs to enable

Now that I got an updated DTS, I tried testing with evtest (more below), but it didn’t work. Here are the two problems I ran into, and their solutions

testing with evtest

Finally tested with evtest. You can either compile it statically for your platform of choice, or install it from postmarketOS/$DISTRO when packaging up your kernel. It’s has a pretty simple interface and should show you a list of inputs, if any are detected.

You can also refer to this and this for more information

thanks

Huge thanks to Alexey Min for helping me! He helped me in the configs to enable section and helped me understand that drive-strength can take a value.

next post

Here’s my next post in this series