0

I'm trying to increase/decrease backlight value on an embedded linux (yocto) with a button connected to a gpio

I worked step by step, so, first of all, I added a kernel module which generates an interruption each time the button is pressed, it's work like a charm (printk each time the button is pressed, the value is also accessible in userspace at /sys/class/gpio/gpio133/value)

The backlight value is configurable in userspace at /sys/class/backlight/backlight/brightness. I configured the device tree to have 8 different levels of luminosity with the value 0 as the maximum, this also works

Then i tried to get the backlight value in my kernel module by adding a gpio_request and gpio_get_value but it always returns the value : 0. I think the kernel module can't access the gpio because it's already in use by the driver in charge of the backlight, am I right ?

As the gpio and backlight's values are accessible in userspace, i wrote a shell script with an infinite loop which read the gpio state, and if it change I get the backlight value and decrease it. It work's but it used more than 10% of the cpu. I also have a running Qt application with sound, and when the script is running the sound is crackling. So i added a sleep in it to slow down the reading of the gpio state with a duration just enough to avoid crackling sound, but now, sometimes, button presses are missed due the sleep.

Here is the working kernel module without without any attempt to control the backlight:

#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Button test management");
MODULE_VERSION("0.1");

static unsigned int GPIO_BUTTON_LUM_PLUS = 133;
static unsigned int irqNumber;

static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs);


static int __init ebbgpio_init (void)
{
   int result=0;
   printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST LKM\n");

   if (!gpio_is_valid(GPIO_BUTTON_LUM_PLUS))
   {
      printk(KERN_INFO "GPIO_TEST: invalid Button GPIO LKM\n");
      return -ENODEV;
   }

   gpio_request(GPIO_BUTTON_LUM_PLUS, "sysfs");
   gpio_direction_input(GPIO_BUTTON_LUM_PLUS);
   gpio_set_debounce(GPIO_BUTTON_LUM_PLUS, 200);
   gpio_export(GPIO_BUTTON_LUM_PLUS, false);

   printk(KERN_INFO "GPIO_TEST: the button state is currently: %d\n", gpio_get_value(GPIO_BUTTON_LUM_PLUS));

   irqNumber = gpio_to_irq(GPIO_BUTTON_LUM_PLUS);
   printk(KERN_INFO "GPIO_TEST: the button is mapped to IRQ: %d\n", irqNumber);

   result = request_irq(irqNumber, (irq_handler_t) ebbgpio_irq_handler, IRQF_TRIGGER_RISING, "ebb_gpio_handler", NULL);

   printk(KERN_INFO "GPIO_TEST: The interrupt request result is: %d\n", result);
   return result;
}

static void __exit ebbgpio_exit(void)
{
   printk(KERN_INFO "GPIO_TEST: The button state is currently: %d\n", gpio_get_value(GPIO_BUTTON_LUM_PLUS));
   gpio_unexport(GPIO_BUTTON_LUM_PLUS);
   free_irq(irqNumber, NULL);
   gpio_free(GPIO_BUTTON_LUM_PLUS);
}   

static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs)
{
   printk(KERN_INFO "GPIO_TEST: Interrupt! (button state is %d)\n", gpio_get_value(GPIO_BUTTON_LUM_PLUS));
   return (irq_handler_t) IRQ_HANDLED;
}


module_init(ebbgpio_init);
module_exit(ebbgpio_exit);

In the future I will need to read 2 gpio,one for decrease backlight value and the other one to increase the value, so i'm searching for a better solution Any help would be appreciate

Nico
  • 1
  • 1
  • Hi not a full answer but you should look into this kind of stuff : https://lwn.net/Articles/604686/ so that you'll be notified when a change happened to your file. – YCN- Apr 17 '19 at 12:50
  • You can check [gpio-watch](https://github.com/larsks/gpio-watch/) for instance. Be careful as it is GPL-3 licensed. You can also look at new GPIO API tool [here](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/tree/tools/gpiomon.c). – Nayfe Apr 17 '19 at 13:04
  • Thanks for your answers. YCN : I added inotify-tools to yocto and successfully manage to detect a change in a folder with a command found in a tutorial (inotifywait). But when i use it in /sys/class/gpio/gpio133 it doesn't see any change. I tried with the backlight, and it works, but in this case i change the value myself because the gpio is configure as an output. Nayfe: I will try your solution tomorow – Nico Apr 17 '19 at 14:58
  • Finally i manage to make inotifywait works, even if the gpio value file change, inotify needs the gpio edge file to be put at a value other than none. Now it works but when i press the button on the gpio, inotify detect 3 modify event on the gpio value file, certainly a debounce problem. Il also successfully cross compile gpio-watch, and it does exactly what i wanted. But and i don't achieved to add it to my custom layer in yocto because i don't know how to add a receipe based on a Makefile. – Nico Apr 19 '19 at 09:14
  • You may use `gpio-keys` kernel module and corresponding record in DT or ACPI to do this without any code to be written. – 0andriy Apr 26 '19 at 18:02

1 Answers1

0

gpio-watch does exactly what i wanted. I created a recipe to add it in my layer and successfully cross-compile the package. But as it is a makefile-based package i had some difficulties to install it directly in my image.

thanks to this : https://www.yoctoproject.org/docs/1.8/mega-manual/mega-manual.html#new-recipe-makefile-based-package I finally manage to install the package by adding this in my recipe :

 EXTRA_OEMAKE = "'CC=${CC}' 'RANLIB=${RANLIB}' 'AR=${AR}' 'CFLAGS=${CFLAGS} -I${S}/include -DWITHOUT_XATTR' 'BUILDDIR=${S}'"

 do_install () {
         oe_runmake install DESTDIR=${D} SBINDIR=${sbindir} MANDIR=${mandir} INCLUDEDIR=${includedir}
 }

Thanks again for your help

Nico
  • 1
  • 1