【Linux基礎系列之】設備模型

  linux設備模型是linux比較基礎的知識,雖然有很多資料很多書籍都會去講設備模型,這裏以我自己的方式清晰的描述下linux設備模型;


(一) kobject

  kobject表示linux設備模型的基本結構,Kobject是基本數據類型,每個Kobject都會在”/sys/“文件系統中以目錄的形式出現。最初只是作爲一個引用計數現在主要有如下幾個方面的作用:

  1. 使用一個引用計數(reference count),來記錄Kobject被引用的次數,並在引用次數變爲0時把它釋放;
  2. 通過parent指針,可以將所有Kobject以層次結構的形式組合起來。
  3. 和sysfs虛擬文件系統配合,將每一個Kobject及其特性,以文件的形式,開放到用戶空間;
  4. 當系統的硬件被熱拔插的時候,在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 };

device

如上圖表示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() :

  1. 校驗kobj以及kobj->name的合法性
  2. 調用kobject_get增加該kobject的parent的引用計數,如果存在parent的話
  3. 如果存在kset(即kobj->kset不爲空),則調用kobj_kset_join接口加入kset。同時,如果該kobject沒有parent,卻存在kset,則將它的parent設爲kset(kset是一個特殊的kobject),並增加kset的引用計數
  4. 通過create_dir接口,調用sysfs的相關接口,在sysfs下創建該kobject對應的目錄
  5. 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 }
  1. device_initialize():初始化該device的kobject,kset指向devices_kset,初始化devres_head列表,numa_node設置爲-1;

  2. arch_setup_pdev_archdata()設置arch 相關data;

  3. 重點實現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 }
  1. 指定driver的回調函數,如果沒有,就用platform 默認的;
  2. 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操作;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章