platform按键驱动学习二

platform按键驱动学习一:https://blog.csdn.net/caijiwyj/article/details/90182865

一、等待队列

(1) 定义并初始化"等待队列头"

wait_queue_head_t w_queue;  

init_waitqueue_head(&w_queue);  //会将自旋锁初始化为未锁,等待队列初始化为空的双向循环链表。

//宏名用于定义并初始化,相当于"快捷方式"

DECLARE_WAIT_QUEUE_HEAD (w_queue);  

定义"等待队列"

/*定义并初始化一个名为name的等待队列 ,注意此处是定义一个wait_queue_t类型的变量name,并将其private与设置为tsk*/

DECLARE_WAITQUEUE(name,tsk);

 

(2)等待事件

#define wait_event(wq, condition)                  

do {                                   

if (condition)                         

break;                         

  __wait_event(wq, condition);                   

} while (0)

wait_event(queue,condition);等待以queue为等待队列头等待队列被唤醒,condition必须满足,否则阻塞  

wait_event_interruptible(queue,condition);可被信号打断  

wait_event_timeout(queue,condition,timeout);阻塞等待的超时时间,时间到了,不论condition是否满足,都要返回  

wait_event_interruptible_timeout(queue,condition,timeout) 

 

(3)唤醒队列

void __wake_up(wait_queue_head_t *q, unsigned int mode,

                int nr_exclusive, void *key)

{

    unsigned long flags;

    spin_lock_irqsave(&q->lock, flags);

    __wake_up_common(q, mode, nr_exclusive, 0, key);

    spin_unlock_irqrestore(&q->lock, flags);

}

    EXPORT_SYMBOL(__wake_up);

//唤醒等待队列.可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERUPTIBLE状态的进程,和wait_event/wait_event_timeout成对使用

 

#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

//和wake_up()唯一的区别是它只能唤醒TASK_INTERRUPTIBLE状态的进程.,与wait_event_interruptiblewait_event_interruptible_timeoutwait_event_interruptible_exclusive成对使用

 

wake_up()wake_event()或者wait_event_timeout成对使用,

wake_up_intteruptible()wait_event_intteruptible()wait_event_intteruptible_timeout()成对使用。

 

#define wake_up_all(x)          __wake_up(x, TASK_NORMAL, 0, NULL)

#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)

#define wake_up_interruptible_all(x)    __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)

//这些也基本都和wake_up/wake_up_interruptible一样

 

sleep_on(wait_queue_head_t *q);

interruptible_sleep_on(wait_queue_head_t *q);

     sleep_on作用是把目前进程的状态置成TASK_UNINTERRUPTIBLE,并定义一个等待队列,之后把他附属到等待队列头q,直到资源可用,q引导的等待队列被唤醒。interruptible_sleep_on作用是一样的, 只不过它把进程状态置为TASK_INTERRUPTIBLE.

 

等待队列的流程首先,定义并初始化等待队列,把进程的状态置成TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE,并将对待队列添加到等待队列头。最后,当进程被其他地方唤醒,将等待队列移除等待队列头。

在Linux内核中,使用set_current_state()和__add_wait_queue()函数来实现目前进程状态的改变,直接使用current->state = TASK_UNINTERRUPTIBLE类似的语句也是可以的。

二、驱动POLL()函数

应用程序使用select()或poll()会调用设备驱动程序的poll()函数,poll函数里poll_wait负责将当前进程放入wait_queue,就是上面说到的等待队列,直到有事件发生,poll_wait返回,最后根据返回的事件给用户返回可读写出错等标志的掩码。

驱动中的poll()函数原型:

unsigned int (*poll)(struct file *filp, struct poll_table *wait);

这个函数要进行下面两项工作。首先,对可能引起设备文件状态变化的等待队列调用poll_wait(),将对应的等待队列头添加到poll_table.最后返回表示是否能对设备进行无阻塞读写访问的掩码。在上面提到了一个poll_wait()函数,它的原型

void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table *wait);

poll()函数最后应该返回设备资源的可获取状态

常量

说明

POLLIN

普通或优先级带数据可读

POLLRDNORM

普通数据可读

POLLRDBAND

优先级带数据可读

POLLPRI

高优先级数据可读

POLLOUT

普通数据可写

POLLWRNORM

普通数据可写

POLLWRBAND

优先级带数据可写

POLLERR

发生错误

POLLHUP

发生挂起

POLLNVAL

描述字不是一个打开的文件

下面是Poll驱动的模板:

static unsigned int XXX_poll(struct file *filp, poll_table *wait)

