標籤:
misc子系統
轉載請註明出處:http://blog.csdn.net/wang_zheng_kai
光源器件與系統研究所
個人學習總結
1、在linux系統中什麼是misc?
在研究網路攝影機驅動的時候,發現網路攝影機驅動的路徑為:/driver/misc/jz_cim/檔案目錄下,經過尋找結果如下:
雜項裝置(misc device)
雜項裝置也是嵌入式系統中用得比較多的一種裝置驅動。在 Linux 核心的include/linux目錄下有miscdevice.h檔案,要把自己定義的misc device從裝置定義在這裡。其實是因為這些字元裝置不符合預先確定的字元裝置範疇,所有這些裝置採用主編號10 ,一起歸於misc device,其實misc_register就是用主標號10調用register_chrdev()的,只不過misc是將一些字元裝置存放在misc類中。換句話說,misc裝置其實也就是特殊的字元裝置。
2、linux核心雜項裝置驅動源碼分析
在Linux驅動中把無法歸類的五花八門的裝置定義為混雜裝置(用miscdevice結構體表述)。miscdevice共用一個主裝置號MISC_MAJOR(即10),但次裝置號不同。所有的miscdevice裝置形成了一個鏈表,對裝置訪問時核心根據次裝置號尋找對應的miscdevice裝置,然後調用其file_operations結構中註冊的檔案操作介面進行操作。在核心中用struct miscdevice表示miscdevice裝置,然後調用其file_operations結構中註冊的檔案操作介面進行操作。miscdevice的API實現在drivers/char/misc.c中,misc裝置的初始化,註冊,登出都在這個檔案中。在核心中,misc雜項裝置驅動介面是對一些字元裝置的簡單封裝,他們共用一個主裝置號,有不同的次裝置號,共用一個open調用,其他的操作函數在開啟後運用linux驅動程式的方法重載進行裝載。
我們首先先來看misc裝置的結構體的描述:
代碼位於:android-4.1/kernel/include/linux/miscdevice.h,該檔案中還有所有misc裝置的次裝置號的宏定義。
struct miscdevice { intminor; //次裝置號 const char*name; //裝置的名稱 const structfile_operations *fops; //檔案操作 structlist_head list; //misc_list的鏈表頭 struct device*parent; //父裝置(Linux裝置模型中的東東了,哈哈) struct device*this_device; //當前裝置,是device_create的傳回值,下邊會看到 constchar *nodename; mode_tmode;};
這個結構體是misc裝置基本的結構體,在註冊misc裝置的時候必須要聲明並初始化一個這樣的結構體,但其中一般只需填充name minor fops欄位就可以了。下面就是led驅動程式中初始化miscdevice的代碼:
static struct miscdevice misc = { .minor =MISC_DYNAMIC_MINOR, .name =DEVICE_NAME, .fops =&dev_fops, };
一般的時候在fops不用實現open方法,因為最初的方法misc_ops包含了open方法。其中minor如果填充MISC_DYNAMIC_MINOR,則是動態次裝置號,次裝置號由misc_register動態分配的。
然後來看看misc子系統的初始化函數:
static int __init misc_init(void) { int err; #ifdef CONFIG_PROC_FS proc_create("misc", 0, NULL,&misc_proc_fops); /*建立一個proc入口項*/ #endif misc_class =class_create(THIS_MODULE, "misc"); /*在/sys/class/目錄下建立一個名為misc的類*/ err = PTR_ERR(misc_class); if(IS_ERR(misc_class)) gotofail_remove; err =-EIO; /*註冊裝置,其中裝置的主裝置號為MISC_MAJOR,為10。裝置名稱為misc,misc_fops是操作函數的集合*/ if(register_chrdev(MISC_MAJOR,"misc",&misc_fops)) gotofail_printk; misc_class->devnode= misc_devnode; return0; fail_printk: printk("unable to get major %d for misc devices/n",MISC_MAJOR); class_destroy(misc_class); fail_remove: remove_proc_entry("misc", NULL); returnerr; } subsys_initcall(misc_init); /*misc作為一個子系統被註冊到linux核心中*/
可以看出,這個初始化函數,最主要的功能就是註冊字元裝置 ,所用的註冊介面是2.4核心的register_chrdev。它註冊了主裝置號為MISC_MAJOR,次裝置號為0-255的256個裝置。並且建立了一個misc類。
下邊是register_chrdev函數的實現:
int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops) { structchar_device_struct *cd; struct cdev*cdev; char *s; int err =-ENOMEM; /*主裝置號是10,次裝置號為從0開始,分配256個裝置*/ cd =__register_chrdev_region(major, 0, 256, name); if(IS_ERR(cd)) returnPTR_ERR(cd); /*分配字元裝置*/ cdev =cdev_alloc(); if(!cdev) gotoout2; cdev->owner = fops->owner; cdev->ops= fops; /*Linux裝置模型中的,設定kobject的名字*/ kobject_set_name(&cdev->kobj, "%s", name); for (s =strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/')) *s ='!'; /*把這個字元裝置註冊到系統中*/ err =cdev_add(cdev, MKDEV(cd->major, 0), 256); if (err) gotoout; cd->cdev =cdev; return major? 0 : cd->major; out: kobject_put(&cdev->kobj); out2: kfree(__unregister_chrdev_region(cd->major, 0, 256)); returnerr; } 來看看這個裝置的操作函數的集合:static const struct file_operations misc_fops = { .owner =THIS_MODULE, .open = misc_open, };
可以看到這裡只有一個開啟函數,使用者開啟miscdevice裝置是通過主裝置號對應的開啟函數,在這個函數中找到次裝置號對應的相應的具體裝置的open函數。它的實現如下:
static int misc_open(struct inode * inode,struct file * file) { intminor = iminor(inode); structmiscdevice *c; interr = -ENODEV; const struct file_operations *old_fops, *new_fops = NULL; lock_kernel(); mutex_lock(&misc_mtx); /*找到次裝置號對應的操作函數集合,讓new_fops指向這個具體裝置的操作函數集合*/ list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { new_fops = fops_get(c->fops); break; } } if(!new_fops) { mutex_unlock(&misc_mtx); /*如果沒有找到,則請求載入這個次裝置號對應的模組*/ request_module("char-major-%d-%d", MISC_MAJOR, minor); mutex_lock(&misc_mtx); /*重新遍曆misc_list鏈表,如果沒有找到就退出,否則讓new_fops指向這個具體裝置的操作函數集合*/ list_for_each_entry(c, &misc_list, list) { if (c->minor == minor) { new_fops =fops_get(c->fops); break; } } if (!new_fops) goto fail; } err= 0; /*儲存舊開啟函數的地址*/ old_fops = file->f_op; /*讓主裝置號的操作函數集合指標指向具體裝置的操作函數集合*/ file->f_op = new_fops; if(file->f_op->open) { /*使用具體裝置的開啟函數開啟裝置*/ err=file->f_op->open(inode,file); if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } } fops_put(old_fops); fail: mutex_unlock(&misc_mtx); unlock_kernel(); return err; }
再來看看misc子系統對外提供的兩個重要的API,misc_register,misc_deregister:
misc_register()函數在misc.c中,最主要的功能是基於misc_class構造一個裝置,將miscdevice結構掛載到misc_list列表上,並初始化與linux裝置模型相關的結構,它的參數是miscdevice結構體。
int misc_register(struct miscdevice *misc) { struct miscdevice *c; dev_t dev; interr = 0; INIT_LIST_HEAD(&misc->list); //鏈表項使用時必須初始化 mutex_lock(&misc_mtx); /*遍曆misc_list鏈表,看這個次裝置號以前有沒有被用過,如果次裝置號已被佔有則退出*/ list_for_each_entry(c, &misc_list, list) { if (c->minor == misc->minor) { mutex_unlock(&misc_mtx); return -EBUSY; } } /*看是否是需要動態分配次裝置號*/ if(misc->minor == MISC_DYNAMIC_MINOR) { /* *#define DYNAMIC_MINORS 64 /* like dynamic majors */ *static unsigned char misc_minors[DYNAMIC_MINORS / 8]; *這裡存在一個次裝置號的位元影像,一共64位。下邊是遍曆每一位, *如果這位為0,表示沒有被佔有,可以使用,為1表示被佔用。 */ int i = DYNAMIC_MINORS; while (--i >= 0) if ( (misc_minors[i>>3] & (1 << (i&7))) == 0) break; if (i<0) { mutex_unlock(&misc_mtx); return -EBUSY; } /*得到這個次裝置號*/ misc->minor = i; } /*設定位元影像中相應位為1*/ if(misc->minor < DYNAMIC_MINORS) misc_minors[misc->minor >> 3] |= 1 << (misc->minor& 7); /*計算出裝置號*/ dev= MKDEV(MISC_MAJOR, misc->minor); /*在/dev下建立裝置節點,這就是有些驅動程式沒有顯式調用device_create,卻出現了裝置節點的原因*/ misc->this_device = device_create(misc_class, misc->parent, dev,NULL, "%s",misc->name); if(IS_ERR(misc->this_device)) { err = PTR_ERR(misc->this_device); goto out; } /* *Add it to the front, so that later devices can "override" *earlier defaults */ /*將這個miscdevice添加到misc_list鏈表中*/ list_add(&misc->list, &misc_list); out: mutex_unlock(&misc_mtx); return err; }
可以看出,這個函數首先遍曆misc_list鏈表,尋找所用的次裝置號是否已經被註冊,防止衝突。如果是動態次裝置號則分配一個,然後調用MKDEV產生裝置號,從這裡可以看出所有的misc裝置共用一個主裝置號MISC_MAJOR,然後調用device_create,產生裝置檔案。最後加入到misc_list鏈表中。
關於device_create,class_create 作用: class_create函數在misc.c中的模組初始化中被調用,現在一起說一下。這兩個函數看起來很陌生,沒有在ldd3中發現過,看原始碼的時候發現class_create會調用底層組件__class_regsiter()是說明它是註冊一個類。而device_create是建立一個裝置,他是建立裝置的便捷實現調用了device_register函數。他們都提供給linux裝置模型使用,從linux核心2.6的某個版本之後,devfs不複存在,udev成為devfs的替代。相比devfs,udev有很多優勢。
struct class *myclass =class_create(THIS_MODULE, “my_device_driver”);
class_device_create(myclass, NULL,MKDEV(major_num, 0), NULL, “my_device”);
這樣就建立了一個類和裝置,模組被載入時,udev daemon就會自動在/dev下建立my_device裝置檔案節點。這樣就省去了自己建立裝置檔案的麻煩。這樣也有助於動態裝置的管理.
這個是miscdevice的卸載函數:
int misc_deregister(struct miscdevice*misc) { inti = misc->minor; if(list_empty(&misc->list)) return -EINVAL; mutex_lock(&misc_mtx); /*在misc_list鏈表中刪除miscdevice裝置*/ list_del(&misc->list); /*刪除裝置節點*/ device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); if(i < DYNAMIC_MINORS && i>0) { /*釋放位元影像相應位*/ misc_minors[i>>3] &= ~(1 << (misc->minor &7)); } mutex_unlock(&misc_mtx); return 0; }
總結一下miscdevice驅動的註冊和卸載流程:
misc_register:匹配次裝置號->找到一個沒有佔用的次裝置號(如果需要動態分配的話)->計算裝置號->建立裝置檔案->miscdevice結構體添加到misc_list鏈表中。
misc_deregister:從mist_list中刪除miscdevice->刪除裝置檔案->位元影像位清零。
總結:
雜項裝置作為字元裝置的封裝,為字元裝置提供的簡單的編程介面,如果編寫新的字元驅動,可以考慮使用雜項裝置介面,方便簡單,只需要初始化一個miscdevice的結構,調用misc_register就可以了。系統最多有255個雜項裝置,因為雜項裝置模組自己佔用了一個次裝置號
linux系統中misc子系統