免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 774 | 回复: 0
打印 上一主题 下一主题

一个阻塞型的小驱动 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-09-19 10:39 |只看该作者 |倒序浏览
前边一篇文章《驱动模块及其用户空间的调用》,在那篇文章里的一个小驱动实现了驱动的最基本的功能,能够在用户空间进行对设备进行数据的读写。这里在那个驱动的基础上继续努力,又添加了一个小功能,就是把驱动的读函数改成了阻塞型的驱动。

于阻塞型驱动,就是说在驱动访问设备的时候,如果条件不满足则驱动被阻塞,即它不会立即返回,只是把进程挂起,直到条件满足之后,他才进行驱动要完成的任
务,之后才返回。非阻塞型驱动,是说驱动访问设备的时候,如果条件不满足则驱动直接返回这个状态,不会在原地等待,用户可以根据返回值进行不同的处理。
Linux通过一些数据结构包括信号量等来实现这种驱动的阻塞。这里简单介绍一下信号量。
一、信号量的定义
信号量数据结构定义在include/asm/semaphore.h中,定义为:
struct semaphore {
              atomic_t count;
              int sleepers;
              wait_queue_head_t wait;
};
二、信号量的常用操作函数
常用的操作函数有如下:
static inline
void sema_init(struct semaphore *sem, int val);给信号量赋初值val
static inline
void init_MUTEX(struct semaphore *sem);初始化信号量,赋值1
static inline
void down(struct semaphore * sem);down()操作可以理解为申请资源
static inline
void up(struct semaphore * sem);up()操作可以理解为释放资源
三、信号量的使用
首先要明白,当信号量的值小于等于0时,信号量不可以被获得,所以初始化信号量时,要给信号量赋值为1,这样进程才能获得信号量。进程一般用down()来获得信号量,他获得同时会是信号量减1变为零,此时其它进程就无法获得信号量。当用up()释放信号量时,会让信号量加1,从而其它进程可以获得信号量,从而避免了冲突。

因为自旋锁和信号量是相似的东西,这里也顺便介绍一下自旋锁。
四、关于自旋锁
自旋锁数据结构定义在linux/spinlock_types.h中,定义比较简单。
typedef struct {
              raw_spinlock_t
raw_lock;
#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)
              unsigned
int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
              unsigned
int magic, owner_cpu;
              void
*owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
              struct
lockdep_map dep_map;
#endif
} spinlock_t;
       关于自旋锁的操作方法常用的有如下:
              spin_lock_init(lock);初始化锁
              spin_lock(lock);申请资源,获得时上锁。
              spin_unlock(lock);解锁,释放资源
       自旋锁的使用方法和信号量比较相似,但他适用于一个不同的场合。
五、区别
    自旋锁在申请资源时,如果该资源已被其他获得,不能立马获得,他将在原地旋转,不停的进新资源查询,知道获得该资源,此时对cpu的消耗较大,它适用于执行任务简短的地方,能快速的获得资源。所以自旋锁不应该被持有时间过长,如果需要长时间锁定的话, 最好使用信号量,信号量不同于自旋锁,它不会关闭内核抢占,持有信号量的代码可以被抢占,故信号量的使用则不受这个限制。
六、代码如下,为了编译通过,屏蔽了关于硬件的代码
#include
#include
#include
#include
#include
#include
//#include
#include    ////TASK_INTERRUPTIBLE   declared
#include
#include
#include

MODULE_LICENSE("Dual BSD/GPL");
typedef unsigned int UINT32;

static UINT32 lcd_light_degree;
int major_lcd_light;
static struct semaphore sem;
static wait_queue_head_t outq;
static int flag=0;

//写函数
static ssize_t lcd_light_write(struct file
*file, const char *buf, size_t count, loff_t *off)
  {
  
if(down_interruptible(&sem))      
     
{
      
return -ERESTARTSYS;
     
}
     
flag=1;

copy_from_user(&lcd_light_degree, (void*)buf,count);
  //__raw_writel(lcd_light_degree
,AIPI_IO_ADDRESS(0x1000600C));//sample

