參考文章:http://www.cnblogs.com/hnrainll/archive/2011/06/10/2077435.html
mdev是busybox自帶的一個簡化版udev,作用是在系統啓動與熱插拔或動態加載驅動時候,自動產生節點文件。
執行mdev -s命令,mdev掃描/sys/block和sys/class兩個目錄下的dev屬性文件,獲取設備編號,並以包含該dev屬性文件的目錄名稱作爲設備名device_name,在dev目錄下創建相應的設備文件。
例如:cat /sys/class/tty/tty0/dev會得到4:0, subsystem爲tty,device_name爲tty0。
當mdev因爲熱插拔(uevent/hotplug)事件被調用,通過uevent時間傳遞的環境變量獲取引起該事件的設備action以及該設備路徑device path。然後判斷該action是什麼:
如果action是add,即有新設備加入系統,mdev通過device path的dev屬性文件獲取設備編號,以device path最後一個目錄作爲設備名,在dev目錄下創建設備文件。
如果action是remove,則設備已從系統移除,刪除對應設備文件。
由上可知:當設備加入或移除,mdev創建/刪除設備文件,必須做到以下三點:
1、在/sys/class的某個subsystem目錄下
2、創建以設備名device name作爲名稱的目錄
3、該目錄下包含dev屬性文件,該文件以"major:minor\n"形式輸出設備編號
linux設備驅動模型兩個重要的數據結構 class 和 class_device。(linux 2.6.24.7)
1、class
class代表一類設備,所有class都屬於class_subsys,即出現在/sys/class目錄下,出了塊設備可能出現在/sys/block或/sys/class/block。
/* class結構體 */ structclass{
constchar * name; /* class的名稱 */
struct module * owner; /* 擁有該class的模塊 */
struct kset subsys; /* 該class對應的子系統 */
struct list_head children; /* 該class的class_device列表 */
struct list_head devices;
struct list_head interfaces;
struct kset class_dirs;
struct semaphore sem; /* locks both the children and interfaces lists */
struct class_attribute * class_attrs;/* 該class的默認屬性,以NULL結尾 */
struct class_device_attribute * class_dev_attrs;/* 添加到class的class_device所擁有的默認屬性 */
struct device_attribute * dev_attrs;
/* 該函數提供在產生熱插拔class_device事件時,添加環境變量的能力 */
int (*uevent)(struct class_device *dev,struct kobj_uevent_env *env);
/* 該函數提供在產生熱插拔device(物理設備)事件時,添加環境變量的能力 */
int (*dev_uevent)(struct device *dev,struct kobj_uevent_env *env);
/* 添加到class的class_device移除時,調用該函數進行必要的清理工作 */
void(*release)(struct class_device *dev);
/* class被移除時,調用該函數進行必要的清理工作 */
void(*class_release)(structclass*class);
void(*dev_release)(struct device *dev);
int (*suspend)(struct device *, pm_message_t state);
int (*resume)(struct device *);
};
/* class註冊函數 */
int __must_check class_register(structclass*);
void class_unregister(structclass*); 建立一個class有兩種方法
a、根據需要,填充一個struct class,然後再調用class_register註冊該class就ok了(此法比較靈活,可以自己定製很多東西)
b、就是通過下面的class_create來創建一個class,該函數會返回一個指向剛建立的class的指針(創建class的最簡單方法)
/* class_create用於創建一個名爲name的class,其owner參數一般爲THIS_MODULE。class_create內部調用了class_register */
structclass*class_create(struct module *owner,constchar*name);
/* class_destroy用於刪除一個class,實際上其內部只是簡單調用了class_unregister(cls)來註銷cls */
void class_destroy(structclass*cls); 一個class屬性對應於/sys/class/class.name(class.name就是該class的名稱)目錄裏的一個文件。通過這些文件, 可以向用戶空間輸出一些關於該class的信息,也可從用戶空間獲取到一些信息。
/* class屬性結構體 */
struct class_attribute {
struct attribute attr;
/* 當用戶空間讀取該屬性時,調用show函數輸出一個"屬性值"給用戶空間 */
ssize_t (*show)(structclass*,char* buf);
/* 當用戶空間寫該屬性時,調用store函數保存用戶寫入的"屬性值" */
ssize_t (*store)(structclass*,constchar* buf,size_tcount);
};
struct attribute {
constchar * name;
struct module * owner;
mode_t mode;
};
/* CLASS_ATTR可以在編譯時創建一個class屬性,該屬性的名稱爲class_attr_name */
#define CLASS_ATTR(_name,_mode,_show,_store) \
struct class_attribute class_attr_##_name = __ATTR(_name,_mode,_show,_store)
/* class_create_file與class_remove_file用於創建與刪除class默認屬性外的屬性 */
int __must_check class_create_file(structclass*,
conststruct class_attribute *);
void class_remove_file(structclass*,conststruct class_attribute *);
2、class_device
class_device對應一個具體設備。
每個class對象包括一個class_device鏈表,每個class_device對象表示一個邏輯設備並通過struct class_device中的dev成員(一個指向struct_device的指針)關聯一個物理設備。
一個邏輯設備總是對應一個物理設備,而一個物理設備可以對應多個邏輯設備。
/* class_device結構體 */ struct class_device {
struct list_head node; /* 僅供驅動核心內部使用 */
struct kobject kobj; /* 該class_device相應的kobject,僅供驅動核心內部使用 */
structclass *class; /* 該class_device所屬的class,必須有 */
dev_t devt; /* 該class_device的設備編號,用於創建其dev屬性文件,僅供驅動核心內部使用 */
struct device * dev; /* 指向該class_device相關的device結構體(物理設備),可選.若不爲NULL,用於創建一個從class入口到/sys/devices 下相應入口的符號連接,以便用戶空間查找設備入口 */
void * class_data; /* 該class_device的私有數據指針 */
struct class_device *parent; /* parent of this child device, if there is one */
struct attribute_group ** groups; /* optional groups */
void (*release)(struct class_device *dev);
int (*uevent)(struct class_device *dev,struct kobj_uevent_env *env);
char class_id[BUS_ID_SIZE]; /* 該class_device的名稱,在其所屬class中應是唯一的,不可重名 */
};
/* class_devic註冊函數 */
int __must_check class_device_register(struct class_device *);
void class_device_unregister(struct class_device *); 與class一樣,建立一個class_device也有兩種方法
a、根據需要,填充一個struct class_device,然後再調用class_device_register註冊該class_device就ok了(此法比較靈活,可以自己定製很多東西)
b、就是通過下面的class_device_create來創建一個class_device,該函數會返回一個指向剛建立的class_device的指針(創建class_device的最簡單方法)
/* class_device_create用於創建一個class_device,其名稱最後兩個參數決定(類似於printf的格式化字符串)
* cls指明瞭其所屬的class,可以是自己填充的class或由class_create返回的class
* parent指明瞭該class_device的父class_device,若沒有,則爲NULL
* 該class_device的設備編號,用於創建其dev屬性文件,必須指明
* device指明瞭該class_device(邏輯設備)對應的device(物理設備),可有可無,無則爲NULL
* 實際上,class_device_create也就是填充一個class_device,然後調用了class_device_register註冊該class_device
*/
struct class_device *class_device_create(structclass*cls,
struct class_device *parent,
dev_t devt,
struct device *device,
constchar*fmt,...)
__attribute__((format(printf,5,6)));
/* class_destroy用於刪除一個class,內部調用了class_unregister(cls)來註銷cls */
void class_device_destroy(structclass*cls, dev_t devt); class_device屬性對應於/sys/class/class.name/class_device.class_id目錄下一個文件。通過這些 文件,可以向用戶空間輸出一些關於該class_device的信息,也可從用戶空間獲取到一些信息。
/* class_device屬性,其show和store函數類似於class屬性的show和store函數 */
struct class_device_attribute {
struct attribute attr;
ssize_t (*show)(struct class_device *,char* buf);
ssize_t (*store)(struct class_device *,constchar* buf,size_tcount);
};
/* CLASS_DEVICE_ATTR可以在編譯時創建一個class_device屬性,該屬性的名稱爲class_device_attr_name */
#define CLASS_DEVICE_ATTR(_name,_mode,_show,_store) \
struct class_device_attribute class_device_attr_##_name = \
__ATTR(_name,_mode,_show,_store)
/* class_device_create_file與class_device_remove_file用於創建與刪除class_device默認屬性外的屬性 */
int __must_check class_device_create_file(struct class_device *,
conststruct class_device_attribute *);
void class_device_remove_file(struct class_device * class_dev,
conststruct class_device_attribute * attr); 其實,在調用class_device_register註冊一個class_device時,該函數內部調用了
int class_device_add(struct class_device *class_dev)
在class_device_add內,通過class_device_create_file創建dev、uevent和該class_device 所擁有的默認屬性(由class_device.class->class_dev_attrs指定)等屬性文件。這樣第3點也有了。
dev屬性文件用於向用戶空間輸出該class_device的設備編號。
uevent屬性文件使用戶可以手動觸發uevent事件(通過向該文件寫,如add、remove等字符串)。
接下來是兩個基於mdev的驅動例子。
struct class_interface {
struct list_head node;
struct class *class; /* 該class_interface所屬的class */
int (*add) (struct class_device *, struct class_interface *); /* class屬性 */
void (*remove) (struct class_device *, struct class_interface *); /* class屬性 */
int (*add_dev) (struct device *, struct class_interface *);
void (*remove_dev) (struct device *, struct class_interface *);
};
/* class_interface註冊函數 */
int __must_check class_interface_register(struct class_interface *);
void class_interface_unregister(struct class_interface *);