官术网_书友最值得收藏!

2.6 驅動模型對象

在上面介紹了內核對象和syfs文件系統,本節分析Linux驅動模型。Linux驅動模型適用于Linux各種子系統,它描述了總線類型、設備、驅動以及類和接口之間的關系。每個子系統都有屬于自己的唯一的總線類型,它上面鏈接了注冊到這一總線類型上的多個設備,另外它還將注冊到這個總線類型的多個驅動鏈接在一起。每個設備可能暫時還未綁定到驅動,或者可能被綁定到最多一個驅動。而驅動則可以暫時未綁定任何設備,也可能已經綁定了一個或多個設備。

總線類型、設備、驅動這三者的關系,使得子系統支持熱插拔變得非常容易。早期的計算機系統不支持熱插拔,驅動必須在設備被發現之前已經被加載。而支持熱插拔的系統還可以允許在驅動已經加載的情況下安裝設備。事實上,Linux驅動模型的處理方式是,在設備被發現時,它被加入所注冊總線類型的設備鏈表,然后在該總線類型的驅動鏈表中查找可以處理這個設備的驅動并綁定。類似地,在驅動被加載時,被加入到它所注冊的總線類型的驅動鏈表,同時在該總線類型的設備鏈表中查找并綁定可以處理的設備。顯然,在這樣的實現思路下,設備發現和驅動加載的順序不再那么重要了。

從上面的描述我們看到,設備最多可以屬于一個驅動。在某些情況下,這個限制過于嚴苛。比如,SCSI磁盤,它作為一個磁盤類設備,被唯一地綁定到SCSI磁盤驅動。但是,有時我們希望把它作為一個普通的SCSI設備,向它發送INQUIRY等SCSI命令。Linux驅動模型對此的解決方案是,引入類和接口。每個設備還可以唯一屬于某個類,設備被鏈入到類的設備鏈表。類還有另一個接口鏈表,用于鏈接注冊到該類的所有接口。在設備被發現,或接口被添加時,無論何種順序,只要它們屬于同一個類,那么接口注冊的回調函數都會被作用在設備之上。

Linux驅動模型實現的對象關系如圖2-11所示,圖中各種關系的功能如下。

圖2-11 Linux驅動模型對象之間的關系

? bus_type和bus_type_private共同描述總線類型。

? device_driver和driver_private共同描述驅動。

? device和device_private共同描述設備。

? class和class_private共同描述類。

? class_interface表示的接口。

2.6.1 總線類型

Linux驅動模型中的總線類型結構使用bus_type和bus_type_private兩個結構體來表示。之所以這樣,純粹是從編程的角度來考慮的,程序員在實現一個新的總線類型時,只需要定義bus_type結構體,并實現其中的方法和函數,而不必關注那些完全被驅動模型核心使用的內部域。

但是,需要注意的是,這兩個結構體是同時存在的,并且有一一對應關系。事實上,兩個結構體各自有一個指針域指向對方。bus_type結構中的域和bus_type_private結構中的域分別如表2-12和表2-13所示。

表2-12 bus_type結構中的域(來自文件include/linux/device.h)

表2-13 bus_type_private結構中的域(來自文件drivers/base/base.h)

每個總線類型被注冊后,將在/sys/bus下創建一個和其名字對應的子目錄,在該子目錄下又有兩個子目錄:drivers和devices。和總線類型子目錄對應的內嵌的kset,即subsys域,而drivers_kset和devices_kset是指向和drivers子目錄以及devices子目錄對應的kset的指針。

總線類型有兩個鏈表:一個是在該總線類型上發現的設備鏈表,另一個是在該總線類型上加載的驅動鏈表。注意這里用作鏈表的表頭和連接件結構分別為klist和klist_node,它們實際上也是Linux內核雙循環鏈表結構list_head的封裝,具體實現的分析留給讀者。

某些子系統(或模塊)會定義自己的總線類型,這時初始化的一個主要工作就是向Linux內核注冊其總線類型,對于某些子系統(或模塊),它甚至是初始化的全部工作。

Linux驅動模型核心提供了bus_register函數(其代碼如程序2-12所示),被用來注冊總線類型。子系統(或模塊)調用該函數前,必須已經聲明了一個bus_type類型的變量,調用時將其指針作為參數傳入。

程序2-12 函數bus_register()代碼(摘自文件drivers/base/bus.c)

bus_register()

880int bus_register(struct bus_type *bus)
881{
882    int retval;
883    struct bus_type_private *priv;
884
885    priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
886    if (!priv)
887        return -ENOMEM;
888
889    priv->bus = bus;
890    bus->p = priv;
891
892    BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
893
894    retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
895    if (retval)
896        goto out;
897
898    priv->subsys.kobj.kset = bus_kset;
899    priv->subsys.kobj.ktype = &bus_ktype;
900    priv->drivers_autoprobe = 1;
901
902    retval = kset_register(&priv->subsys);
903    if (retval)
904        goto out;
905
906    retval = bus_create_file(bus, &bus_attr_uevent);
907    if (retval)
908        goto bus_uevent_fail;
909
910    priv->devices_kset = kset_create_and_add("devices", NULL,
911                         &priv->subsys.kobj);
912    if (!priv->devices_kset) {
913        retval = -ENOMEM;
914        goto bus_devices_fail;
915   }
916
917    priv->drivers_kset = kset_create_and_add("drivers", NULL,
918                         &priv->subsys.kobj);
919    if (!priv->drivers_kset) {
920        retval = -ENOMEM;
921        goto bus_drivers_fail;
922   }
923
924    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
925    klist_init(&priv->klist_drivers, NULL, NULL);
926
927    retval = add_probe_files(bus);
928    if (retval)
929        goto bus_probe_files_fail;
930
931    retval = bus_add_attrs(bus);
932    if (retval)
933        goto bus_attrs_fail;
934
935    pr_debug("bus: '%s': registered\n", bus->name);
936    return 0;
937
938bus_attrs_fail:
939    remove_probe_files(bus);
940bus_probe_files_fail:
941    kset_unregister(bus->p->drivers_kset);
942bus_drivers_fail:
943    kset_unregister(bus->p->devices_kset);
944bus_devices_fail:
945    bus_remove_file(bus, &bus_attr_uevent);
946bus_uevent_fail:
947    kset_unregister(&bus->p->subsys);
948    kfree(bus->p);
949out:
950    bus->p = NULL;
951    return retval;
952}

總線類型注冊的主要任務是分配和初始化相關的結構,以及在sysfs文件系統中創建相應的子目錄樹。

我們說過,在調用這個函數前,用戶已經聲明了一個bus_type類型的變量,但是bus_type_private結構的空間還沒有分配,第885行分配之。第889和890行讓兩個結構相互關聯起來。