{

    unsigned int mask = 0;

        struct XXX_dev *dev = filp->private_data;     //获得设备结构指针

    ...

    poll_wait(filp, &dev->r_wait, wait);    //加读等待对列头

    poll_wait(filp ,&dev->w_wait, wait);    //加写等待队列头

    

    if(...)//可读

    {

          mask |= POLLIN | POLLRDNORM;    //标识数据可获得

     }

    if(...)//可写

    {

          mask |= POLLOUT | POLLRDNORM;    //标识数据可写入

     }

    ..

    return mask;

}

参考:https://www.cnblogs.com/hanyan225/archive/2010/10/13/1850497.html

三、Platform按键驱动测试代码

测试代码的功能是按键按下LED灯亮:

/*********************************************************************************

 *      Copyright:  (C) 2019 WuYujun<540726307@qq.com>

 *                  All rights reserved.

 *

 *       Filename:  test_plat_buttom_ctl_LED.c

 *    Description:  This file test platform buttom driver

 *                 

 *        Version:  1.0.0(2019年05月14日)

 *         Author:  WuYujun <540726307@qq.com>

 *      ChangeLog:  1, Release initial version on "2019年05月14日 15时44分40秒"

 *                 

 ********************************************************************************/

#include <stdlib.h>  

#include <stdio.h>  

#include <errno.h>  

#include <stdarg.h>  

#include <fcntl.h>  

#include <unistd.h>  

#include <sys/types.h>  

#include <sys/ioctl.h>  

#include <sys/stat.h>  

#include <sys/select.h>

#include <string.h>





#define PLATDRV_MAGIC             0x60  

#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)  

#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)  

/*  read() 读到的是返回的值对应的位就是对应的按键状态     */

#define KEY1                      0x1  

#define KEY2                      0x2  

#define KEY3                      0x4  

#define KEY4                      0x8



#define DEV_LED                   "/dev/led"

#define DEV_KEY                   "/dev/key"





int main(int argc, char **argv)

{



    int     led_fd ;

    int     key_fd ;

    fd_set  rset;

    int     rv = -1 ;

    int     key_status = 0 ;

    int     flag1 = 0 ;

    int     flag2 = 0 ;

    int     flag3 = 0 ;

    int     flag4 = 0 ;



    led_fd = open(DEV_LED,O_RDWR) ;

    key_fd = open(DEV_KEY,O_RDWR) ;



    if(led_fd < 0)

    {

        printf("open %s failed: %s\n", DEV_LED, strerror(errno)) ;

        return -1 ;

    }

    if(key_fd < 0)

    {

        printf("open %s failed: %s\n",DEV_KEY, strerror(errno)) ;

        return -2 ;

    }

    while(1)

    {

        FD_ZERO(&rset) ;

        FD_SET(key_fd, &rset) ;

        rv = select(key_fd+1, &rset, NULL, NULL, NULL) ;

        if(rv < 0)

        {

            printf("select() failed: %s\n", strerror(errno)) ;

            goto cleanup ;

        }

        if(rv == 0)

        {

            printf("select() time out!\n") ;

            goto cleanup ;

        }

        if( FD_ISSET(key_fd, &rset) )

        {

            rv = read(key_fd,&key_status,sizeof(key_status)) ;

            if(rv < 0)

            {

                printf("read from %s failed:%s\n", DEV_KEY, strerror(errno)) ;

                return -2 ;

            }

            if(KEY1==key_status)

            {

                if(flag1 == 0)

                {

                    ioctl(led_fd, LED_ON,0) ;

                    flag1 = 1 ;

                }

                else

                {

                    ioctl(led_fd, LED_OFF,0) ;

                    flag1 = 0 ;

                }

            }

            if(KEY2==key_status)

            {

                 if(flag2 == 0)

                {

                    ioctl(led_fd, LED_ON,1) ;

                    flag2 = 1 ;

                }

                else

                {

                    ioctl(led_fd, LED_OFF,1) ;

                    flag2 = 0 ;

                }

            }

            if(KEY3==key_status)

            {

                if(flag3 == 0)

                {

                    ioctl(led_fd, LED_ON,2) ;

                    flag3 = 1 ;

                }

                else

                {

                    ioctl(led_fd, LED_OFF,2) ;

                    flag3 = 0 ;

                }

            }

            if(KEY4==key_status)

            {

                if(flag4 == 0)

                {

                    ioctl(led_fd, LED_ON,3) ;

                    flag4 = 1 ;

                }

                else

                {

                    ioctl(led_fd, LED_OFF,3) ;

                    flag4 = 0 ;

                }

            }

        }

        key_status = 0 ;   

    }

cleanup:

   close(key_fd) ;

   close(led_fd) ;

    return 0 ;

}

运行效果: 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值