Linux并发与竞争-原子操作

warren
2024-11-02 / 0 评论 / 2 阅读 / 正在检测是否收录...

并发与竞争

并发与竞争简介

因为Linux系统是个多任务操作系统,会存在多个任务同时使用一个资源的问题。就会导致系统奔溃。原因有如下几点:
① 多线程并发访问
② 抢占式并发访问
③ 中断程序并发访问
④ 多核见并发访问
总的来说就是多个程序并发访问同一个资源的问题,解决办法就是阻止多个程序同时访问同一个资源。就是说A程序在访问这个资源时候,B程序访问不了这个资源。需要用到下面4个东东(原子操作、自旋锁、信号量、互斥体)

原子操作

简介

原子操作就是指不能再进一步分割的操作。就是CPU只需要一条指向就可以执行完的操作

原子整形操作API函数

Linux 内核定义了叫做 atomic_t 的结构体来完成整形数据的原子操作。此结构体定义在 include/linux/types.h 文件中,定义如下:

  /*32bit*/
typedef struct {
    int counter;
} atomic_t;

  /*64bit*/
typedef struct {
  long long counter;
} atomic64_t;

如果要使用原子操作 API 函数,首先要先定义一个 atomic_t 的变量

  atomic_t a;

也可以在定义原子变量的时候给原子变量赋初值

  atomic_t b = ATOMIC_INIT(0);

关于原子操作API函数如下

m2zjdu4c.png
相应的也提供了 64 位原子变量的操作 API 函数; API 函数用法一样,只是将“atomic_”前缀换为“atomic64_”,将 int 换为 long long。

关于原子位操作API函数如下

原子位操作是直接对内存进行操作,不像原子整形变量那样有个 atomic_t 的数据结构。API 函数如下;
m2zjkw72.png

原子操作实验

设备树文件修改

在iomuxc节点下的imx6ul-evk节点下创建pinctrl_led节点

  pinctrl_led : ledgrp{
    fsl,pins<
      IMX6UL_PAD_GPIO01_IO03__GPIO1_IO03    0x10B0  
    >;
  };

在根节点/下创建LED节点

  gpioled : {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "warren-gpioled";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_led>;
    led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
  };

驱动程序

#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    "led"
#define LEDON           1
#define LEDOFF          0


struct gpioled_dev{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    int led_gpio;
    atomic_t lock;
};

struct gpioled_dev gpioled;




static int led_open(struct inode *inode, struct file *filp)
{
    if(!atomic_dec_and_test(&gpioled.lock)){
        atomic_inc(&gpioled.lock);
        return -EBUSY;
    }
    filp->private_data = &gpioled;
    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    struct gpioled_dev *dev = filp->private_data;

    atomic_inc(&dev->lock);
    return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

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

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0){
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }
    ledstat = databuf[0];
    if(ledstat == LEDON){
        gpio_set_value(dev->led_gpio, 0);
    }else if(ledstat == LEDOFF){
        gpio_set_value(dev->led_gpio, 1);
    }
    return 0;
}

static struct file_operations gpioled_fops = {
    .owner      = THIS_MODULE,
    .open       = led_open,
    .release    = led_release,
    .write      = led_write,
    .read       = led_read,
};

static int __init led_init(void)
{
    int ret = 0;

    atomic_set(&gpioled.lock, 1);

    gpioled.nd = of_find_node_by_path("/led");
    if(gpioled.nd == NULL){
        printk("gpioled node find failed!\r\n");
        return -EINVAL;
    }else{
        printk("gpioled node find success!\r\n");
    }

    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    if(gpioled.led_gpio < 0){
        printk("can't get led-gpio\r\n");
        return -EINVAL;
    }
    printk("led-gpio num = %d\r\n", gpioled.led_gpio);

    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if(ret < 0){
        printk("can't set gpio!\r\n");
    }

    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);

    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_fops);

    cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);

    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if(IS_ERR(gpioled.class)){
        return PTR_ERR(gpioled.class);
    }

    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if(IS_ERR(gpioled.device)){
        return PTR_ERR(gpioled.device);
    }

    return 0;
}

static void __exit led_exit(void)
{
    cdev_del(&gpioled.cdev);
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
    
    device_destroy(gpioled.class, gpioled.devid);
    class_destroy(gpioled.class);
}



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