接下來對總線類型私有數據結構的內嵌kset的內嵌kobject進行初始化,包括如下內容。

? 設置其名字為總線類型名字(第894行)。

? 設置其kset為bus_kset,而后者在系統初始化過程的buses_init函數(見文件drivers/base/bus.c)中調用kset_create_and_add創建,它在sysfs文件系統中對應的目錄為sys/bus,它的kset_uevent_ops為bus_uevent_ops(第895行)。

? 設置其ktype為bus_ktype(第896行)。

做過上面的設置后,調用kset_register一次性初始化總線類型私有數據結構的內嵌kset,并將它添加到sysfs文件系統。這將在sys/bus/下創建一個以總線類型名字(為敘述方便,假設為###)為名字的子目錄,即sys/bus/###/。

第906~908行調用bus_create_file在該子目錄下添加uevent屬性文件,即sys/bus/###/uevent。這是一個只寫的屬性文件,這用于手動觸發熱插拔。熱插拔機制需要用戶空間服務程序(例如udevd)的配合,該程序偵聽內核空間發出的uevent事件,匹配規則做相應的動作。在系統啟動后內核注冊了很多設備,但用戶空間還沒有啟動,所以這些事件沒有機會被處理。為此,在每個注冊的設備文件夾下生成一個uevent屬性文件,當服務程序啟動后,可以掃描sysfs文件系統中所有uevent屬性文件,向其寫入add命令,從而觸發uevent事件,這樣服務程序就有機會處理這些事件了。

第910~915行在該子目錄下添加devices子目錄(即sys/bus/###/devices),對應的kset被保存在devices_kset域。以便將來在該總線類型下發現設備的時候,向對應的子目錄中添加設備的符號鏈接。

第917~922行在該子目錄下添加drivers子目錄(即sys/bus/###/drivers),對應的kset被保存在drivers_kset域。以便將來在該總線類型下加載驅動的時候,向對應的子目錄中添加驅動的子目錄。

第924行和第925行分別初始化總線類型的設備鏈表和驅動鏈表。

第927~929行調用add_probe_files在該總線類型的子目錄下添加drivers_probe和drivers_autoprobe兩個文件,即sys/bus/###/drivers_probe和sys/bus/###/drivers_autoprobe。后者用來顯示和修改總線類型描述符的drivers_ autoprobe域,而前者是一個只寫的屬性文件,向其中寫任何值都會觸發對總線類型對它的設備重新掃描。

最后,第931~933行調用bus_add_attrs在總線類型子目錄下為聲明bus_type時給出的總線類型屬性添加文件。例如對于PCI總線,只定義了一個這樣的屬性:rescan。

和bus_register對應的函數是bus_unregister,用于總線類型注銷時,它執行如下一些相反的操作。

? 調用bus_remove_attrs為總線類型刪除公有屬性文件,這些屬性由bus_type類型變量的bus_attrs域指針所指向。

? 調用remove_probe_files從總線類型下刪除probe和autoprobe兩個屬性文件。

? 調用kset_unregister從子樹中刪除drivers子目錄。

? 調用kset_unregister從子樹中刪除devices子目錄。

? 調用bus_remove_file從總線類型中刪除uevent屬性文件。

? 調用kset_unregister從系統中注銷總線類型內嵌的kset。

Linux驅動模型核心提供了遍歷每個鏈表的輔助函數。

? 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 *));

這兩個輔助函數的使用會在后面看到,它們都在成功時返回0,“錯誤”時返回非零值。

bus_for_each_dev函數遍歷總線類型的設備鏈表,bus_for_each_drv函數遍歷總線類型的驅動鏈表,對于鏈表中每個設備或驅動,調用傳入的回調函數fn。回調函數返回零就繼續處理鏈表的后一個設備或驅動,返回非零值則退出循環。這主要用在設備和驅動的匹配,即在設備鏈表中查找驅動可以綁定的設備,或者在驅動鏈表中查找可以綁定設備的驅動。

我們有必要對“錯誤”的引號做一下說明。記住設備只能被綁定到最多一個驅動。用設備在總線類型的驅動鏈表上尋求匹配的時候,無論是否找到匹配項,都是正常的。因為一定要區分,我們將匹配作為一種“錯誤”,出現時就退出循環,不需要繼續處理鏈表中余下的驅動了。

對比地看,驅動可以綁定一個或多個設備。用驅動在總線類型的設備鏈表上進行匹配的時候,應該處理完所有的設備,除非出現真正意義上的錯誤。而且對每個設備,無論是否匹配,都是正常的。這里的錯誤用法采用的是正常的思維。

更具體的解釋請參看后面關于device_attach和driver_attach函數的跟蹤。

2.6.2 設備

Linux驅動模型中的設備,我們有時簡稱為驅動模型設備,使用device和device_private兩個結構體來表示。兩個結構體相互關聯,各自有一個域指向對方。這兩個結構體中的域分別如表2-14和表2-15所示。

表2-14 device結構中的域(來自文件include/linux/device.h)

表2-15 device_private結構中的域(來自文件drivers/base/base.h)

在Linux驅動模型下,每個設備被歸到一種總線類型,通過bus域指向它。設備也可以屬于唯一的類,由class域指向。如果設備被綁定,則通過driver域指向對應的驅動。同內核對象一樣,設備也被組織成層次結構,通過parent域指向父親設備。對應地,設備被加入到如下四個鏈表。

? 所屬總線類型的設備鏈表,總線類型的klist_devices域為這個鏈表的表頭,設備的knode_bus域為鏈入此鏈表的連接件。

? 所屬類的設備鏈表,類的class_devices域為這個鏈表的表頭,設備的knode_class域為鏈入此鏈表的連接件。

? 綁定它的驅動的設備鏈表,驅動的klist_devices域為這個鏈表的表頭,設備的knode_driver域為鏈入此鏈表的連接件。

? 父設備的孩子鏈表,父設備的klist_children域為這個鏈表的表頭,設備的knode_parent域為鏈入此鏈表的連接件。

在實際使用中,驅動模型設備被內嵌到業務子系統的設備結構中。后者通過驅動模型設備被鏈接到業務子系統的總線類型實例,或者被鏈接到業務子系統的類實例,兩者取其一,也就是說每個驅動模型設備只會使用knode_bus和knode_class兩個域中的一個。如果業務子系統的設備結構需要同時被鏈接到總線類型實例和類實例,則需要包含兩個內嵌的驅動模型設備,用于鏈接到總線類型實例的驅動模型設備被稱為通用設備,而用于鏈接到類實例的驅動模型設備被稱為類設備。

業務子系統可以借助于驅動模型中的鏈表實現對設備的遍歷,在其總線類型實例或類實例中查找滿足某些條件的業務子系統設備。例如PCI子系統中的for_each_pci_dev宏,還有SCSI子系統中的scsi_host_lookup函數。

同樣,類似于kobject,每個device都屬于一種設備類型,其結構為device_type(該結構中的域如表2-16所示),定義了這種類型設備的公共方法和成員。

表2-16 device_type結構中的域(來自文件include/linux/device.h)

設備被發現后,需要向Linux驅動模型注冊。設備注冊的函數為device_register(其代碼如程序2-13所示),它有唯一的參數,即指向device描述符的指針。調用者已經為這個device描述符分配好了空間,實際上,device描述符往往被嵌入其他結構中,它的空間隨著其他結構分配。

程序2-13 函數device_register()代碼(摘自文件drivers/base/core.c)

device_register()

1043int device_register(struct device *dev)
1044{
1045    device_initialize(dev);
1046    return device_add(dev);
1047}

設備注冊分配兩個步驟:初始化設備,以及將設備添加到sysfs。分別調用device_initialize(代碼如程序2-14所示)和device_add函數(代碼如程序2-15所示)。

程序2-14 函數device_initialize()代碼(摘自文件drivers/base/core.c)

device_register()→device_initialize()

557void device_initialize(struct device *dev)
558{
559    dev->kobj.kset = devices_kset;
560    kobject_init(&dev->kobj, &device_ktype);
561    INIT_LIST_HEAD(&dev->dma_pools);
562    init_MUTEX(&dev->sem);
563    spin_lock_init(&dev->devres_lock);
564    INIT_LIST_HEAD(&dev->devres_head);
565    device_init_wakeup(dev, 0);
566    device_pm_init(dev);
567    set_dev_node(dev, -1);
568}

device_initializate是設備注冊的第一步。我們只關注前面兩行代碼。第559行將設備內嵌的kobject關聯到內核對象集devices_kset,而后者在系統初始化過程的devices_init函數(見文件drivers/base/core.c)中調用kset_create_and_add創建,它在sysfs文件系統中對應的目錄為sys/devices。此外,devices_kset的uevent操作表并賦值為device_uevent_ops。如前所述,這將控制內核中設備狀態發生變化時向用戶空間發送uevent的方式。

第560行將設備的內嵌對象的對象類型設置為device_ktype,其中定義了設備的公共屬性操作表,以及釋放方法。后者確保在對設備的引用計數減到0時,銷毀整個device結構。

程序2-15 函數device_add()代碼(摘自文件drivers/base/core.c)

device_register()→device_add()

890int device_add(struct device *dev)
891{
892    struct device *parent = NULL;
893    struct class_interface *class_intf;
894    int error = -EINVAL;
895
896    dev = get_device(dev);
897    if (!dev)
898        goto done;
899
900    if (!dev->p) {
901        error = device_private_init(dev);
902        if (error)
903            goto done;
904   }
905
906    /*
907     * for statically allocated devices, which should all be converted
908     * some day, we need to initialize the name. We prevent reading back
909     * the name, and force the use of dev_name()
910     */
911    if (dev->init_name) {
912        dev_set_name(dev, "%s", dev->init_name);
913        dev->init_name = NULL;
914   }
915
916    if (!dev_name(dev)) {
917        error = -EINVAL;
918        goto name_error;
919   }
920
921    pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
922
923    parent = get_device(dev->parent);
924    setup_parent(dev, parent);
925
926    /* use parent numa_node */
927    if (parent)
928        set_dev_node(dev, dev_to_node(parent));
929
930    /* first, register with generic layer. */
931    /* we require the name to be set before, and pass NULL */
932    error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
933    if (error)
934        goto Error;
935
936    /* notify platform of device entry */
937    if (platform_notify)
938        platform_notify(dev);
939
940    error = device_create_file(dev, &uevent_attr);
941    if (error)
942        goto attrError;
943
944    if (MAJOR(dev->devt)) {
945        error = device_create_file(dev, &devt_attr);
946        if (error)
947            goto ueventattrError;
948
949        error = device_create_sys_dev_entry(dev);
950        if (error)
951            goto devtattrError;
952
953        devtmpfs_create_node(dev);
954   }
955
956    error = device_add_class_symlinks(dev);
957    if (error)
958        goto SymlinkError;
959    error = device_add_attrs(dev);
960    if (error)
961        goto AttrsError;
962    error = bus_add_device(dev);
963    if (error)
964        goto BusError;
965    error = dpm_sysfs_add(dev);
966    if (error)
967        goto DPMError;
968    device_pm_add(dev);
969
970    /* Notify clients of device addition. This call must come
971     * after dpm_sysf_add() and before kobject_uevent().
972     */
973    if (dev->bus)
974        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
975                       BUS_NOTIFY_ADD_DEVICE, dev);
976
977    kobject_uevent(&dev->kobj, KOBJ_ADD);
978    bus_probe_device(dev);
979    if (parent)
980        klist_add_tail(&dev->p->knode_parent,
981                &parent->p->klist_children);
982
983    if (dev->class) {
984        mutex_lock(&dev->class->p->class_mutex);
985        /* tie the class to the device */
986        klist_add_tail(&dev->knode_class,
987                &dev->class->p->class_devices);
988
989        /* notify any interfaces that the device is here */
990        list_for_each_entry(class_intf,
991                  &dev->class->p->class_interfaces, node)
992            if (class_intf->add_dev)
993                class_intf->add_dev(dev, class_intf);
994        mutex_unlock(&dev->class->p->class_mutex);
995   }
996done:
997    put_device(dev);
998    return error;
999 DPMError:
1000    bus_remove_device(dev);
1001 BusError:
1002    device_remove_attrs(dev);
1003 AttrsError:
1004    device_remove_class_symlinks(dev);
1005 SymlinkError:
1006    if (MAJOR(dev->devt))
1007        devtmpfs_delete_node(dev);
1008    if (MAJOR(dev->devt))
1009        device_remove_sys_dev_entry(dev);
1010 devtattrError:
1011    if (MAJOR(dev->devt))
1012        device_remove_file(dev, &devt_attr);
1013 ueventattrError:
1014    device_remove_file(dev, &uevent_attr);
1015 attrError:
1016    kobject_uevent(&dev->kobj, KOBJ_REMOVE);
1017    kobject_del(&dev->kobj);
1018 Error:
1019    cleanup_device_parent(dev);
1020    if (parent)
1021        put_device(parent);
1022name_error:
1023    kfree(dev->p);
1024    dev->p = NULL;
1025    goto done;
1026}

