嵌入式LINUX驱动学习之8竞态和并发相关问题(二)自旋锁

一、头文件、函数及说明

/* spinlock_t
源码位置 : include/linux/spinlock_types.h
*/ 
typedef struct spinlock {
    
    
        union {
    
    
                struct raw_spinlock rlock;//实现见附A 1.1

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
                struct {
    
    
                        u8 __padding[LOCK_PADSIZE];
                        struct lockdep_map dep_map;
                };
#endif
        };
} spinlock_t;

/*
    spin_lock_init()
    源码位置 : include/linux/spinlock.h
*/
#define spin_lock_init(_lock)                           \
do {                                                    \
        spinlock_check(_lock);                          \
        raw_spin_lock_init(&(_lock)->rlock);            \
} while (0)

/*
    spin_lock()
    源码位置 : include/linux/spinlock.h
*/
static inline void spin_lock(spinlock_t *lock)
{
    
    
        raw_spin_lock(&lock->rlock);
}
/*
    spin_unlock()
    源码位置 : include/linux/spinlock.h
*/
static inline void spin_unlock(spinlock_t *lock)
{
    
    
        raw_spin_unlock(&lock->rlock);
}

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

==支持在用户空间控制LED灯开和关,也支持按键对LED灯进行操作 ==

#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/delay.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
    }
};

/*按键硬件信息*/
struct btn_src {
    
    
    char *name ;
    int  gpio;
};
struct btn_src btn_info[] = {
    
    
    {
    
    
        .name = "BTN1",
        .gpio = PAD_GPIO_A +28
    },
    {
    
    
        .name = "BTN2",
        .gpio = PAD_GPIO_B +30
    },
    {
    
    
        .name = "BTN3",
        .gpio = PAD_GPIO_B +31
    },
    {
    
    
        .name = "BTN4",
        .gpio = PAD_GPIO_B +9
    }
};

/*打开LED灯函数*/
static void led_on(unsigned long buf[4]){
    
    
    unsigned long *tmp_buf;
    tmp_buf = buf;
    if(gpio_get_value(led_info[tmp_buf[0]-1].gpio))
        gpio_set_value(led_info[tmp_buf[0] - 1 ].gpio,0);
    else
        printk("LED%ld灯已经是开启状态\n",tmp_buf[0]);
}

/*关闭LED灯函数*/
static void led_off(unsigned long buf[4]){
    
    
    unsigned long *tmp_buf;
    tmp_buf = buf;
    if(gpio_get_value(led_info[tmp_buf[0] -1].gpio))
        printk("LED%ld灯已经是关闭状态\n",tmp_buf[0]);
    else
        gpio_set_value(led_info[tmp_buf[0] - 1].gpio,1);
}

/*LED灯状态信息函数)*/
static unsigned long * led_state(unsigned long buf[4]){
    
    
    int i = 0;
    unsigned long *tmp_buf;
    tmp_buf = buf;
    if(tmp_buf[0])
        tmp_buf[0] = gpio_get_value(led_info[tmp_buf[0] -1].gpio);
    else
        for(; i < ARRAY_SIZE(led_info);i ++)
            tmp_buf[i] = gpio_get_value(led_info[i].gpio);
    return buf;
}

/*字符设备文件打开处理函数*/
static int file_count_num=1;//记录文件打开次数
spinlock_t lock;//自旋锁对象
static int led_open(struct inode * inode ,struct file * file){
    
    
    spin_lock(&lock);//使用自旋锁保存全局变量
    if(--file_count_num){
    
    
        printk("字符设备文件已经被打开\n");
        file_count_num ++;
        spin_unlock(&lock);//如果设备已经被打开,在这里释放自旋锁;
        return -EBUSY;
    }
    printk("字符设备文件打开成功\n");
    spin_unlock(&lock);//释放自旋锁
    return 0;
}
/*字符设备打开关闭函数*/
static int led_close(struct inode * inode , struct file * file){
    
    
    spin_lock(&lock);//使用自旋锁保存全局变量
    file_count_num  = 1;
    printk("字符设备文件关闭\n");
    spin_unlock(&lock);//释放自旋锁
    return 0;
}

/*ioctl()函数底层处理方式*/
#define LED_ON    0X1000
#define LED_OFF   0X1001
#define LED_STATE 0X1002
long myioctl_func(struct file * open_file , unsigned int ucmd, unsigned long ubuf){
    
    
    int kcmd = ucmd;
    unsigned long kbuf[4];
    copy_from_user(kbuf,(unsigned long *) ubuf,sizeof(long) * 4);
    switch(kcmd){
    
    
        case  LED_ON :
            led_on(kbuf);
            break;
        case  LED_OFF :
            led_off(kbuf);
            break;
        case  LED_STATE :
            led_state(kbuf);
            copy_to_user((unsigned long *)ubuf,kbuf,sizeof(long) * 4);
            break;
        default :
            break;
    }
    return 0;
}

/*定义文件操作接口*/
struct file_operations led_fops = {
    
    
    .owner          = THIS_MODULE,
    .open           = led_open,
    .release        = led_close,
    .unlocked_ioctl = myioctl_func
};

