Linux 原子操作实验-基于正点原子IMX6ULL开发板

该博客介绍了如何在Linux驱动程序中使用原子操作(atomic operations)来确保LED设备的互斥访问。通过在驱动程序中添加原子变量,实现了当一个应用程序正在使用LED时,其他应用程序无法同时访问。测试APP展示了这一机制,当一个APP占用LED时,其他尝试打开LED的APP会收到错误提示,需等待前一个APP完成操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们使用原子操作来实现对 LED 这个设备的互斥访问,也就是一次只允许一个应用程序可以使用 LED 灯。 《pinctrl 和 gpio 子系统实验-基于正点原子IMX6ULL开发板》实验上基础上增加 atomic 相关代码即可。

 
1 编写驱动程序

atomic.c
文件内容如下:
 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>

#define GPIOLED_CNT 1
#define GPIOLED_NAME "gpioled"
#define LEDOFF 0
#define LEDON 1

/*gpioled 设备结构体*/
struct gpioled_dev
{
    dev_t devid;
    int major;
    int minor;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    int led_gpio;

    atomic_t lock;
};

struct gpioled_dev gpioled; /*LED*/

static int led_open(struct inode *inode, struct file *filp)
{

    filp->private_data = &gpioled;

    /* 通过判断原子变量的值来检查 LED 有没有被别的应用使用 */
    if (!atomic_dec_and_test(&gpioled.lock)) /*不能使用驱动*/
    {
        atomic_inc(&gpioled.lock);/* 小于 0 的话就加 1,使其原子变量等于 0 */
        return -EBUSY;/* LED 被使用,返回忙 */
    }

#if 0
     if (atomic_read(&gpioled.lock, 1) <= 0)
    {
        return -EBUSY;
    }
    else
    {
        atomic_dec(&gpioled.lock);
#endif

    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    struct gpioled_dev *dev = filp->private_data;
    atomic_inc(&dev->lock); /* 关闭驱动文件的时候释放原子变量 */
    printk("led_release\r\n");
    return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf,
                         size_t count, loff_t *ppos)
{
    int ret;
    unsigned char databuf[1];
    struct gpioled_dev *dev = filp->private_data;



    ret = copy_from_user(databuf, buf, count);
    if (ret < 0)
    {
        return -EINVAL;
    }

    if (databuf[0] == LEDON)
    {
        gpio_set_value(dev->led_gpio, 0);
    }
    else if (databuf[0] == LEDOFF)
    {
        gpio_set_value(dev->led_gpio, 1);
    }
    return 0;
}

/*操作集*/
static const struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .write = led_write,
    .open = led_open,
    .release = led_release,
};

/*驱动入口函数*/
static int __init led_init(void)
{
    int ret = 0;

    printk("led_init\r\n");

    /*初始化原子变量*/
    atomic_set(&gpioled.lock, 1);

    /*1,注册字符设备驱动*/
    gpioled.major = 0;
    if (gpioled.major)
    { /*给定主设备号*/
        gpioled.devid = MKDEV(gpioled.major, 0);
        register_chrdev_region(gpioled.devid, GPIOLED_CNT, "GPIOLED_NAME");
    }
    else
    {
        alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, "GPIOLED_NAME");
        gpioled.major = MAJOR(gpioled.devid);
        gpioled.minor = MINOR(gpioled.devid);
    }
    printk("gpioled major =%d, minor =%d \r\n", gpioled.major, gpioled.minor);

    /*2,初始化cdev*/
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &led_fops);

    /*3,添加cdev*/
    cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);

    /*4,创建类*/
    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if (IS_ERR(gpioled.class))
    {
        return PTR_ERR(gpioled.class);
    }
    /*5,创建设备*/
    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if (IS_ERR(gpioled.device))
    {
        return PTR_ERR(gpioled.device);
    }

    /*1,获取设备节点*/
    gpioled.nd = of_find_node_by_path("/gpioled");
    if (gpioled.nd == NULL)
    {
        ret = -EINVAL;
        goto fail_findnode;
    }

    /*2,获取LED所对应的GPIO*/
    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpios", 0);
    if (gpioled.led_gpio < 0)
    {
        printk("can't find led gpio\r\n");
        ret = -EINVAL;
        goto fail_findnode;
    }
    printk("led gpio num =%d\r\n", gpioled.led_gpio);

    /*3,申请IO*/
    ret = gpio_request(gpioled.led_gpio, "led-gpio");
    if (ret)
    {
        printk("Failed to request the led gpio\r\n");
        ret = -EINVAL;
        goto fail_findnode;
    }

    /*4,使用IO,设置为输出*/
    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if (ret)
    {
        goto fail_setoutput;
    }

    /*5,输出低电平,点亮led灯*/
    gpio_set_value(gpioled.led_gpio, 0);

    return 0;

fail_setoutput:
    gpio_free(gpioled.led_gpio);
fail_findnode:

    return ret;
}

/*驱动出口函数*/
static void __exit led_exit(void)
{
    printk("led_exit\r\n");
    /*关灯*/
    gpio_set_value(gpioled.led_gpio, 1);

    /*注销字符设备驱动*/
    cdev_del(&gpioled.cdev);
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
    device_destroy(gpioled.class, gpioled.devid);
    class_destroy(gpioled.class);

    /*释放IO*/
    gpio_free(gpioled.led_gpio);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("supersmart");

涉及到原子操作的几个API分别如下:
atomic_set(&gpioled.lock, 1);

atomic_dec_and_test(&gpioled.lock)

atomic_inc(&gpioled.lock);

2 编写测试 APP
新建名为 atomicAPP.c 的测试 APP ,在里面输入如下所示内容:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

/*
 *argc:应用程序参数个数
 * argv[]:具体的参数内容,字符串形式
 * ./atomicAPP <filename> <0:1> 0 关灯,1 开灯
 * ./atomicAPP /dev/gpioled 0 关灯
 * ./atomicAPP /dev/gpioled 1 开灯
 */

#define LEDOFF 0
#define LEDON 1
int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename;
    unsigned char databuf[1];
    unsigned char cnt;

    if (argc != 3)
    {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("file %s open failed!\r\n", filename);
        return -1;
    }

    databuf[0] = atoi(argv[2]); /*将字符转化为数字*/
    retvalue = write(fd, databuf, sizeof(databuf));
    if (retvalue < 0)
    {
        printf("LED Control Failed ! \r\n");
        close(fd);
        return -1;
    }
    
    /*模拟应用占用驱动25s*/
    while(1)
    {
        sleep(5);
        cnt++;
        printf("App runing times:%d\r\n",cnt);
        if(cnt>=5) break;
    }
    printf("App runing finish!\r\n");
    close(fd);
    return 0;
}

运行测试
 

1、编译驱动程序(略)
2、编译测试 APP (略)
3、运行测试

atomicApp 运行正常,输出了“ App running times:1 ”和“App running times:2”,这就是模拟 25S 占用,说明 atomicApp 这个软件正在使用 LED 灯。
此时再输入 
./atomicAPP /dev/gpioled 0

 提示:“file /dev/gpioled open failed!”

因为必须等待图上一个 atomicApp 运行结束,也就是 25S 结束后其他软件才能去操作 /dev/gpioled 。 这个就是采用原子变量实现一次只能有一个应用程序访问 LED 灯。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值