Linux驱动:异步通讯

本文介绍了Linux驱动中异步通讯的概念,以按键驱动为例,说明如何从应用程序主动读取到驱动程序通知读取的转变。内容涵盖异步通讯的引入、相关函数解析、启用异步通知的步骤,以及实验平台、程序和结果的详细分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的信号。

启用异步通知的步骤

  1. signal(SIGIO, sig_handler);
    调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。
  2. fcntl(fd, F_SET_OWNER, getpid());
    指定一个进程作为文件的“属主(filp->owner)”,这样驱动程序才知道信号要发给哪个进程。
  3. 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; 
    unsigned long ul_regval;

    switch(i_pinselect)
    {
        case 1:
        case 2:
            ul_regval = *gpfdat;		//读寄存器gpfdat的值
            if (i_pinselect == 1)
                i_pinval = (ul_regval & (1<<0)) ? 1 : 0;
            else
                i_pinval = (ul_regval & (1<<2)) ? 1 : 0;
            break;
        case 3:
        case 4:
            ul_regval = *gpgdat;		//读寄存器gpgdat的值
            if (i_pinselect == 3)
                i_pinval = (ul_regval & (1<<3)) ? 1 : 0;
            else
                i_pinval = (ul_regval & (1<<11)) ? 1 : 0;
            break;
    }

    if (i_pinval)	//按下读回来的值为0,松开读回来的值为1
    {
        /* 松开 */
        key_val = 0x80 | i_pinselect;
    }
    else
    {
        /* 按下 */
        key_val = i_pinselect;
    }

    kill_fasync (&button_async, SIGIO, POLL_IN);

    return IRQ_RETVAL(IRQ_HANDLED);
}


static int buttons_open(struct inode *inode, struct file *file)
{
    int i_ret;
	/* 注册一个名为S2的外部中断,上升沿和下降沿触发,中断执行函数为buttons_irq */
    i_ret = request_irq(IRQ_EINT0,  buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S2", (void *)&t_s3c2440_devid[0]);	
    i_ret = request_irq(IRQ_EINT2,  buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S3", (void *)&t_s3c2440_devid[1]);	
    i_ret = request_irq(IRQ_EINT11, buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S4", (void *)&t_s3c2440_devid[2]);	
    i_ret = request_irq(IRQ_EINT19, buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S5", (void *)&t_s3c2440_devid[3]);	

    return 0;
}

ssize_t buttons_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    int i_ret;
    if (size != 1)
        return -EINVAL;

    /* 返回键值 */
    i_ret = copy_to_user(buf, &key_val, 1);


    return 1;
}

static int buttons_drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT0, (void *)&t_s3c2440_devid[0]);	//释放中断
    free_irq(IRQ_EINT2, (void *)&t_s3c2440_devid[1]);
    free_irq(IRQ_EINT11, (void *)&t_s3c2440_devid[2]);
    free_irq(IRQ_EINT19, (void *)&t_s3c2440_devid[3]);
    return 0;
}


static int buttons_drv_fasync (int fd, struct file *filp, int on)
{
	//printk("driver: fifth_drv_fasync\n");
	return fasync_helper (fd, filp, on, &button_async);
}


static struct file_operations buttons_fops=
{
    .owner = THIS_MODULE,
    .open = buttons_open,
    .read = buttons_read,
    .release =  buttons_drv_close,
    .fasync	 =  buttons_drv_fasync,
};

static int buttons_init(void)
{
    major = register_chrdev(0, "buttons", &buttons_fops);	//注册一个字符设备

    buttons_class = class_create(THIS_MODULE, "buttons");	//创建一个类

    device_create(buttons_class, NULL, MKDEV(major,0), NULL, "buttons");	//在类下面创建一个设备


    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);		//寄存器指针重定位
    gpfdat = gpfcon + 1;

    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);		//寄存器指针重定位
    gpgdat = gpgcon + 1;

    return 0;
}

static void buttons_exit(void)
{
    device_destroy(buttons_class, MKDEV(major,0));

    class_destroy(buttons_class);

    unregister_chrdev(major, "buttons");

    iounmap(gpfcon);
    iounmap(gpgcon);
}

module_init(buttons_init);

module_exit(buttons_exit);

MODULE_LICENSE("GPL");

/* 测试程序 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>

int fd;

void my_signal_fun(int signum)
{
	unsigned char key_val;
	read(fd, &key_val, 1);              //从驱动读取键值
	printf("key_val: 0x%x\n", key_val);
}

int main(int argc, char **argv)
{
	unsigned char key_val;
    int i_ret;
    int Oflags;
	
	fd = open("/dev/buttons", O_RDWR);
	if (fd < 0)
	{
	    return 0;
		printf("can't open!\n");
	}
    
    signal(SIGIO, my_signal_fun);   //设置该测试程序收到SIGIO信号时执行的函数

    fcntl(fd, F_SETOWN, getpid());
	
	Oflags = fcntl(fd, F_GETFL); 
	
	fcntl(fd, F_SETFL, Oflags | FASYNC);
    
	while (1)
	{
		sleep(1000);
	}
	
	return 0;
}
实验结果

在这里插入图片描述

过程分析

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值