I.MX6U嵌入式Linux驱动开发(13)异步通知实验

Linux 提供了异步通知这个机制来完成:驱动程序能主动向应用程序发出通知,报告自己可以访问,然后应用程序在从驱动程序中读取或写入数据。

阻塞非阻塞与异步通知的区别:
阻塞/非阻塞:需要应用程序主动的去查询设备的使用情况。通过阻塞方式访问的话应用程序会处于休眠态,等待驱动设备可以使用,非阻塞方式的话会通过 poll 函数来不断的轮询,查看驱动设备文件是否可以使用。
异步通知:通过主动向应用程序发送信号的方式来报告自己可以访问了,应用程序获取到信号以后就可以从驱动设备中读取或者写入数据了。

异步通知的核心就是信号,在 arch/xtensa/include/uapi/asm/signal.h 文件中定义了 Linux 所支持的所有信号。

在应用程序中可以使用 signal 函数来设置指定信号的处理,使用函数信号处理函数来关闭应用程序。

//signal 函数
sighandler_t signal(int signum, sighandler_t handler)
//信号处理函数
typedef void (*sighandler_t)(int)

1、驱动中的信号处理:
(1)在驱动程序中定义一个 fasync_struct 结构体指针变量;
(2)设备驱动中实现 file_operations 操作集中的 fasync 函数,fasync还需要借助fasync_helper函数;

int (*fasync) (int fd, struct file *filp, int on);
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp);

(3)驱动里面调用fasync向应用发送信号;

void kill_fasync(struct fasync_struct **fp, int sig, int band)

(4)关闭驱动的时候要删除信号;

2、应用程序对异步处理的通知:
(1)注册信号处理函数,应用程序根据驱动程序所使用的信号来设置信号的处理函数;
(2)使用fcntl(fd, F_SETOWN, getpid())将本应用程序的进程号告诉给内核,;
(3)开启异步通知;

flags = fcntl(fd, F_GETFL); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC); /* 开启当前进程异步通知功能 */

1、驱动程序编写

cd /liunx/IMX6ULL/Linux_Driver/
mkdir 16_asyncnoti
cp 14_imx6uirq/ * 16_asyncnoti/ -rf
cp 14_imx6uirq/.vscode 16_asyncnoti/ -rf
cd 15_blockio/
ls
rm tasklet.c work.c
rm imx6uirqAPP
mv imx6uirqAPP.c asyncnotiAPP.c

在设备结构体中添加一个struct fasync_struct* fasync_queue;

编写驱动代码Linux内核源码中的joydev.c这个文件。

找到file_operations

static int imx6uirq_fasync(int fd, struct file *filp, int on)
{
	struct imx6uirq_dev *dev = filp->private_data;
	return fasync_helper(fd,filp,on,&dev->fasync_queue);
}
int imx6uirq_release(struct inode *inode, struct file *filp)
{
	return = imx6uirq_fasync(-1, filp, 0);
}
//操作集
struct const struct file_operations imx6uirq_fops = {
	......
	.fasync = imx6uirq_fasync,
	.release = imx6uirq_release,
};

当按键有效的时候,需要向应用程序通知,在timer_func()中按键处理完了之后添加:

static void timer_func(unsigned long arg)
{
	.......
	if(atomic_read(&dev->releasekey))//为真,表示一个完整的按键过程,接下来向应用程序发送信号
	{
		if(dev->fasync_queue)
			kill_fasync(&dev->fasync_queue, SIGIO, POLL_IN);
	}
}

编译报错,添加头文件:

#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/ide.h>

2、应用程序编写

添加头文件:

#include <signal.h>

不需要while()里面的内容:

我们需要设置信号处理函数:

signal(SIGIO, sigio_signal_func);

当驱动发送SIGIO时,这个sigio_signal_func()就会执行,
下面实现sigio_signal_func();

int fd;
static void sigio_signal_func(int num)
{
	int err = 0;
	unsigned int keyvalue = 0;
	err = read(fd, &keyvalue, sizeof(keyvalue));
	if(err < 0) {
	} else {
		printf("sigio signal! key value=%d", keyvalue);
	}
}

驱动怎么知道向哪一个应用发送呢?或者这个应用怎么知道开启异步通知呢?使用fcntl()函数。

设置一个状态位来获取当前进程状态;

fcntl(fd, F_SETOWN, getpid()); /* 将当前进程的进程号告诉给内核 */
flags = fcntl(fd, F_GETFD); /* 获取当前的进程状态 */
fcntl(fd, F_SETFL, flags | FASYNC);/* 设置进程启用异步通知功能 */

编译:

make
arm-linux-gnueabihf-gcc asyncnotiAPP.c -o asyncnotiAPP
sudo cp imx6uirq.ko asyncnotiAPP /home/yang/nfs/rootfs/lib/modules/4.1.15/ -f

开发板:

depmod
modprobe imx6uirq.ko
./asyncnotiAPP /dev/imx6uirq

直接退出了,应用程序有问题,在设置完了之后,需要一个死循环。

while(1){
	sleep(2);
}

编译验证:按下按键没有任何反应
在驱动程序中,在有效的按键按下后面打印一个标记:

if(atomic_read(&dev->releasekey))//为真,表示一个完整的按键过程,接下来向应用程序发送信号
	{
		if(dev->fasync_queue){
			kill_fasync(&dev->fasync_queue, SIGIO, POLL_IN);
			printk("kill_fasync\r\n");}
	}

检查按键中断函数有没有执行?打印一条消息。----->有反应
检查定时器中断?打印一条消息。----->有反应
检查timer_func()value的值,有没有效?

if(atomic_read(&dev->releasekey))//为真,表示一个完整的按键过程,接下来向应用程序发送信号
	{
		//if(dev->fasync_queue){
			kill_fasync(&dev->fasync_queue, SIGIO, POLL_IN);
			printk("kill_fasync\r\n");}
	}

也可以执行,这说明驱动没有问题。
最终发现在应用程序打打印中没有添加\r\n。然后在终端上并没有打印出来。其实打印了,只是存到缓存区了。
参考:https://blog.csdn.net/qq_37284607/article/details/112470262

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值