如果要实现驱动程序,在同一时间只能被一个应用程序打开。也可以用信号量。

信号量
信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。
当获取不到信号量时,进程进入休眠等待状态。

定义信号量
struct semaphore sem;
初始化信号量
void sema_init (struct semaphore *sem, int val);
void init_MUTEX(struct semaphore *sem);//初始化为0

static DECLARE_MUTEX(button_lock);     //定义互斥锁

获得信号量
void down(struct semaphore * sem);
int down_interruptible(struct semaphore * sem); 
int down_trylock(struct semaphore * sem); //试图获取信号量,如果获取不到会立刻返回,而不会休眠
释放信号量
void up(struct semaphore * sem);

驱动程序:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>static struct class *fifthdrv_class;
static struct class_device  *fifthdrv_class_dev;volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;static DECLARE_WAIT_QUEUE_HEAD(button_waitq);/* 中断事件标志, 中断服务程序将它置1,fifth_drv_read将它清0 */
static volatile int ev_press = 0;static struct fasync_struct *button_async;struct pin_desc{unsigned int pin;unsigned int key_val;
};/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;struct pin_desc pins_desc[4] = {{S3C2410_GPF0, 0x01},{S3C2410_GPF2, 0x02},{S3C2410_GPG3, 0x03},{S3C2410_GPG11, 0x04},
};static DECLARE_MUTEX(button_lock);     //定义互斥锁   定义信号量
/** 确定按键值*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{struct pin_desc * pindesc = (struct pin_desc *)dev_id;unsigned int pinval;pinval = s3c2410_gpio_getpin(pindesc->pin);if (pinval){/* 松开 */key_val = 0x80 | pindesc->key_val;}else{/* 按下 */key_val = pindesc->key_val;}ev_press = 1;                  /* 表示中断发生了 */wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */kill_fasync (&button_async, SIGIO, POLL_IN);  //return IRQ_RETVAL(IRQ_HANDLED);
}static int fifth_drv_open(struct inode *inode, struct file *file)
{/* 获取信号量 */down(&button_lock); /* 配置GPF0,2为输入引脚 *//* 配置GPG3,11为输入引脚 */request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); return 0;
}ssize_t fifth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{if (size != 1)return -EINVAL;/* 如果没有按键动作, 休眠 */wait_event_interruptible(button_waitq, ev_press);/* 如果有按键动作, 返回键值 */copy_to_user(buf, &key_val, 1);ev_press = 0;return 1;
}int fifth_drv_close(struct inode *inode, struct file *file)
{free_irq(IRQ_EINT0, &pins_desc[0]);free_irq(IRQ_EINT2, &pins_desc[1]);free_irq(IRQ_EINT11, &pins_desc[2]);free_irq(IRQ_EINT19, &pins_desc[3]);up(&button_lock);  //释放信号量return 0;
}static unsigned fifth_drv_poll(struct file *file, poll_table *wait)
{unsigned int mask = 0;poll_wait(file, &button_waitq, wait); // 不会立即休眠if (ev_press)mask |= POLLIN | POLLRDNORM;return mask;
}static int fifth_drv_fasync (int fd, struct file *filp, int on)
{printk("driver: fifth_drv_fasync\n");return fasync_helper (fd, filp, on, &button_async);  //初始化&button_async结构体,给此结构体分配内存,此结构体包含了应用进程ID
}static struct file_operations sencod_drv_fops = {.owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */.open    =  fifth_drv_open,     .read     = fifth_drv_read,    .release =  fifth_drv_close,.poll    =  fifth_drv_poll,.fasync  =  fifth_drv_fasync,
};int major;
static int fifth_drv_init(void)
{major = register_chrdev(0, "fifth_drv", &sencod_drv_fops);fifthdrv_class = class_create(THIS_MODULE, "fifth_drv");fifthdrv_class_dev = class_device_create(fifthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);gpfdat = gpfcon + 1;gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);gpgdat = gpgcon + 1;return 0;
}static void fifth_drv_exit(void)
{unregister_chrdev(major, "fifth_drv");class_device_unregister(fifthdrv_class_dev);class_destroy(fifthdrv_class);iounmap(gpfcon);iounmap(gpgcon);return 0;
}module_init(fifth_drv_init);module_exit(fifth_drv_exit);MODULE_LICENSE("GPL");

