Linux驱动之异步通知的应用

发布时间:2024-08-20  

前面的按键驱动方式都是应用程序通过主动查询的方式获得按键值的:


1、查询方式


2、中断方式


3、poll机制


下面介绍第四种按键驱动的方式


4、异步通知:它可以做到应用程序不用随时去查询按键的状态,而等待有按键被按下后,驱动程序主动发消息给应用程序,应用程序再去处理。


比如说:kill -9 pid即是一种发信号的过程:其中9就是信号值,pid就是被发送的进程的进程号


a、一个简单的异步通知的例子


b、编写测试程序实现异步通知


c、更改按键驱动实现异步通知


1、一个简单的异步通知的例子


直接看到程序源码,可以看到这个程序在主程序里面什么事情也没有做,一直处于睡眠状态。


#include



void my_signal_test(int signum)

{

    static int cnt = 0;

    printf('signal = %d,%d timesn',signum,++cnt);

}


int main(int argc,char **argv)

{

    signal(SIGUSR1, my_signal_test);//建立一个信号函数,接收的信号是SIGUSR1表示用户可用的信号值


    while(1)

    {

        sleep(1000);

    }

    return 0;

}


首先这个程序调用了signal这个C库中的函数,在linux下查询它的用法输入man 2 signal


#include //需要包含的头文件


typedef void (*sighandler_t)(int);//信号函数原型,它的参数是信号值


sighandler_t signal(int signum, sighandler_t handler);//函数,其中signum代表发送的信号值,handler表示信号函数

编译这个程序,然后在命令行输入kill -USR1 2333,my_signal函数被运行。


2、更改测试程序实现异步通知


直接看代码,从代码可以看出,实现异步通知在应用层需要如下几步:


1、利用signal(SIGIO, fifth_testsignal)函数注册一个信号,信号处理的函数为fifth_testsignal。SISGIO说明是IO信号量,因为按键驱动属于IO型的。


2、利用fcntl(fd, F_SETOWN, getpid())函数将本应用程序的进程号告诉给内核,最终使得驱动程序可以成功发送信号给应用程序。


3、利用fcntl(fd, F_SETFL, oflags | FASYNC)函数改变fasync标记,最终会调用到驱动的faync > fasync_helper。


4、signal、与fcntl的系统调用过程比较复杂,后面再去分析。这里只能记住是怎么使用它们来是实现异步通知的功能。


#include

#include

#include

#include

#include

#include


static int fd;


static void fifth_testsignal(int signum)

{

    unsigned char key_val;

    

    printf('signal = %dn',signum);


    read(fd, &key_val, 1);//读取按键数据,只有收到按键数据驱动层才会发送消息给应用层。

         printf('signumkey_val: 0x%xnn',key_val);

}


/*

  *usage ./buttonstest

  */

int main(int argc, char **argv)

