Linux驱动开发 -----互斥和同步(下)

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");



  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值