应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
int main(int argc, int *argv[])
{
    int fd; 
    char *filename = NULL;
    char data = 0;
    int retvalue = 0;
    int cnt = 0;

    if(argc != 3){
        printf("error usage!\r\n");
        return -1;
    }
    filename = (char*)argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("file %s open failed\r\n", filename);
        return -1;
    }
    data = atoi(argv[2]);
    retvalue = write(fd, &data, 1);
    if(retvalue < 0){
        printf("write %s failed\r\n", filename);
        close(fd);
        return -1;
    }
    while(1){
      sleep (1);
      cnt++;
      printf("App runing times : %d\r\n", cnt);
      if(cnt > 5){
          break;
      }
    }
    printf("App runing finished\r\n");
    retvalue = close(fd);
    if(retvalue < 0){
        printf("file %s close failed\r\n", filename);
        return -1;
    }
    return 0;
}

自旋锁

简介

原子操作只能保护一个变量的值,并不能保护结构体变量。或者说原子操作的进一步使用就是自旋锁。
自旋锁的缺点:那就等待自旋锁的线程会一直处于自旋状态,这样会浪费处理器时间,降低系统性能,所以自旋锁的持有时间不能太长。
Linux 内核使用结构体 spinlock_t 表示自旋锁,结构体定义如下所示:

typedef struct spinlock {
    union {
        struct raw_spinlock rlock;
#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;

在使用自旋锁之前,肯定要先定义一个自旋锁变量,定义方法如下

spinlock_t lock;

自旋锁API函数

m31m14ep.png
被自旋锁保护的临界区一定不能调用任何能够引起睡眠和阻塞的API 函数,否则的话会可能会导致死锁现象的发生。
比如有两个线程AB。A线程加锁后进入睡眠了。B线程因为获取不到锁,导致B线程一直死等。就会导致锁死现象。
还有种情况就是在中断里面加锁,假设在中断A中加锁,还没来得及解锁就被新中断打断或者被抢占。导致锁死。所以说在中断中使用自旋锁时候需要在获取锁之前先禁止本地中断。在释放锁之后使能本地中断。Linux内核提供了相应的API函数,如下。
m31mifsc.png
不推荐使用spin_lock_irq/spin_unlock_irq。建议使用 spin_lock_irqsave/ spin_unlock_irqrestore,因为这一组函数会保存中断状态,在释放锁的时候会恢复中断状态。
一般在线程中使用 spin_lock_irqsave/spin_unlock_irqrestore,在中断中使用 spin_lock/spin_unlock。
在Linux中,中断处理被分为两个主要部分:顶半部(上半部,Top Half)和下半部(Bottom Half)。这种设计是为了解决中断处理程序执行时间过长和中断丢失的问题,同时确保关键任务能够迅速得到响应。
m31mmrbr.png
顶半部的主要任务是快速处理中断,并暂时关闭中断请求。它主要处理与硬件紧密相关或时间敏感的事情,比如对硬件的读取寄存器中的中断状态并清除中断标志。当一个中断发生时,顶半部会进行相应的硬件操作,并将中断例程的下半部挂到该设备的下半部执行队列中。由于顶半部的执行速度很快,它可以服务更多的中断请求
下半部用于延迟处理顶半部未完成的工作。与顶半部不同,下半部是可以被中断的,这意味着它可以在执行过程中被新的中断打断。下半部几乎执行了中断处理程序的所有任务,但它并不是非常紧急的,通常比较耗时。因此,它的执行时机由系统自行安排,并不在中断服务上下文中执行。
下半部(BH)也会竞争共享资源,如果要在下半部里面使用自旋锁,可以使用下列API函数
m31moiqg.png

自旋锁实验

设备树文件修改

驱动程序编写

#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    "led"
#define LEDON           1
#define LEDOFF          0


struct gpioled_dev{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    int led_gpio;
    int dev_stats;
    spinlock_t lock;
};

struct gpioled_dev gpioled;




static int led_open(struct inode *inode, struct file *filp)
{
    unsigned long flags;
    filp->private_data = &gpioled;

    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); //解锁
    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    struct gpioled_dev *dev = filp->private_data;
    unsigned long flags;
    
    spin_lock_irqsave(&dev->lock, flags); //加锁
    if(gpioled.dev_stats){
        gpioled.dev_stats--;
    }
    spin_unlock_irqrestore(&dev->lock, flags); //解锁
    return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

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

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0){
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }
    ledstat = databuf[0];
    if(ledstat == LEDON){
        gpio_set_value(dev->led_gpio, 0);
    }else if(ledstat == LEDOFF){
        gpio_set_value(dev->led_gpio, 1);
    }
    return 0;
}

