4、信号量:
semaphore 睡眠锁,不占CPU,可以进行调度或休眠等操作,也可以通过设置信号的初值来设置对同一设备的访问次数或权限
本质:
struct semaphore {
raw_spinlock_t lock; //基于自旋锁来实现的
unsigned int count; //信号量的个数
struct list_head wait_list; //等待的链表
};
1、定义一个信号量
struct semaphore mysemaphore;
2、初始化
static inline void sema_init(struct semaphore *sem, int val)
3、信号量的获取
void down(struct semaphore *sem); //直接获取,不判断返回值,一定能获取成功
int __must_check down_interruptible(struct semaphore *sem); //加锁–请求变量的时候可以被中断
int __must_check down_killable(struct semaphore *sem); //加锁–请求变量的时候可以响应信号
int __must_check down_trylock(struct semaphore *sem); //尝试枷锁
int __must_check down_timeout(struct semaphore *sem, long jiffies); //加超时
成功返回0,失败返回非0
4、信号量的释放:
void up(struct semaphore *sem);
整合使用 :xxx
int demo_open(struct inode *inode, struct file *filp)
{
int retval;
printk("----%s----%d.\n",__func__,__LINE__);
//down(&mysemaphore);
retval = down_trylock(&mysemaphore);
if(retval){
printk("设备忙,请重试.\n");
return -EBUSY;
}
return 0;
}
int demo_close (struct inode *inode, struct file *filp)
{
printk("----%s----%d.\n",__func__,__LINE__);
up(&mysemaphore);
return 0;
}
应用层控制的是逻辑,驱动层控制的是设备。
扩展:
struct fileops fops {xx xx xx }
结构体变量定义的时候初始化需要全部进行初始化操作,
如果用点 . 的话可以指定结构体成员进行初始化操作。
代码实例:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <asm/atomic.h>
#define FS4412LEDON 1
#define FS4412LEDOFF 0
#define GPX2CON 0x11000C40
#define GPX2_7DAT 0x11000c44
#define GPX1CON 0x11000c20
#define GPX1DAT 0x11000c24
#define GPF3CON 0x114001e0
#define GPF3DAT 0x114001e4
void __iomem * gpx2con_vir;
void __iomem * gpx2dat_vir;
void __iomem * gpx1con_vir;
void __iomem * gpx1dat_vir;
void __iomem * gpf3con_vir;
void __iomem * gpf3dat_vir;
typedef struct led_desc{
int which;
int state;
}led_desc_t;
/*
ioremap
void __iomem *ioremap(phys_addr_t addr, unsigned long size)
功能:将物理地址映射为虚拟地址
参数:
@addr 物理地址
@size 映射的大小
返回值:成功返回虚拟地址,
失败返回NULL
*/
struct semaphore mysemaphore;//定义一个信号量
struct cdev *cdev;//定义一个字符设备对象
unsigned int major=0;//定义主设备号
const char *name="ledchar";//定义设备名
struct class *cls;
struct device *device;
int kbuf[5]={1,2,3,4,5};
int led_open (struct inode *inode, struct file *filp)
{
int retval;
retval=down_trylock(&mysemaphore);//加锁
if(retval){
printk("设备忙!请稍等");
return -EBUSY;
}
printk("OPEN!\n");
return 0;
}
int led_close (struct inode *inode, struct file *filp)
{
up(&mysemaphore);//释放信号量
printk("CLOSE!\n");
return 0;
}
ssize_t led_read (struct file *filp, char __user *usrbuf, size_t size, loff_t *offset)
{
int readby;
printk("READ!\n");
readby=copy_to_user(usrbuf,kbuf, 20);
/*
copy_to_user: - Copy a block of data into user space.
* Copy data from kernel space to user space.
* @to: Destination address, in user space.
* @from: Source address, in kernel space.
* @n: Number of bytes to copy.
*/
return readby;
}
ssize_t led_write (struct file *filp, const char __user *usrbuf, size_t size, loff_t *offset)
{
int writeby;
printk("WRITE!\n");
writeby=copy_from_user(kbuf, usrbuf,20);
/*
* copy_from_user: - Copy a block of data from user space.
* @to: Destination address, in kernel space.
* @from: Source address, in user space.
* @n: Number of bytes to copy.
* Copy data from user space to kernel space.
*/
return writeby;
}
long led_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
int lednum=0;
led_desc_t *led=(led_desc_t *)args;//通过函数传参决定是哪一个灯,与应用层对应
printk("IO!\n");
switch(cmd){
case FS4412LEDON:
lednum = led->which;
if(lednum==2){
printk("led2 on\n");
writel((readl(gpx2dat_vir) | 0x1 << 7),gpx2dat_vir);
}
else if(lednum==3){
printk("led3 on\n");
writel(readl(gpx1dat_vir) | (0x1 << 0),gpx1dat_vir);
}
else if(lednum==4){
printk("led4 on\n");
writel(readl(gpf3dat_vir) | (0x1 << 4),gpf3dat_vir);
}
else if(lednum==5){
printk("led5 on\n");
printk("fsled %d on.\n",lednum);
}
break;
case FS4412LEDOFF:
lednum = led->which;
if(lednum==2){
printk("led2 on\n");
writel((readl(gpx2dat_vir) & ~(0x1 << 7)),gpx2dat_vir);
}
else if(lednum==3){
printk("led3 on\n");
writel(readl(gpx1dat_vir)&~(0x1 << 0),gpx1dat_vir);
}
else if(lednum==4){
printk("led4 on\n");
printk("fsled %d off.\n",lednum);
}
else if(lednum==5){
printk("led5 on\n");
printk("fsled %d off.\n",lednum);
}
break;
default:
break;
}
return 0;
}
void gpio_ioremap(void)
{
gpx2con_vir = ioremap(GPX2CON, 4);
gpx2dat_vir = gpx2con_vir +4;
gpx1con_vir = ioremap(GPX1CON,4);
gpx1dat_vir = gpx1con_vir + 4;
gpf3con_vir = ioremap(GPF3CON,4);
gpf3dat_vir = gpf3con_vir + 4;
writel((readl(gpx2dat_vir) | 0x0 << 7),gpx2dat_vir);
writel(( readl(gpx2con_vir) & (~(0xF << 28))) | 0x1 << 28,gpx2con_vir);
writel((readl(gpx1con_vir) & ~(0XF<< 0))| (0x1 << 0),gpx1con_vir);
writel((readl(gpf3con_vir) & ~(0XFF<< 16 ))| (0x11 << 16),gpf3con_vir);
}
void gpio_iounmap(void)
{
iounmap(gpx2con_vir);
iounmap(gpx1con_vir);
iounmap(gpf3con_vir);
}
const struct file_operations fops={
.open=led_open,
.release=led_close,
.read=led_read,
.write=led_write,
.unlocked_ioctl=led_ioctl,
};//定义操纵方法集
static int __init led_init(void)
{
printk("INIT\n");
sema_init(&mysemaphore,2);//初始化信号量
//1.创建并注册一个设备节点
major=register_chrdev(0, name,&fops);
/*register_chrdev函数用法:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
其中参数major如果等于0,则表示采用系统动态分配的主设备号;不为0,则表示静态注册。
name :设备的名字
fops :操作方法集
返回值:
* If @major == 0 返回主设备号major
* If @major > 0 成功返回0
失败返回错误码
*/
//判断字符设备是否注册成功
if(major<=0){
printk("register_chrdev failed!\n");
return major;
}
printk("major :%d\n",major);//如果注册成功打印主设备号major
//2.自动创建设备节点
//(1)创建一个class目录
cls=class_create(THIS_MODULE, "led");
/*#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
功能:**创建一个目录 目录位于 -- /sys/class/xxx**
参数:
@owner: THIS_MODULE 第一个参数指定类的所有者是哪个模块
@name : 字符设备 第二个参数指定类名。
返回值:
struct class *_ 句柄 cls
*/
if(cls==NULL){
printk("class_create faild!\n");
goto err0;
}
//(2)自动创建设备节点
device = device_create(cls, NULL,MKDEV(major,0), NULL,name,0);
/*
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
功能:创建一个设备节点
参数:
@class: THIS_MODULE 指定所要创建的设备所从属的类
@parent : NULL 这个设备的父设备,如果没有就指定为NULL
@devt :devt 设备号
@drvdata:驱动的私有数据
@fmt :可变参数 一般是设备名
}
*/
if(device==NULL){
printk("device_create failed!");
goto err1;
}
return 0;
err1:device_destroy(cls, MKDEV(major,0));
err0:unregister_chrdev(major,name);//释放所注册的字符设备
return -1;
}
static void __exit led_exit(void)
{
printk("EXIT!\n");
unregister_chrdev(major,name);//释放所注册的字符设备
/*
static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}
*/
class_destroy(cls);//释放创建的class目录
device_destroy(cls, MKDEV(major,0));//释放创建的设备节点
gpio_iounmap();
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
5、互斥体
mutex 用法和信号量一样
本质:
struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count; //原子变量
spinlock_t wait_lock; //自旋锁
struct list_head wait_list; //链表---包含它的可以睡眠
};
1、定义
struct mutex mymutexlock;
2、初始化
mutex_init(&mymutexlock)
__mutex_init((mutex), #mutex, &__key);
void __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
3、上锁
#define mutex_lock(lock) mutex_lock_nested(lock, 0)
#define mutex_lock_interruptible(lock) mutex_lock_interruptible_nested(lock, 0)
#define mutex_lock_killable(lock) mutex_lock_killable_nested(lock, 0)
int mutex_trylock(struct mutex *lock);
4、解锁
void mutex_unlock(struct mutex *lock);
代码实例:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <asm/atomic.h>
#define FS4412LEDON 1
#define FS4412LEDOFF 0
#define GPX2CON 0x11000C40
#define GPX2_7DAT 0x11000c44
#define GPX1CON 0x11000c20
#define GPX1DAT 0x11000c24
#define GPF3CON 0x114001e0
#define GPF3DAT 0x114001e4
void __iomem * gpx2con_vir;
void __iomem * gpx2dat_vir;
void __iomem * gpx1con_vir;
void __iomem * gpx1dat_vir;
void __iomem * gpf3con_vir;
void __iomem * gpf3dat_vir;
typedef struct led_desc{
int which;
int state;
}led_desc_t;
/*
ioremap
void __iomem *ioremap(phys_addr_t addr, unsigned long size)
功能:将物理地址映射为虚拟地址
参数:
@addr 物理地址
@size 映射的大小
返回值:成功返回虚拟地址,
失败返回NULL
*/
struct cdev *cdev;//定义一个字符设备对象
struct mutex *mymutexlock;//定义
unsigned int major=0;//定义主设备号
const char *name="ledchar";//定义设备名
struct class *cls;
struct device *device;
int kbuf[5]={1,2,3,4,5};
int led_open (struct inode *inode, struct file *filp)
{
int retval;
retval=mutex_trylock(&mymutexlock);//上锁
if(retval){
printk("设备忙!请稍等");
return -EBUSY;
}
printk("OPEN!\n");
return 0;
}
int led_close (struct inode *inode, struct file *filp)
{
mutex_unlock(&mymutexlock);//解锁
printk("CLOSE!\n");
return 0;
}
ssize_t led_read (struct file *filp, char __user *usrbuf, size_t size, loff_t *offset)
{
int readby;
printk("READ!\n");
readby=copy_to_user(usrbuf,kbuf, 20);
/*
copy_to_user: - Copy a block of data into user space.
* Copy data from kernel space to user space.
* @to: Destination address, in user space.
* @from: Source address, in kernel space.
* @n: Number of bytes to copy.
*/
return readby;
}
ssize_t led_write (struct file *filp, const char __user *usrbuf, size_t size, loff_t *offset)
{
int writeby;
printk("WRITE!\n");
writeby=copy_from_user(kbuf, usrbuf,20);
/*
* copy_from_user: - Copy a block of data from user space.
* @to: Destination address, in kernel space.
* @from: Source address, in user space.
* @n: Number of bytes to copy.
* Copy data from user space to kernel space.
*/
return writeby;
}
long led_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
{
int lednum=0;
led_desc_t *led=(led_desc_t *)args;//通过函数传参决定是哪一个灯,与应用层对应
printk("IO!\n");
switch(cmd){
case FS4412LEDON:
lednum = led->which;
if(lednum==2){
printk("led2 on\n");
writel((readl(gpx2dat_vir) | 0x1 << 7),gpx2dat_vir);
}
else if(lednum==3){
printk("led3 on\n");
writel(readl(gpx1dat_vir) | (0x1 << 0),gpx1dat_vir);
}
else if(lednum==4){
printk("led4 on\n");
writel(readl(gpf3dat_vir) | (0x1 << 4),gpf3dat_vir);
}
else if(lednum==5){
printk("led5 on\n");
printk("fsled %d on.\n",lednum);
}
break;
case FS4412LEDOFF:
lednum = led->which;
if(lednum==2){
printk("led2 on\n");
writel((readl(gpx2dat_vir) & ~(0x1 << 7)),gpx2dat_vir);
}
else if(lednum==3){
printk("led3 on\n");
writel(readl(gpx1dat_vir)&~(0x1 << 0),gpx1dat_vir);
}
else if(lednum==4){
printk("led4 on\n");
printk("fsled %d off.\n",lednum);
}
else if(lednum==5){
printk("led5 on\n");
printk("fsled %d off.\n",lednum);
}
break;
default:
break;
}
return 0;
}
void gpio_ioremap(void)
{
gpx2con_vir = ioremap(GPX2CON, 4);
gpx2dat_vir = gpx2con_vir +4;
gpx1con_vir = ioremap(GPX1CON,4);
gpx1dat_vir = gpx1con_vir + 4;
gpf3con_vir = ioremap(GPF3CON,4);
gpf3dat_vir = gpf3con_vir + 4;
writel((readl(gpx2dat_vir) | 0x0 << 7),gpx2dat_vir);
writel(( readl(gpx2con_vir) & (~(0xF << 28))) | 0x1 << 28,gpx2con_vir);
writel((readl(gpx1con_vir) & ~(0XF<< 0))| (0x1 << 0),gpx1con_vir);
writel((readl(gpf3con_vir) & ~(0XFF<< 16 ))| (0x11 << 16),gpf3con_vir);
}
void gpio_iounmap(void)
{
iounmap(gpx2con_vir);
iounmap(gpx1con_vir);
iounmap(gpf3con_vir);
}
const struct file_operations fops={
.open=led_open,
.release=led_close,
.read=led_read,
.write=led_write,
.unlocked_ioctl=led_ioctl,
};//定义操纵方法集
static int __init led_init(void)
{
printk("INIT\n");
mutex_init(&mymutexlock);//初始化
//1.创建并注册一个设备节点
major=register_chrdev(0, name,&fops);
/*register_chrdev函数用法:
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, 0, 256, name, fops);
}
其中参数major如果等于0,则表示采用系统动态分配的主设备号;不为0,则表示静态注册。
name :设备的名字
fops :操作方法集
返回值:
* If @major == 0 返回主设备号major
* If @major > 0 成功返回0
失败返回错误码
*/
//判断字符设备是否注册成功
if(major<=0){
printk("register_chrdev failed!\n");
return major;
}
printk("major :%d\n",major);//如果注册成功打印主设备号major
//2.自动创建设备节点
//(1)创建一个class目录
cls=class_create(THIS_MODULE, "led");
/*#define class_create(owner, name) \
({ \
static struct lock_class_key __key; \
__class_create(owner, name, &__key); \
})
功能:**创建一个目录 目录位于 -- /sys/class/xxx**
参数:
@owner: THIS_MODULE 第一个参数指定类的所有者是哪个模块
@name : 字符设备 第二个参数指定类名。
返回值:
struct class *_ 句柄 cls
*/
if(cls==NULL){
printk("class_create faild!\n");
goto err0;
}
//(2)自动创建设备节点
device = device_create(cls, NULL,MKDEV(major,0), NULL,name,0);
/*
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
功能:创建一个设备节点
参数:
@class: THIS_MODULE 指定所要创建的设备所从属的类
@parent : NULL 这个设备的父设备,如果没有就指定为NULL
@devt :devt 设备号
@drvdata:驱动的私有数据
@fmt :可变参数 一般是设备名
}
*/
if(device==NULL){
printk("device_create failed!");
goto err1;
}
return 0;
err1:device_destroy(cls, MKDEV(major,0));
err0:unregister_chrdev(major,name);//释放所注册的字符设备
return -1;
}
static void __exit led_exit(void)
{
printk("EXIT!\n");
unregister_chrdev(major,name);//释放所注册的字符设备
/*
static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, 0, 256, name);
}
*/
class_destroy(cls);//释放创建的class目录
device_destroy(cls, MKDEV(major,0));//释放创建的设备节点
gpio_iounmap();
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");