device_add是設備注冊的第二步,負責將設備添加到系統。

設備的私有數據結構可能還沒有被分配,在第900~904行分配。在第911~919行設置并驗證設備的名字。然后,調用setup_parent設置設備的父親,同時確定了設備在sysfs中的位置。這是一個比較復雜的過程,我們后面會專門討論它。

第932~934行,調用kobject_add將設備的內嵌kobject添加到系統,這將在sys/devices/目錄下添加一個以設備名為名字的子目錄;

然后在sysfs文件系統中創建屬性文件:第940~942行創建uevent屬性對應的文件,這是一個只寫的屬性文件,可以從用戶空間寫事件到這個文件,從而手動產生事件。

第944~954行,如果設備有設備號:

? 在設備目錄下創建dev屬性對應的文件,這是一個只讀的屬性文件,用戶空間讀取這個文件時,將返回設備的設備號,以##:##的形式;

? 調用device_create_sys_dev_entry函數(文件drivers/base/core.c)在sys/block/或sys/char/下創建一個到設備所在目錄的符號鏈接,鏈接名為##:##的形式的設備號。具體選擇目錄取決于設備所屬的類。如果屬于block_class,即為塊設備,將在sys/block下創建;如果屬于其他類,或者不屬于任何類,都會在sys/char/下創建。