相比之前程序的改动:

1.50行,

static DECLARE_MUTEX(button_lock); 

定义信号量。

2.85行

    down(&button_lock);  

获取信号量。

3.119行,

 up(&button_lock);  //释放信号量

测试程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>/* fifthdrvtest */
int fd;void my_signal_fun(int signum)  //signal(SIGIO, my_signal_fun)的回调函数
{unsigned char key_val;read(fd, &key_val, 1);printf("key_val: 0x%x\n", key_val);
}int main(int argc, char **argv)
{unsigned char key_val;int ret;int Oflags;signal(SIGIO, my_signal_fun);  //注册信号处理函数fd = open("/dev/buttons", O_RDWR);if (fd < 0){printf("can't open!\n");return -1;}fcntl(fd, F_SETOWN, getpid());  //调用此函数,把应用程序PID告诉驱动Oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, Oflags | FASYNC);  //把flag修改一下,当执行这个函数时,会对应到.fasync   =  fifth_drv_fasync,//从而执行static int fifth_drv_fasync (int fd, struct file *filp, int on),  //初始化&button_async结构体while (1){sleep(1000);}return 0;
}

测试程序没有改变。

在操作之前要先去申请信号量,如果申请不到,可以先返回,或者休眠。如果申请到了,就继续执行接下来程序,在执行完程序后,要释放信号量,然后信号量会去唤醒之前因等待它而休眠的应用程序。继续执行被唤醒的应用程序。

对驱动程序的解释:

如果应用程序第一次来执行sixth_drv_open()函数,就会获得信号量;如果又来一个应用程序,那么此应用程序就会陷入休眠。此时这个应用程序用ps命令查看,发现处于D状态,D状态是僵死,不可中断的睡眠状态。直到先打开这个驱动的应用程序在sixth_drv_close()函数中把这个信号量释放掉,第二个程序才会被唤醒,继续执行下面的程序。

加载驱动程序之后,连续运行两个应用程序。发现两个应用程序都能打开驱动程序。可是用ps命令查看,先打开的应用程序状态为S,即:正常运行状态,后打开的应用程序为D,即睡眠状态。这个时候用kill -9 846命令把先打开的应用程序的进程杀死。再次ps。就可以看到后打开的驱动程序状态变为了S,即正常运行状态。

