標籤:des os 使用 ar for 檔案 資料 sp art
- 簡介
- 2.6核心的裝置模型提供一個對系統結構的一般性抽象描述,用以支援多種不同的任務
- 電源管理和系統關機
- 與使用者空間通訊
- 熱插拔裝置
- 裝置類型
- 對象生命週期
- kobject、kset和子系統
- kobject是組成裝置模型的基本結構
- 對象的引用計數
- sysfs表述
- 資料結構關聯
- 熱插拔事件處理
- kobject基礎知識
- <linux/kobject.h>
- 嵌入的kobject
- 核心代碼很少去建立一個單獨的kobject對象,kobject用於控制對大型域相關對象的訪問
- kobject的初始化
- 首先將kobject設定為0,通常使用memset
- void kobject_init(struct kobject *kobj);
- int kobject_set_name(struct kobject *kobj, const char *format, …);
- ktype、kset和parent
- 對引用計數的操作
- struct kobject *kobjct_get(struct kobject *kobj);
- void kobject_put(struct kobject *kobj);
- release函數和kobject類型
- void my_object_readse(struct kobject *kobj)
- struct my_object *mine = container_of(kobj, struct my_object, kobj);
- kfree(mine);
- struct kobj_type
- void (*release) (struct kobject *);
- struct sysfs_ops *sysfs_ops;
- struct attribute **default_attrs;
- struct kobj_type *get_ktype(struct kobject *kobj);
- kobject階層、kset和子系統
- 核心用kobject結構將各個對象串連起來組成一個分層的結構體系,有兩種獨立的機制用於串連:parent指標和kset
- 在kobject結構的parent成員中,儲存了另外一個kobject結構的指標,這個結構表示了分層結構中上一層的節點
- kset
- 一個kset是嵌入相同類型結構的kobject的集合
- kset結構關心提對象的聚焦與集合
- 可以認為kset是kobject的頂層容器類
- kset總是在sysfs中出現
- 先把kobject的kset成員指向目的kset,然後使用下面函數添加kobject
- int kobject_add(struct kobject *kobj);
- extern int kobject_register(struct kobject *kobj);
- kobject_init和kobject_add的簡單組合
- void kobject_del(struct kobject *kobj);
- kset在一個標準的核心鏈表中儲存了它的子節點
- kset上的操作
- void kset_init(struct kset *kset);
- int kset_add(struct kset *kset);
- int kset_register(struct kset *kset);
- void kset_unregister(struct kset *kset);
- 子系統
- 子系統通常顯示在sysfs分層結構中的頂層
- 核心中的子系統
- block_subsys(對塊裝置來說是/sys/block)
- devices_subsys(/sys/devices,裝置分層結構的核心)
- 核心所知曉的用於各種匯流排的特定子系統
- decl_subsys(name, struct kobj_type *type, struct kset_hotplug_ops *hotplug_ops);
- void subsysstem_init(struct subsystem *subsys);
- int subsystem_register(struct subsystem *subsys);
- void subsystem_unregister(struct subsystem *subsys);
- struct subsystem *subsys_get(struct subsystem *subsys);
- void subsys_put(struct subsystem *subsys);
- 低層sysfs操作
- kobject是隱藏在sysfs虛擬檔案系統後的機制,對於sysfs中的每個目錄,核心中都會存在一個對應的kobject
- <linux/sysfs.h>
- sysfs入口
- kobject在sysfs中的入口始終是一個目錄
- 分配閥給kobject的名字是sysfs中的目錄名
- sysfs入口在目錄中的位置對應於kobject的parent指標
- 預設屬性
- kobject預設屬性儲存在kobj_type結構中
- default_attrs成員儲存了屬性列表
- sysfs_ops提供了實現這些屬性的方法
- struct attribute
- char *name
- struct module *owner
- mode_t mode
- 唯讀:S_IRUGO
- 可寫:S_IWUSR
- <linux/stat.h>
- struct sysfs_ops
- ssize_t (*show) (struct kobject * kobj, struct attribute *attr, char *buffer);
- ssize_t (*store) (struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size);
- 非預設屬性
- int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
- int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);
- 二進位屬性
- struct bin_attribute
- struct attribute attr;
- size_t size;
- ssize_t (*read) (struct kobject *kobj, char *buffer, loff_t pos, size_t size);
- ssize_t (*write) (struct kobject *kobj, char *buffer, loff_t pos, size_t size);
- int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);
- int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
- 符號連結
- int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);
- void sysfs_remove_link(struct kobject *kobj, char *name);
- 熱插拔事件的產生
- 一個熱插拔事件是從核心空間發送到使用者空間的通知,它表明系統配置出現了變化
- 無論kobject被建立還是被刪除,都會產生這種事件
- 熱插拔操作
- struct kset_hotplug_ops
- int (*filter) (struct kset *kset, struct kobject *kobj);
- char * (*name) (struct kset *kset, struct kobject *kobj);
- int (*hotplug) (struct kset *kset, struct kobject *kobj, char **envp, int num_envp, char *buffer, int buffer_size);
- 當核心要為指定的kobject產生事件時,都要調用filter函數,如果返回0,將不產生事件
- 匯流排、裝置和驅動程式
- 匯流排
- 匯流排是處理器與一個或者多個裝置之間的通道
- <linux/device.h>
- struct bus_type
- char *name;
- struct subsystem subsys;
- struct kset drivers;
- struct kset devices;
- int (*match) (struct device *dev, struct device_driver *drv);
- struct device *(*add) (struct device *parent, char *bus_id);
- int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
- 每個匯流排都有自己的子系統
- 一個匯流排包含兩個kset,分別代表了匯流排的驅動程式和插入匯流排的所有裝置
- 匯流排的註冊
- example
- struct bus_type ldd_bus_type = {.name=”ldd”, .match=ldd_match, .hotplug=ldd_hotplug,};
- ret = bus_register(&ldd_bus_type);
- if (res) return ret;
- void bus_unregister(struct bus_type *bus);
- 匯流排方法
- 當一個匯流排上的新裝置或者新驅動程式被添加時,會一次或多次調用match函數
- 在為使用者空間產生熱插拔事件前,hotplug方法允許匯流排添加環境變數
- 在調用真實的硬體時,match函數通常對裝置提供的硬體ID和驅動所支援的ID做某種類型的比較
- 對裝置和驅動程式的迭代
- int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn) (struct device *, void *));
- int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));
- 匯流排屬性
- <linux/device.h>
- struct bus_attribute
- struct attribute attr;
- ssize_t (*show) (struct bus_type *bus, char *buf);
- ssize_t (*store) (struct bus_type *bus, const char *buf, size_t count);
- BUS_ATTR(name, mode, show, store);
- int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
- void bus_remove_file(struct bus_tyep *bus, struct bus_attribute *attr);
- 裝置
- struct device
- struct device *parent;
- struct kobject kobj;
- char bus_id[BUS_ID_SIZE];
- struct bus_type *bus;
- struct device_driver *driver;
- void *driver_data
- void (*release) (struct device *dev);
- 裝置註冊
- int device_register(struct device *dev);
- void device_unregister(struct device *dev);
- 裝置屬性
- struct device_attribute
- struct attribute attr;
- ssize_t (*show) (struct device *dev, char *buf);
- ssize_t (*store) (struct device *dev, const char *buf, size_t count);
- DEVICE_ATTR(name, mode, show, store);
- int device_create_file(struct device *device, struct device_attribute *entry);
- void device_remove_file(struct device *dev, struct device_attribute *attr);
- 裝置結構的嵌入
- device結構中包含了裝置模型核心用來類比系統的資訊
- 通常,底層驅動程式並不知道device結構
- 裝置驅動程式
- struct device_driver
- char *name;
- struct bus_type *bus;
- struct kobject kobj;
- struct list_head devices;
- int (*probe) (struct device *dev);
- int (*remove) (struct device *dev);
- void (*shutdown) (struct device *dev);
- probe是用來查詢特定裝置是否存在的函數
- 當裝置從系統中刪除的時候要調用remove函數
- 在關機的時候調用shutdown函數關閉裝置
- int driver_register(struct device_driver *drv);
- void driver_unregister(struct device_driver *drv);
- struct driver_attribute
- struct attribute attr;
- ssize_t (*show) (struct device_driver *drv, char *buf);
- ssize_t (*store) (struct device_driver *drv, const char *buf, size_t count);
- int driver_create_file(struct device_driver *drv, struct driver_attribute *attr);
- void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr);
- 驅動程式結構的嵌入
- device_driver結構通常被包含在高層和總結相關的結構中
- 類
- 類是一個裝置的高層視圖,它抽象出了低層的實現細節
- 幾乎所有類都顯示在/sys/class目錄中
- /sys/class/net
- /sys/class/input
- /sys/class/tty
- /sys/block
- class_simple介面
- 建立類本身
- struct class_simple *class_simple_create(struct module *owner, char *name);
- 銷毀一個簡單類
- void class_simple_destroy(struct class_simple *cs);
- 為一個簡單類添加裝置
- struct class_device *class_simple_device_add(struct class_simple *cs, dev_t devnum, struct device *device, const char *fmt, …);
- int class_simple_set_hotplug(struct class_simple *cs, int (*hotplug) (struct class_device *dev, char **envp, int num_envp, crah *buffer, int buffer_size));
- void class_simple_device_remove(dev_t dev);
- 完整的類介面
- 管理類
- struct class
- char *name;
- struct class_attribute *class_attrs;
- struct class_device_attribute *class_dev_attrs;
- int (*hotplug) (struct class_device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
- void (*release) (struct class_device *dev);
- void (*class_release) (struct class *class);
- int class_register(struct class *cls);
- void class_unregister(struct class *cls);
- struct class_attribute
- struct attribute attr;
- ssize_t (*show) (struct class *cls, char *buf);
- ssize (*store) (struct class *cls, const char *buf, size_t count);
- CLASS_ATTR(name, mode, show, store);
- int class_create_file(struct class *cls, const struct class_attribute *attr);
- void class_remove_file(struct class *cls, const struct class_attribute *attr);
- 類裝置
- strict class_device
- struct kobject kobj;
- struct class *class;
- struct device *dev;
- void *class_data;
- char class_id[BUS_ID_SIZE];
- int class_device_register(struct class_device *cd);
- void class_device_unregister(struct class_device *cd);
- int class_device_renmae(struct class_device *cd, char *new_name);
- struct class_device_attribute
- struct attribute attr;
- ssize_t (*show) (struct class_device *cls, char *buf);
- ssize_t (*store) (struct class_device *cls, const char *buf, size_t count);
- CLASS_DEVICE_ATTR(name, mode, show, store);
- int class_deivce_create_file(struct class_device *cls, const struct class_device_attribute *attr);
- void class_device_remove_file(struct class_device *cls, const struct class_device_attribute *attr);
- 類介面
- struct class_interface
- struct class *class;
- int (*add) (struct class_device *cd);
- void (*remove) (struct class_device *cd);
- int class_interface_register(struct class_interface *intf);
- void class_interface_unregister(struct class_interface *intf);
- 各環節的整合
- 添加一個裝置
- PCI子系統聲明了一個bus_type結構,稱為pci_bus_type
- struct bus_type pci_bus_type
- .name = “pci”,
- .match = pci_bus_match,
- .hotplug = pci_hotplug,
- .suspend = pci_device_suspend,
- .resume = pci_device_resume,
- .dev_attrs = pci_dev_attrs,
- 註冊後,將會建立一個sysfs目錄/sys/bus/pci,其中包含了兩個目錄:devices和drivers
- 所有的PCI驅動程式都必須定義一個pci_driver結構變數,這個結構中包含了一個device_driver結構,在註冊PCI驅動程式時,這個結構將被初始化
- drv->driver.name = drv->name;
- drv->driver.bus = &pci_bus_type;
- drv->driver.probe = pci_device_probe;
- drv->driver.remove = pci_device_remove;
- drv->driver.kobj.ktype = &pci_deiver_kobj_type;
- error = driver_register(&drv->driver);
- 當一個PCI裝置被找到時,PCI核心在記憶體中建立一個pci_dev類型的結構變數
- struct pci_dev
- unsigned int devfn;
- unsigned short vendor;
- unsigned short device;
- unsigned short subsystem_vendor;
- unsigned short subsystem_device;
- unsigned int class;
- struct pci_driver *driver;
- struct device dev;
- 初始化時,device結構變數的parent變數被設定為PCI裝置所在的匯流排裝置
- device_register(&dev->dev);
- device_register函數中,驅動程式核心向kobject核心註冊裝置的kobject
- 接著裝置將被添加到與匯流排相關的所有裝置鏈表中,遍曆這個鏈表,並且為每個驅動程式調用該匯流排的match函數,同時指定該裝置
- 如果匹配工作圓滿完成,函數向驅動程式核心返回1,驅動程式核心將device結構中的driver指標指向這個驅動程式,然後調用device_driver結構中指定的probe函數
- 刪除裝置
- 調用pci_remove_bus_device函數
- 該函數做些PCI相關的清理工作,然後使用指向pci_dev中的device結構的指標,調用device_unregister函數
- device_unregister函數中,驅動程式核心只是刪除了從綁定裝置的驅動程式到sysfs檔案的符號連結,從內部裝置鏈表中刪除了該裝置,並且以device結構中的kobject結構指標為參數,調用kobject_del函數
- kobject_del函數刪除了裝置的kobject引用,如果該引用是最後一個,就要調用該PCI裝置的release函數
- 添加驅動程式
- 調用pci_register_driver函數時,一個PCI驅動程式被添加到PCI核心中
- 該函數只是初始化了包含在pci_driver結構中的device_driver結構
- PCI核心用包含在pci_driver結構的device_driver結構指標作為參數,在驅動程式核心內調用driver_register函數
- driver_reigster函數初始化了幾個device_driver中的鎖,然後調用bus_add_driver函數,該函數按以下步驟操作
- 尋找與驅動程式相關的匯流排
- 根據驅動程式的名字以及相關的匯流排,建立驅動程式的sysfs目錄
- 擷取匯流排內部的鎖,接著遍曆所有向匯流排註冊的裝置,為這些裝置調用match函數
- 刪除驅動程式
- 調用pci_unregister_driver函數
- 該函數用包含在pci_driver結構中的device_driver結構作為參數,調用驅動程式核心函數driver_unregister
- driver_unregister函數清除在sysfs樹中屬於驅動程式的sysfs屬性
- 遍曆所有屬於該驅動程式的裝置,並為其調用release函數
- 處理韌體
- 將韌體代碼放入驅動程式會使驅動程式代碼膨脹,使得韌體升級困難,並容易導致許可證問題
- 不要把包含韌體的驅動程式放入核心,或者包含在Linux發行版中
- 核心韌體介面
- <linux/firmware.h>
- int request_firmware(const struct firmware **fw, char *name, struct device *device);
- struct firmware
- void release_firmware(struct firmware *fw);
- int request_firmware_nowait(struct module *module, char *name, struct device *device, void *context, void (*cont)(const struct firmware *fw, void *context));
- 工作原理
- 韌體子系統使用sysfs和熱插拔機制工作
- 調用request_firmware時,在/sys/class/firmware下將建立一個目錄,該目錄使用裝置名稱作為它的目錄名,該目錄包含三個屬性
- loading
- data
- device
- 該屬性是到/sys/devices下相應入口的符號連結
- 一旦sysfs入口被建立,核心將為裝置產生熱插拔事件,傳遞給熱插拔處理常式的環境包括一個FIRMWARE變數,它將設定為提供給request_firmware的名字
- 處理常式定位韌體檔案,使用所提供的屬性把韌體檔案拷貝到核心,如果不能發現韌體檔案,處理常式將設定loading屬性為-1
- 如果在10秒鐘之內不能為韌體的請求提供服務,核心將放棄努力並向驅動程式返回錯誤狀態,這個逾時值可以通過修改sysfs屬性/sys/class/firmware/timeout來改變
- 不能在沒有製造商許可的情況下發行裝置的韌體
《Linux Device Drivers》第十四章 Linux 裝置模型