Linux字符驅動中動態分配設備號與動態生成設備節點


    在編寫Linux內核驅動程序的時候,如果不動態生成設備號的話,需要自己手動分配設備號,有可能你分配的設備號會與已有設備號相同而產生沖突。因此推薦自動分配設備號。使用下面的函數:

int alloc_chrdev_region(dev_t *dev,  unsigned baseminor,  unsigned count,  const char *name)

該函數需要傳遞給它指定的第一個次設備號baseminor(一般為0)和要分配的設備數count,以及設備名,調用該函數后自動分配得到的設備號保存在dev中。

當使用了alloc_chrdev_region()動態分配設備號之后,需要依次使用:

 

cdev_init(struct cdev * cdev,const struct file_operations * fops)

cdev_add(struct cdev * p,dev_t dev,unsigned count)

將字符設備注冊到內核中。通過上面三個函數就可以動態生成設備號了。

在卸載的時候需要使用:unregister_chrdev_region(dev_t from,unsigned count) 來釋放設備編號

 

動態創建設備號之后,將驅動加載到內核,通過 : cat /proc/devices   命令可以查看設備號

 

如果上層應用程序需要訪問驅動程序,則需要為該驅動創建設備節點。

如果手動創建設備結點需要這樣做:(這里假設通過 cat /proc/devices 發現字符設備 CDEV_ZHU的設備號為 254)

$mknod  /dev/CDEV_ZHU c 254 0

 

如果我們在驅動里面動態創建的話需要這樣做:

cdev_class = class_create(owner,name)         // cdev_class 為 struct class 類型

然后使用:

device_create(_cls,_parent,_devt,_device,_fmt)

當動態創建了設備節點之后,在卸載的時候需要使用:

device_destroy(_cls,_device)  class_destroy(struct class * cls)

來銷毀設備和類。

下面給出一組測試代碼:(該組代碼實現了應用程序通過打開驅動訪問和修改驅動的一個全局變量 “global_var”)

/*驅動部分:globalvar.c */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/device.h>  //下面這三個頭文件是由於動態創建需要加的
#include <linux/device.h>
#include <linux/cdev.h>

 
MODULE_LICENSE("GPL");
 
#define DEVICE_NAME  "CDEV_ZHU"
static struct class *cdev_class;
 
static ssize_t globalvar_read(struct file *, char *, size_t, loff_t*);
static ssize_t globalvar_write(struct file *, const char *, size_t, loff_t*);
 
//初始化字符設備驅動的 file_operations 結構體
struct file_operations globalvar_fops = 
{
    read: globalvar_read,
    write: globalvar_write,
};

static int global_var = 0;      //CDEV_ZHU設備的全局變量

dev_t dev = 0;                 //這里是動態分配設備號和動態創建設備結點需要用到的
struct cdev  dev_c;
 
static int __init globalvar_init(void)
{
    int ret,err;
 
    //注冊設備驅動
 
    ret = alloc_chrdev_region(&dev, 0, 1,DEVICE_NAME); //動態分配設備號
    if (ret)
    {
        printk("globalvar register failure\n"); 
    unregister_chrdev_region(dev,1);
    return ret;
    }
    else
    {
        printk("globalvar register success\n");
    }

   cdev_init(&dev_c, &globalvar_fops);
 
   err = cdev_add(&dev_c, dev, 1);

   if(err)
   {
    printk(KERN_NOTICE "error %d adding FC_dev\n",err);
    unregister_chrdev_region(dev, 1);
    return err;
   }
 
 cdev_class = class_create(THIS_MODULE, DEVICE_NAME);//動態創建設備結點
 if(IS_ERR(cdev_class))
 { 
        printk("ERR:cannot create a cdev_class\n");  
    unregister_chrdev_region(dev, 1);
    return -1;
    }
 device_create(cdev_class,NULL, dev, 0, DEVICE_NAME);
 
    return ret;
}
 
static void __exit globalvar_exit(void)
{
 
    //注銷設備驅動 
    
 device_destroy(cdev_class, dev);
 class_destroy(cdev_class);
 unregister_chrdev_region(dev,1);
 printk("globalvar_exit \n");
}
 
static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
    //將 global_var 從內核空間復制到用戶空間
    if(copy_to_user(buf, &global_var, sizeof(int)))
    {
        return    - EFAULT;    
    }  
    return sizeof(int);
}
 
static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
    //將用戶空間的數據復制到內核空間的 global_var
    if(copy_from_user(&global_var, buf, sizeof(int)))
    {
        return    - EFAULT;
    }  
    return sizeof(int);
}
 
module_init(globalvar_init);
module_exit(globalvar_exit);

/*應用程序: globalvartest.c  */

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
int main()
{
    int fd, num;
    //打開"/dev/CDEV_ZHU"
    fd = open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR | S_IWUSR);
    if (fd != -1 )
    {
      //初次讀 global_var
        read(fd, &num, sizeof(int));
        printf("The globalvar is %d\n", num);
 
      //寫 global_var
        printf("Please input the num written to globalvar\n");
        scanf("%d", &num);
        write(fd, &num, sizeof(int));
 
      //再次讀 global_var
        read(fd, &num, sizeof(int));
        printf("The globalvar is %d\n", num);
 
        //關閉“/dev/CDEV_ZHU”
        close(fd);
    }
    else
    {
        printf("Device open failure\n");
    }

    return 0;
}

說明:這個程序是我修改了“深入淺出Linux設備編程”這本書的代碼的來的,在項目中使用動態創建設備節點和動態生成設備號比較方便,於是就在這里分享了。

使用一個簡單的makefile將(驅動) globalvar.c  編譯過后 使用 insmod globalvar.ko 將驅動加載到內核,然后就將globalvartest.c 生成的可執行文件運行起來就可以操作驅動中的全局變量了。不用像書上一樣還要在命令行去創建設備節點。

我使用的內核版本是2.6.33.4 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM