嵌入式LINUX驱动学习之8竞态和并发相关问题(一)中断屏蔽

一、中断屏蔽的方法头文件及说明

//源码位置:include/linux/irqflags.h
#define local_irq_save(flags)                                   \
        do {                                                    \
                raw_local_irq_save(flags);                      \
        } while (0)

#define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0)
/*中断屏蔽的使用:
   unsigned long flags;
   local_irq_save(flags;
   ......这个区域内容为共享资源(如:全局变量、寄存器等...............
   local_irq_restore(flags);
*/

二、代码举例(内核空间)

include <linux/init.h>
#include <linux/module.h>
#include <cfg_type.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
/*定义LED灯硬件信息*/
struct led_src {
    char *name;
    int   gpio;
};
struct led_src led_info[] = {
    {
        .name = "LED1",
        .gpio = PAD_GPIO_B + 26
    },
    {
        .name = "LED2",
        .gpio = PAD_GPIO_C + 11
    },
    {
        .name = "LED3",
        .gpio = PAD_GPIO_C + 7
    },
    {
        .name = "LED4",
        .gpio = PAD_GPIO_C + 12
    }
};

int file_open_state = 1; //记录文件打开状态
int led_open_state = 1;  //记录LED灯的开光状态
static int led_open(struct inode * inode , struct file * file){
    unsigned long flags;
    local_irq_save(flags);//使用中断屏蔽对可能修改的数据进行保护
    if(--file_open_state){
        printk("设备已经被打开\n");
        fil_open_state ++;
        local_irq_restore(flags);
        return -EBUSY;
    }
    local_irq_restore(flags);//操作完毕,恢复中断
    printk("设备文件打开成功\n");
    return 0;
}

static int led_close (struct inode * inode,struct file * file){
    unsigned long flags;
    local_irq_save(flags);
    file_open_state = 1;
    local_irq_restore(flags);
    printk("设备文件关闭\n");
    return 0;
}
void led_on(unsigned int num){
    if(gpio_get_value(led_info[num-1].gpio)){//检查灯是否已经打开
        gpio_set_value(led_info[num-1].gpio,0);
        printk("%s开启\n",led_info[num-1].name);
    }
    else
        printk("%s已经开启\n",led_info[num-1].name);
}
void led_off(unsigned int num){
    if(gpio_get_value(led_info[num-1].gpio))//检查灯是否已经关闭
        printk("%s已经关闭\n",led_info[num-1].name);
    else {
        gpio_set_value(led_info[num-1].gpio,1);
        printk("%s关闭完成\n",led_info[num-1].name);
    }
}
/*
当查看单个LED灯状态时,kbuf[0] == 对应灯的编号:1 ~ 4;
当查看所有LED的状态时,kbuf[0]== 0;
*/
void led_state(unsigned long kbuf[4]){
    int i = 0;
    if(kbuf[0]){
        kbuf[0] = gpio_get_value(led_info[kbuf[0] -1].gpio);
    }
    else {
        for(; i <ARRAY_SIZE(led_info);i++){
            kbuf[i] = gpio_get_value(led_info[i].gpio);
        }
    }
}
/*定义LED灯的三个命令*/
#define LED_ON    0X100
#define LED_OFF   0X101
#define LED_STATE 0X110
static long led_ioctl(struct file * file , unsigned int ucmd ,\
                      unsigned long ubuf){
    unsigned long kbuf[4];//保存用户空间传过来的LED灯信息
    unsigned int kcmd = ucmd;//保存用户空间传过来的命令
    copy_from_user(kbuf,(unsigned long *) ubuf,sizeof(int) * 4);
    printk("kcmd : %u , kbuf[0] : %lu\n",kcmd ,kbuf[0]);
    switch(kcmd){
        case LED_ON :
            led_on(kbuf[0]);
            break;
        case LED_OFF :
            led_off(kbuf[0]) ;
            break;
        case LED_STATE :
            led_state(kbuf);
            copy_to_user((unsigned long *)ubuf,kbuf,sizeof(long) *4 );
        default :
            break;
    }
    return 0;
}

