国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

跟著內核學框架-從misc子系統到3+2+1設備識別驅動框架

2019-11-08 02:53:08
字體:
來源:轉載
供稿:網友

http://www.cnblogs.com/xiaojiang1025/p/6413017.html

misc子系統在linux中是一個非常簡單的子系統,但是其清晰的框架結構非常適合用來研究設備識別模型。本文從misc子系統的使用出發,通過了解其機制來總結一套的設備識別的驅動框架,即使用使用同一個驅動,向上提供多個設備文件接口,向下控制多個(相應的)設備,這就需要該驅動可以根據不同的設備文件來控制與之相應的設備。

misc的使用

Linux 中有三大類設備:字符,網絡,塊設備,每一種設備又細分為很多類,比如字符設備就被預先分為很多種類,并在文件中標記了這些種類都使用了哪個主設備號,但即便如此,硬件千千萬,總還是有漏網之魚,對于這些難以劃分類別的字符設備,Linux中使用"混雜",設備來統一描述,并分配給他們一個共同的主設備號10,只用此設備號進行區分設備,,這些設備主要包括隨機數發生器,LCD,時鐘發生器等。此外,和很多同樣是對cdev進行再次封裝的子系統一樣,misc也會自動創建設備文件,免得每次寫cdev接口都要使用class_create()和device_create()等。

內核中提供的misc對象:

//include/linux/miscdevice.h 55 struct miscdevice  {     56         int minor; 57         const char *name; 58         const struct file_Operations *fops; 59         struct list_head list; 60         struct device *parent; 61         struct device *this_device; 62         const char *nodename; 63         umode_t mode; 64 };

我們只要像字符設備一樣實現fops接口再給一個minor即可,如果minor使用宏MISC_DYNAMIC_MINOR(其實就是255),內核會自動分配一個次設備號,其他的內核已經實現約定好的次設備號可以參考"include/linux/miscdevice.h"。萬事具備之后只需使用下面的API注冊/注銷到內核

178 int misc_register(struct miscdevice * misc)238 int misc_deregister(struct miscdevice *misc)

misc的分析

misc的使用是不是很簡單?但麻雀雖小五臟俱全,正是因為misc精簡的結構,我們可以很容易的抓到其中體現的分層思想,misc的設計方法體現在很多使用cdev作為接口的子系統,而其中的清晰的分層思想更是Linux驅動的兩大支柱之一(另外一個是分離)。我們可以借鑒其中的設計思路,提升我們的驅動程序的質量。下面,我們簡單的分析一下misc的內部機制。

misc_init

作為Linux的一個子系統,misc子系統在Linux啟動過程中就會完成準備工作,主要包括初始化數據結構,創建相應的class,創建、初始化并注冊cdev對象到內核等。有了這些基礎,我們就可以使用misc的眾多好處進行編程。

//drivers/char/misc.c56 static const struct file_operations misc_fops = {157         .owner          = THIS_MODULE,158         .open           = misc_open,159         .llseek         = noop_llseek,160 };268 static int __init misc_init(void)269 {272 #ifdef CONFIG_PROC_FS273         proc_create("misc", 0, NULL, &misc_proc_fops);274 #endif275         misc_class = class_create(THIS_MODULE, "misc");281         if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))282                 goto fail_printk;283         misc_class->devnode = misc_devnode;284         return 0;292 }293 subsys_initcall(misc_init);   

misc_init()--293-->系統啟動的過程中就會初始化misc子系統--273-->根據系統配置,可能需要提供/proc接口--275-->在/sysfs中創建一個類,名為misc--281-->使用靜態主設備號(10)、封裝好的方法集misc_fops,register_chrdev()內部會創建一個cdev對象并使用這兩個參數將其初始化并注冊到內核,這個cdev對象將負責所有的混雜設備的設備號。關于cdev對象和設備號之間的關系參見cdev_map。--158-->misc的cdev對象使用的fops,顯然,至此和普通字符設備的調用過程一樣,chrdev_open()->misc_open()。

misc_register

接下來,老規矩,我們從"XXX_register"開始分析,在Linux內核中,這些"XXX_register"往往就是一個設備對象注冊到內核的接口,是研究當相應對象注冊進去之后內核動作的最佳入口。

178 int misc_register(struct miscdevice * misc)179 {  180         dev_t dev;187         if (misc->minor == MISC_DYNAMIC_MINOR) {188                 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);193                 misc->minor = DYNAMIC_MINORS - i - 1;194                 set_bit(i, misc_minors);195         } 206         dev = MKDEV(MISC_MAJOR, misc->minor);208         misc->this_device = device_create(misc_class, misc->parent, dev,209                                           misc, "%s", misc->name);210         if (IS_ERR(misc->this_device)) {211                 int i = DYNAMIC_MINORS - misc->minor - 1;212                 if (i < DYNAMIC_MINORS && i >= 0)213                         clear_bit(i, misc_minors);214                 err = PTR_ERR(misc->this_device);216         }222         list_add(&misc->list, &misc_list);226 }

