一、互斥体

  互斥访问表示一次只有一个线程可以访问共享资源,不能递归申请互斥体。Linux 内核使用 mutex 结构体表示互斥体。使用 mutex 之前要先定义一个 mutex 变量。在使用 mutex 的时候要注意如下几点:
  ①、 mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁。
  ②、和信号量一样, mutex 保护的临界区可以调用引起阻塞的 API 函数。
  ③、因为一次只有一个线程可以持有 mutex,因此,必须由 mutex 的持有者释放 mutex。并且 mutex 不能递归上锁和解锁

1.1、互斥 API 函数

DEFINE_MUTEX(name)

   定义并初始化一个 mutex 变量。

void mutex_init(mutex *lock)

  初始化 mutex。

void mutex_lock(struct mutex *lock)

  获取 mutex,也就是给 mutex 上锁。如果获取不到就进休眠。

void mutex_unlock(struct mutex *lock)

  释放 mutex,也就给 mutex 解锁。

int mutex_trylock(struct mutex *lock)

  尝试获取 mutex,如果成功就返回 1,如果失败就返回 0。

int mutex_is_locked(struct mutex *lock)

  判断 mutex 是否被获取,如果是的话就返回1,否则返回 0。

int mutex_lock_interruptible(struct mutex *lock)

  使用此函数获取信号量失败进入休眠以后可以被信号打断。

二、编写驱动程序

2.1 修改设备树文件

ledtest{#address-cells = <1>;#size-cells = <1>;compatible = "ledtest";pinctrl-names = "default";linux,default-trigger = "input";gpios = <&gpio 26 0>;status = "okay";
};

2.2、LED 驱动修改

