自旋锁
自旋锁顾名思义就是一个一直在旋转循环忙等待的锁,哪个线程有锁就是哪个线程在访问共享资源。而其它线程只能在那等待,当锁被释放了之后,才轮到其它线程占有这个锁。自旋锁只适用于线程持有锁的时间不算太长的情况下,否则会浪费处理器时间,降低系统性能。
自旋锁API函数
适用于线程和线程之间。
需要注意的是:被自旋锁保护的临界区绝对不能调用任何能够引起睡眠或阻塞的API函数,不然可能导致死锁现象。如果在线程之间的并发访问时,中断也要参与的话,在中断里可以使用自旋锁,但是在获取锁之前要先禁止本地中断(本CPU中断),否则就可能导致死锁现象。
线程与中断并发访问处理API函数
自旋锁使用注意事项
- 锁的持有时间不能太长;
- 自旋锁保护的临界区内不能调用任何可能导致线程休眠的 API 函数;
- 不能递归申请自旋锁;
- 为了驱动的可移植性,不管用的是单核的还是多核的 SOC,都将其当做多核 SOC 来编写驱动程序。
定时器相关的API函数
-
init_timer
/* @function: 负责初始化 timer_list 类型变量 @param: timer:要初始化的定时器 */ void init_timer(struct timer_list *timer)
-
add_timer
/* @function: 负责向 Linux 内核注册定时器 @param: timer:要注册的定时器 */ void add_timer(struct timer_list *timer)
-
del_timer
/* @function: 用于删除一个定时器 @param: timer:要删除的定时器 @return: 0,定时器还没被激活;1,定时器已经激活 */ int del_timer(struct timer_list * timer)
-
del_timer_sync
/* @function: 用于删除一个定时器,但是不能使用在中断上下文中,会等待其他处理器使用完定时器再删除 @param: timer:要删除的定时器 @return: 0,定时器还没被激活;1,定时器已经激活 */ int del_timer_sync(struct timer_list *timer)
-
mod_timer
/* @function: 用于修改定时值 @param: timer:要修改的定时器 expires:修改后的超时时间 @return: 0,调用 mod_timer 函数前定时器未被激活 1,调用 mod_timer 函数前定时器已被激活 */ int mod_timer(struct timer_list *timer, unsigned long expires)
Linux内核短延时函数
ioctl命令
//不带参数的ioctl命令
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
//带读参数的ioctl命令
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
//带写参数的ioctl命令
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
//带读写参数的ioctl命令
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
type:用来区分不同驱动的,类似设备号,详细描述位于内核源码的/Documentation/ioctl/ioctl_number.txt文档中。
nr:命令编号。
定时器驱动代码
驱动功能:
实现开启、关闭定时器;配置定时器超时时间;用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/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/* IO控制命令 */
#define CLOSE_CMD (_IO(0XEF, 0x1))
#define OPEN_CMD (_IO(0XEF, 0x2))
#define SETPERIOD_CMD (_IO(0XEF, 0x3))
#define DEVICE_CNT 1 /* 设备号个数 */
#define DEVICR_NAME "xxx" /* 设备名字 */
#define DEFAULT_PERIOD 1000 /* 默认定时器的超时时间 */
/* timer 设备结构体 */
struct device_dev
{
/* 字符驱动设备 */
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
struct device_node *nd; /* 设备节点 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
/* GPIO */
int xxx_gpio; /* 设备所使用的 GPIO 编号 */
/* 自旋锁 */
#if 1
spinlock_t lock; /* 定义自旋锁,用来保护 timeperiod */
#endif
#if 1
/* 定时器 */
int timeperiod; /* 定时周期,单位为 ms */
struct timer_list timer; /* 定义一个定时器 */
#endif
};
/* 创建一个 device 设备 */
struct device_dev device;
/* 定义一个 sta 作为 led 的状态值 */
static int sta = 1;
/* 利用 pinctrl 和 gpio 子系统初始化 IO */
static int gpio_init(void)
{
int ret = 0;
/* 通过设备树路径找到节点, 这里的node指的是在设备树中的节点的名字*/
device.nd = of_find_node_by_path("/node");
/* 获取设备树中的GPIO的属性,节点,xxx-gpio属性,gpio编号 */
device.xxx_gpio = of_get_named_gpio(device.nd, "xxx-gpio", 0);
if(timer.xxx_gpio < 0)
{
printk("can't get device\r\n");
return -EINVAL;
}
/* 申请一个GPIO,并命名为xxx */
gpio_request(device.xxx_gpio, "xxx");
/* 配置 GPIO 为输出模式,默认输出高电平 */
ret = gpio_direction_output(device.xxx_gpio, 1);
if(ret < 0)
{
printk("can't set gpio!\r\n");
}
return 0;
}
/* led 翻转 */
void led_toggle(void)
{
sta =! sta;
gpio_set_value(dev->led_gpio, sta);
}
/* io控制函数 */
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct timer_dev *dev = (struct timer_dev *)filp->private_data;
int timerperiod;
unsigned long flags;
/* 定时器控制命令 */
switch(cmd)
{
/* 关闭定时器 */
case CLOSE_CMD:
del_timer_sync(&dev->timer);
break;
/* 打开定时器 */
case OPEN_CMD:
spin_lock_irqsave(&dev->lock, flags);
timerperiod = dev->timeperiod;
spin_unlock_irqrestore(&dev->lock, flags);
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
break;
/* 配置定时器定时周期 */
case SETPERIOD_CMD:
spin_lock_irqsave(&dev->lock, flags);
dev->timeperiod = arg;
spin_unlock_irqrestore(&dev->lock, flags);
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
break;
default:
break;
}
return 0;
}
#if 1
/* 定时器回调函数,当定时器定时完毕会自动执行 */
void timer_function(unsigned long arg)
{
struct device_dev *dev = (struct device_dev *)arg;
int timerperiod;
unsigned long flags;
led_toggle();
/* 重启定时器 */
/* 1. 保存中断状态,关闭本地中断,获取自旋锁 */
spin_lock_irqsave(&dev->lock, flags);
/* 2. 更新一下定时器周期 */
timerperiod = dev->timeperiod;
/* 3. 恢复中断状态,打开本地中断,释放自旋锁 */
spin_unlock_irqrestore(&dev->lock, flags);
/* 4. 修改定时器的超时时间 */
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
}
#endif
/* 设备开启函数 */
static int device_open(struct inode *inode, struct file *filp)
{
int ret = 0;
filp->private_data = &device; /* 设置私有数据 */
#if 1
device.timeperiod = DEFAULT_PERIOD; /* 配置定时器默认周期 */
#endif
ret = gpio_init(); /* 初始化IO */
if (ret < 0)
{
return ret;
}
return 0;
}
/* 从设备读取数据 */
static ssize_t device_read(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
unsigned char stat;
struct device_dev *dev = filp->private_data;
stat = gpio_get_value(dev->xxx_gpio);
/* some operations of stat */
return ret;
}
/* 向设备写入数据 */
static ssize_t device_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue = 0;
unsigned char databuf[1];
#if 1
unsigned char user_1 = 0;
#if 0
unsigned char i = 0;
unsigned char index = 1;
unsigned char user_buf[index];
#endif
struct device_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf,buf,cnt);
#if 1
user_1 = databuf[0];
#if 0
for ( i = 0; i < index; i++)
{
user_buf[index] = databuf[index];
}
#endif
/* some operations of data */
return 0;
}
/* 设备释放函数 */
static int device_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations key_fops =
{
.owner = THIS_MODULE,
.open = device_open,
.write = device_write,
.read = device_read,
.release = device_release,
}
/* 驱动入口函数 */
static int __init device_init(void)
{
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (device.major) { /* 定义了设备号 */
device.devid = MKDEV(device.major, 0);
register_chrdev_region(device.devid, DEVICE_CNT, DEVICE_NAME);
} else { /* 没有定义设备号 */
alloc_chrdev_region(&device.devid, 0, DEVICE_CNT, DEVICE_NAME);
device.major = MAJOR(device.devid); /* 获取主设备号 */
device.minor = MINOR(device.devid); /* 获取次设备号 */
}
/* 2、注册设备 */
device.cdev.owner = THIS_MODULE;
cdev_init(&device.cdev, &device_fops);
cdev_add(&device.cdev, device.devid, DEVICE_CNT);
/* 3、创建类 */
device.class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(device.class)) {
return PTR_ERR(device.class);
}
/* 4、创建设备 */
device.device = device_create(device.class, NULL, device.devid, NULL, DEVICE_NAME);
if (IS_ERR(device.device)) {
return PTR_ERR(device.device);
}
#if 1
/* 初始化自旋锁 */
spin_lock_init(&device.lock);
#endif
#if 1
/* 初始化 timer,设置定时器处理函数,还未设置周期,还不会激活定时器 */
init_timer(&device.timer);
/* 配置定时器回调函数 */
device.timer.function = timer_function;
device.timer.data = (unsigned long)&timer;
#endif
return 0;
}
/* 驱动出口函数 */
static void __exit timer_exit(void)
{
gpio_set_value(device.xxx_gpio,0);
del_timer_sync(&device.timer);
cdev_del(&device.cdev);
unregister_chrdev_region(device.devid, DEVICE_CNT);
device_destroy(device.class, device.devid);
class_destroy(device.class);
gpio_free(device.xxx_gpio);
}
定时器应用代码
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"
/* 命令值 */
#define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */
#define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */
#define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令 */
int main(int argc, char *argv[])
{
int fd, ret;
char *filename;
unsigned int cmd;
unsigned int arg;
unsigned char str[100];
if(argc != 2){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0){
printf("Can't open file %s\r\n", filename);
return -1;
}
while (1)
{
printf("Input CMD:");
ret = scanf("%d", &cmd);
if(ret != 1)
{
gets(str);
}
if(cmd == 1)
cmd = CLOSE_CMD;
else if (cmd == 2)
{
cmd = OPEN_CMD;
}
else if (cmd == 3)
{
cmd = SETPERIOD_CMD;
printf("Input Timer Period:");
ret = scanf("%d", &arg);
if(ret != 1)
{
gets(str);
}
}
ioctl(fd, cmd, arg);
}
close(fd);
}