static struct file_operations gpioled_fops = {
    .owner      = THIS_MODULE,
    .open       = led_open,
    .release    = led_release,
    .write      = led_write,
    .read       = led_read,
};

static int __init led_init(void)
{
    int ret = 0;

    spin_lock_init(&gpioled.lock);

    gpioled.nd = of_find_node_by_path("/led");
    if(gpioled.nd == NULL){
        printk("gpioled node find failed!\r\n");
        return -EINVAL;
    }else{
        printk("gpioled node find success!\r\n");
    }

    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    if(gpioled.led_gpio < 0){
        printk("can't get led-gpio\r\n");
        return -EINVAL;
    }
    printk("led-gpio num = %d\r\n", gpioled.led_gpio);

    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if(ret < 0){
        printk("can't set gpio!\r\n");
    }

    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);

    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_fops);

    cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);

    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if(IS_ERR(gpioled.class)){
        return PTR_ERR(gpioled.class);
    }

    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if(IS_ERR(gpioled.device)){
        return PTR_ERR(gpioled.device);
    }

    return 0;
}

static void __exit led_exit(void)
{
    cdev_del(&gpioled.cdev);
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
    
    device_destroy(gpioled.class, gpioled.devid);
    class_destroy(gpioled.class);
}



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

实验程序编写

实验现象

信号量

信号量简介

信号量就是自旋锁的升级版本,自旋锁在解锁失败时候会死等,直到解锁成功为止。信号量则在获取不到时候进入到休眠状态。可以看到信号量的开销要比自旋锁大,因为信号量使线程进入休眠状态以后会切换线程,切换线程就会有开销。
信号量的特点:.

  1. 因为信号量可以使等待资源线程进入休眠状态,因此适用于那些占用资源比较久的场

  1. 因此信号量不能用于中断中,因为信号量会引起休眠,中断不能休眠
  2. 如果共享资源的持有时间比较短,那就不适合使用信号量了,因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势
    信号量就是生产者与消费者的关系,因为它允许多个线程同时访问共享资源。
    Linux 内核使用 semaphore 结构体表示信号量,结构体内容如下所示
  struct semaphore {
    raw_spinlock_t lock;
    unsigned int count;
    struct list_head wait_list;
  };

信号量API函数

m31ouzx0.png
信号量的使用如下:

  struct semaphore sem;  /*定义信号量*/
  sema_init(&sem, 1); /*初始化信号量*/
  down(&sem); /*申请信号量, 申请不到会进入到睡眠但睡眠可以被信号打断*/
  down_trylock(&sem);/*申请信号量, 申请不到立即返回非0,不会进入到睡眠*/
  dowm_interruptible(&sem);/*申请信号量, 申请不到会进入到睡眠, 但睡眠可以被信号打断*/
  up(&sem); /*释放信号量*/

驱动程序编写

#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"
#include "linux/semaphore.h"



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


struct gpioled_dev{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    int led_gpio;
    struct semaphore sem;
};

struct gpioled_dev gpioled;




static int led_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &gpioled;

    if(down_interruptible(&gpioled.sem)){
        return -ERESTARTSYS;
    }
    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    struct gpioled_dev *dev = filp->private_data;

    up(&dev->sem);
    return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

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

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0){
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }
    ledstat = databuf[0];
    if(ledstat == LEDON){
        gpio_set_value(dev->led_gpio, 0);
    }else if(ledstat == LEDOFF){
        gpio_set_value(dev->led_gpio, 1);
    }
    return 0;
}

static struct file_operations gpioled_fops = {
    .owner      = THIS_MODULE,
    .open       = led_open,
    .release    = led_release,
    .write      = led_write,
    .read       = led_read,
};

static int __init led_init(void)
{
    int ret = 0;

    sema_init(&gpioled.sem, 1);

    gpioled.nd = of_find_node_by_path("/led");
    if(gpioled.nd == NULL){
        printk("gpioled node find failed!\r\n");
        return -EINVAL;
    }else{
        printk("gpioled node find success!\r\n");
    }

    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    if(gpioled.led_gpio < 0){
        printk("can't get led-gpio\r\n");
        return -EINVAL;
    }
    printk("led-gpio num = %d\r\n", gpioled.led_gpio);

    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if(ret < 0){
        printk("can't set gpio!\r\n");
    }

    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);

    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_fops);

    cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);

    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if(IS_ERR(gpioled.class)){
        return PTR_ERR(gpioled.class);
    }

    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if(IS_ERR(gpioled.device)){
        return PTR_ERR(gpioled.device);
    }

    return 0;
}

