如何从Linux内核空间向用户空间发送信号以通知输入硬件事件 [英] How to send signal from Linux kernel space to user space in order to notify about an input hardware event

查看:19
本文介绍了如何从Linux内核空间向用户空间发送信号以通知输入硬件事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的内核模块代码需要向用户域程序发送信号,以便将其执行转移到注册的信号处理程序。

事实上,我已经为我的嵌入式板卡开发了一个C程序,当我按下按钮(输入事件)时,LED会亮起和熄灭。另一方面,我刚刚开发了一个简单的Linux模块,包括它的基本功能(打开、关闭、读、写)。

我只是不知道如何修改我的主程序和内核模块以达到我的目标。

我与您分享我的用户空间计划:

#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 green "green"

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] = '';

    led_fd = fopen(lpath, "w");

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

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

    fclose(led_fd);
}

void reset_leds(void)
{

    change_led_state(LED_PATH "/" green "/brightness", 0);
}

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

    /* Configure leds for hand control */

    r_fd = fopen(LED_PATH "/" green "/trigger", "w");




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


    fclose(r_fd);


    /* Switch off leds */
    reset_leds();

    return 0;
}

void eval_keycode(int code)
{

    static int green_state = 0;

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

        /* figure out green state */

        green_state = green_state ? 0 : 1;

        change_led_state(LED_PATH "/" green "/brightness", green_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
");

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

    printf("File Path: %s
", 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));

        if (rb < (int) sizeof(struct input_event)) {
            perror("simplekey: short read");
            exit(1);
        }

        for (yalv = 0;
            yalv < (int) (rb / sizeof(struct input_event));
            yalv++) {
            if (ev[yalv].type == EV_KEY) {
                printf("%ld.%06ld ",
                    ev[yalv].time.tv_sec,
                    ev[yalv].time.tv_usec);
                printf("type %d code %d value %d
",
                        ev[yalv].type,
                        ev[yalv].code, ev[yalv].value);

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

    close(file);

这是基本的内核模块:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>  
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/input.h>

MODULE_LICENSE("GPL");      
MODULE_AUTHOR("Gaston");  
MODULE_DESCRIPTION("A simple Linux char driver"); 
MODULE_VERSION("0.1"); 


ssize_t exer_open(struct inode *pinode, struct file *pfile) {

    printk(KERN_INFO "Device has been opened
");

    return 0;
}



ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {

    return 0;
}



ssize_t exer_write(struct file *pfile, const char __user *buffer, size_t length, loff_t *offset) {

    return 0;

}   




ssize_t exer_close(struct inode *pinode, struct file *pfile) {

    printk(KERN_INFO "Device successfully closed
");
    return 0;
}


struct file_operations exer_file_operations = { 
    .owner = THIS_MODULE,
    .open = exer_open,
    .read = exer_read,
    .write = exer_write,
    .release = exer_close,
};


int exer_simple_module_init(void) {

    printk(KERN_INFO "Initializing the LKM
");
    register_chrdev(240, "Simple Char Drv", &exer_file_operations);
    return 0;
}


void exer_simple_module_exit(void) {

    unregister_chrdev(240, "Simple Char Drv");
}


module_init(exer_simple_module_init);
module_exit(exer_simple_module_exit);

我希望你能帮助我。谢谢!

推荐答案

我将专注于发送信号,因为这就是您所要求的,尽管向进程发送信号是相当残酷的。最好是实现pollread文件操作,以便用户代码可以等待设备中的事件并读取它们。

无论如何,要向打开设备的进程发送信号,您需要的是:

  1. 您的设备的私有数据中需要struct fasync_struct *

    struct fasync_struct *pasync_queue;
    
    在初始化您的设备私有数据时,需要通过某种方式将其初始化为。如何做到这一点由您自己决定。

  2. 您需要struct file_operationsfasync成员指向的Fasync文件操作处理程序。Fasync处理程序的实现非常简单,因为它只需要使用提供的参数和指向您设备的私有struct fasync_struct *的指针来调用fasync_helper()

    static int exer_fasync(int fd, struct file *pfile, int mode)
    {
         // N.B. Change this code to use the pasync_queue member from your device private data.
         struct fasync_struct **fapp = &pasync_queue;
         return fasync_helper(fd, pfile, mode, fapp);
    }
    
    struct file_operations exer_file_operations = { 
         .owner = THIS_MODULE,
         .open = exer_open,
         .read = exer_read,
         .write = exer_write,
         .release = exer_close,
         .fasync = exer_fasync,
    };
    
  3. 您的设备驱动程序可以通过调用kill_fasync()发送SIGIO信号,如下所示:

         // N.B. Change this code to use the pasync_queue member from your device private data.
         struct fasync_struct **fapp = &pasync_queue;
         kill_fasync(fapp, SIGIO, POLL_IN);
    

    注意。最后一个参数(本例中的值POLL_IN)影响应用程序在其信号处理程序中看到的siginfo_t成员的值。

  4. 应用程序需要为SIGIO信号设置一个信号处理程序。我建议使用sigaction()进行设置。

    /li>
  5. 您的应用程序需要在打开设备文件时设置O_ASYNC标志,或者在打开设备文件后调用fcntl(fd, F_SETFL, O_ASYNC);进行设置。

这篇关于如何从Linux内核空间向用户空间发送信号以通知输入硬件事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