linux設備模型是linux比較基礎的知識,雖然有很多資料很多書籍都會去講設備模型,這裏以我自己的方式清晰的描述下linux設備模型;
(一) kobject
kobject表示linux設備模型的基本結構,Kobject是基本數據類型,每個Kobject都會在”/sys/“文件系統中以目錄的形式出現。最初只是作爲一個引用計數現在主要有如下幾個方面的作用:
- 使用一個引用計數(reference count),來記錄Kobject被引用的次數,並在引用次數變爲0時把它釋放;
- 通過parent指針,可以將所有Kobject以層次結構的形式組合起來。
- 和sysfs虛擬文件系統配合,將每一個Kobject及其特性,以文件的形式,開放到用戶空間;
- 當系統的硬件被熱拔插的時候,在kobject子系統的控制下,將產生的事件通知用戶空間;
struct kobject:
63 struct kobject {
64 const char *name; //該Kobject的名稱,同時也是sysfs中的目錄名稱。由於Kobject添加到Kernel時,需要根據名字註冊到sysfs中,之後就不能再直接修改該字段。如果需要修改Kobject的名字,需要調用kobject_rename接口;
65 struct list_head entry; //用於將Kobject加入到Kset中的list_head。
66 struct kobject *parent; //指向parent kobject,以此形成層次結構(在sysfs就表現爲目錄結構);
67 struct kset *kset; //該kobject屬於的Kset。可以爲NULL。如果存在,且沒有指定parent,則會把Kset作爲parent(Kset是一個特殊的Kobject)。
68 struct kobj_type *ktype; //每個Kobject必須有一個ktype,否則Kernel會提示錯誤,Ktype中的release回調函數負責釋放Kobject;
69 struct kernfs_node *sd; //該Kobject在sysfs中的表示;
70 struct kref kref; //一個可用於原子操作的引用計數;當該計數減爲0時,自動釋放;
71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
72 struct delayed_work release;
73 #endif
74 unsigned int state_initialized:1; //Kobject是否已經初始化;
75 unsigned int state_in_sysfs:1;
76 unsigned int state_add_uevent_sent:1;
77 unsigned int state_remove_uevent_sent:1; //記錄是否已經向用戶空間發送ADD uevent,如果有,且沒有發送remove uevent,則在自動註銷時,補發REMOVE uevent,以便讓用戶空間正確處理。
78 unsigned int uevent_suppress:1; //1則表示忽略所有上報的uevent事件;
79 };
如上圖表示kobject,kset,ktype的關係,說明如下:
(1) Kset是一個特殊的Kobject(因此它也會在”/sys/“文件系統中以目錄的形式出現),它用來集合相似的Kobject(這些Kobject可以是相同屬性的,也可以是不同屬性的)。成員list用於保存該kset下所有的kobject的鏈表;
成員uevent_ops:該kset的uevent操作函數集。當任何Kobject需要上報uevent時,都要調用它所從屬的kset的uevent_ops,添加環境變量,或者過濾event(kset可以決定哪些event可以上報)。因此,如果一個kobject不屬於任何kset時,是不允許發送uevent的;
(2) kobject一般內嵌在一些數據結構裏面,例如kset、device、device_driver等等,每個都要實現一個Ktype,並定義其中的回調函數。同理,sysfs相關的操作也一樣,必須經過ktype的中轉,因爲sysfs看到的是Kobject,而真正的文件操作的主體,是內嵌Kobject的上層數據結構;
其中release可以將包含該種類型kobject的數據結構的內存空間釋放掉。sysfs_ops:該種類型的Kobject的sysfs文件系統接口。default_attrs:該種類型的Kobject的atrribute列表,也以文件的形式在sysfs體現,用戶層面就可以通過都寫來操作這寫文件;
(1) attribute
attibute,就是內核空間和用戶空間進行信息交互的一種方法。例如某個driver定義了一個變量,卻希望用戶空間程序可以修改該變量,以控制driver的運行行爲,那麼就可以將該變量以sysfs attribute的形式開放出來。
attibute分爲普通的attribute和bin attribute:
//struct attribute爲普通的attribute,使用該attribute生成的sysfs文件,只能用字符串的形式讀寫
29 struct attribute {
30 const char *name;
31 umode_t mode;
32 #ifdef CONFIG_DEBUG_LOCK_ALLOC
33 bool ignore_lockdep:1;
34 struct lock_class_key *key;
35 struct lock_class_key skey;
36 #endif
37 };
//struct bin_attribute在struct attribute的基礎上,增加了read、write等函數,因此它所生成的sysfs文件可以用任何方式讀寫。
121 struct bin_attribute {
122 struct attribute attr;
123 size_t size;
124 void *private;
125 ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,
126 char *, loff_t, size_t);
127 ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *,
128 char *, loff_t, size_t);
129 int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,
130 struct vm_area_struct *vma);
131 };
在linux內核中,attibute文件的創建是由fs/sysfs/file.c中sysfs_create_file接口完成的;
以device 添加attr寫的一個模版:
void size_t test_show(struct device dev,struct device_attribute *attr, char *buf)
{
//test屬性所記錄的值寫到buf裏面,通常是用snprintf();
//實現對test的讀操作;
}
void size_t test_store(struct device dev,struct device_attribute *attr, const char *buf,size_t count)
{
//實現對test的屬性值寫操作;
}
DEVICE_ATTR(test,S_IWUSR | S_IRUGO,test_show,test_store);
device_create_file(device,&dev_attr_test);
通過上面自己添加設備屬性文件之後我們可以在/sys/devices/platform/或者/sys/devices/soc/目錄下找到test屬性文件,userspace就可以通過對這個文件進行都寫操作,來實現於底層交互;
(2) uevent
Uevent是Kobject的一部分,用於在Kobject狀態發生改變時,例如增加、移除等,通知用戶空間程序。有事件需要上報時,會觸發Uevent提供的接口,用戶空間程序收到這樣的事件後,會做相應的處理。 前面知道每個kset含有uevent_ops來實現kobject的事件上報,上報的事件類型有:
53 enum kobject_action {
54 KOBJ_ADD, //添加
55 KOBJ_REMOVE, //移除
56 KOBJ_CHANGE, //狀態發生改變
57 KOBJ_MOVE, //更改parent
58 KOBJ_ONLINE, //上線
59 KOBJ_OFFLINE, //掉線
60 KOBJ_MAX
61 };
//uevent_ops
131 struct kset_uevent_ops {
132 int (* const filter)(struct kset *kset, struct kobject *kobj);//當任何Kobject需要上報uevent時,它所屬的kset可以通過該接口過濾,阻止不希望上報的event,從而達到從整體上管理的目的。
133 const char *(* const name)(struct kset *kset, struct kobject *kobj);//該接口可以返回kset的名稱。如果一個kset沒有合法的名稱,則其下的所有Kobject將不允許上報uvent;
134 int (* const uevent)(struct kset *kset, struct kobject *kobj,
135 struct kobj_uevent_env *env);//當任何Kobject需要上報uevent時,它所屬的kset可以通過該接口統一爲這些event添加環境變量。因爲很多時候上報uevent時的環境變量都是相同的,因此可以由kset統一處理,就不需要讓每個Kobject獨自添加;
136 };
123 struct kobj_uevent_env {
124 char *argv[3];
125 char *envp[UEVENT_NUM_ENVP];//指針數組,用於保存每個環境變量的地址,最多可支持的環境變量數量爲UEVENT_NUM_ENVP
126 int envp_idx; //用於訪問環境變量指針數組的index。
127 char buf[UEVENT_BUFFER_SIZE]; //保存環境變量的buffer,最大爲UEVENT_BUFFER_SIZE。
128 int buflen; //訪問buf的變量
129 };
kobject_uevent_env來完成真正的上報的動作,以envp爲環境變量,上報一個指定action的uevent。環境變量的作用是爲執行用戶空間程序指定運行環境。
Uevent模塊準備好上報事件的格式後,可以通過兩個途徑把事件上報到用戶空間:一種是通過kmod模塊,直接調用用戶空間的可執行文件;另一種是通過netlink通信機制,將事件從內核空間傳遞給用戶空間。
以komd爲例,Uevent模塊通過Kmod上報Uevent時,會通過call_usermodehelper函數,調用用戶空間的可執行文件(或者腳本,簡稱uevent helper )處理該event。而該uevent helper的路徑保存在uevent_helper數組中;
usb 熱插拔原理:當設備插入到集線器時,由1.5K的上拉電阻和15K的下拉電阻分壓,結果就將差分數據線中的一條拉高了。集線器檢測到這個狀態後,它就報告給USB主控制器(或者通過它上一層的集線器報告給USB主控制器),這樣就檢測到設備的插入了。可參考文檔:Linux USB 驅動開發(四)—— 熱插拔那點事
(3) kobject 操作
kobject操作流程:
1. 定義一個struct kset類型的指針,並在初始化時爲它分配空間,添加到內核中;
2. 根據實際情況,定義自己所需的數據結構原型,該數據結構中包含有Kobject;
3. 定義一個適合自己的ktype,並實現其中回調函數;
4. 在需要使用到包含Kobject的數據結構時,動態分配該數據結構,並分配Kobject空間,添加到內核中;
5. 每一次引用數據結構時,調用kobject_get接口增加引用計數;引用結束時,調用kobject_put接口,減少引用計數;
6. 當引用計數減少爲0時,Kobject模塊調用ktype所提供的release接口,釋放上層數據結構以及Kobject的內存空間;
初始化kobject:kobject_init(struct kobject *kobj, struct kobj_type *ktype) –> kobject_init_internal() : 初始化kobj內部的參數,包括引用計數、list、各種標誌等;
添加kobject到內存空間 : 調用kobject_add() -> kobject_add_internal() :
- 校驗kobj以及kobj->name的合法性
- 調用kobject_get增加該kobject的parent的引用計數,如果存在parent的話
- 如果存在kset(即kobj->kset不爲空),則調用kobj_kset_join接口加入kset。同時,如果該kobject沒有parent,卻存在kset,則將它的parent設爲kset(kset是一個特殊的kobject),並增加kset的引用計數
- 通過create_dir接口,調用sysfs的相關接口,在sysfs下創建該kobject對應的目錄
- kobj_kset_join,負責將kobj加入到對應kset的鏈表中。
kobject_create分配空間 : kobject_create() 或者 kobject_create_and_add();
通過kobject_get和kobject_put可以修改kobject的引用計數,並在計數爲0時,調用ktype的release接口,釋放佔用空間。
(二) bus
總線bus:在linux設備模型當中,所有的設備都是通過總線連接起來,總線並不一定是真實存在的,也可以是虛擬總線,比如platform bus;這裏就以通用的虛擬總線platform bus爲例來描述總線,通過struct bus_type定義:
1010 struct bus_type platform_bus_type = {
1011 .name = "platform",
1012 .dev_groups = platform_dev_groups,
1013 .match = platform_match,
1014 .uevent = platform_uevent,
1015 .pm = &platform_dev_pm_ops,
1016 };
1017 EXPORT_SYMBOL_GPL(platform_bus_type);
platform bus 的註冊:
1019 int __init platform_bus_init(void)
1020 {
1021 int error;
1022
1023 early_platform_cleanup();
1024
1025 error = device_register(&platform_bus);
1026 if (error)
1027 return error;
1028 error = bus_register(&platform_bus_type);
1029 if (error)
1030 device_unregister(&platform_bus);
1031 return error;
1032 }
early_platform_cleanup():一些設備需要在early階段就啓動比如一些serial,consoles設備,因爲執行到這裏的時候,證明系統已經完成了Early階段的啓動,轉而進行正常的設備初始化、啓動操作,所以這裏清除註冊在early_platform_device_list鏈表的設備;
device_register(): platform bus本身也是一個設備,這裏通過這個函數註冊設備到device list,這個函數在後面device註冊的時候在具體分析;
bus_register()主要完成如下操作:
873 int bus_register(struct bus_type *bus)
874 {
875 int retval;
876 struct subsys_private *priv;
877 struct lock_class_key *key = &bus->lock_key;
878
879 priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
880 if (!priv)
881 return -ENOMEM;
882
883 priv->bus = bus;
884 bus->p = priv;
885
886 BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
887
888 retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); //初始化priv->subsys.kobj,kset本身也是一個特殊的kobject;
889 if (retval)
890 goto out;
891
892 priv->subsys.kobj.kset = bus_kset;//該bus kobject 所屬的kset;
893 priv->subsys.kobj.ktype = &bus_ktype;
894 priv->drivers_autoprobe = 1;//選擇是否調用driver_attach進行probe;
895
896 retval = kset_register(&priv->subsys);
897 if (retval)
898 goto out;
899
900 retval = bus_create_file(bus, &bus_attr_uevent); //向bus目錄下添加一個uevent attribute(如/sys/bus/platform/uevent)
901 if (retval)
902 goto bus_uevent_fail;
903
904 priv->devices_kset = kset_create_and_add("devices", NULL,
905 &priv->subsys.kobj);
906 if (!priv->devices_kset) {
907 retval = -ENOMEM;
908 goto bus_devices_fail;
909 }
911 priv->drivers_kset = kset_create_and_add("drivers", NULL,
912 &priv->subsys.kobj);
913 if (!priv->drivers_kset) {
914 retval = -ENOMEM;
915 goto bus_drivers_fail;
916 }
917 //interfaces 實現的是該bus的一些特殊藉口,這裏初始化這個接口鏈表;
918 INIT_LIST_HEAD(&priv->interfaces);
919 __mutex_init(&priv->mutex, "subsys mutex", key);
//初始化該bus上的device和drvier list;
920 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
921 klist_init(&priv->klist_drivers, NULL, NULL);
922
923 retval = add_probe_files(bus);
924 if (retval)
925 goto bus_probe_files_fail;
926
927 retval = bus_add_groups(bus, bus->bus_groups);//添加由bus_attrs指針定義的bus的默認attribute,如果有定義的話;
928 if (retval)
929 goto bus_groups_fail;
930
931 pr_debug("bus: '%s': registered\n", bus->name);
932 return 0;
933
948 }
949 EXPORT_SYMBOL_GPL(bus_register);
(1) 申請subsys_private空間,並賦值給註冊的總線,subsys_private主要集合了一些bus模塊需要使用的私有數據:設備驅動的kset,klist;
(2) 初始化改bus kobject,通過kset_register()把該bus註冊到內核空間,並生成/sys/bus/platform目錄;
(3) 通過kset_create_and_add 創建kset,分別生成/sys/bus/platform/device和/sys/bus/platform/driver目錄;
(4) 通過add_probe_files()在/sys/bus/platform/下添加drivers_probe和drivers_autoprobe兩個attribute,其中drivers_probe允許用戶空間程序主動觸發指定bus下的device_driver的probe動作,而drivers_autoprobe控制是否在device或device_driver添加到內核時,自動執行probe;
添加了bus之後,就可以在sys目錄下形成如下:
sys/---
bus/---
platform/---
devices/---
drivers/---
drivers_autoprobe
drivers_probe
uevent
現在bus上還沒有device,就需要通過bus_add_device()添加device:
506 int bus_add_device(struct device *dev)
507 {
508 struct bus_type *bus = bus_get(dev->bus);
509 int error = 0;
510
511 if (bus) {
512 pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
513 error = device_add_attrs(bus, dev);
514 if (error)
515 goto out_put;
516 error = device_add_groups(dev, bus->dev_groups);
517 if (error)
518 goto out_id;
519 error = sysfs_create_link(&bus->p->devices_kset->kobj,
520 &dev->kobj, dev_name(dev));
521 if (error)
522 goto out_groups;
523 error = sysfs_create_link(&dev->kobj,
524 &dev->bus->p->subsys.kobj, "subsystem"); //在該設備的sysfs目錄中創建一個指向該設備所在bus目錄的鏈接,取名爲subsystem;
525 if (error)
526 goto out_subsys;
527 klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
528 }
529 return 0;
}
bus_add_device()主要完成如下操作:
(1) device_add_attrs()添加該bus的dev_attrs屬性,如果有定義;device_add_groups()添加該bus的dev_groups屬性組:
782 static struct attribute *platform_dev_attrs[] = {
783 &dev_attr_modalias.attr,
784 &dev_attr_driver_override.attr,
785 NULL,
786 };
787 ATTRIBUTE_GROUPS(platform_dev);
(2) 調用sysfs_create_link接口,將該device在sysfs中的目錄,鏈接到該bus的devices目錄下;
/sys # ls bus/platform/devices/led@0 -l
lrwxrwxrwx 1 root root 0 2015-01-05 16:43 bus/platform/devices/dev_xx -> ../../../devices/dev_xx
(3) 通過調用klist_add_tail把該設備指針保存在bus->priv->klist_devices中;
現在已經有了bus,有了device,剩下的driver就通過bus_add_driver來添加:
666 int bus_add_driver(struct device_driver *drv)
667 {
668 struct bus_type *bus;
669 struct driver_private *priv;
670 int error = 0;
671
672 bus = bus_get(drv->bus);
673 if (!bus)
674 return -EINVAL;
675
676 pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
677
678 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
679 if (!priv) {
680 error = -ENOMEM;
681 goto out_put_bus;
682 }
683 klist_init(&priv->klist_devices, NULL, NULL);
684 priv->driver = drv;
685 drv->p = priv;
686 priv->kobj.kset = bus->p->drivers_kset;
687 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
688 "%s", drv->name);
689 if (error)
690 goto out_unregister;
691
692 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
//將該driver保存在bus的klist_drivers鏈表中,並根據drivers_autoprobe的值,選擇是否調用driver_attach進行probe
693 if (drv->bus->p->drivers_autoprobe) {
694 error = driver_attach(drv);
695 if (error)
696 goto out_unregister;
697 }
698 module_add_driver(drv->owner, drv);
699
700 error = driver_create_file(drv, &driver_attr_uevent);//創建uevent attribute
701 if (error) {
702 printk(KERN_ERR "%s: uevent attr (%s) failed\n",
703 __func__, drv->name);
704 }
705 error = driver_add_groups(drv, bus->drv_groups);
706 if (error) {
708 printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
709 __func__, drv->name);
710 }
711
712 if (!drv->suppress_bind_attrs) { //同時根據suppress_bind_attrs標誌,決定是否在sysfs的該driver的目錄下,創建bind和unbind attribute;
713 error = add_bind_files(drv);
714 if (error) {
715 /* Ditto */
716 printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
717 __func__, drv->name);
718 }
719 }
720
721 return 0;
這個函數主要做的操作有:
(1) 該driver的struct driver_private指針(priv)分配空間,並初始化其中的priv->klist_devices、priv->driver、priv->kobj.kset等變量;將driver的kset(priv->kobj.kset)設置爲bus的drivers kset(bus->p->drivers_kset),這就意味着所有driver的kobject都位於bus->p->drivers_kset之下(寄/sys/bus/xxx/drivers目錄下);
(2) 因爲每個驅動可以兼容多個設備,所以這裏有klist_devices來表示兼容的設備列表;
(3)該driver保存在bus的klist_drivers鏈表中,並根據drivers_autoprobe的值,選擇是否調用driver_attach進行probe ;
driver_attach() –>bus_for_each_dev(..(*fn)()) –>while遍歷device列表執行__driver_attach() : driver_match_device()–>調用paform bus的 mach函數 platform_match 判斷名字是否匹配:
當device和driver匹配完之後就開始 driver_probe_device–> really_probe() –> drv->probe即調用了驅動的probe 函數;
(4) 創建attribute文件,bind/unbind是從用戶空間手動的爲driver綁定/解綁定指定的設備的機制;
添加了device和驅動之後,這樣在bus目錄下:
sys/---
bus/---
platform/---
devices/---
xx_dev --> ../../../devices/soc/xx_dev
drivers/---
xx_drv/---
bind
uevent
unbind
drivers_autoprobe
drivers_probe
uevent
(三) device and driver
在linux系統中通過device來表示一個設備的屬性,這個設備具有什麼功能,通過driver來實現其作用,並能夠到給上一層操作使用;前面的bus章節已經講到將device和driver添加到bus上面;
上圖描述device ,driver ,class ,bus的關係,說明如下:
(1) 這幾個結構都有一個privte結構,bus的privte結構主要包含了devie和driver的kset,對應生成sys/bus/platform/devices和sys/bus/platform/drivers目錄,並且通過device/driver list把所有device和driver串聯起來;device_private的klist_children保存了子設備鏈表;device_driver的klist_devices保存了該driver所支持的deivce列表;
(2) 內核要保證在driver運行前,設備所依賴的總線能夠正確初始化;在設備模型的結構下,只有driver和device同時存在時,才需要開始執行driver的代碼邏輯。這也是probe和remove兩個接口名稱的由來:檢測到了設備和移除了設備; device和device_driver必須掛載在一個bus之下,該bus可以是實際存在的,也可以是虛擬的。
(3) device的成員dev_t是一個32位的整數,它由兩個部分(Major和Minor)組成,在需要以設備節點的形式(字符設備和塊設備)向用戶空間提供接口的設備中,當作設備號使用;
(4) device的成員devres_head保存了設備資源的鏈表;系統資源包括I/O、Memory、Register、IRQ、DMA、Bus等多種類型。這些資源大多具有獨佔性,不允許多個設備同時使用,通常在driver probe的時候完成這寫資源的分配;
16 struct devres_node {
17 struct list_head entry;
18 dr_release_t release;
19 #ifdef CONFIG_DEBUG_DEVRES
20 const char *name;
21 size_t size;
22 #endif
23 };
25 struct devres {
26 struct devres_node node;
27 /* -- 3 pointers */
28 unsigned long long data[]; /* guarantee ull alignment */
29 };
下面來描述設備驅動註冊的過程:
22 struct platform_device {
23 const char *name; //設備的名稱,和struct device結構中的init_name;
24 int id; //用於標識該設備的ID;
25 bool id_auto; //指示在註冊設備時,是否自動賦予ID值;
26 struct device dev; //真正的設備(Platform設備只是一個特殊的設備,因此其核心邏輯還是由底層的模塊實現
27 u32 num_resources;
28 struct resource *resource;
29
30 const struct platform_device_id *id_entry;
31 char *driver_override; /* Driver name to force a match */
32
33 /* MFD cell pointer */
34 struct mfd_cell *mfd_cell;
35
36 /* arch specific additions */
37 struct pdev_archdata archdata; //保存一些architecture相關的數據
38 };
174 struct platform_driver {
175 int (*probe)(struct platform_device *);
176 int (*remove)(struct platform_device *);
177 void (*shutdown)(struct platform_device *);
178 int (*suspend)(struct platform_device *, pm_message_t state);
179 int (*resume)(struct platform_device *);
180 struct device_driver driver;
181 const struct platform_device_id *id_table; //match table;
182 bool prevent_deferred_probe;
183 };
設備註冊:platform_device_register():
428 int platform_device_register(struct platform_device *pdev)
429 {
430 int ret;
434
436 device_initialize(&pdev->dev);
437 arch_setup_pdev_archdata(pdev);
438 ret = platform_device_add(pdev);
440 bootprof_pdev_register(ts, pdev);
441 return ret;
442 }
device_initialize():初始化該device的kobject,kset指向devices_kset,初始化devres_head列表,numa_node設置爲-1;
arch_setup_pdev_archdata()設置arch 相關data;
- 重點實現platform_device_add() :(a)指定bus,當然這裏是&platform_bus_type; 根據device instance id來設置kobject name,這個id通常設爲0;(b) 循環resource列表,分爲IO類型和MEM類型,插入到全局ioport_resource,iomem_resource列表中;(c)device_add() 來實現真正的添加:初始化device_private, 添加kobject,創建device file,最後通過bus_add_device添加到bus上;
驅動註冊:platform_driver_register() -> __platform_driver_register() :
578 int __platform_driver_register(struct platform_driver *drv,struct module *owner)
580 {
581 drv->driver.owner = owner;
582 drv->driver.bus = &platform_bus_type;
583 if (drv->probe)
584 drv->driver.probe = platform_drv_probe;
585 if (drv->remove)
586 drv->driver.remove = platform_drv_remove;
587 if (drv->shutdown)
588 drv->driver.shutdown = platform_drv_shutdown;
589
590 return driver_register(&drv->driver);
591 }
- 指定driver的回調函數,如果沒有,就用platform 默認的;
- driver_register():driver_find()先查找驅動是否已經註冊,bus_add_driver() 添加到bus上,前面已經有分析;kobject_uevent(&drv->p->kobj, KOBJ_ADD),最後調用uevent_ops->uevent()來通知userspace driver添加成功;
下面一個platform設備驅動模版:
const struct file_operations cdev_test_fops = {
.owner = THIS_MODULE,
.open = cdev_Open,
.release = cdev_Release,
.unlocked_ioctl = cdev_Ioctl
};
struct platform_driver test_driver {
.probe = test_probe,
.remove = test_remove,
.driver = {
.name = "test",
.owner = THIS_MODULE,
}
}
struct platform_device test_device {
.name = "test",
.id = 0,
.dev = {}
}
static dev_t test_dev_num;
static struct cdev * test_cdev;
static int __init test_init(void)
{
struct device * testdevice = NULL;
struct class * test_class = NULL;
alloc_chrdev_region(&test_dev_num,0,1,"test");
test_cdev = cdev_alloc();
cdev_init(test_cdev, &cdev_test_fops);
test_cdev->owner = THIS_MODULE;
test_class = class_create(THIS_MODULE, "test");
device_create(test_class, NULL, test_dev_num, NULL, "test");
platform_driver_register(&test_driver);
platform_device_register(&test_device);
return 0;
}
上面的模版,我們就可以對/dev/test設備文件直接操作,可以open,releas,也可以通過添加cmd,通過ioctl來實現更多IO操作;