misc_register()--187--> 如果指定的minor是動態分配,那么進入相關語句塊。--188--> 使dev用位圖遍歷API-find_first_zero_bit找到最小未用的設備號。--193--> 得到分配好的次設備號。--208--> (根據設備號)創建設備文件,使用的是misc_init中創建的misc_class,至此就可以實現misc設備文件的自動創建。就相當與我們在純粹的cdev驅動中使用class_create()+device_create()創建設備文件。一個設備文件和一個設備號相聯系,而misc的所有的設備號都和misc_init創建的cdev對象相聯系,所以打開的任何一個misc設備文件首先回調的就是(chrdev_open()->)misc_open()。--222--> 關鍵,將這個新分配的misc加入到misc鏈表中,用于管理所有的misc設備,便于misc_open()提取具體設備的fops。

misc_open

構建的misc子系統,將設備添加到了該子系統中,接下來我們來看一下應用層程序是如何打開一個misc設備的。由于misc也是一種字符設備,所以其提供的接口也是位于/dev中。但是正如misc的定義,其中的設備五花八門卻共用同一個主設備號,這就意味著最終被chrdev_open回調的misc_open一定要具備根據被打開的不同文件為file結構準備不同的操作方法這一能力,即在驅動中實現對子設備的識別,或者稱之為"多態"。

112 static int misc_open(struct inode * inode, struct file * file)113 {114         int minor = iminor(inode);115         struct miscdevice *c;116         int err = -ENODEV;117         const struct file_operations *new_fops = NULL;121         list_for_each_entry(c, &misc_list, list) {122                 if (c->minor == minor) {123                         new_fops = fops_get(c->fops);           124                         break;125                 }126         }144         replace_fops(file, new_fops);145         if (file->f_op->open) {146                 file->private_data = c;147                 err = file->f_op->open(inode,file);148         }152 }

misc_open()--121-->遍歷misc設備鏈表,根據被打開的設備的次設備號找到設備對象。--123-->存儲這個設備對象的操作方法集unique_fops。--144-->將misc設備具體的操作方法集unique_fops替換到filp中的f_op中,這個位置原來是misc的cdev對象的fops,filp帶著這個unique_fops從open()返回,就實現了不同的設備對應不同的操作方法,即面向對象的"多態"

3+2+1多設備識別驅動模型

通過上述對misc機制的分析,我們不難總結出一個支持設備識別的3+2+1驅動模型(3個函數+2個數據結構+1個封裝):

初始化整個驅動組的xxx_init(),通常用模塊加載函數或總線的probe函數實現;用于注冊一個子驅動的xxx_register(),需要EXPORT到符號表;能夠根據傳入的inode識別具體的設備并將其操作方法集放到filp中的xxx_open()。

+

用于存儲每一個驅動對象的通用鏈表或數組+priv_data用于存儲子設備號的位圖。

+

將所有的不同的設備用一個統一的結構進行封裝

至此,我們就可以寫一寫這個3+2+1驅動模型的模板

1個封裝

struct multidevice{    struct list_head head;    int minor;    struct file_operations* fops;    void *priv;     //私有數據,供read/write等接口識別的信息,以及其他數據都放這里};

2個數據結構

struct list_head multi_dev_list;unsigned int minors_map;   //根據設備號數目的不同選數據類型or數組

3個函數

int major,baseminor = 0,max_dev = sizeof(minors_map)*8;#define DEV_NAME "multi_device"struct class *cls;xxx_open(struct inode *inode,struct file *file){    int minor = iminor(inode);         struct multidevice *dp;         const struct file_operations *new_fops = NULL;                  list_for_each_entry(dp, &multi_dev_list, head) {                     if (dp->minor == minor) {                         new_fops = fops_get(dp->fops);                                    break;                 }         }         replace_fops(file, new_fops);         if (file->f_op->open) {                 file->private_data = dp                 file->f_op->open(inode,file);         }}xxx_init(void){    dev_t devno,    INIT_LIST_HEAD(&multi_dev_list);    init_map(&minors_map);    struct cdev *multi_cdev = cdev_alloc();    cdev_init(multi_cdev, multi_fops);    alloc_chrdev_region(&devno, baseminor, count,DEV_NAME);    major = MAJOR(devno);    cdev_add(multi_cdev , devno, count);    cls = class_create(THIS_MODULE, DEV_NAME);}/*---------------下面是給待加驅動用的----------------------*/xxx_register(struct *multidevice dev){            dev_t dev;         if (dev->minor == MISC_DYNAMIC_MINOR) {                 int i = find_first_zero_bit(minors_map, DYNAMIC_MINORS);                 dev->minor = DYNAMIC_MINORS - i - 1;                 set_bit(i, minors_map);         }          dev_t pri_devno = MKDEV(major, dev->minor);         device_create(multi_class, NULL, pri_devno, "%s", misc->name);         list_add(dev->head, &multi_dev_list);}EXPORT_SYMBOL(xxx_register)




發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 财经| 墨玉县| 安多县| 瓦房店市| 霍城县| 安溪县| 皋兰县| 工布江达县| 沾益县| 民勤县| 新源县| 车险| 宣城市| 伊宁市| 滁州市| 望城县| 凭祥市| 东海县| 绥滨县| 偏关县| 常宁市| 宜城市| 剑阁县| 上栗县| 汉阴县| 乌拉特后旗| 壤塘县| 阿图什市| 宾川县| 西城区| 四子王旗| 即墨市| 织金县| 逊克县| 竹山县| 宣城市| 镇巴县| 南雄市| 龙泉市| 大理市| 金山区|