第956~958行,調用device_add_class_symlinks,如果設備屬于某個類,則為設備創建和類相關的符號鏈接。這又包括下面的步驟:

? 在設備目錄下,創建到所屬類的符號鏈接,鏈接名為subsystem;

? 在所屬類的目錄下,創建到設備的符號鏈接,鏈接名為設備名;

? 如果設備有父設備,并且設備不為分區,在設備目錄下,創建到其父設備的符號鏈接,鏈接名為device。

第959~961行,調用device_add_attrs為設備的默認屬性添加對應的文件。

第962~964行,調用bus_add_device,如果設備有指定總線類型,則會將設備添加到該總線類型。這又包括下面的步驟:

? 為設備添加該總線類型下設備的公有屬性文件,這些屬性由設備所屬bus_type類型變量的dev_attrs域指針所指向;

? 在總線類型子目錄的devices目錄下創建到該設備的符號鏈接,鏈接名為設備名;

? 在設備的目錄下創建到總線類型的符號鏈接,鏈接名為subsystem;

? 將設備連接到總線類型的設備鏈表。

在bus_add_device函數過程中,還會調用make_deprecated_bus_links函數,后者被早期的版本用于在設備目錄下創建另外一個到總線類型的符號鏈接,鏈接名為bus。如果內核編譯未指定CONFIG_SYSFS_DEPRECATED選項,那么這個函數為空,什么也不做。

至此,設備自身的添加工作已經完成,第977行調用kobject_uevent向用戶空間報告KOBJ_ADD事件。

然后調用bus_probe_device試圖將設備綁定到某個驅動,這個過程我們也在后面專門討論。

第979~981行,如果指定了parent,則將設備添加到它的孩子鏈表中。

此外,第983~995行,如果設備設置了class域,則將設備添加到類的設備鏈表。而且,對于注冊到該類的每個接口,如果定義了add_dev回調函數,則調用它。這個動作是系統對設備熱插處理在類上的體現。

到現在,設備注冊過程已經比較完整了,除了第924行的setup_parent函數和第978行的bus_probe_device函數。它們的作用都很重要,并且過程也比較復雜,下面用專門的篇幅來討論。

1.設備在sysfs的位置

setup_parent函數的根本目的就是確定設備在sysfs文件系統中的位置。之所以說它重要,是因為理解內核對象之間的層次關系就是理解Linux驅動模型的一個關鍵。

分析device的結構,可以看到有兩個parent域:

? device描述符的parent域,指向設備的父設備;

? device描述符內嵌的kobject的parent域,決定了設備在sysfs文件系統中的位置。

setup_parent函數(其代碼如程序2-16所示)的實質就是為后者賦值。它傳入兩個參數:第一個為指向設備本身的device描述符的指針;第二個為指向其父設備的device描述符的指針。

程序2-16 函數setup_parent()代碼(摘自文件drivers/base/core.c)

device_register()→device_add()→setup_parent()

676static void setup_parent(struct device *dev, struct device *parent)
677{
678    struct kobject *kobj;
679    kobj = get_device_parent(dev, parent);
680    if (kobj)
681        dev->kobj.parent = kobj;
682}

setup_parent函數將主要工作交給get_device_parent函數(其代碼如程序2-17所示)。若后者的返回值非NULL,則保存在設備的內嵌對象的parent域。

程序2-17 函數get_device_parent()代碼(摘自文件drivers/base/core.c)

device_register()→device_add()→setup_parent()→get_device_parent()

599static struct kobject *get_device_parent(struct device *dev,
600                     struct device *parent)
601{
602    int retval;
603
604    if (dev->class) {
605        static DEFINE_MUTEX(gdp_mutex);
606        struct kobject *kobj = NULL;
607        struct kobject *parent_kobj;
608        struct kobject *k;
609
610        /*
611         * If we have no parent, we live in "virtual".
612         * Class-devices with a non class-device as parent, live
613         * in a "glue" directory to prevent namespace collisions.
614         */
615        if (parent == NULL)
616            parent_kobj = virtual_device_parent(dev);
617        else if (parent->class)
618            return &parent->kobj;
619        else
620            parent_kobj = &parent->kobj;
621
622        mutex_lock(&gdp_mutex);
623
624        /* find our class-directory at the parent and reference it */
625        spin_lock(&dev->class->p->class_dirs.list_lock);
626        list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
627            if (k->parent == parent_kobj) {
628                kobj = kobject_get(k);
629                break;
630           }
631        spin_unlock(&dev->class->p->class_dirs.list_lock);
632        if (kobj) {
633            mutex_unlock(&gdp_mutex);
634            return kobj;
635       }
636
637        /* or create a new class-directory at the parent device */
638        k = kobject_create();
639        if (!k) {
640            mutex_unlock(&gdp_mutex);
641            return NULL;
642       }
643        k->kset = &dev->class->p->class_dirs;
644        retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
645        if (retval < 0) {
646            mutex_unlock(&gdp_mutex);
647            kobject_put(k);
648            return NULL;
649       }
650        /* do not emit an uevent for this simple "glue" directory */
651        mutex_unlock(&gdp_mutex);
652        return k;
653   }
654
655    if (parent)
656        return &parent->kobj;
657    return NULL;
658}

決定device描述符的內嵌kobject的父kobject的因素有如下三個:

? 設備本身device描述符的parent域;

? 設備本身device描述符的class域;

? 父設備device描述符的class域(如果父設備存在的話)。

Linux當前版本的處理方法和以前版本稍有不同。我們只考慮當前版本的處理規則,即代碼中未定義CONFIG_SYS_DEPRECATED的部分。設置設備內嵌內核對象的父對象的情況有6種,如圖2-12所示,具體說明如下。

