在前面已經提到裝置号有主裝置号和次裝置号,其中主裝置号表示裝置類 型,對應于确定的驅動程式,具備相同主裝置号的裝置之間共用同一個驅動程 序,而用次裝置号來辨別具體實體裝置。是以在建立字元裝置之前,必須先獲 得裝置的編号(可能需要配置設定多個裝置号)。
在 Linux 2.6 的版本中,用 dev_t 類型來描述裝置号(dev_t 是 32 位數值類型,其 中高 12 位表示主裝置号,低 20 位表示次裝置号)。用兩個宏 MAJOR 和 MINOR 分别 獲得 dev_t 裝置号的主裝置号和次裝置号,而且用 MKDEV 宏來實作逆過程,即組合 主裝置号和次裝置号而獲得 dev_t 類型裝置号。
配置設定裝置号有靜态和動态的兩種方法。靜态配置設定(register_chrdev_region()函數)是 指在事先知道裝置主裝置号的情況下,通過參數函數指定第一個裝置号(它的次裝置 号通常為 0)而向系統申請配置設定一定數目的裝置号。動态配置設定(alloc_chrdev_region()) 是指通過參數僅設定第一個次裝置号(通常為 0,事先不會知道主裝置号)和要配置設定 的裝置數目而系統動态配置設定所需的裝置号。通過 unregister_chrdev_region()函數釋放已配置設定的(無論是靜态的還是動态的)設 備号。
在Linux 2.6核心中的字元裝置用cdev結構來描述,其定義如下:
struct cdev
{
struct kobject kobj;
struct module *owner; //所屬子產品
const struct file_operations *ops; //檔案操作結構
struct list_head list;
dev_t dev; //裝置号,int 類型,高12位為主裝置号,低20位為次裝置号
unsigned int count;
};
下面一組函數用來對cdev結構進行操作:
struct cdev *cdev_alloc(void);//配置設定一個cdev
void cdev_init(struct cdev *, const struct file_operations *);//初始化cdev的file_operation
void cdev_put(struct cdev *p);// //減少使用計數
//注冊裝置,通常發生在驅動子產品的加載函數中
int cdev_add(struct cdev *, dev_t, unsigned);
//登出裝置,通常發生在驅動子產品的解除安裝函數中
void cdev_del(struct cdev *);
使用cdev_add注冊字元裝置前應該先調用register_chrdev_region或alloc_chrdev_region配置設定裝置号。register_chrdev_region函數用于指定裝置号的情況,alloc_chrdev_region函數用于動态申請裝置号,系統自動傳回沒有占用的裝置号。
int register_chrdev_region(dev_t from, unsigned count, const char *name) ;
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name);
函數傳入值 | first:要配置設定的裝置号的初始值 count:要配置設定(釋放)的裝置号數目 name:要申請裝置号的裝置名稱(在/proc/devices 和 sysfs 中顯示) dev:動态配置設定的第一個裝置号 |
函數傳回值
成功:0(隻限于兩種注冊函數)
出錯: 1(隻限于兩種注冊函數)
alloc_chrdev_region申請一個動态主裝置号,并申請一系列次裝置号。baseminor為起始次裝置号,count為次裝置号的數量。登出裝置号(cdev_del)後使用unregister_chrdev_region:
- void unregister_chrdev_region(dev_t from,unsigned count) ;
這裡講解 2.6 核心中的字元裝置的注冊和登出過程。
在 Linux 核心中使用 struct cdev 結構來描述字元裝置,我們在驅動程式中必 須将已配置設定到的裝置号以及裝置操作接口(即為 struct file_operations 結構)賦予 struct cdev 結構變量。首先使用 cdev_alloc()函數向系統申請配置設定 struct cdev 結構, 再用 cdev_init()函數初始化已配置設定到的結構并與 file_operations 結構關聯起來。最 後調用 cdev_add()函數将裝置号與 struct cdev 結構進行關聯并向核心正式報告新 裝置的注冊,這樣新裝置可以被用起來了。
如果要從系統中删除一個裝置,則要調用 cdev_del()函數。
例1.4 cdev_add注冊字元裝置執行個體
代碼見CD光牒\src\1drivermodel\1-4cdev。核心代碼如下所示:
- struct file_operations simple_fops = {
- .owner = THIS_MODULE,
- .read = simple_read,
- .write = simple_write,
- .open = simple_open,
- .release = simple_release,
- };
- /*******************************************************
- MODULE ROUTINE
- *******************************************************/
- void simple_cleanup_module(void)
- {
- dev_t devno = MKDEV(simple_MAJOR, simple_MINOR);
- if (simple_devices)
- {
- cdev_del(&simple_devices->cdev);
- kfree(simple_devices);
- }
- unregister_chrdev_region(devno,1);
- }
- //子產品初始化
- int simple_init_module(void)
- {
- int result;
- dev_t dev = 0;
- dev = MKDEV(simple_MAJOR, simple_MINOR);
- result = register_chrdev_region(dev, 1, "DEMO");//申請裝置号
- if (result < 0)
- {
- printk(KERN_WARNING "DEMO: can't get major %d\n", simple_MAJOR);
- return result;
- }
- simple_devices = kmalloc(sizeof(struct simple_dev), GFP_KERNEL);
- if (!simple_devices)
- {
- result = -ENOMEM;
- goto fail;
- }
- memset(simple_devices, 0, sizeof(struct simple_dev));
- //初始化裝置結構
- cdev_init(&simple_devices->cdev, &simple_fops);
- simple_devices->cdev.owner = THIS_MODULE;
- simple_devices->cdev.ops = &simple_fops;
- result = cdev_add (&simple_devices->cdev, dev, 1);//添加字元裝置
- if(result)
- {
- printk(KERN_NOTICE "Error %d adding DEMO\n", result);
- goto fail;
- }
- return 0;
- fail:
- simple_cleanup_module();
- return result;
- }
- module_init(simple_init_module);
- module_exit(simple_cleanup_module);
本例的應用層代碼與運作結果同上例。