1、driver
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/siginfo.h>
#include <linux/pid.h>
#include <linux/uaccess.h>
#include <linux/sched/signal.h>
#include <linux/pid_namespace.h>
#include <linux/interrupt.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/miscdevice.h>
#include <linux/atomic.h>
#include <linux/delay.h>
#include <dt-bindings/gpio/meson-t7-gpio.h>
// 设备名称
#define MYDEV_NAME "ef1e_irq"
#define RFDM_IRQ_GPIO 467 //GPIOT_21
#define DHU_WAKEUP_IRQ_GPIO 493 //GPIOY_9
#define SET_PID 100
#define GET_DHU_WAKEUP_GPIO_VAL 200
// 设备类
static struct class *my_class;
// 用来保存设备
struct cdev my_cdev;
// 用来保存设备号
int mydev_major = 0;
int mydev_minor = 0;
// 中断号
int virq1 = 0;
int virq2 = 0;
// 用来保存向谁发送信号,应用程序通过 ioctl 把自己的进程 ID 设置进来。
static int g_pid = 0;
// 用来发送信号给应用程序
static void send_signal(int sig_no, int sig_code)
{
int ret;
struct siginfo info;
struct task_struct *my_task = NULL;
if (0 == g_pid)
{
// 说明应用程序没有设置自己的 PID
printk("pid[%d] is not valid!\n", g_pid);
return;
}
printk("send signal %d code %d to pid %d.\n", sig_no, sig_code, g_pid);
// 构造信号结构体
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = sig_no;
info.si_errno = 0;
info.si_code = sig_code;
// 获取自己的任务信息,使用的是 RCU 锁
rcu_read_lock();
my_task = pid_task(find_vpid(g_pid), PIDTYPE_PID);
rcu_read_unlock();
if (my_task == NULL)
{
printk("get pid_task failed!\n");
return;
}
// 发送信号
ret = send_sig_info(sig_no, (struct kernel_siginfo *)&info, my_task);
if (ret < 0)
{
printk("send signal failed!\n");
}
printk("send signal succeed!\n");
}
//中断处理函数
static irqreturn_t rfdm_irq_handler(int irq, void * dev)
{
send_signal(SIGIO, 1);
printk("rfdm_irq_handler send SIGIO 1.\n");
return IRQ_HANDLED;
}
static irqreturn_t dhu_wakeup_irq_handler(int irq, void * dev)
{
send_signal(SIGIO, 2);
printk("dhu_wakeup_irq_handler send SIGIO 2.\n");
return IRQ_HANDLED;
}
// 当应用程序打开设备的时候被调用
static int mydev_open(struct inode *inode, struct file *file)
{
printk("mydev_open is called.\n");
return 0;
}
static long mydev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
void __user *pArg;
int val = -1;
printk("mydev_ioctl is called. cmd = %d\n", cmd);
if (SET_PID == cmd) // 设置进程号
{
pArg = (void *)arg;
if (!access_ok(pArg, sizeof(int)))
{
printk("access failed!\n");
return -EACCES;
}
// 把用户空间的数据复制到内核空间
if (copy_from_user(&g_pid, pArg, sizeof(int)))
{
printk("copy_from_user failed!\n");
return -EFAULT;
}
printk("mydev_ioctl pid = %d.\n", g_pid);
}
if(GET_DHU_WAKEUP_GPIO_VAL == cmd) // 读 DHU_WAKEUP_IRQ_GPIO 的值
{
val = gpio_get_value(DHU_WAKEUP_IRQ_GPIO);
pArg = (void *)arg;
if (!access_ok(pArg, sizeof(int)))
{
printk("access failed!\n");
return -EACCES;
}
// 把内核空间的数据复制到用户空间
if (copy_to_user(pArg, &val, sizeof(int)))
{
printk("copy_to_user failed!\n");
return -EFAULT;
}
printk("mydev_ioctl val = %d.\n", val);
}
return 0;
}
static const struct file_operations mydev_ops={
.owner = THIS_MODULE,
.open = mydev_open,
//.unlocked_ioctl = mydev_ioctl
.compat_ioctl = mydev_ioctl
};
static int __init mydev_driver_init(void)
{
int devno, ret;
dev_t num_dev;
printk("mydev_driver_init is called. \n");
gpio_direction_input(RFDM_IRQ_GPIO);
printk("RFDM_IRQ_GPIO(GPIOT_21) = %d\n", gpio_get_value(RFDM_IRQ_GPIO));
gpio_direction_input(DHU_WAKEUP_IRQ_GPIO);
printk("DHU_WAKEUP_IRQ_GPIO(GPIOY_9) = %d\n", gpio_get_value(DHU_WAKEUP_IRQ_GPIO));
/*---------------- 注册RFDM irq -------------------*/
ret = gpio_request(RFDM_IRQ_GPIO, "rfdm_irq");
if (ret != 0) {
pr_err("RFDM-IRQ: gpio_request failed! ret=%d\n", ret);
return ret;
}
virq1 = gpio_to_irq(RFDM_IRQ_GPIO);
if (virq1 < 0) {
pr_err("RFDM-IRQ: gpio_to_irq failed! virq=%d\n", virq1);
return -EINVAL;
}
ret = request_irq(virq1, rfdm_irq_handler, IRQF_TRIGGER_FALLING, "RFDM_IRQ_0", NULL);
if (ret < 0) {
pr_err("RFDM-IRQ: request_irq failed! ret=%d\n", ret);
return -EINVAL;
}
pr_info("RFDM-IRQ: request_irq succeed!\n");
/*---------------- 注册DHU wakeup irq -------------------*/
ret = gpio_request(DHU_WAKEUP_IRQ_GPIO, "dhu_wakeup_irq");
if (ret != 0) {
pr_err("DHU-WAKEUP-IRQ: gpio_request failed! ret=%d\n", ret);
return ret;
}
virq2 = gpio_to_irq(DHU_WAKEUP_IRQ_GPIO);
if (virq2 < 0) {
pr_err("DHU-WAKEUP-IRQ: gpio_to_irq failed! virq=%d\n", virq2);
return -EINVAL;
}
ret = request_irq(virq2, dhu_wakeup_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "DHU_WAKEUP_IRQ_0", NULL);
if (ret < 0) {
pr_err("DHU-WAKEUP-IRQ: request_irq failed! ret=%d\n", ret);
return -EINVAL;
}
pr_info("DHU-WAKEUP-IRQ: request_irq succeed!\n");
// 动态申请设备号(严谨点的话,应该检查函数返回值)
alloc_chrdev_region(&num_dev, mydev_minor, 1, MYDEV_NAME);
// 获取主设备号
mydev_major = MAJOR(num_dev);
printk("mydev_major = %d. \n", mydev_major);
// 创建设备类
my_class = class_create(THIS_MODULE, MYDEV_NAME);
// 创建设备节点
devno = MKDEV(mydev_major, mydev_minor);
// 初始化cdev结构
cdev_init(&my_cdev, &mydev_ops);
// 注册字符设备
cdev_add(&my_cdev, devno, 1);
// 创建设备节点
device_create(my_class, NULL, devno, NULL, MYDEV_NAME);
printk("mydev_driver_init done and succeed.\n");
return 0;
}
static void __exit mydev_driver_exit(void)
{
printk("mydev_driver_exit is called. \n");
// 删除设备节点
cdev_del(&my_cdev);
device_destroy(my_class, MKDEV(mydev_major, mydev_minor));
// 释放设备类
class_destroy(my_class);
// 注销设备号
unregister_chrdev_region(MKDEV(mydev_major, mydev_minor), 1);
// 注销中断处理函数
//free_irq(virq1, &mydev);
//free_irq(virq2, &mydev);
}
MODULE_LICENSE("GPL");
module_init(mydev_driver_init);
module_exit(mydev_driver_exit);
2、app
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <errno.h>
char *dev_name = "/dev/ef1e_irq";
// 信号处理函数
static void signal_handler(int signum, siginfo_t *info, void *context)
{
// 打印接收到的信号值
printf("signal_handler: signum = %d \n", signum);
printf("signo = %d, code = %d, errno = %d \n",
info->si_signo,
info->si_code,
info->si_errno);
}
int main(int argc, char *argv[])
{
int fd, count = 0;
int pid = getpid();
int val = -1;
int ret;
// 打开GPIO
if((fd = open(dev_name, O_RDWR | O_NDELAY)) < 0){
printf("open dev failed! \n");
return -1;
}
printf("open dev success! \n");
// 注册信号处理函数
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = &signal_handler;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGIO, &sa, NULL);
// set PID
printf("call ioctl 100. pid = %d \n", pid);
ret = ioctl(fd, 100, &pid);
if(ret != 0)
printf("call ioctl 100 failed. errno = %d \n", errno);
ret = ioctl(fd, 200, &val);
printf("call ioctl 200. val = %d \n", val);
if(ret != 0)
printf("call ioctl 200 failed. errno = %d \n", errno);
// 休眠1秒,等待接收信号
while (1)
sleep(1);
// 关闭设备
close(fd);
}