Linux驱动:异步通讯
异步通讯的引入
还是以按键驱动为例,之前我们写的两个驱动( Linux驱动:POLL机制 和 Linux驱动:外部中断)都是应用程序主动去读按键值,如果没有按键就做休眠等操作。而现在我们使用异步通讯,当有按键值的时候,驱动程序会通知应用程序去读取按键值。这种更加符合系统中断的思想。
函数解析
sighandler_t signal(int signum, sighandler_t handler);
-
输入参数
- signum 设置的信号类型,他的一般取值可以看该文章 POSIX多线程笔记(5):信号 中的表格部分
-
handler 获得该信号以后执行的函数,函数原型为
typedef void (*sighandler_t)(int)
函数功能
- 设置该应用程序在获得输入参数 signum设置的信号时,执行 handler指向的函数。
有接收信号然后处理的函数,就有发送信号的函数,在驱动程序中我们使用kill_fasync发送信号
void kill_fasync(struct fasync_struct **fp, int sig, int band)
-
输入参数
- fp 传入结构体指针的地址,该结构体可以用fasync_helper函数初始化
- sig 发送的信号类型
- band 带宽,一般都是使用 POLL_IN,表示设备可读,如果设备可写,使用 POLL_OUT 函数功能
- 对 fp结构体中设置的进程发送变量 sig的信号。
启用异步通知的步骤
- signal(SIGIO, sig_handler);
调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。 - fcntl(fd, F_SET_OWNER, getpid());
指定一个进程作为文件的“属主(filp->owner)”,这样驱动程序才知道信号要发给哪个进程。 - f_flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, f_flags | FASYNC);
在设备文件中添加FASYNC标志,驱动中就会调用将要实现的test_fasync函数。
三个步骤执行后,一旦有信号产生,相应的进程就会收到。
测试
实验平台
内核版本:Linux-4.19.5
开发板:SAMSUNG JZ2440
实验程序
/* 驱动程序 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
int major; //主设备号
static struct class *buttons_class;
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;
/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val; //返回给用户的键值
static struct fasync_struct *button_async;
const int t_s3c2440_devid[4] = {
1, 2, 3, 4}; //键值数组
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
int i_pinselect = *((int *)dev_id);
int i_pinval = 0;