Embedded LINUX driver learning 9 waiting queue

1. Header files, functions and descriptions

/*
    wait_queue_head_t 结构体
    源码位置:include/linux/wait.h
*/
struct __wait_queue_head {
    
    
        spinlock_t lock;
        struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

/*
    初始化等待队列头 :init_waitqueue_head(wait_queue_head_t *q);  
    源码位置:include/linux/wait.h
*/
    extern void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *);
    
#define init_waitqueue_head(q)                          \
        do {                                            \
                static struct lock_class_key __key;     \
                                                        \
                __init_waitqueue_head((q), #q, &__key); \
        } while (0)


/*
    进行等待队列 :wait_event(wait_queue_head_t wq,int condition)
    功能:进行不可中断的休眠操作,底层实现调用了衍生自旋锁
    源码位置:include/linux/wait.h
*/
#define wait_event(wq, condition)                                       \
do {                                                                    \
        if (condition)/*当condition为真,不会进行休眠*/                \
                break;                                                  \
        __wait_event(wq, condition);                                    \
} while (0)

/*
    进行等待队列 :wait_event_interruptible(wait_queue_head_t wq,int condition)
    功能:进行可中断的休眠操作,
    源码位置:include/linux/wait.h
*/
#define wait_event_interruptible(wq, condition)                         \
({                                                                      \
        int __ret = 0;                                                  \
        if (!(condition))                                               \
                __wait_event_interruptible(wq, condition, __ret);       \
        __ret;                                                          \
})  

/*
    唤醒等待队列的方法:wake_up(wait_queue_head_t *wq)
    功能,唤醒等待队列所有的进程
    源码位置:include/linux/wait.h
*/
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key);
#define wake_up(x)                      __wake_up(x, TASK_NORMAL, 1, NULL)

/*
    唤醒等待队列的方法: wake_up_interruptible()
    功能,唤醒等待队列可中断的进程
    源码位置:include/linux/wait.h
*/
    #define wake_up_interruptible(x)        __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

2. Code example (kernel space)

Features:
Realize that only one user can open the character device file at the same time, that is: only one user can operate the hardware device at the same time
Through the button to control the on and off of the LED light, the user space program can display the button operation signal

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <cfg_type.h>
#include <linux/uaccess.h>
#include <linux/timer.h>
/*定义硬件信号*/
struct dev_src {
    
    
    char *btn_name;
    int   btn_gpio;
    char *led_name;
    int   led_gpio;
};
struct dev_src dev_info[] = {
    
    
    {
    
    
        .btn_name = "BTN_1",
        .btn_gpio = PAD_GPIO_A + 28,
        .led_name = "LED_1",
        .led_gpio = PAD_GPIO_B + 26
    },
    {
    
    
        .btn_name = "BTN_2",
        .btn_gpio = PAD_GPIO_B + 30,
        .led_name = "LED_2",
        .led_gpio = PAD_GPIO_C + 11
    },
    {
    
    
        .btn_name = "BTN_3",
        .btn_gpio = PAD_GPIO_B + 31,
        .led_name = "LED_3",
        .led_gpio = PAD_GPIO_C + 7
    },
    {
    
    
        .btn_name = "BTN_4",
        .btn_gpio = PAD_GPIO_B + 9,
        .led_name = "LED_4",
        .led_gpio = PAD_GPIO_C + 12
    }
};
/*通过等待队列实只能一个用户操作硬件设备*/
static int fops_couter = 1;
static wait_queue_head_t wqt;
/*通过等待队列实现LED灯状态显示*/
static int led_counter = 0;//当有按键操作时,变量在0/1变动,
static wait_queue_head_t led_wqt;
static unsigned long  g_led_buf[3];//保存要拷贝到用户空间的信息:中断号、LED灯编号 、LED灯状态
static struct timer_list s_timer;//通过软件定时器给按键去抖动
static int f_open(struct inode * inode , struct file * file){
    
    
    printk("文件准备打开,有可能进入休眠等待!\n");
    wait_event(wqt,fops_couter);//第一个用户可以正常打开,后续用户只能进入等待队列,因为后续fops_couter =0;
    fops_couter = 0;
    printk("文件打开完成\n");
    return 0;
};