static void __exit led_exit(void)
{
    cdev_del(&gpioled.cdev);
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
    
    device_destroy(gpioled.class, gpioled.devid);
    class_destroy(gpioled.class);
}



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

应用程序编写

实验现象

m31p65bk.png

互斥体

互斥体简介

互斥体就是信号量的特殊化,一次只允许一个线程拥有资源的所有权。将信号量的值设置为 1 就可以使用信号量进行互斥访问了。Linux 内核使用 mutex 结构体表示互斥体,定义如下。

struct mutex {
  /* 1: unlocked, 0: locked, negative: locked, possible waiters */
  atomic_t count;
  spinlock_t wait_lock;
};

mutex 之前要先定义一个 mutex 变量

  struct mutex lock;

使用 mutex 的时候要注意如下几点
{mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁}
{和信号量一样, mutex 保护的临界区可以调用引起阻塞的 API 函数}
{因为一次只有一个线程可以持有 mutex,因此,必须由 mutex 的持有者释放 mutex。并
且 mutex 不能递归上锁和解锁}

互斥体API函数

m31ps53f.png
互斥体的使用如下

    struct mutex lock; /*互斥体定义*/
    mutex_init(&lock); /*互斥体初始化*/
    mutex_lock(&lock); /*获取互斥锁, 获取不到就睡觉且不能被打断*/
    mutex_trylock(&lock);/*获取互斥锁,获取不到直接返回0*/
    mutex_is_locked(&lock);/*查看互斥锁是否被获取,是返回1 否返回0*/
    mutex_lock_interruptible(&lock);/*获取互斥锁,获取不到就睡觉,且可以被信号打断*/
    mutex_unlock(&lock);

驱动程序编写

#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"
#include "linux/mutex.h"



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


struct gpioled_dev{
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    int major;
    int minor;
    struct device_node *nd;
    int led_gpio;
    struct mutex lock;
};

struct gpioled_dev gpioled;




static int led_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &gpioled;

    if(mutex_lock_interruptible(&gpioled.lock)){
        return -ERESTARTSYS;
    }
    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    struct gpioled_dev *dev = filp->private_data;

    mutex_unlock(&dev->lock);
    return 0;
}

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

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

    retvalue = copy_from_user(databuf, buf, cnt);
    if(retvalue < 0){
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }
    ledstat = databuf[0];
    if(ledstat == LEDON){
        gpio_set_value(dev->led_gpio, 0);
    }else if(ledstat == LEDOFF){
        gpio_set_value(dev->led_gpio, 1);
    }
    return 0;
}

static struct file_operations gpioled_fops = {
    .owner      = THIS_MODULE,
    .open       = led_open,
    .release    = led_release,
    .write      = led_write,
    .read       = led_read,
};

static int __init led_init(void)
{
    int ret = 0;

    mutex_init(&gpioled.lock);

    gpioled.nd = of_find_node_by_path("/led");
    if(gpioled.nd == NULL){
        printk("gpioled node find failed!\r\n");
        return -EINVAL;
    }else{
        printk("gpioled node find success!\r\n");
    }

    gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    if(gpioled.led_gpio < 0){
        printk("can't get led-gpio\r\n");
        return -EINVAL;
    }
    printk("led-gpio num = %d\r\n", gpioled.led_gpio);

    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if(ret < 0){
        printk("can't set gpio!\r\n");
    }

    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);

    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev, &gpioled_fops);

    cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);

    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if(IS_ERR(gpioled.class)){
        return PTR_ERR(gpioled.class);
    }

    gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
    if(IS_ERR(gpioled.device)){
        return PTR_ERR(gpioled.device);
    }

    return 0;
}

static void __exit led_exit(void)
{
    cdev_del(&gpioled.cdev);
    unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
    
    device_destroy(gpioled.class, gpioled.devid);
    class_destroy(gpioled.class);
}



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

应用程序编写

实验现象


完结 表情

1

评论 (0)

取消