自旋锁实验

该代码实现了一个基于Linux内核的GPIOLED驱动程序,包括设备打开、写入和关闭操作。使用自旋锁保护设备状态,并通过of_get_named_gpio获取GPIO引脚。驱动在设备被占用时会设置状态标志,并在释放时恢复GPIO状态。
摘要由CSDN通过智能技术生成
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"



struct gpioled_dev {
    int major;
    int minor;
    dev_t devide;//设备号
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node * bl_nd1;/*设备节点*/
    int led_gpio;/*属性编号*/
    int dev_stats;   /*设备状态,0,设备未使用;>0设备已经使用*/
    spinlock_t lock;/*自旋锁*/
};

struct gpioled_dev gpioled;
/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
 static int gpioled_open(struct inode *inode, struct file *filp)
{
    unsigned long flags;
    filp->private_data = &gpioled; /* 设置私有数据 */

    /*判断lock*/
    spin_lock_irqsave(&gpioled.lock,flags);//上锁
    if(gpioled.dev_stats){
        spin_unlock_irqrestore(&gpioled.lock,flags);
        return -EBUSY;
    }
    gpioled.dev_stats++;
    spin_unlock_irqrestore(&gpioled.lock,flags);/*解锁*/

	printk("chrdevbase open!\r\n");
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    struct gpioled_dev *dev=(struct gpioled_dev * )filp->private_data;
    int retvalue;
    unsigned char databuf[1];
    unsigned char ledstat;

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0) {
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }

    ledstat = databuf[0]; /* 获取状态值 */
    if(ledstat == 0) { 
       gpio_set_value(dev->led_gpio,0); /* 打开 LED 灯 */
    } else if(ledstat == 1) {
       gpio_set_value(dev->led_gpio,1); /* 关闭 LED 灯 */
    }
 
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int gpioled_release(struct inode *inode, struct file *filp)
{
    unsigned long flags;
    struct gpioled_dev *dev=(struct gpioled_dev * )filp->private_data;
    gpio_set_value(dev->led_gpio,1); /* 关闭 LED 灯 */
    spin_lock_irqsave(&gpioled.lock,flags);
    if(dev->dev_stats){
        dev->dev_stats--;
    }
    spin_unlock_irqrestore(&gpioled.lock,flags);/*解锁*/
	
    //printk("chrdevbase release!\r\n");
	return 0;
}


static struct file_operations gpioled_ops={
    .owner=THIS_MODULE,
    .open=gpioled_open,
    .release=gpioled_release,
    .write=gpioled_write,
};


static int __init gpioled_init(void)
{
    int ret=0;
    
    
    /*初始化自旋锁*/
    spin_lock_init(&gpioled.lock);
    
    
    /*获取设备节点*/
    gpioled.bl_nd1=of_find_node_by_path("/gpioled");
    if(gpioled.bl_nd1==NULL)
    {
        printk("of_find_node_by_path failed\r\n");
        return -EINVAL;
    }
    /*获取设备cd-gpio属性的第一个GPIO编号*/
    gpioled.led_gpio=of_get_named_gpio(gpioled.bl_nd1,"cd-gpio",0);
    if(gpioled.led_gpio<0)
    {
        printk("of_get_named_gpio failed\r\n");
        return -EINVAL;
    }

    /*申请一个GPIO管脚*/
    ret=gpio_request(gpioled.led_gpio,"cd-gpio");
    if(ret==0){
        printk("gpio_requests success\n");
    }

    /*将GPIO设置为输出,默认输出高电平*/
    ret=gpio_direction_output(gpioled.led_gpio,1);
    if(ret<0)
    {
        printk("gpio_direction_output failed\n");
    }

    /*注册设备号*/
    gpioled.major=0;
    if(gpioled.major){
        gpioled.devide=MKDEV(gpioled.major,0);
        register_chrdev_region(gpioled.devide,GPIOLED_CNT,GPIOLED_NAME);
    }else{
        ret=alloc_chrdev_region(&gpioled.devide, 0, GPIOLED_CNT, GPIOLED_NAME);	/* 申请设备号 */
		if(ret<0)
            {
                goto fail_devide;
            }
        gpioled.major = MAJOR(gpioled.devide);	/* 获取分配号的主设备号 */
		gpioled.minor = MINOR(gpioled.devide);	/* 获取分配号的次设备号 */
        printk("major=%d\n",gpioled.major);
        printk("minor=%d\n",gpioled.minor);
    }


     /*初始化cdev*/
    gpioled.cdev.owner=THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_ops);
    
	
	/* 3、添加一个cdev */
	ret= cdev_add(&gpioled.cdev, gpioled.devide, GPIOLED_CNT);
    if(ret<0)
        {
            goto fail_cdev;
        }

    //创建设备节点
	/* 4、创建类 */
	gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
	if (IS_ERR(gpioled.class)) {
		ret= PTR_ERR(gpioled.class);
        goto fail_class;
	}

	/* 5、创建设备 */
	gpioled.device = device_create(gpioled.class, NULL, gpioled.devide, NULL, GPIOLED_NAME);
	if (IS_ERR(gpioled.device)) {
		ret=PTR_ERR(gpioled.device);
        goto fail_device;
	}    
    return 0;
    fail_device:
        class_destroy(gpioled.class);
    fail_class:
        cdev_del(&gpioled.cdev);
    fail_cdev:
        unregister_chrdev_region(gpioled.devide, GPIOLED_CNT);
    fail_devide:
        return ret;
}

static void __exit gpioled_exit(void)
{


    gpio_set_value(gpioled.led_gpio,1);
    device_destroy(gpioled.class,gpioled.devide);
    
    class_destroy(gpioled.class);
    cdev_del(&gpioled.cdev);

    gpio_free(gpioled.led_gpio);
    unregister_chrdev_region(gpioled.devide, GPIOLED_CNT);
    printk("exit\n");
    printk("exit\n");
}


/*模块的加载与卸载*/
module_init(gpioled_init);
module_exit(gpioled_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("liuchuanqiang");

程序说明:

1、自旋锁保护的临界区要尽可能的短,因此在 open 函数中申请自旋锁,然后在 release

数中释放自旋锁的方法就不可取。我们可以使用一个变量来表示设备的使用情况,如果设备被使用了那么变量就加一,设备被释放以后变量就减 1,我们只需要使用自旋锁保护这个变量即可。

2、定义一个变量 dev_stats 表示设备的使用情况,dev_stats0 的时候表示设备没有被使用,dev_stats 大于 0 的时候表示设备被使用。

3、 spin_lock_init(&gpioled.lock); /*初始化自旋锁*/

4、 spin_lock_irqsave(&gpioled.lock,flags);//上锁

    if(gpioled.dev_stats){

        spin_unlock_irqrestore(&gpioled.lock,flags);

        return -EBUSY;

    }

    gpioled.dev_stats++;

spin_unlock_irqrestore(&gpioled.lock,flags);/*解锁*/

自旋锁的使用方法,先上锁,然后进行操作,最后进行解锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值