1. 非共享设备
目的:使得同时只有一个应用程序可以打开驱动程序。
基于 buttons_irq 驱动程序的基础上进行修改。
1.1 使用原子操作
原子操作指的是在执行过程中,不会被别的代码路径所中断的操作。
常用的原子操作函数:
atomic_t v = ATOMIC_INIT(0); // 定义原子变量v并初始化为0
atomic_read(atomic_t *v); // 返回原子变量的值
atomic_inc(atomic_t *v); // 原子变量增加1
atomic_dec(atomic_t *v); // 原子变量减少1
atomic_dec_and_test(atomic_t *v); // 自减操作后测试其是否为0,为0则返回true,
// 否则返回false
驱动程序
-
初始化一个原子变量:
static unsigned int major; static atomic_t canopen = ATOMIC_INIT(1); /* 新添加 */ static struct class *s3c2440_single_class; static struct class_device *s3c2440_single_dev;
-
修改 open 函数:
static ssize_t s3c2440_single_open(struct inode *inode, struct file *file) { if (atomic_dec_and_test(&canopen)) { atomic_inc(&canopen); return -EBUSY; } /* 配置按键:GPF0\2和GPG3的引脚为中断引脚 * 注册irqaction处理函数 */ request_irq(IRQ_EINT0, s3c2440_single_handler, IRQT_BOTHEDGE, "s2", &pins_desc[0]); request_irq(IRQ_EINT2, s3c2440_single_handler, IRQT_BOTHEDGE, "s3", &pins_desc[1]); request_irq(IRQ_EINT11, s3c2440_single_handler, IRQT_BOTHEDGE, "s4", &pins_desc[2]); request_irq(IRQ_EINT19, s3c2440_single_handler, IRQT_BOTHEDGE, "s5", &pins_desc[3]); return 0; }
-
修改 close 函数:
static ssize_t s3c2440_single_close(struct inode *inode, struct file *file) { atomic_inc(&canopen); 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]); return 0; }
1.2 使用信号量
信号量(semaphore)是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。当获取不到信号量时,进程进入休眠等待状态。
定义信号量/互斥锁:
struct sempahore sem; /* 信号量 */
static DECLEARE_MUTEX(button_lock); /* 互斥锁 */
互斥锁与信号量的区别:
/* 互斥锁是基于等待队列实现的 */ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; }; #define __SEMAPHORE_INIT(name, cnt) \ { \ .count = ATOMIC_INIT(cnt), \ .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait), \ } #define __DECLARE_SEMAPHORE_GENERIC(name,count) \ struct semaphore name = __SEMAPHORE_INIT(name,count) #define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) #define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
这个 Linux-2.6.22 版本 arm 架构的互斥锁与信号量的区别在于,互斥锁是 sleepers 为1的特殊情况下的信号量。
初始化信号量:
void sema_init(struct sempahore *sem, int val);
void init_MUTEX(struct sempahore *sem); // 初始化为0
获取信号量:
void down(struct sempahore *sem);
int down_interruptible(struct sempahore *sem);
int down_trylock(struct sempahore *sem);
三者的区别:
- down:尝试获取信号量,获取失败则会阻塞;
- down_interruptible:尝试获取信号量,获取失败会阻塞,但这个阻塞状态可以被打断;
- down_trylock:尝试获取信号量,如果成功获取了信号量,则返回0,如果不能获取信号量,则返回1。
释放信号量:
void up(struct sempahore *sem);
驱动程序
-
初始化一个信号量变量:
static DECLARE_MUTEX(button_lock);
-
在 open 函数处获取信号量:
static ssize_t s3c2440_buttons_irq_open(struct inode *inode, struct file *file) { /* 获取信号量 */ down(&button_lock); /* 配置按键:GPF0\2和GPG3的引脚为中断引脚 * 注册irqaction处理函数 */ request_irq(IRQ_EINT0, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s2", &pins_desc[0]); request_irq(IRQ_EINT2, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s3", &pins_desc[1]); request_irq(IRQ_EINT11, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s4", &pins_desc[2]); request_irq(IRQ_EINT19, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s5", &pins_desc[3]); return 0; }
-
在 close 函数处释放信号量:
static ssize_t s3c2440_buttons_irq_close(struct inode *inode, struct file *file) { up(&button_lock); 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]); return 0; }
2. 阻塞与非阻塞
阻塞操作:是指在执行设备操作时,若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作:进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直到可以进行操作为止。
修改驱动程序
修改 open 函数的获取信号量方式:
static ssize_t s3c2440_buttons_irq_open(struct inode *inode, struct file *file)
{
/* 获取信号量 */
if (file->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock) == 1)
return -EBUSY;
}
else
{
down(&button_lock);
}
/* 配置按键:GPF0\2和GPG3的引脚为中断引脚
* 注册irqaction处理函数
*/
request_irq(IRQ_EINT0, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s2", &pins_desc[0]);
request_irq(IRQ_EINT2, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s3", &pins_desc[1]);
request_irq(IRQ_EINT11, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s4", &pins_desc[2]);
request_irq(IRQ_EINT19, s3c2440_buttons_irq_handler, IRQT_BOTHEDGE, "s5", &pins_desc[3]);
return 0;
}
修改 read 函数的读操作:
static ssize_t s3c2440_sempahore_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long ret;
if (count < sizeof(key_val))
return -EINVAL;
if (file->f_flags & O_NONBLOCK)
{
if (!ev_press)
return -EAGAIN;
}
else
{
/* 如果没有按键按下,就休眠 */
wait_event_interruptible(button_waitq, ev_press);
}
/* 如果没有按键按下,就休眠 */
wait_event_interruptible(button_waitq, ev_press);
/* 如果有按键被按下,从休眠状态唤醒 */
ret = copy_to_user(buf, &key_val, sizeof(key_val));
ev_press = 0;
return ret;
}