? device有class,有parent,且parent有class,不管parent->class和device->class是否相同,parent_kobj都為parent內嵌的kobject;這分別對應圖中的情況①和②。

? device有class,有parent,且parent沒有class,則parent_obj為新創建的一個kobject,保存在class的class_dirs(這是個kset)里。這個新創建的kobject的parent為parent內嵌的kobject,名稱為device的class_name,也就是說,它對應的sysfs目錄為sys/devices/.../parent_name/ class_name/。這對應圖中的情況③。

圖2-12 設置設備內嵌內核對象的父對象

? device有class,沒有parent,則parent_kobj為新創建的一個kobject,保存在class的class_dirs(這是個kset)里。這個新創建的kobject的parent為sys/devices/virtual/目錄對應的kobject,名稱為device的class_name,也就是說,它對應的sys目錄為sys/devices/virtual/class_name。這對應圖中的情況④。

? device無class,有parent,則device->kobj.parent = parent.kobj。這對應圖中的情況⑤。

? device無class,無parent,則device->kobj.parent為空。這對應圖中的情況⑥。應該說這是一種理論上的可能,在Linux代碼中沒有找到這樣的例子(萬一出現,設備目錄將直接建在sys/之下)。我們將它列舉在這里,純粹出于完整性的考慮。

對于情況③和④的補充說明:class_dirs是用于保存那些為了將其父設備為空,或其父設備的class為空的設備掛入sys/devices/子樹,而新創建的內核對象,它將這些內核對象鏈接在一起。這些內核對象將作為前述設備內嵌內核對象的父對象。

綜合上述情況,deivce在sysfs中的目錄有以下幾種形式:

? sys/devices/.../parent_name/device_name;

? sys/devices/.../parent_name/class_name/device_name;

? sys/devices/virtual/class_name/device_name。

如果采用新規則,所有的設備都放在sys/devices/目錄下(這不同于舊規則將設備分散在sys/devices/和sys/class/兩個目錄中),這個系統的設備樹統一在sys/devices/一個目錄中。

2.將設備綁定到驅動

將設備添加到系統,還需要嘗試將設備綁定到驅動。我們現在來看對bus_probe_device函數(其代碼如程序2-18所示)的調用。它只有一個參數,即指向設備本身的device描述符的指針;

程序2-18 函數bus_probe_device()代碼(摘自文件drivers/base/bus.c)

device_register()→device_add()→bus_probe_device()

509void bus_probe_device(struct device *dev)
510{
511    struct bus_type *bus = dev->bus;
512    int ret;
513
514    if (bus && bus->p->drivers_autoprobe) {
515        ret = device_attach(dev);
516        WARN_ON(ret < 0);
517   }
518}

從字面上理解,這個函數的功能是自動探測設備,探測的主體是總線類型上的驅動。探測需要設備所在總線類型的支持。如果設備不屬于任何總線類型,或者總線類型沒有自動探測能力(即driver_autoprobe域為0),則什么也不需要做;否則調用device_attach函數,其代碼如程序2-19所示。

程序2-19 函數device_attach()代碼(摘自文件drivers/base/dd.c)

device_register()→device_add()→bus_probe_device()→device_attach()

238int device_attach(struct device *dev)
239{
240    int ret = 0;
241
242    device_lock(dev);
243    if (dev->driver) {
244        ret = device_bind_driver(dev);
245        if (ret == 0)
246            ret = 1;
247        else {
248            dev->driver = NULL;
249            ret = 0;
250       }
251   } else {
252        pm_runtime_get_noresume(dev);
253        ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
254        pm_runtime_put_sync(dev);
255   }
256    device_unlock(dev);
257    return ret;
258

device_attach函數的目的是試圖將設備綁定到某個驅動,返回1表示設備被綁定到驅動,返回0表示沒有找到匹配的驅動。在調用device_attach時,存在兩種情況:

? 已經為設備指定了驅動,即設備的driver域已經設置,就直接調用device_bind_driver函數。如果它返回錯誤,說明設備沒辦法被綁定到這個驅動,那么就清零設備的driver域,將錯誤報告給調用者;

? 否則遍歷總線類型的驅動鏈表,調用__device_attach函數(其代碼如程序2-20所示)嘗試將設備綁定到這些驅動。如果設備不能被綁定到任何驅動,那么bus_for_each_drv宏最終會返回0,我們直接回傳給調用者,報告錯誤。

我們只跟蹤后面一種情況的執行流程。事實上,前面一種情況,所做的工作只是其中的一部分。

程序2-20 函數__device_attach()代碼(摘自文件drivers/base/dd.c)

device_register()→device_add()→bus_probe_device()→device_attach()→__device_attach()

214static int __device_attach(struct device_driver *drv, void *data)
215{
216    struct device *dev = data;
217
218    if (!driver_match_device(drv, dev))
219        return 0;
220
221    return driver_probe_device(drv, dev);
222}

記住,__device_attach函數試圖綁定具體驅動和具體設備。同樣,它返回1表示設備被綁定到驅動,返回0表示沒有找到匹配的驅動。這個返回值被bus_for_each_drv宏進行了非正常思維的解釋:返回1被當成一種“錯誤”,遇到即退出循環,放棄處理驅動鏈表中余下的驅動。

它調用driver_match_device函數(其代碼如程序2-21所示)檢查是否匹配,匹配失敗,返回0。若匹配成功,則調用driver_probe_device函數(其代碼如程序2-22所示)進一步探測,并向調用者傳遞它的返回值:成功時為1;否則為0。

程序2-21 函數driver_match_device()代碼(摘自文件drivers/base/base.h)

device_register()→device_add()→bus_probe_device()→device_attach()→__device_attach()
→driver_match_device()

120static inline int driver_match_device(struct device_driver *drv,
121                   struct device *dev)
122{
123    return drv->bus->match ? drv->bus->match(dev, drv) : 1;
124}

驅動和設備的匹配是子系統特定的,Linux驅動模型必須通過回調函數來實現。子系統在聲明總線類型時,可以給出其match方法。它以device和device_driver作為其參數,在具體實現中,需要先轉換為對應的設備和驅動描述符(例如pci_dev和pci_driver)。match回調函數判斷驅動是否能“匹配”設備。若是,返回1;否則返回0。如果沒定義match回調函數,我們直接返回1。認為是總線類型自動匹配了驅動和設備。

程序2-22 函數driver_probe_device()代碼(摘自文件drivers/base/dd.c)

device_register()→device_add()→bus_probe_device()→device_attach()→__device_attach()
→driver_probe_device()

196int driver_probe_device(struct device_driver *drv, struct device *dev)
197{
198    int ret = 0;
199
200    if (!device_is_registered(dev))
201        return -ENODEV;
202
203    pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
204         drv->bus->name, __func__, dev_name(dev), drv->name);
205
206    pm_runtime_get_noresume(dev);
207    pm_runtime_barrier(dev);
208    ret = really_probe(dev, drv);
209    pm_runtime_put_sync(dev);
210
211    return ret;
212}