嵌入式驱动程序之信号量相关推荐

  1. 嵌入式驱动程序(5-5)点灯大师⑤之TM1668

    嵌入式驱动程序(5-5)点灯大师⑤之TM1668 作为一个嵌入式工程师,"点灯"是必备技能,哈哈哈(手动斜眼笑),除了普通IO口可以控制led灯亮灭之外,还有很多驱动ic,只需要2 ...

  2. 嵌入式驱动程序(5-3)点灯大师③之TM1650

    嵌入式驱动程序(5-3)点灯大师③之TM1650 作为一个嵌入式工程师,"点灯"是必备技能,哈哈哈(手动斜眼笑),除了普通IO口可以控制led灯亮灭之外,还有很多驱动ic,只需要2 ...

  3. 嵌入式系统开发-学习路线

    嵌入式系统开发 课程链接 随着嵌入式设备的普及,嵌入式应用的需求量也随之增大,本课程将重点培养基于linux系统下的嵌入式应用开发,包括5个阶段,其中课程涵盖了linux系统下的多进程.多线程嵌入式开 ...

  4. Linux并发与竞争介绍(原子操作、自旋锁、信号量、互斥体)

    目录 并发与竞争 并发与竞争简介 保护内容是什么 原子操作 原子操作简介 原子整形操作API函数(atomic_t 结构体) 原子位操作API 函数 自旋锁 自旋锁简介 自旋锁API函数 线程与线程 ...

  5. C语言到嵌入式Linux开发项目指导

    C语言到嵌入式Linux开发项目指导 第一阶段C语言 1.常量与变量,数据类型,数据类型转换,数据输入与输出: 2.C语言运算符,C语言操作符,C语言表达式,表达式优先级: 3.C语言流程控制,分支, ...

  6. 嵌入式基础(2)---硬件基础知识

    嵌入式系统是一种专用的计算机系统,作为装置或设备的一部分.国内普遍认同的嵌入式系统定义为:以应用为中心,以计算机技术为基础,软硬件可 裁剪,适应应用系统对功能.可靠性.成本.体积.功耗等严格要求的专用 ...

  7. 如何学习嵌入式系统(硬件篇),含51单片机学习资料

    学习嵌入式之前我们需要了解什么是嵌入式. (官方说法)嵌入式系统是一种专用的计算机系统,作为装置或设备的一部分.国内普遍认同的嵌入式系统定义为:以应用为中心,以计算机技术为基础,软硬件可裁剪,适应应用 ...

  8. 嵌入式Linux开发23——Linux并发与竞争

    文章目录 并发与竞争 1.并发与竞争简介 2.保护内容 原子操作 1.原子操作简介 2.原子整形操作API函数 3.原子位操作API函数 自旋锁 1.自旋锁简介 2.自旋锁API函数 3.其他类型的锁 ...

  9. 新手怎么学习嵌入式?什么人适合转行学习嵌入式?

    转行想学嵌入式的朋友是越来越多,那么真正付出行动转行学嵌入式的朋友也不少,但是还有很多的朋友都在反复的去思考自己转行学嵌入式适合吗?到底什么样的人适合转行学嵌入式? 首先必须是热爱编程,对嵌入式相当感 ...

最新文章

  1. 实验四-常用图像增强方法
  2. 机器学习算法如何调参?这里有一份神经网络学习速率设置指南
  3. UIScrollView无法滚动可能的原因及解决办法分析
  4. nginx实现http服务配置
  5. P5253-丢番图【数论】
  6. me shy是什么歌 抖音make_内含活动福利 | 小红书、抖音爆赞的高颜值的北欧家居神店开到卜蜂中心啦!...
  7. C语言圈叉游戏,圈叉棋小游戏的简单实现代码
  8. golang 关闭gc 并手动gc_Golang垃圾回收 屏障技术
  9. Python利用openpyxl来操作Excel(一)
  10. 关于信息安全工作方法论的一点猜想
  11. 计算机应用项目教案,计算机应用基础2项目二--电子教案.doc
  12. 用Python在地图上模拟疫情扩散
  13. e书制作工具和反编译工具
  14. php 计算壬子,(14条消息)计算生辰八字五行属性的算法
  15. Ubuntu 下配置 Typora 图片上传到 smms
  16. 你所不知的X86 CPU微码机制
  17. YUV420视频上面添加字幕
  18. 【TCP网络编程】C语言实现TCP服务器和客户端之间的通信(linux)
  19. linux java.library.path,设置java.library.path的值(Mac/Linux/Windows)
  20. 印象笔记:网页版与mac客户端工具栏都不一样

热门文章

  1. 多少个没收到会收敛_做多少个俯卧撑算是合格?坚持做俯卧撑,会有什么变化?...
  2. 封装 继承 多态_Java基础知识——封装、继承、多态
  3. 进程间基于消息队列的通信_Linux 进程间的通信方式
  4. fastreport.net 交叉表居中显示_浅析Sql中内连接、外连接、全连接、交叉连接的区别...
  5. 三菱伺服电机选型手册_PLC触摸屏控制伺服电机程序设计
  6. kruskai算法c语言实现,并查集 - xiaobaoqiu Blog
  7. sort函数pythonreverse_Python基础 7 ---- Python内置sort和sorted函数
  8. 找出重复的数java_剑指offer:1.找出数组中重复的数(java版)
  9. “元宇宙”概念引发AR/VR新一轮投资潮,去年Q4融资总额达120亿,超过此前2年总和...
  10. 今年你的双11包裹,也是自动驾驶卡车送来的吗?