{

    char* filename='dev/buttons';

   int oflags;


    

    fd = open(filename, O_RDWR);//打开dev/firstdrv设备文件

    if (fd < 0)//小于0说明没有成功

    {

        printf('error, can't open %sn', filename);

        return 0;

    }

    

    if(argc !=1)

    {

        printf('Usage : %s ',argv[0]);

     return 0;

    }

    signal(SIGIO, fifth_testsignal);//注册一个信号,函数为fifth_testsignal

    

    fcntl(fd, F_SETOWN, getpid());  // 告诉内核,发给谁

    

    oflags = fcntl(fd, F_GETFL); //取得当前的状态

    

    fcntl(fd, F_SETFL, oflags | FASYNC);  // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct

    

  while(1)

  {

    sleep(1000);

  }

    

   return 0;

}


3、更改按键驱动实现异步通知功能


与原先的按键驱动程序相比:


1、定义fasync_struct结构


struct fasync_struct *fifth_fasync;//定义fasync_struct结构

2、在fifth_drv_ops 结构体中增加fifth_drv_fasync异步通知处理函数


static struct file_operations fifth_drv_ops = 

{

    .owner   = THIS_MODULE,

    .open    =  fifth_drv_open,

    .read     = fifth_drv_read,

    .release = fifth_drv_close,

    .poll      =  fifth_drv_poll,

    .fasync   = fifth_drv_fasync,//增加异步通知处理的函数

    

};


3、编写fifth_drv_fasync异步通知处理函数,这个函数会在C库函数fcntl设置FASYNC时被调用


static int fifth_drv_fasync(int fd, struct file * file, int on)

{

    int err;

    printk('fansync_helpern');

    err = fasync_helper(fd, file, on, &fifth_fasync);//利用fasync_helper初始化fifth_fasync

    if (err < 0)

        return err;

    return 0;

}


下面是完整的代码


#include

#include

#include

#include

#include         //含有iomap函数iounmap函数

#include //含有copy_from_user函数

#include //含有类相关的处理函数

#include //含有S3C2410_GPF0等相关的

#include     //含有IRQ_HANDLEDIRQ_TYPE_EDGE_RISING

#include    //含有IRQT_BOTHEDGE触发类型

#include //含有request_irq、free_irq函数

#include

//#include




static struct class *fifth_drv_class;//类

static struct class_device *fifth_drv_class_dev;//类下面的设备

static int fifthmajor;


static unsigned long *gpfcon = NULL;

static unsigned long *gpfdat = NULL;

static unsigned long *gpgcon = NULL;

static unsigned long *gpgdat = NULL;


struct fasync_struct *fifth_fasync;//定义fasync_struct结构

    

static unsigned int key_val;


struct pin_desc 

{

    unsigned int pin;

    unsigned int key_val;

};


static struct pin_desc  pins_desc[4] = 

{

    {S3C2410_GPF0,0x01},

    {S3C2410_GPF2,0x02},

    {S3C2410_GPG3,0x03},

    {S3C2410_GPG11,0x04}

};



unsigned int ev_press;

DECLARE_WAIT_QUEUE_HEAD(button_waitq);//注册一个等待队列button_waitq


 

/*

  *0x01、0x02、0x03、0x04表示按键被按下

  */

  

/*

  *0x81、0x82、0x83、0x84表示按键被松开

  */


/*

  *利用dev_id的值为pins_desc来判断是哪一个按键被按下或松开

  */

static irqreturn_t buttons_irq(int irq, void *dev_id)

{

    unsigned int pin_val;

    struct pin_desc * pin_desc = (struct pin_desc *)dev_id;//取得哪个按键被按下的状态

    

    pin_val = s3c2410_gpio_getpin(pin_desc->pin);

    

    if(pin_val) //按键松开

        key_val = 0x80 | pin_desc->key_val;

    else

        key_val = pin_desc->key_val;



    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

    ev_press = 1;    

    

    kill_fasync(&fifth_fasync, SIGIO, POLL_IN);//发生信号给进程

    

    return IRQ_HANDLED;

}




static int fifth_drv_open (struct inode * inode, struct file * file)

{

    int ret;

    ret = request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, 's1', (void * )&pins_desc[0]);

    if(ret)

    {

        printk('open failed 1n');

        return -1;

    }

    ret = request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, 's2', (void * )& pins_desc[1]);

    if(ret)

    {

        printk('open failed 2n');

        return -1;

    }

    ret = request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, 's3', (void * )&pins_desc[2]);

    if(ret)

    {

        printk('open failed 3n');

        return -1;

    }

    ret = request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, 's4', (void * )&pins_desc[3]);

    if(ret)

    {

        printk('open failed 4n');

        return -1;

    }

    

    return 0;

}



static int fifth_drv_close(struct inode * inode, struct file * file)

{

    free_irq(IRQ_EINT0 ,(void * )&pins_desc[0]);


     free_irq(IRQ_EINT2 ,(void * )& pins_desc[1]);


    free_irq(IRQ_EINT11 ,(void * )&pins_desc[2]);


    free_irq(IRQ_EINT19 ,(void * )&pins_desc[3]);


    return 0;

}


static ssize_t fifth_drv_read(struct file * file, char __user * userbuf, size_t count, loff_t * off)

{

    int ret;


    if(count != 1)

    {

        printk('read errorn');

        return -1;

    }


//    wait_event_interruptible(button_waitq, ev_press);//将当前进程放入等待队列button_waitq中

    

    ret = copy_to_user(userbuf, &key_val, 1);

    ev_press = 0;//按键已经处理可以继续睡眠

    

    if(ret)

    {

        printk('copy errorn');

        return -1;

    }

    

    return 1;

}


static unsigned int fifth_drv_poll(struct file *file, poll_table *wait)