up(&sem);

wake_up_interruptible(&outq);

printk("LCD light degree is %d", lcd_light_degree);

return count;

}

static ssize_t lcd_light_read(struct file
*file, char *buf, size_t bytes, loff_t *off){
  
if(wait_event_interruptible(outq,flag!=0))   //obstruct here!!
     
{
     
return -ERESTARTSYS;
     
}
  
if(down_interruptible(&sem))
     
{
      
return -ERESTARTSYS;
     
}
     
flag=0;
  
//__raw_readl(lcd_light_degree ,AIPI_IO_ADDRESS(0x1000600C));//sample
  
copy_to_user((void*)buf, &lcd_light_degree,bytes);
  
up(&sem);
  
return bytes;
}
static ssize_t lcd_light_open(struct inode
*inode, struct file *file){

printk("device
open:%d,%d\n",inode->i_rdev>>8,inode->i_rdev&0xFF);

return 0;

}
static ssize_t lcd_light_release(struct
inode *inode, struct file *file){

printk("device
release:%d,%d\n",inode->i_rdev>>8,inode->i_rdev&0xFF);

return 0;
}

//定义操作的数据结构
static struct file_operations
lcd_light_fops =
  {

.owner = THIS_MODULE, .write = lcd_light_write, .read = lcd_light_read,
.open = lcd_light_open, .release = lcd_light_release,
  };

static int LCD_light_init(void)
  {

lcd_light_degree = 10;

major_lcd_light = register_chrdev(0, "lcd_light",
&lcd_light_fops); //自动分配主节点号
  if
(major_lcd_light
    {
   
printk(KERN_INFO "Unable to get a major for lcd light");
   
return major_lcd_light;
    }

else
    {
  //__raw_writel(__raw_readl(AIPI_IO_ADDRESS(0x10015400))|0x00000020,AIPI_IO_ADDRESS(0x10015400));//direction
   
//__raw_writel(__raw_readl(AIPI_IO_ADDRESS(0x10015420))&0xffffffdf,AIPI_IO_ADDRESS(0x10015420));//gpio1
OR multiplex0
   
//__raw_writel(__raw_readl(AIPI_IO_ADDRESS(0x10015438))&0xffffffdf,AIPI_IO_ADDRESS(0x10015438));//0,primary;1,alternate
   
//__raw_writel(0x00030000,AIPI_IO_ADDRESS(0x10006000));//init
   
//__raw_writel(0x0000000D,AIPI_IO_ADDRESS(0x10006010));//period
   
//__raw_writel(lcd_light_degree ,AIPI_IO_ADDRESS(0x1000600C));//sample
   
//__raw_writel(0x00030001,AIPI_IO_ADDRESS(0x10006000));//en
   
init_MUTEX(&sem);
   
init_waitqueue_head(&outq);
   
printk(KERN_ALERT " lcd light major is %d.\n",
major_lcd_light);
   
return 0;
    }
  }
static void LCD_light_exit(void)
  {

unregister_chrdev(major_lcd_light, "lcd_light");

printk(KERN_ALERT "LCD light exit.\n");
  }
module_init(LCD_light_init);
module_exit(LCD_light_exit);
MODULE_AUTHOR("beny");
MODULE_DESCRIPTION("LCD backlight
driver");
MODULE_ALIAS("lcd back light driver
module");
七、一点说明
       可以看到以上的代码中,以下几个定义很重要,也就是信号量、flag、以及队列。
     static
struct semaphore sem;
static wait_queue_head_t outq;
static int flag=0;
     阻塞的实现正是靠队列来完成的。
    把以上的编译通过后,进行驱动模块的装载,然后使用应用程序进行read和write,能够很好的实现阻塞的读。
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/79677/showart_1205987.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP