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

实际上,我为嵌入式板开发了一个C程序,当按BUTTON(输入事件)时,LED会亮起和熄灭。另一方面,我刚刚开发了一个具有其基本功能(OPEN,CLOSE,READ,WRITE)的简单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] = '\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 "/" 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\n", 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\n");

        /* 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\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));

        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\n",
                        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\n");

    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\n");
    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\n");
    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文件操作,以便用户代码可以等待设备中的事件并读取它们。

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


您需要在设备的私人数据中输入struct fasync_struct *

struct fasync_struct *pasync_queue;


在设备专用数据初始化期间,需要通过某种方式将其初始化为NULL。您的操作方式取决于您。
您需要一个由fasyncstruct file_operations成员指向的fasync文件操作处理程序。 fasync处理程序的实现非常简单,因为它只需要使用提供的参数和指向设备专用fasync_helper()的指针来调用struct fasync_struct *

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,
};

您的设备驱动程序可以通过调用SIGIO来发送kill_fasync()信号,如下所示:

     // 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);


N.B.最后一个参数(在这种情况下为值POLL_IN)影响应用程序在其信号处理程序中看到的si_bandsiginfo_t成员的值。
您的应用程序需要为SIGIO信号设置信号处理程序。我建议使用sigaction()进行设置。
您的应用程序在打开设备文件时需要设置O_ASYNC标志,或者在打开设备文件后通过调用fcntl(fd, F_SETFL, O_ASYNC);进行设置。

关于c - 如何从Linux内核空间向用户空间发送信号以通知输入硬件事件,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58639288/

10-16 11:40