一个阻塞型的小驱动

rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

前边一篇文章《驱动模块及其用户空间的调用》,在那篇文章里的一个小驱动实现了驱动的最基本的功能,能够在用户空间进行对设备进行数据的读写。这里在那个驱动的基础上继续努力,又添加了一个小功能,就是把驱动的读函数改成了阻塞型的驱动。

关于阻塞型驱动,就是说在驱动访问设备的时候,如果条件不满足则驱动被阻塞,即它不会立即返回,只是把进程挂起,直到条件满足之后,他才进行驱动要完成的任务,之后才返回。非阻塞型驱动,是说驱动访问设备的时候,如果条件不满足则驱动直接返回这个状态,不会在原地等待,用户可以根据返回值进行不同的处理。

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 <linux/kernel.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/device.h>

#include <linux/slab.h>

#include <linux/fs.h>

//#include <linux/config.h>

#include <linux/sched.h>   TASK_INTERRUPTIBLE   declared

#include <asm/semaphore.h>

#include <linux/wait.h>

#include <asm/uaccess.h>

 

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(0x 1000600C ));//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(0x 1000600C ));//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("<1>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("<1>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 < 0)

    {

    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(0x 1000600C ));//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;

     阻塞的实现正是靠队列来完成的。

    把以上的编译通过后,进行驱动模块的装载,然后使用应用程序进行readwrite,能够很好的实现阻塞的读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值