原子操作
原子操作指在执行过程中不会被别的代码路径所中断的操作。
常用函数列举:
atomic_t v = ATOMIC_INIT(0); // 定义原子变量v并初始化为0
atomic_read(atomic_t *v); // 返回原子变量的值
void atomic_inc(atomic_t *v); // 原子变量+1
void atomic_dec(atomic_t *v); // 原子变量-1
int atomic_dec_and_test(atomic_t *v); // 自减操作后测试其是否为0,=0则返回true,!=0则返回false
信号量
信号量(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); // 释放信号量
阻塞操作
阻塞操作:在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。
非阻塞操作:进程在不能进行设备操作时并不挂起,它或者放弃,或者不停的查询,直至可以进行操作为止。
如何使用?
在open时传入一个参数:
fd = open(“...”, O_RDWR | O_NONBLOCK); // 若不加这个标记,默认是阻塞方式
源码
驱动程序:
/*
* 引脚:PI0,1,2
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <asm/signal.h>
#define IF_WANT_USE_ATOMIC 0 // 使用原子变量实现同一时刻只有一个应用程序能打开本驱动
#define IF_WANT_USE_SEMA 0 // 使用信号量实现同一时刻只有一个应用程序能打开本驱动
#define IF_WANT_USE_NOBLOCK 1 // 使用非阻塞打开本驱动,同时使用信号量
// 第二次打开本驱动时,若定义为1则立即返回,若为0,则休眠直到资源能获取
#if IF_WANT_USE_ATOMIC
#include <asm/atomic.h>
#include <linux/types.h>
#endif
#if IF_WANT_USE_SEMA
#include <linux/semaphore.h>
#endif
struct pin_desc{
int pin;
int val;
};
/*
* 按下时,返回:0x81, 0x82, 0x83
*
*/
static struct pin_desc pins_desc[3] = {
{NUC970_PI0, 0x1},
{NUC970_PI1, 0x2},
{NUC970_PI2, 0x3},
};
static unsigned char val;
static DECLARE_WAIT_QUEUE_HEAD(buttons_waitq);
int evpress = 0;
struct fasync_struct *buttons_async;
#if IF_WANT_USE_ATOMIC
static atomic_t canopen = ATOMIC_INIT(1); // 定义并初始化原子变量
#endif
#if IF_WANT_USE_SEMA
static struct semaphore button_lock;
#endif
#if IF_WANT_USE_NOBLOCK
static struct semaphore button_lock;
#endif
static irqreturn_t buttons_handler(int irq, void *dev_id)
{
struct pin_desc *pin = (struct pin_desc *)dev_id;
//int res;
//res = gpio_get_value(pin->pin);
val = 0x80 | pin->val;
evpress = 1;
wake_up_interruptible(&buttons_waitq);
kill_fasync(&buttons_async, SIGIO, POLL_IN); // 发送信号
return IRQ_HANDLED;
}
static int buttons_open(struct inode *inode, struct file *filp)
{
// request_irq会自动设置引脚,此处不再配置
#if IF_WANT_USE_ATOMIC
if (!atomic_dec_and_test(&canopen))
{
atomic_inc(&canopen);
return -EBUSY;
}
#endif
#if IF_WANT_USE_SEMA
down(&button_lock);
#endif
#if IF_WANT_USE_NOBLOCK
if (filp->f_flags & O_NONBLOCK)
{
if (down_trylock(&button_lock))
{
return -EBUSY;
}
}
else
{
down(&button_lock);
}
#endif
request_irq(gpio_to_irq(pins_desc[0].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S1", &pins_desc[0]);
request_irq(gpio_to_irq(pins_desc[1].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S2", &pins_desc[1]);
request_irq(gpio_to_irq(pins_desc[2].pin), buttons_handler, IRQF_TRIGGER_FALLING, "S3", &pins_desc[2]);
return 0;
}
static ssize_t buttons_read (struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
if (count != 1)
{
return -EINVAL;
}
#if IF_WANT_USE_NOBLOCK
if (filp->f_flags & O_NONBLOCK)
{
if (evpress == 0)
{
return -EAGAIN;
}
}
#endif
wait_event_interruptible(buttons_waitq, evpress);
evpress = 0;
copy_to_user(buf, &val, 1);
return 1;
}
int buttons_release (struct inode *inode, struct file *filp)
{
free_irq(gpio_to_irq(pins_desc[0].pin), &pins_desc[0]);
free_irq(gpio_to_irq(pins_desc[1].pin), &pins_desc[1]);
free_irq(gpio_to_irq(pins_desc[2].pin), &pins_desc[2]);
#if IF_WANT_USE_ATOMIC
atomic_inc(&canopen);
#endif
#if (IF_WANT_USE_SEMA | IF_WANT_USE_NOBLOCK)
up(&button_lock);
#endif
return 0;
}
static unsigned int buttons_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &buttons_waitq, wait); // 不会立即休眠,只是把进程挂到buttons_waitq队列
if (evpress)
{
mask = POLLIN | POLLRDNORM;
}
return mask;
}
// 每当应用程序修改FAYNC标记时,此程序就会被调用
static int buttons_fasync(int fd, struct file *filp, int on)
{
return fasync_helper(fd, filp, on, &buttons_async); // 此函数会初始化buttons_async
}
static struct file_operations buttons_fops = {
.owner = THIS_MODULE,
.open = buttons_open,
.read = buttons_read,
.release = buttons_release,
.poll = buttons_poll,
.fasync = buttons_fasync,
};
static int major;
static struct class *buttons_class;
static struct device *button_device;
static int buttons_init(void)
{
major = register_chrdev(0, "buttons", &buttons_fops);
buttons_class = class_create(THIS_MODULE, "buttons");
button_device = device_create(buttons_class, NULL, MKDEV(major, 0), NULL, "buttons");
#if (IF_WANT_USE_SEMA | IF_WANT_USE_NOBLOCK)
sema_init(&button_lock, 1);
#endif
return 0;
}
static void buttons_exit(void)
{
device_destroy(buttons_class, MKDEV(major, 0));
class_destroy(buttons_class);
unregister_chrdev(major, "buttons");
}
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
测试程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <fcntl.h>
/* ./buttontest <O_BLOCK | O_NONBLOCK> */
int main(int argc, char *argv[])
{
int fd;
int ret;
unsigned char key_val = 0;
if (argc != 2)
{
printf("Usage:\n");
printf("%s <O_BLOCK | O_NONBLOCK>\n", argv[0]);
return -1;
}
if (strcmp(argv[1], "O_BLOCK") == 0)
{
fd = open("/dev/buttons", O_RDONLY);
}
else if (strcmp(argv[1], "O_NONBLOCK") == 0)
{
fd = open("/dev/buttons", O_RDONLY | O_NONBLOCK);
}
else
{
printf("Usage:\n");
printf("%s <O_BLOCK | O_NONBLOCK>\n", argv[0]);
return -1;
}
if (fd < 0)
{
printf("Can't open /dev/buttons\n");
return -1;
}
while (1)
{
ret = read(fd, &key_val, 1);
printf("key_val = 0x%x, ret = %d\n", key_val, ret);
sleep(5);
}
close(fd);
return 0;
}