一、原子操作
原子操作是指不能在进一步分割的操作,一般用于操作变量或者位操作,
保护的是一段临界区
疑问:
感觉保护的是驱动,保证驱动只有一个应用程序使用。
二、驱动
#include <linux/ide.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
//#include <>
/********************************************************************
* gpioled.c
* 作者:张亚胜
* 版本:V1.0
* 描述:gpioled驱动文件 原子操作实验
* 其他:无
* 网站:www.s123.xyz
* 日志:出版V1.0 2020/7/29 张亚胜创建
* ******************************************************************/
#define GPIO_CNT 1 /* 设备号个数 */
#define GPIO_NAME "atomicled" /* 设备名字 */
#define LED_OFF 0 /* 关灯 */
#define LED_ON 1 /* 开灯 */
/* gpioled设备结构体 */
struct gpioled_dev{
dev_t dev_id; /* 设备号 */
struct cdev cdev; /* 字符设备 */
struct class *class; /* mdev 自动节点文件使用 */
struct device *device; /* class使用 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int led_gpio; /* led gpio 编号 */
atomic_t lock; /* 原子变量 */
};
struct gpioled_dev gpioled; /* led设备 */
static int led_open(struct inode *inode,struct file *filp)
{
/* 通过判断原子变量的值来检查LED有没有被别的应用使用 */
if(!atomic_dec_and_test(&gpioled.lock))//初值为1 这里减一 为0返回!真
{
atomic_inc(&gpioled.lock);//说明有人在使用 把刚刚减的1再还原
return -EBUSY;
}
filp->private_data = &gpioled; /* 设置私有数据 */
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 retval = 0;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retval = copy_from_user(databuf,buf,cnt);
if(retval<0){
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0];
if(ledstat == LED_ON){
gpio_set_value(dev->led_gpio,0);
}
else if(ledstat == LED_OFF)
{
gpio_set_value(dev->led_gpio,1);
}
return 0;
}
static int led_release(struct inode *inode,struct file *filp)
{
struct gpioled_dev *dev = filp->private_data;
/* open的时候减一了 所以关闭驱动的时候再恢复 */
atomic_inc(&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,
};
static int __init led_init(void)
{
int ret;
/* 初始化原子变量 */
atomic_set(&gpioled.lock,1);
/* 获取led的GPIO */
/* 获取设备节点 */
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL){
printk("gpioled node can not found!\r\n");
return -EINVAL;
}
/* 获取gpio属性 得到led的gpio编号 */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-gpio",0);
if(gpioled.led_gpio < 0){
printk("led-gpio get failed!\r\n");
}else {
printk("led-gpio = %d\r\n",gpioled.led_gpio);
}
/* 设置GPIO1_IO03为输出 并且输出高点平 默认关闭led */
ret = gpio_direction_output(gpioled.led_gpio,1);
if(ret < 0){
printk("gpio set failed!\r\n");
}
/* 注册字符设备驱动 */
if(gpioled.major){
gpioled.dev_id = MKDEV(gpioled.major,0);//构建设备号 子设备号一般设置0
register_chrdev_region(gpioled.dev_id,GPIO_CNT,GPIO_NAME);
}else{
alloc_chrdev_region(&gpioled.dev_id,0,GPIO_CNT,GPIO_NAME);//申请设备号
gpioled.major = MAJOR(gpioled.dev_id);//获取主设备号
gpioled.minor = MINOR(gpioled.dev_id);//获取子设备号
}
printk("gpioled major=%d,minor=%d\r\n",gpioled.major,gpioled.minor);
/* 初始化cdev */
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev,&gpioled_fops);//初始化cdev并绑定设备操作集合
/* 添加一个cdev */
cdev_add(&gpioled.cdev,gpioled.dev_id,GPIO_CNT);
/* 创建类 */
gpioled.class = class_create(THIS_MODULE,GPIO_NAME);
if(IS_ERR(gpioled.class)){
return PTR_ERR(gpioled.class);
}
/* 创建设备节点 mknod 向类中添加设备 */
gpioled.device = device_create(gpioled.class,NULL,gpioled.dev_id,NULL,GPIO_NAME);//成功后就会生成/dev/GPIO_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.dev_id,GPIO_CNT);
/* 删除设备节点 */
device_destroy(gpioled.class,gpioled.dev_id);
/* 删除类 */
class_destroy(gpioled.class);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zys");
三、测试app
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
/********************************************************************
* 文件名:atomic.c
* 作者:张亚胜
* 版本:V1.0
* 描述:测试led原子访问
* 其他:使用方法:./atomic /dev/atomicled 0 //写0 关灯
* ./atomic /dev/atomicled 1 //写1 开灯
* 网站:www.s123.xyz
* 日志:出版V1.0 2020/7/30 张亚胜创建
* ******************************************************************/
/*
* @description : main 主程序
* @param - argc : argv 数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd,retval,cnt=0;
char *filename;
char buf[100];
if(argc != 3){
printf("params err!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename,O_RDWR);
if(fd<0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
buf[0] = atoi(argv[2]);
retval = write(fd,buf,1);
if(retval < 0){
printf("write err!\r\n");
close(fd);
return -1;
}
while(1)
{
sleep(5);
printf("atomicledApp running,%d\r\n",cnt);
if(cnt++ >= 5){
break;
}
}
printf("App running finished!\r\n");
retval = close(fd);
if(retval < 0){
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return retval;
}