static int f_close(struct inode * inode ,struct file * file){
    
    
    printk("文件准备关闭,并唤醒等待队列其它进程\n");
    /*关闭字符设备文件,唤醒等待队列其它进程,并将fops_couter置1;*/
    wake_up(&wqt);
    fops_couter  =1 ;

    return  0;
}
/*当有按键操作时,进程被唤醒,拷贝内核空间信息到用户空间,并执行led_counter置0*/
static long f_ioctl(struct file * file , unsigned int cmd , unsigned long ubuf){
    
    
   /*当led_counter ==1时,可以向下执行,否则进行休眠等待,但可以因超时或中断而结束*/
    wait_event_interruptible(led_wqt,led_counter);//
    copy_to_user((unsigned long *)ubuf,g_led_buf,sizeof(int) * 3);
    led_counter = 0;
    return 0;
}


struct file_operations f_ops = {
    
    
    .owner = THIS_MODULE,
    .open = f_open,
    .release = f_close,
    .unlocked_ioctl = f_ioctl
};
struct miscdevice misc_ops = {
    
    
    .minor = MISC_DYNAMIC_MINOR,
    .name  = "file_drv",
    .fops  = &f_ops
};

static irqreturn_t btn_irq(int irq,void * args){
    
    
    struct dev_src *led_info =(struct dev_src *) args;
    //保存中断号
    g_led_buf[0] = irq;
    //保存LED灯编号 
    g_led_buf[1] = led_info -> led_name[strlen(led_info ->\
                               led_name) -1] - '0';
    //保存ELD灯状态
    g_led_buf[2] =  1 - gpio_get_value(led_info ->led_gpio);
    gpio_set_value(led_info -> led_gpio,g_led_buf[2]);//设置LED灯状态
    mod_timer(&s_timer, msecs_to_jiffies(10) + jiffies);//重量置软件定时器,用于按键去抖动 ;//也可以考虑使用延迟函数mdelay()

    return IRQ_HANDLED;
}
/*软件定时器超时处理函数*/
static void btn_timer_func(unsigned long data){
    
    
    led_counter =1;
    wake_up_interruptible(&led_wqt);//唤醒可中断的等待队列中的进程
}

static int mywait_init(void){
    
    
/*申请GPIO资源、设置GPIO功能、申请irq*/
    int i = 0;
    for(; i <ARRAY_SIZE(dev_info); i ++){
    
    
        gpio_request(dev_info[i].btn_gpio,dev_info[i].btn_name);
        gpio_direction_input(dev_info[i].btn_gpio);
        request_irq(gpio_to_irq(dev_info[i].btn_gpio),btn_irq,\
                    IRQF_TRIGGER_RISING,dev_info[i].btn_name,\
                    &dev_info[i]);
        gpio_request(dev_info[i].led_gpio,dev_info[i].led_name);
        gpio_direction_output(dev_info[i].led_gpio,1);
    }
    misc_register(&misc_ops);//注册混杂设备对象
    init_timer(&s_timer);//初始化定时器
    s_timer.function = btn_timer_func;//指定软件定时器超时处理函数
    init_waitqueue_head(&wqt);//初始化,不能中断的等待队列,用于字符设备文件打开的关闭
    init_waitqueue_head(&led_wqt);//初始化,能中断的等待队列,用于LED灯
    return 0;
}

static void mywait_exit(void){
    
    
    int i = 0;
    /*释放GPIO资源,释放IRQ*/
    for(; i<ARRAY_SIZE(dev_info);i++){
    
    
        free_irq(gpio_to_irq(dev_info[i].btn_gpio),&dev_info[i]);
        gpio_free(dev_info[i].btn_gpio);
        gpio_set_value(dev_info[i].led_gpio,1);
        gpio_free(dev_info[i].led_gpio);
    }
    del_timer(&s_timer);//删除定器对象
    misc_deregister(&misc_ops);//卸载混杂设备对象


}
module_init(mywait_init);
module_exit(mywait_exit);
MODULE_LICENSE("GPL");

Three, code example (user space)

Features:
When data is sent from the kernel space, printing is executed, and other time is dead

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define F_R 0X1000
static void r_prinf(int ubuf[3]){
    
    
    printf("中断信号:%d,LED_%d灯响应,执行动作%s,LED灯状态%s\n",\
            ubuf[0],ubuf[1],ubuf[2] ? "关灯" : "开灯",\
            ubuf[2] ? "关闭" : "开启");
}
int main(int argc ,char * argv[]){
    
    
    if(argc != 2)
        goto err;
    int ubuf[3];
    int fp = open(argv[1],O_RDWR);
    if(fp < 0)
        printf("文件打开失败\n");
    for(;;){
    
    
       ioctl(fp,F_R,ubuf);
        r_prinf(ubuf);
    }
    close(fp);
    return 0;
err :
    printf("命令错误!\n");
    printf("        comm <cdev_file>\n");
    return -1;
}

Guess you like

Origin blog.csdn.net/weixin_47273317/article/details/108106056