進一步探測設備要求設備已經注冊。如果設備被注冊,對本驅動以及后續驅動的匹配都沒有意義,返回負的錯誤碼,這是一種真正意義上的錯誤。

這個函數調用了多個與電源管理有關的函數,因為電源管理不是本書的重點,我們跳過它們,直接跟蹤第208行的really_probe函數,其代碼如程序2-23所示。

程序2-23 函數really_probe()代碼(摘自文件drivers/base/dd.c)

device_register()→device_add()→bus_probe_device()→device_attach()→__device_attach()
→driver_probe_device()→really_probe()

104static int really_probe(struct device *dev, struct device_driver *drv)
105{
106    int ret = 0;
107
108    atomic_inc(&probe_count);
109    pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
110         drv->bus->name, __func__, drv->name, dev_name(dev));
111    WARN_ON(!list_empty(&dev->devres_head));
112
113    dev->driver = drv;
114    if (driver_sysfs_add(dev)) {
115        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
116            __func__, dev_name(dev));
117        goto probe_failed;
118   }
119
120    if (dev->bus->probe) {
121        ret = dev->bus->probe(dev);
122        if (ret)
123            goto probe_failed;
124   } else if (drv->probe) {
125        ret = drv->probe(dev);
126        if (ret)
127            goto probe_failed;
128   }
129
130    driver_bound(dev);
131    ret = 1;
132    pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
133         drv->bus->name, __func__, dev_name(dev), drv->name);
134    goto done;
135
136probe_failed:
137    devres_release_all(dev);
138    driver_sysfs_remove(dev);
139    dev->driver = NULL;
140
141    if (ret != -ENODEV && ret != -ENXIO) {
142        /* driver matched but the probe failed */
143        printk(KERN_WARNING
144            "%s: probe of %s failed with error %d\n",
145            drv->name, dev_name(dev), ret);
146   }
147    /*
148     * Ignore errors returned by ->probe so that the next driver can try
149     * its luck.
150     */
151    ret = 0;
152done:
153    atomic_dec(&probe_count);
154    wake_up(&probe_waitqueue);
155    return ret;
156}

最終,如果設備可以綁定到驅動,則需要執行下列操作:

? 第113行將設備的driver域指向驅動;

? 第114~118行,調用driver_sysfs_add在sysfs文件系統中“綁定”,即在驅動對應的目錄下創建目標為設備的符號鏈接,鏈接名使用設備名,同時在設備對應的目錄下創建目標為驅動的符號鏈接,鏈接名為“driver”;

? 第120~128行,如果設備的總線類型給出了probe方法,則調用之;否則如果設備的驅動給出了probe方法,調用之。probe函數返回0表示成功;否則返回錯誤碼;

? 第130行調用driver_bound將設備鏈入驅動的設備鏈表。

如果一切正常,really_probe函數返回1,表示匹配成功。如果中間出現問題,回滾已經做過的操作,向調用者返回0。

我們再看一下總線類型和驅動的probe回調函數(如圖2-13所示)。這兩個函數都是在設備可以被綁定到驅動的情況下,用來對設備進行初始化。若成功,返回0;否則返回錯誤碼。

對于PCI子系統,總線類型的probe回調函數是pci_device_probe,驅動的probe回調函數為NULL。PCI設備的初始化是具體HBA特定的,比如PCI-SCSI適配器要在這時開始SCSI設備掃描,因此pci_device_probe進而會調用PCI驅動的probe回調函數,即具體HBA驅動的探測方法實現。注意,這里驅動的probe回調函數是指device_driver結構的probe域;而PCI驅動的probe回調函數是指pci_driver結構的probe域。

對于SCSI子系統,總線類型的probe回調函數為NULL,驅動的probe回調函數隨具體SCSI驅動的定義提供。例如對于SCSI磁盤驅動,該回調函數被定義為sd_probe,它將為這個SCSI磁盤設備分配通用磁盤描述符。

總結一下,Linux驅動模型會為設備在sysfs文件系統中創建的符號鏈接列舉如下。相應代碼分散在前面的函數里,集中整理于此。

類相關的符號鏈接:

? device對應的目錄下,名稱為subsystem,指向device->class對應的目錄;

圖2-13 PCI驅動和SCSI驅動的probe回調函數

? device->class對應的目錄下,名稱為device_name,指向device對應的目錄,僅當device對應的目錄不在device->class對應的目錄下,并且device不是塊設備分區的情況下才創建;

? device對應的目錄下,名稱為device,指向device->parent對應的目錄,僅當device有parent并且device不是塊設備分區的情況下才創建。

總線類型相關的符號鏈接:

? device->bus的devices_kset對應的目錄下,名稱為device_name,指向device對應的目錄;

? device對應的目錄下,名稱為subsystem,指向device->bus對應的目錄。

驅動相關的符號鏈接:

? device->driver對應的目錄下,名稱為device_name,指向device對應的目錄;

? device對應的目錄下,名稱為driver,指向device->driver對應的目錄。

注意:在device對應的目錄下,名稱為subsystem的符號鏈接,可能指向device->class或是device->bus對應的目錄。這里并不會出現沖突的情況,即便對應SCSI設備(可能屬于總線類型scsi_bus_type或類sg_sysfs_class)。它在scsi_device結構中定義了兩個內嵌對象域(內嵌通用對象域和內嵌類對象域)。

和device_register對應的函數是device_unregister,用于從系統注銷設備。

2.6.3 驅動

同總線類型和設備一樣,在Linux驅動模型中表示一個驅動也需要兩個結構體:device_driver和driver_private。這兩個結構體同時存在,并且一一對應。程序員在遵循驅動模型編程時只需要實現device_driver,其結構中的域如表2-17所示,而結構體driver_private中的域如表2-18所示。

表2-17 device_driver結構中的域(來自文件include/linux/device.h)

表2-18 driver_private結構中的域(來自文件drivers/base/base.h)

驅動和總線類型與設備的關系是:驅動的bus域指向所屬的總線類型,以knode_bus域為連接件鏈入到它以klist_drivers域為表頭的驅動鏈表。驅動還有一個綁定到它的設備鏈表,以klist_devices為表頭,設備鏈入此鏈表的連接件為knode_driver域。

在驅動被加載時,需要向Linux驅動模型注冊,為此調用它提供的公共函數driver_register(其代碼如程序2-24所示)。這個函數具有唯一的參數,指向device_driver描述符的指針。

程序2-24 函數driver_register()代碼(摘自文件drivers/base/driver.c)

driver_register()

