21.字符設備的另一種寫法

1.之前註冊字符設備用的如下函數註冊字符設備驅動:

register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);

  但其實這個函數是linux版本2.4之前的註冊方式,它的原理是:

 (1)確定一個主設備號

 (2)構造一個file_operations結構體, 然後放在chrdevs數組中

 (3)註冊:register_chrdev

  然後當讀寫字符設備的時候,就會根據主設備號從chrdevs數組中取出相應的結構體,並調用相應的處理函數。

  它會有個很大的缺點:

  每註冊個字符設備,還會連續註冊0~255個次設備號,使它們綁定在同一個file_operations操作方法結構體上,在大多數情況下,都只用極少的次設備號,所以會浪費很多資源.

2.所以在2.4版本後,內核裏就加入了以下幾個函數也可以來實現註冊字符設備:

  分爲了靜態註冊(指定設備編號來註冊)、動態分配(不指定設備編號來註冊),以及有連續註冊的次設備編號範圍區間,避免了register_chrdev()浪費資源的缺點

2.1:

/*指定設備編號來靜態註冊一個字符設備*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);   

  from: 註冊的指定起始設備編號,比如:MKDEV(100, 0),表示起始主設備號100, 起始次設備號爲0

  count:需要連續註冊的次設備編號個數,比如: 起始次設備號爲0,count=100,表示0~99的次設備號都要綁定在同一個file_operations操作方法結構體上

  *name:字符設備名稱

  當返回值小於0,表示註冊失敗

2.2:

/*動態分配一個字符設備,註冊成功並將分配到的主次設備號放入*dev裏*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);

  dev: 存放起始設備編號的指針,當註冊成功, *dev就會等於分配到的起始設備編號,可以通過MAJOR()和MINNOR()函數來提取主次設備號

  baseminor:次設備號基地址,也就是起始次設備號

  count:需要連續註冊的次設備編號個數,比如: 起始次設備號(baseminor)爲0,baseminor=2,表示0~1的此設備號都要綁定在同一個file_operations操作方法結構體上

  *name:字符設備名稱

  當返回值小於0,表示註冊失敗

2.3:

 /*初始化cdev結構體,並將file_operations結構體放入cdev-> ops 裏*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops);

  其中cdev結構體的成員,如下所示:

struct cdev {
       struct kobject    kobj;                   // 內嵌的kobject對象 
       struct module   *owner;                   //所屬模塊
       const struct file_operations  *ops;     //操作方法結構體
       struct list_head  list;            //與 cdev 對應的字符設備文件的 inode->i_devices 的鏈表頭
       dev_t dev;                    //起始設備編號,可以通過MAJOR(),MINOR()來提取主次設備號
       unsigned int count;                //連續註冊的次設備號個數
};

2.4:

/*將cdev結構體添加到系統中,並將dev(註冊好的設備編號)放入cdev-> dev裏,  count(次設備編號個數)放入cdev->count裏*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

2.5:

/*將系統中的cdev結構體刪除掉*/
void cdev_del(struct cdev *p);

2.6:

/*註銷字符設備*/
void unregister_chrdev_region(dev_t from, unsigned count);

  from: 註銷的指定起始設備編號,比如:MKDEV(100, 0),表示起始主設備號100, 起始次設備號爲0

  count:需要連續註銷的次設備編號個數,比如: 起始次設備號爲0,baseminor=100,表示註銷掉0~99的次設備號

3.接下來,我們便來寫一個字符設備驅動

  裏面調用兩次上面的函數,構造兩個不同的file_operations操作結構體,

  次設備號0~1對應第一個file_operations,

  次設備號2~3對應第二個file_operations,

  然後在/dev/下,通過次設備號(0~4)創建5個設備節點, 利用應用程序打開這5個文件,看有什麼現象

3.1 驅動代碼如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/list.h>
#include <linux/cdev.h>

static int hello_fops1_open(struct inode *inode, struct file *file)
{
    printk("open_hello1!!!\n");
    return 0;
}

 

static int hello_fops2_open (struct inode *inode, struct file *file)
{
    printk("open_hello2!!!\n");
    return 0;
}

 
  /*  操作結構體1   */
static struct file_operations hello1_fops={
        .owner=THIS_MODULE,
        .open =hello_fops1_open,
};

  /*  操作結構體2   */
static struct file_operations hello2_fops={
        .owner=THIS_MODULE,
        .open =hello_fops2_open,
};

 
static int major;                                 //主設備
static struct cdev hello1_cdev;        //保存 hello1_fops操作結構體的字符設備 
static struct cdev hello2_cdev;         //保存 hello2_fops操作結構體的字符設備 
static struct class *cls;

static int chrdev_ragion_init(void)
{
     dev_t  devid;  

     alloc_chrdev_region(&devid, 0, 4,"hello");    //動態分配字符設備: (major,0)  (major,1)   (major,2)  (major,3)

     major=MAJOR(devid);

     cdev_init(&hello1_cdev, &hello1_fops);
     cdev_add(&hello1_cdev, MKDEV(major,0), 2);           //(major,0) (major,1)


     cdev_init(&hello2_cdev, &hello2_fops);
     cdev_add(&hello2_cdev,MKDEV(major,2), 2);           //(major,2) (major,3)     

     cls=class_create(THIS_MODULE, "hello");
     /*創建字符設備節點*/
     class_device_create(cls,0, MKDEV(major,0), 0, "hello0");   //對應hello_fops1操作結構體
     class_device_create(cls,0, MKDEV(major,1), 0, "hello1");   //對應hello_fops1操作結構體
     class_device_create(cls,0, MKDEV(major,2), 0, "hello2");   //對應hello_fops2操作結構體
     class_device_create(cls,0, MKDEV(major,3), 0, "hello3");   //對應hello_fops2操作結構體
     class_device_create(cls,0, MKDEV(major,4), 0, "hello4");   //對應空
        return 0;
}

void chrdev_ragion_exit(void)
{
   class_device_destroy(cls, MKDEV(major,4));
   class_device_destroy(cls, MKDEV(major,3));
   class_device_destroy(cls, MKDEV(major,2));
   class_device_destroy(cls, MKDEV(major,1));
   class_device_destroy(cls, MKDEV(major,0));

   class_destroy(cls);


   cdev_del(&hello1_cdev);     
   cdev_del(&hello2_cdev); 
   unregister_chrdev_region(MKDEV(major,0), 4);     //註銷(major,0)~(major,3)
} 

module_init(chrdev_ragion_init);
module_exit(chrdev_ragion_exit);
MODULE_LICENSE("GPL");

3.2 測試代碼如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void print_useg(char arg[])    //打印使用幫助信息
{
         printf("useg:  \n");
         printf("%s   [dev]\n",arg);
}

int main(int argc,char **argv)
{

  int fd;
  if(argc!=2)
    {
        print_useg(argv[0]);
        return -1;
    }

  fd=open(argv[1],O_RDWR);
  if(fd<0)
      printf("can't open %s \n",argv[1]);
  else
      printf("can open %s \n",argv[1]);
  return 0;
}
發佈了76 篇原創文章 · 獲贊 1 · 訪問量 1868
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章