struct file_operations led_fops = {
    .owner          = THIS_MODULE,
    .open           = led_open,
    .release        = led_close,
    .unlocked_ioctl = led_ioctl
};

struct miscdevice misc_fops = {
    .minor = MISC_DYNAMIC_MINOR,
    .fops  = &led_fops,
    .name  = "myled_drv"
};

static int led_int(void){
    int i = 0;
    for(; i < ARRAY_SIZE(led_info); i++){
        gpio_request(led_info[i].gpio,led_info[i].name);
        gpio_direction_output(led_info[i].gpio,1);
    }
    misc_register(&misc_fops);




    return 0;
}
static void led_exit(void){
    int i = 0;
    for(;i<ARRAY_SIZE(led_info); i++){
        gpio_set_value(led_info[i].gpio,1);
        gpio_free(led_info[i].gpio);
    }
    misc_deregister(&misc_fops);


}
module_init(led_int);
module_exit(led_exit);
MODULE_LICENSE("GPL");

三、代码举例(用户空间)

#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#define LED_ON     0X100
#define LED_OFF    0X101
#define LED_STATE  0X110
void led_state_printf(int num,int ubuf[4]){
    int i = 0;
    if(num)
        printf("LED%d灯状态为%s\n",num,ubuf[0] ? "关闭" : "打开");
    else
        for(;i < 4; i++)
            printf("LED%d灯状态为%s\n",i + 1,ubuf[i] ? "关闭" : "打开");
}
int main(int argc , char * argv[]){
    int ubuf[4] = {0};
    int ucmd = 0;
    unsigned long tmp_state_num = 0;//用于临时保存要操作的LED灯编号
    if((argc < 3) || (argc > 4)){
        goto err;
    }
    int fp = open(argv[1],O_RDWR);
    if(fp < 0){
        printf("打开文件失败!\n");
        return -1;
    }
    if(argc == 4){
        ubuf[0]= strtoul(argv[3],NULL,0);
        tmp_state_num = ubuf[0];
        if((ubuf[0] >4) || (ubuf[0] < 1)){
            printf("LED灯编号为:LED1 ~ 4\n");
            return -1;
        }
    }
    if((argc ==3 ) && \
      (! strcmp(argv[2],"state"))){
            ucmd= LED_STATE;
    }
    else if(argc == 4){
        if(!strcmp(argv[2],"on"))
            ucmd = LED_ON;
        else if (! strcmp(argv[2],"off"))
            ucmd = LED_OFF;
        else if(! strcmp(argv[2], "state"))
            ucmd = LED_STATE;
        else
            goto err;
    }
    else {
        goto err;
    }
    ioctl(fp,ucmd,ubuf);
    if(!strcmp(argv[2],"state"))
        led_state_printf(tmp_state_num,ubuf);
    sleep(30);//延迟30秒,用于测试中断屏蔽功能
    close(fp);
    return 0;
err:
    printf("命令错误!\n");
    printf("        comm <cdev_name> <state>\n");
    printf("        comm <cdev_name> <state> <led_num>\n");
    printf("        comm <cdev_name> <on|off> <led_num>\n");
    return -1;
}

四、测试

#加载myled模块
/drivers/test # insmod myled.ko 
#打开LED灯,用户空间的程序中等待30秒
/drivers/test # ./a /dev/myled_drv on 1 &
 设备文件打开成功
kcmd : 256 , kbuf[0] : 1
LED1开启
#因上一个程序使用了中断屏蔽,所以在上一个程序没有关闭前,打开会失败
/drivers/test # ./a /dev/myled_drv on 2
设备已经被打开
打开文件失败
#30秒后,用户空间的第一个程序自动关闭了
/drivers/test # 设备文件关闭
#此时可以打开第2个灯了
/drivers/test # ./a /dev/myled_drv on 2 
设备文件打开成功
kcmd : 256 , kbuf[0] : 2
LED2开启
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值