222int driver_register(struct device_driver *drv)
223{
224    int ret;
225    struct device_driver *other;
226
227    BUG_ON(!drv->bus->p);
228
229    if ((drv->bus->probe && drv->probe) ||
230      (drv->bus->remove && drv->remove) ||
231      (drv->bus->shutdown && drv->shutdown))
232        printk(KERN_WARNING "Driver '%s' needs updating - please use "
233            "bus_type methods\n", drv->name);
234
235    other = driver_find(drv->name, drv->bus);
236    if (other) {
237        put_driver(other);
238        printk(KERN_ERR "Error: Driver '%s' is already registered, "
239            "aborting...\n", drv->name);
240        return -EBUSY;
241   }
242
243    ret = bus_add_driver(drv);
244    if (ret)
245        return ret;
246    ret = driver_add_groups(drv, drv->groups);
247    if (ret)
248        bus_remove_driver(drv);
249    return ret;
250}

在注冊驅動前,必須為驅動指定了總線類型,并且該總線類型已經注冊到Linux驅動模型,第227行確保這一點。

第229~233行給出一個警告消息,如果總線類型和驅動都定義了相同名字的函數。實際上,Linux驅動模型只會使用其中的一個。前面,在really_probe函數中,我們已經看到過了。

第235~241行調用driver_find在總線類型的驅動鏈表中查找該驅動名,如果找到,表明這個驅動名已經在使用中,我們只好返回錯誤。

否則繼續,在第243行調用bus_add_driver添加驅動到系統,我們馬上會詳細分析它。

如果一切正常,第246行添加驅動的屬性組文件并結束驅動注冊過程。

就驅動注冊來講,函數bus_add_driver(該函數的代碼如程序2-25所示)無疑是最主要的函數,因為大部分工作還是要在總線類型上做的。這個函數返回0表示成功,否則表示錯誤。

程序2-25 函數bus_add_driver代碼(摘自文件drivers/base/bus.c)

driver_register()→bus_add_driver()

648int bus_add_driver(struct device_driver *drv)
649{
650    struct bus_type *bus;
651    struct driver_private *priv;
652    int error = 0;
653
654    bus = bus_get(drv->bus);
655    if (!bus)
656        return -EINVAL;
657
658    pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
659
660    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
661    if (!priv) {
662        error = -ENOMEM;
663        goto out_put_bus;
664   }
665    klist_init(&priv->klist_devices, NULL, NULL);
666    priv->driver = drv;
667    drv->p = priv;
668    priv->kobj.kset = bus->p->drivers_kset;
669    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
670                   "%s", drv->name);
671    if (error)
672        goto out_unregister;
673
674    if (drv->bus->p->drivers_autoprobe) {
675        error = driver_attach(drv);
676        if (error)
677            goto out_unregister;
678   }
679    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
680    module_add_driver(drv->owner, drv);
681
682    error = driver_create_file(drv, &driver_attr_uevent);
683    if (error) {
684        printk(KERN_ERR "%s: uevent attr (%s) failed\n",
685            __func__, drv->name);
686   }
687    error = driver_add_attrs(bus, drv);
688    if (error) {
689        /* How the hell do we get out of this pickle? Give up */
690        printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
691            __func__, drv->name);
692   }
693
694    if (!drv->suppress_bind_attrs) {
695        error = add_bind_files(drv);
696        if (error) {
697            /* Ditto */
698            printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
699                __func__, drv->name);
700       }
701   }
702
703    kobject_uevent(&priv->kobj, KOBJ_ADD);
704    return 0;
705
706out_unregister:
707    kobject_put(&priv->kobj);
708    kfree(drv->p);
709    drv->p = NULL;
710out_put_bus:
711    bus_put(bus);
712    return error;
713}

因為要把驅動添加到總線類型,自然驅動描述符的bus域應該已經設置。第654~656行的代碼確保這一點。

一般來說,驅動的私有數據結構應該還沒有被分配,在第660~664行進行分配,并在第666和667行將它和驅動結構相互關聯在一起。

然后,第668行,將驅動通過私有數據結構的內嵌kobj關聯到總線類型的drivers_kset,接下來,第669行執行kobject_init_and_add會添加驅動到sysfs文件系統,將它放在總線類型所對應目錄的drivers子目錄下。

第673~678行,如果總線類型支持自動探測,我們調用driver_attach。這個函數(其代碼如程序2-26所示)在后面分析。

第679行將驅動添加到總線類型的驅動鏈表。

接下來,在驅動目錄下創建各種屬性文件:

? 第682~685行,調用driver_create_file在驅動子目錄下添加uevent屬性文件,即sys/bus/###/drivers/###/ uevent。這是一個只寫的屬性文件,可以從用戶空間寫事件到這個文件,從而手動產生事件;

? 第687~692行,調用driver_add_attrs為驅動的默認屬性添加對應的文件;

? 第694~701行,如果允許通過sysfs文件系統從用戶空間綁定/松綁,則調用add_bind_files,這會在驅動目錄下創建bind和unbind兩個屬性文件。這兩個文件都是只寫的,被用來手動綁定某個設備,或者松綁某個設備。

至此,驅動自身的添加工作已經完成,第703行調用kobject_uevent函數向用戶空間報告KOBJ_ADD事件。

程序2-26 函數driver_attach()代碼(摘自文件drivers/base/dd.c)

driver_register()→bus_add_driver()→driver_attach()

299int driver_attach(struct device_driver *drv)
300{
301    return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
302}

driver_attach函數的目的是試圖將驅動去綁定所有可能的設備,成功返回0,否則返回非零值。

它調用bus_for_each_dev遍歷總線類型的設備鏈表,調用是傳入__driver_attach作為回調函數(其代碼如程序2-27所示)。

程序2-27 函數__driver_attach()代碼(摘自文件drivers/base/dd.c)

driver_register()→bus_add_driver()→driver_attach()→__driver_attach()

261static int __driver_attach(struct device *dev, void *data)
262{
263    struct device_driver *drv = data;
264
265    /*
266     * Lock device and try to bind to it. We drop the error
267     * here and always return 0, because we need to keep trying
268     * to bind to devices and some drivers will return an error
269     * simply if it didn't support the device.
270     *
271     * driver_probe_device() will spit a warning if there
272     * is an error.
273     */
274
275    if (!driver_match_device(drv, dev))
276        return 0;
277
278    if (dev->parent)    /* Needed for USB */
279        device_lock(dev->parent);
280    device_lock(dev);
281    if (!dev->driver)
282        driver_probe_device(drv, dev);
283    device_unlock(dev);
284    if (dev->parent)
285        device_unlock(dev->parent);
286
287    return 0;
288}

