轉自:http://www.cnblogs.com/zhuyp1015/archive/2012/05/22/2514008.html bird1015的部落格
在編寫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 產生的可執行檔運行起來就可以操作驅動中的全域變數了。不用像書上一樣還要在命令列去建立裝置節點。