{

    unsigned int ret = 0;

    poll_wait(file, &button_waitq, wait);//将当前进程放到button_waitq列表


    if(ev_press)

        ret |=POLLIN;//说明有数据被取到了


    return ret;

}




static int fifth_drv_fasync(int fd, struct file * file, int on)

{

    int err;

    printk('fansync_helpern');

    err = fasync_helper(fd, file, on, &fifth_fasync);//利用fasync_helper初始化fifth_fasync

    if (err < 0)

        return err;

    return 0;

}



static struct file_operations fifth_drv_ops = 

{

    .owner   = THIS_MODULE,

    .open    =  fifth_drv_open,

    .read     = fifth_drv_read,

    .release = fifth_drv_close,

    .poll      =  fifth_drv_poll,

    .fasync   = fifth_drv_fasync,//增加异步通知处理的函数

    

};


static int fifth_drv_init(void)

{

    fifthmajor = register_chrdev(0, 'buttons', &fifth_drv_ops);//注册驱动程序


    if(fifthmajor < 0)

        printk('failes 1 buttons_drv registern');

    

    fifth_drv_class = class_create(THIS_MODULE, 'buttons');//创建类

    if(fifth_drv_class < 0)

        printk('failes 2 buttons_drv registern');

    fifth_drv_class_dev = class_device_create(fifth_drv_class, NULL, MKDEV(fifthmajor,0), NULL,'buttons');//创建设备节点

    if(fifth_drv_class_dev < 0)

        printk('failes 3 buttons_drv registern');


    

    gpfcon = ioremap(0x56000050, 16);//重映射

    gpfdat = gpfcon + 1;

    gpgcon = ioremap(0x56000060, 16);//重映射

    gpgdat = gpgcon + 1;


    printk('register buttons_drvn');

    return 0;

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    一个RTE机制(运行时, 借鉴了高级编程语言的理念),在操作系统之上充当一个壳, 让其他汽车软件全部运行在RTE之上,或接受RTE的管理,调度和跨进程通信, 这样从Ring0内核的角度来看, 整个......
    责虚拟地址到物理地址的映射,并提供硬件机制的内存访问权限检查。在多用户、多进程的操作系统中,MMU使得各个用户进程都有独立的地址空间。 图2 MMU的地位 任何微控制器都存在一个程序能够产生的地址集合,被称......
    式操作系统支持TCP/IP及其他协议并提供通信协议动态挂接技术,以及操作系统内部的进程通信应用接口技术。本设计采用稳定的2.4版本的内核,并对它进行合理的裁减和加载,作为操作平台。μClinux己对......
    不是一次性执行完毕,而是走走停停,以不可知的速度向前推进。 基本功能 1. 进程管理 进程控制、进程同步、进程通信、死锁处理、处理机调度等。 2. 内存管理 内存......
    搭建计算机主机与PLC 通信的桥梁,当计算机使用语言编写控制逻辑时,需要专门的 通信程序用于与PLC 通信。由于大部分的PLC 设备都不直接与Python程序直连,而是通过C/C++ 的方......
    三个任务,选用了一款内核精练的嵌入式操作系统μC/OS一Ⅱ控制三个任务之间的调度和分配。uC/OS一Ⅱ包括了任务调度,进程通信,内存管理等系统功能。系统的工作过程以及软件流程如图5: 图5 系统流程图 系统......
    无需套接字,使用VXI11(LXI)和Python进行LAN控制;为了满足远程监控和控制的需求,现代测试仪器所用的通信总线比以往更多。每种类型的总线还具有多个层,这些层展现出特定的使用优势。VXI......
    单片机STM32可以用python写吗?可以的开发板有哪些?;普通的开发板肯定不行。 市面上目前有尝试用python语言去开发单片机程序,但是不太适合大多数产品,为什么? 很简单,就是......
    罗德与施瓦茨深耕亚太赫兹信道传播测量,推动ITU 6G标准化进程; 只有对传播的特性有深入的了解,才能实现所设想的亚太赫兹通信的愿景。100 GHz和330 GHz之间的 新频......
    脚本。Pyserial 允许我们创建一个可以通过串行端口进行通信python 脚本,Matplotlib 使我们能够从通过串行端口接收的数据生成绘图,drawnow 为我......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>