Quantcast
Channel: Active questions tagged linux-kernel - Stack Overflow
Viewing all articles
Browse latest Browse all 12244

Use signals to notify about an input hardware event for a push button in C?

$
0
0

I am developing a Linux kernel module that communicates with a user-space application through signals in order to notify about the push-button event from my embedded board, therefore, the LED turns on.

For that, I started developing a simple module and userspace application as an example and to understand well how it works. The module searches for the application PID, send a signal through call back function named receiveData and print 1234 as a result.

This is the module :

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/siginfo.h>    //siginfo
#include <linux/rcupdate.h>    //rcu_read_lock
#include <linux/sched/signal.h>    //find_task_by_pid_type
#include <linux/debugfs.h>
#include <linux/uaccess.h>

#define SIG_TEST 44    // we choose 44 as our signal number (real-time signals are in the range of 33 to 64)

struct dentry *file;

static ssize_t write_pid(struct file *file, const char __user *buf,
                                size_t count, loff_t *ppos)
{
    char mybuf[10];
    int pid = 0;
    int ret;
    struct siginfo info;
    struct task_struct *t;
    /* read the value from user space */
    if(count > 10)
        return -EINVAL;
    copy_from_user(mybuf, buf, count);
    sscanf(mybuf, "%d", &pid);
    printk("pid = %d\n", pid);

    /* send the signal */
    memset(&info, 0, sizeof(struct siginfo));
    info.si_signo = SIG_TEST;
    info.si_code = SI_QUEUE;    // this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,
                    // and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data 
                    // is not delivered to the user space signal handler function. 
    info.si_int = 1234;          //real time signals may have 32 bits of data.

    rcu_read_lock();
    t = pid_task(find_vpid(pid), PIDTYPE_PID);  //find the task_struct associated with this pid
    if(t == NULL){
        printk("no such pid\n");
        rcu_read_unlock();
        return -ENODEV;
    }
    rcu_read_unlock();

    ret = send_sig_info(SIG_TEST, &info, t);    //send the signal
    if (ret < 0) {
        printk("error sending signal\n");
        return ret;
    }
    return count;
}

static const struct file_operations my_fops = {
    .write = write_pid,
};

static int __init signalexample_module_init(void)
{
    /* we need to know the pid of the user space process
      * -> we use debugfs for this. As soon as a pid is written to 
      * this file, a signal is sent to that pid
      */
    /* only root can write to this file (no read) */
    file = debugfs_create_file("signalconfpid", 0200, NULL, NULL, &my_fops);
    return 0;
}
static void __exit signalexample_module_exit(void)
{
    debugfs_remove(file);

}

module_init(signalexample_module_init);
module_exit(signalexample_module_exit);
MODULE_LICENSE("GPL");

This is the user space application :

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#define SIG_TEST 44 /* we define our own signal, hard coded since SIGRTMIN is different in user and in kernel space */ 


void receiveData(int n, siginfo_t *info, void *unused) {
    printf("received value %i\n", info->si_int);
}

int main ( int argc, char **argv )
{
    int configfd;
    char buf[10];
    /* setup the signal handler for SIG_TEST 
      * SA_SIGINFO -> we want the signal handler function with 3 arguments
      */

    struct sigaction sig;
    sig.sa_sigaction = receiveData;
    sig.sa_flags = SA_SIGINFO;
    sigaction(SIG_TEST, &sig, NULL);

     /* kernel needs to know our pid to be able to send us a signal ->
      * we use debugfs for this -> do not forget to mount the debugfs!
      */
    configfd = open("/sys/kernel/debug/signalconfpid", O_WRONLY);
    if(configfd < 0) {
        perror("open");
        return -1;
    }
    sprintf(buf, "%i", getpid());
    if (write(configfd, buf, strlen(buf) + 1) < 0) {
        perror("fwrite"); 
        return -1;
    }

    return 0;
}

This works fine. But the problem for me now, how can we base on that method in order to notify about an embedded board push-button device that make led turn on and off?

I developed an application for the led and button that works fine but without using signals or kernel modules :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

#include <linux/input.h>

#define BTN_FILE_PATH "/dev/input/event0"
#define LED_PATH "/sys/class/leds"
#define red "red"

void change_led_state(char *led_path, int led_value)
{
    char    lpath[64];
    FILE    *led_fd;

    strncpy(lpath, led_path, sizeof(lpath) - 1);
    lpath[sizeof(lpath) - 1] = '\0';

    led_fd = fopen(lpath, "w");

    if (led_fd == NULL) {
        fprintf(stderr, "simplekey: unable to access led\n");
        return;
    }

    fprintf(led_fd, "%d\n", led_value);

    fclose(led_fd);
}

void reset_leds(void)
{
    change_led_state(LED_PATH "/" red "/brightness", 0);
}

int configure_leds(void)
{
    FILE    *r_fd;
    char    *none_str = "none";

    /* Configure leds for hand control */
    r_fd = fopen(LED_PATH "/" red "/trigger", "w");

    fprintf(r_fd, "%s\n", none_str);

    fclose(r_fd);

    /* Switch off leds */
    reset_leds();

    return 0;
}

void eval_keycode(int code)
{
    static int red_state = 0;

    switch (code) {
    case 260:
        printf("BTN left pressed\n");

        /* figure out red state */
        red_state = red_state ? 0 : 1;

        change_led_state(LED_PATH "/" red "/brightness", red_state);
        break;
    }
}


int main(void)
{
    int file;
    /* how many bytes were read */
    size_t  rb;
    int ret;
    int yalv;
    /* the events (up to 64 at once) */
    struct input_event  ev[64];
    char    *str = BTN_FILE_PATH;

    printf("Starting simplekey app\n");

    ret = configure_leds();
    if (ret < 0)
        exit(1);

    printf("File Path: %s\n", str);

    if((file = open(str, O_RDONLY)) < 0) {
        perror("simplekey: File can not open");
        exit(1);
    }

    for (;;) {
        /* Blocking read */
        rb= read(file, &ev, sizeof(ev));

        for (yalv = 0;
            yalv < (int) (rb / sizeof(struct input_event));
            yalv++) {
            if (ev[yalv].type == EV_KEY){

                /* Change state on button pressed */
                if (ev[yalv].value == 0)
                    eval_keycode(ev[yalv].code);
            }
        }
    }

    close(file);
    reset_leds();
    exit(0);
}

If anybody could help me I will be grateful. I have been stuck in that for so many days and I did not find a solution.

Thank you.


Viewing all articles
Browse latest Browse all 12244

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>