#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 <linux/semaphore.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define GPIOLED_CNT            1           /* 设备号个数 */
#define GPIOLED_NAME        "ledtest" /* 名字 */
#define LEDOFF              0           /* 关灯 */
#define LEDON               1           /* 开灯 *//* gpioled设备结构体 */
struct gpioled_dev{dev_t devid;         /* 设备号   */struct cdev cdev;        /* cdev     */struct class *class;  /* 类        */struct device *device;    /* 设备    */int major;               /* 主设备号   */int minor;              /* 次设备号   */struct device_node  *nd; /* 设备节点 */int led_gpio;            /* led所使用的GPIO编号        */struct mutex lock;        /* 互斥体 */
};struct gpioled_dev gpioled;   /* led设备 *//** @description        : 打开设备* @param - inode     : 传递给驱动的inode* @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量*                       一般在open的时候将private_data指向设备结构体。* @return             : 0 成功;其他 失败*/
static int led_open(struct inode *inode, struct file *filp)
{filp->private_data = &gpioled; /* 设置私有数据 *//* 获取互斥体,可以被信号打断 */if (mutex_lock_interruptible(&gpioled.lock)) {return -ERESTARTSYS;}return 0;
}/** @description      : 从设备读取数据 * @param - filp  : 要打开的设备文件(文件描述符)* @param - buf    : 返回给用户空间的数据缓冲区* @param - cnt  : 要读取的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** @description      : 向设备写数据 * @param - filp   : 设备文件,表示打开的文件描述符* @param - buf     : 要写给设备写入的数据* @param - cnt     : 要写入的数据长度* @param - offt  : 相对于文件首地址的偏移* @return             : 写入的字节数,如果为负值,表示写入失败*/
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);    /* 打开LED灯 */} else if(ledstat == LEDOFF) {gpio_set_value(dev->led_gpio, 1);    /* 关闭LED灯 */}return 0;
}/** @description      : 关闭/释放设备* @param - filp   : 要关闭的设备文件(文件描述符)* @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 struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = led_open,.read = led_read,.write = led_write,.release =    led_release,
};/** @description : 驱动入口函数* @param       : 无* @return       : 无*/
static int __init led_init(void)
{int ret = 0;/* 初始化互斥体 */mutex_init(&gpioled.lock);/* 设置LED所使用的GPIO *//* 1、获取设备节点:gpioled */gpioled.nd = of_find_node_by_path("/ledtest");if(gpioled.nd == NULL) {printk("ledtest node not find!\r\n");return -EINVAL;} else {printk("ledtest node find!\r\n");}/* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "gpios", 0);if(gpioled.led_gpio < 0) {printk("can't get gpios");return -EINVAL;}printk("gpios 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");}/* 注册字符设备驱动 *//* 1、创建设备号 */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, &gpioled_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);}return 0;
}/** @description  : 驱动出口函数* @param       : 无* @return       : 无*/
static void __exit led_exit(void)
{/* 注销字符设备驱动 */cdev_del(&gpioled.cdev);/*  删除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("zsx");

2.3、编写测试APP

  测试 APP 使用https://blog.csdn.net/xxxx123041/article/details/120421290中的 App.c 即可。

三、运行测试

  运行测试方法和https://blog.csdn.net/xxxx123041/article/details/120422112中的测试方法基本相同。

树莓派linux led字符设备驱动(互斥)相关推荐

  1. 树莓派linux led字符设备驱动(信号量)

    一.信号量   Linux 内核提供了信号量机制,信号量常常用于控制对共享资源的访问.相比于自旋锁,信号量可以使线程进入休眠状态,使用信号量会提高处理器的使用效率.但是,信号量的开销要比自旋锁大,因为 ...

  2. 树莓派linux led字符设备驱动(原子操作)

    一.原子操作   linux驱动中为了处理对共享资源的并发访问,引入了原子操作.原子操作就是指不能再进一步分割的操作,一般原子操作用于变量或者位操作. Linux 内核提供了两组原子操作 API 函数 ...

  3. Linux之字符设备驱动框架

    目录 一.驱动介绍 1.内核模块 2.日志级别 3.模块符号的导出 4.内核模块参数 二.字符设备驱动(一) 1.模块加载 2.注册字符设备驱动 3.内存映射 三.字符设备驱动(二) 1.模块加载 2 ...

  4. linux内核字符驱动设备,Linux学习笔记——linux内核字符设备驱动-Go语言中文社区...

    尝试在树莓派安装的raspbian系统上进行linux字符设备驱动 1.更新安装kernel header源码 sudo apt-get update sudo apt-get install ras ...

  5. linux 编译字符设备驱动错误,linux字符设备驱动框架及编写流程

    流程: init { } exit { } 申请设备号 (动态注册/静态注册) 创建一个字符设备 cdev_alloc 初始化字符设备 cdev_init 设备号和字符设备关联 cdev_add 销毁 ...

  6. Linux SPI 字符设备 驱动例子

    其实,在Linux中,SPI和IIC的注册流程很相似.在这里我还是用iTOP4412做演示. 从原理图可以得知我们要用到的引脚是这几个.用的是SPI_2.记住这个数字,下面设备注册要用到. 首先我们要 ...

  7. 【正点原子MP157连载】第二十章 字符设备驱动开发-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  8. Linux驱动之字符设备驱动

    系列文章目录 第一章 Linux入门之驱动框架 第二章 Linux驱动之字符设备驱动 文章目录 系列文章目录 前言 一.认识字符设备驱动 1.基本概念 2.基本概念 二.字符设备旧框架 1.注册和注销 ...

  9. linux用户空间flash驱动,全面掌握Linux驱动框架——字符设备驱动、I2C驱动、总线设备驱动、NAND FLASH驱动...

    原标题:全面掌握Linux驱动框架--字符设备驱动.I2C驱动.总线设备驱动.NAND FLASH驱动 字符设备驱动 哈~ 这几天都在发图,通过这种方式,我们希望能帮大家梳理学过的知识,全局的掌握Li ...

  10. 一张图掌握 Linux 字符设备驱动架构!【建议收藏】

    目录 一. Linux 中字符设备驱动简介 二. 字符设备驱动快速入门(超简单demo) 1. demo 2. 代码编译 3. 加载驱动模块 4. 创建设备节点文件 5. APP设备文件操作 6. 卸 ...

最新文章

  1. python字符编码是什么_python3字符编码是什么?怎么用?
  2. Sublime text 3 汉化教程
  3. 销售Invoice管理流程
  4. 用HOOK禁用鼠标与键盘点击
  5. php随机产生4乘4矩阵,PHP 用二维矩阵生成一个给定层数的杨辉(PASCAL)三角形
  6. root cause of error messagw for a mixed usage of 01 and 03
  7. 判断session失效,并跳转到登录页面
  8. corel产品注册样机安装代码_为你的产品创建一个高逼格的宣传片
  9. l298n使能端跳线帽_L298N 驱动模块的应用
  10. 人工智能技术与计算机科学区别,智能科学与技术与人工智能的区别
  11. PIL中的paste方法拼接透明背景照片
  12. MathType注册表位置
  13. 小程序使用腾讯视频插件及插件未授权使用
  14. Periodic Strings (求HoHoHo字符串最小周期)
  15. 为什么你看书记不住,记住不会用?-我的技术学习方法论
  16. 电脑插上扩展坞后会有显示的空盘符
  17. 潍坊正大光明老年病医院开展防患于未“燃”消防安全演练活动
  18. 继续教育-计算机会计答案,计算机会计模板答案
  19. ArcGIS下图像矢量化的基本步骤
  20. XctNet:从单个X射线图像重建体积图像的网络

热门文章

  1. 两个很重要的极限和夹逼准则
  2. 20200209毕设日记-开题报告
  3. mysql 自定义插件调试
  4. ZigBee的应用场景
  5. hbuilder_工具的服务端口已关闭。要使用命令行调用工具,请在下方输入 y 以确认开启,或手动打开工具 -> 设置 -> 安全设置,将服务端口开启。
  6. 上海链家网租房信息分析报告
  7. 计算机程序班搞笑口号,16字高一班级搞笑口号.docx
  8. 记Chrome崩溃解决方案
  9. android 4.2 dropbox,android 下用脚本备份文档到dropbox的实现
  10. java实现华氏温度和摄氏温度互相转换