嵌入式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;