/*定义混杂设备接口*/
struct miscdevice misc_fops = {
    
    
    .minor = MISC_DYNAMIC_MINOR,
    .name = "myled_drv",
    .fops = &led_fops
};

/*中断处理函数*/
static irqreturn_t btn_irq_func(int irq,void * argv){
    
    
    struct btn_src *btn_irq = (struct btn_src *) argv;
    char btn_name[10];
    unsigned long  led_num[4];
    mdelay(5);
    strcpy(btn_name,btn_irq ->name);
    led_num[0] = btn_name[strlen(btn_name)-1] -'0';
    gpio_get_value(led_info[led_num[0] -1].gpio) ? \
    led_on(led_num) : led_off(led_num) ;
    printk("%s按键触发,%s状态为%s\n",btn_irq -> name,\
             led_info[led_num[0] -1].name,\
             gpio_get_value(led_info[led_num[0] -1].gpio) ? "关闭" : "开启");
    return IRQ_HANDLED;
}

/*驱动模块加载函数*/
static int led_btn_misc_spin_init(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);
        gpio_request(btn_info[i].gpio,btn_info[i].name);
        gpio_direction_input(btn_info[i].gpio);
        request_irq(gpio_to_irq(btn_info[i].gpio),\
                    btn_irq_func,IRQF_TRIGGER_RISING,\
                    btn_info[i].name,&btn_info[i]);
    }
    misc_register(&misc_fops);//注册混杂设备对象
    spin_lock_init(&lock);//初始化自旋锁对象
    return 0;
}

/*驱动模块卸载函数*/
static void led_btn_misc_spin_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);
        free_irq(gpio_to_irq(btn_info[i].gpio),&btn_info[i]);
        gpio_free(btn_info[i].gpio);
    }
    misc_deregister(&misc_fops);//释放混杂设备对象
}
module_init(led_btn_misc_spin_init);
module_exit(led_btn_misc_spin_exit);
MODULE_LICENSE("GPL");

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

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define LED_ON    0X1000
#define LED_OFF   0X1001
#define LED_STATE 0X1002
static void led_state(unsigned long led_num , unsigned long *buf){
    
    
    int i = 0;
    if(led_num)
        printf("led%d状态为%s\n",led_num,buf[0] ? "关闭" : "开启");
    else
        for(; i < 4 ; i ++)
            printf("led%d状态为%s\n",i,buf[i] ? "关闭" : "开启");
}
int main(int argc , char * argv[]){
    
    
    unsigned long  led_num = 0;
    int fp;
    unsigned int ucmd;
    unsigned long  ubuf[4];
    if((argc <3) || (argc > 4))
        goto err;
    if(argc == 4){
    
    
        led_num = strtoul(argv[3],NULL,0);
        if((led_num <0) || (led_num > 4))
            goto err_led_num;
    }
    if( ! strcmp(argv[2],"state"))
        ucmd = LED_STATE;
    else if((! strcmp(argv[2] , "on")) && (argc == 4))
        ucmd = LED_ON;
    else if((! strcmp(argv[2], "off"))  && (argc == 4))
        ucmd = LED_OFF;
    else
        goto err;
    fp = open(argv[1] , O_RDWR);
    if(fp < 0 )
        goto err_file;
    ubuf[0] = led_num;
    ioctl(fp,ucmd,ubuf);
    if(ucmd == LED_STATE)
        led_state(led_num,ubuf);
    sleep(30);//用于测试字符设备文件只能同时一个用户打开
    close(fp);
    return 0;
err :
    printf("命令错误:\n");
    printf("         comm <cdev_file> <state> [led_num]\n");
    printf("         comm <cdev_file> <on | off > <led_num>\n");
    return -1;

err_led_num :
    printf("LED灯编号错误(1 ~ 4);\n");
    return -1;

err_file :
    printf("字符设备文件打开失败!\n");
    return -1;
}

四、测试

#加载驱动模块
/drivers/test # insmod myspin_led_btn.ko 
#开启LED1,并且后台运行30秒
/drivers/test # ./a /dev/myled_drv on 1 &
/drivers/test # [  828.536000] 字符设备文件打开成功
fp: 3 ,ucmd : 4096 , ubuf[0]:1
//程序没有结束前,通过按钮可以进行控制LED灯,
/drivers/test # [  833.015000] BTN2按键触发,LED2状态为开启
//上一个进程没有结束前,新的进程不能打开这个字符设备文件
/drivers/test # ./a /dev/myled_drv on 3
[  838.674000] 字符设备文件已经被打开
字符设备文件打开失败!
/drivers/test # 

附A 1.1

//源码位置: include/linux/spinlock_types.h
typedef struct raw_spinlock {
    
    
        arch_spinlock_t raw_lock;//实现见:附A 1.1.1
#ifdef CONFIG_GENERIC_LOCKBREAK
        unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
        unsigned int magic, owner_cpu;
        void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

附A 1.1.1

//源码位置:arch/arm/inclde/asm/spinlock_types.h
typedef struct {
    
    
        volatile unsigned int lock;
} arch_spinlock_t;

猜你喜欢

转载自blog.csdn.net/weixin_47273317/article/details/108019960
今日推荐