一個阻塞型的小驅動

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

     阻塞的實現正是靠隊列來完成的。

    把以上的編譯通過後,進行驅動模塊的裝載,然後使用應用程序進行readwrite,能夠很好的實現阻塞的讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章