到了__driver_attach函數,我們又碰到了具體驅動和具體設備的綁定問題。除了為某些USB設備所加入的特定鎖代碼,大體流程和__device_attach是相同的。這里也調用了driver_match_device和driver_probe_device兩個函數。需要注意,這個函數只返回0,因為當前沒有一種錯誤可以阻止我們讓驅動繼續嘗試匹配設備鏈表的下一個設備。

關于device_attach和driver_attach函數,做一個小結:

? device_attach函數的目的是試圖將設備綁定到某個驅動。它以回調函數__device_attach調用bus_for_each_drv。device_attach和__device_attach函數都用返回1表示設備被綁定到驅動,返回0表示沒有找到匹配的驅動;

? driver_attach函數的目的是試圖將驅動去綁定所有可能的設備。它以回調函數__driver_attach調用bus_for_each_dev。driver_attach和__driver_attach函數都用返回0表示成功,返回1表示錯誤。但實際上__driver_attach函數只會返回0。

和driver_register對應的函數是driver_unregister,用于從系統注銷驅動。

2.6.4 類

Linux驅動模型中的類用相互關聯的class和class_private域表示。class結構中的域如表2-19所示,class_private結構中的域如表2-20所示。

表2-19 class結構中的域(來自文件include/linux/class.h)

表2-20 class_private結構中的域(來自文件drivers/base/base.h)

每個類有一個內嵌的kset,即class_subsys域,因為它,在類被注冊后,會在/sys/class下創建一個和類名對應的子目錄。類有兩個鏈表,一個鏈接屬于該類的所有設備,另一個鏈接注冊到這個類的接口。另外,需要說明的是class_dirs域,它的作用在前面已經介紹過。

子系統要注冊類,需調用Linux驅動模型提供的class_register方法。它被定義為一個宏,在文件include/linux/device.h,是__class_register的簡單封裝。我們直接看__class_register,其函數的代碼如程序2-28所示。

程序2-28 函數__class_register()代碼(摘自文件drivers/base/class.c)

__class_register()

154int __class_register(struct class *cls, struct lock_class_key *key)
155{
156    struct class_private *cp;
157    int error;
158
159    pr_debug("device class '%s': registering\n", cls->name);
160
161    cp = kzalloc(sizeof(*cp), GFP_KERNEL);
162    if (!cp)
163        return -ENOMEM;
164    klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put);
165    INIT_LIST_HEAD(&cp->class_interfaces);
166    kset_init(&cp->class_dirs);
167    __mutex_init(&cp->class_mutex, "struct class mutex", key);
168    error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
169    if (error) {
170        kfree(cp);
171        return error;
172   }
173
174    /* set the default /sys/dev directory for devices of this class */
175    if (!cls->dev_kobj)
176        cls->dev_kobj = sysfs_dev_char_kobj;
177
178#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
179    /* let the block class directory show up in the root of sysfs */
180    if (cls != &block_class)
181        cp->class_subsys.kobj.kset = class_kset;
182#else
183    cp->class_subsys.kobj.kset = class_kset;
184#endif
185    cp->class_subsys.kobj.ktype = &class_ktype;
186    cp->class = cls;
187    cls->p = cp;
188
189    error = kset_register(&cp->class_subsys);
190    if (error) {
191        kfree(cp);
192        return error;
193   }
194    error = add_class_attrs(class_get(cls));
195    class_put(cls);
196    return error;
197}

第161~163行分配類的私有數據結構,接著進行一些初始化。第168~172行設置類的class_subsys域的內嵌kobject的名字為類的名字,這就是顯示在/sys/class/下的類的目錄名。

早期的類被分開放置。block類位于/sys/目錄下,而其他類則位于/sys/class/目錄下,最新版本的內核作了統一。所有類都被放在/sys/class/目錄下,這就是第178~184行代碼產生的效果。

第186和187行,將類結構和類私有數據結構相互關聯起來。

第189~193行調用kset_register注冊類的class_subsys,這最終創建類在sysfs文件系統中的對應目錄。第194行,調用add_class_attrs在類的目錄下創建默認的類屬性文件。

和class_register對應的函數是class_unregister,用于從系統注銷類。

2.6.5 接口

相對于前面的結構,接口比較簡單,它用class_interface(其結構中的域如表2-21所示)來表示。

表2-21 class_interface結構中的域(來自文件include/linux/device.h)

每個接口都是相對于某個類的,通過class域指向它。接口被鏈接到類的接口鏈表,類的class_interfaces域為表頭,接口的node域為連接件。

Linux驅動模型提供用于類接口注冊的公共函數為class_interface_register,該函數的代碼如程序2-29所示。

程序2-29 函數class_interface_register()代碼(摘自文件drivers/base/class.c)

class_interface_register()

447int class_interface_register(struct class_interface *class_intf)
448{
449    struct class *parent;
450    struct class_dev_iter iter;
451    struct device *dev;
452
453    if (!class_intf || !class_intf->class)
454        return -ENODEV;
455
456    parent = class_get(class_intf->class);
457    if (!parent)
458        return -EINVAL;
459
460    mutex_lock(&parent->p->class_mutex);
461    list_add_tail(&class_intf->node, &parent->p->class_interfaces);
462    if (class_intf->add_dev) {
463        class_dev_iter_init(&iter, parent, NULL, NULL);
464        while ((dev = class_dev_iter_next(&iter)))
465            class_intf->add_dev(dev, class_intf);
466        class_dev_iter_exit(&iter);
467   }
468    mutex_unlock(&parent->p->class_mutex);
469
470    return 0;
471}

函數要求傳入的類接口不為NULL,并且設置了class域,即被關聯到某個類(第453~454行)。

注冊類接口的具體操作只有兩個:

? 將類接口添加到類的接口鏈表,這是第461行;

? 如果類接口提供了add_dev方法,對類的設備鏈表上的每個設備調用之,這是第462~467行。

需要強調的是后面一點,也是“熱插拔”的一種表現。類接口雖然不綁定到設備,但它可以通過自定義的回調方法作用于設備,并且無論是先發現設備,還是先注冊接口。

和class_interface_register對應的函數是class_interface_unregister,用于從系統注銷接口。

主站蜘蛛池模板: 八宿县| 当雄县| 张家界市| 乳源| 咸宁市| 清丰县| 金寨县| 定安县| 平江县| 南郑县| 龙里县| 丹江口市| 沂源县| 临澧县| 沈阳市| 阿合奇县| 滁州市| 木兰县| 澄迈县| 望谟县| 尼玛县| 耿马| 顺昌县| 紫云| 大庆市| 腾冲县| 普陀区| 仁化县| 彭阳县| 阿拉善右旗| 天津市| 孟州市| 高要市| 周至县| 临西县| 澄江县| 宜州市| 泗洪县| 汉川市| 玛曲县| 鄂伦春自治旗|