Я использую встроенную плату с операционной системой Linux.
На этой плате есть кнопка и светодиод.
Я хочу управлять светодиодом, включать и выключать через кнопку. Кроме того, я хочу использовать сигналы для уведомления о происходящем нажатии кнопки. Когда событие произойдет, сигнал будет отправлен в пространство пользователя.
Для этого я разработал загружаемый модуль ядра Linux и основное приложение пользовательского пространства.
Это модуль:
#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>
#include<linux/slab.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/fs.h>
#define SIG_TEST 44 // we choose 44 as our signal number (real-time signals are in the range of 33 to 64)
#define BTN_FILE_PATH "/dev/input/event0"
char *str = BTN_FILE_PATH;
int file;
struct file *f;
static ssize_t write_pid(struct file *pfile, const char __user *buffer,
size_t length, loff_t *offset)
{
return 0;
}
static ssize_t read_pid(struct file *pfile, char __user *buffer,
size_t length, loff_t *offset)
{
char mybuf[10];
enum { MAX_BUF_SIZE = 4096 };
size_t buf_size = 0;
char *buf = NULL;
ssize_t total = 0;
ssize_t rc = 0;
struct task_struct *t;
struct input_event *ev;
int yalv;
int ret;
struct siginfo info;
int pid =0;
/* Allocate temporary buffer. */
if (length) {
buf_size = min_t(size_t, MAX_BUF_SIZE, length);
ev = kmalloc(buf_size, GFP_KERNEL);
if (ev == NULL) {
return -ENOMEM;
}
}
/* read the value from user space */
if(length > 10)
return -EINVAL;
copy_from_user(mybuf, buffer, length);
sscanf(mybuf, "%d", &pid);
printk("pid = %d\n", pid);
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();
/* Read file to buffer in chunks. */
size_t amount = min_t(size_t, length, buf_size);
rc = kernel_read(f, ev, amount, offset);
if (rc > 0) {
/* Have read some data from file. */
if (copy_to_user(buffer, ev, rc) != 0) {
/* Bad user memory! */
rc = -EFAULT;
} else {
/* Update totals. */
total += rc;
buffer += rc;
*offset += rc;
length -= rc;
for (yalv = 0; yalv < (int) (rc / sizeof(struct input_event)); yalv++) {
if (ev[yalv].type == EV_KEY) {
printk("signal was send\n");
if (ev[yalv].value == 0)
ret = send_sig_info(SIG_TEST, &info, t); //send the signal
if (ret < 0) {
printk("error sending signal\n");
kfree(buf);
return ret;
}
}
}
if (rc < amount) {
/* Didn't read the full amount, so terminate early. */
rc = 0;
}
}
}
/* Free temporary buffer. */
kfree(buf);
}
static const struct file_operations my_fops = {
.owner = THIS_MODULE,
.write = write_pid,
.read = read_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) */
register_chrdev(240, "Simple Char Drv", &my_fops);
file = debugfs_create_file("signalconfpid", 0200, NULL, NULL, &my_fops);
f = filp_open(str, 0 , O_RDONLY);
return 0;
}
static void __exit signalexample_module_exit(void)
{
unregister_chrdev(240, "Simple Char Drv");
debugfs_remove(file);
}
Это основное приложение пользовательского пространства:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <signal.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"
#define SIG_TEST 44
#define BUFFER_LENGTH 256
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 */
l_fd = fopen(LED_PATH "/" red "/trigger", "w");
if ( r_fd == NULL) {
perror("simplekey: unable to configure led");
return -EACCES;
}
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;
}
}
void receiveData(int n, siginfo_t *info, void *unused)
{
printf("received value %i\n", info->si_int);
}
int main ( int argc, char **argv )
{
int ret;
int nc;
int fd;
int configfd;
char buf[10];
char buffer[BUFFER_LENGTH];
/* 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);
fd = open("/dev/char_device", O_RDONLY); // Open the device with read access
if (fd < 0)
{
perror("Failed to open the device...");
return errno;
}
sprintf(buffer, "%i", getpid());
nc = read(fd,buffer,strlen(buffer));
if (nc < 0)
{
perror("Failed to read the message from the device.");
return errno;
}
ret = configure_leds();
if (ret < 0)
exit(1);
reset_leds();
getchar();
return 0;
}
После вставки модуля и запуска приложения, мне возвращается только PID моего кода пользователя,но когда я нажимаю кнопку, ничего не происходит. Сигнал не отправляется как я догадываюсь.
Если кто-нибудь мне поможет, я буду благодарен. Спасибо!