<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux 驅(qū)動(dòng)開發(fā) | 驅(qū)動(dòng)世界里的宏偉建筑

          共 15823字,需瀏覽 32分鐘

           ·

          2021-03-17 07:42

          哈嘍,我是老吳。

          是否每一個(gè)上進(jìn)的人都會(huì)覺得自己還可以再努力一點(diǎn)?

          事情到了最后,只要沒達(dá)成目的,總能把失敗的原因歸為 "沒有再努力一點(diǎn)"。

          但是,對(duì)努力的最大錯(cuò)誤認(rèn)知就是:時(shí)間越長,過程越痛苦,代表我越努力。

          想一想,是否有更合理的努力方式?

          以下是正文:

          一、什么是 device model?
          二、device model 的 3 個(gè)核心概念
          三、bus、device、driver 是如何關(guān)聯(lián)的?
          四、bus、device、driver 最簡單示例
          五、小結(jié)
          六、相關(guān)參考

          一、什么是 device model?

          Linux 的 device model 是一個(gè)旨在統(tǒng)一管理所有設(shè)備驅(qū)動(dòng)的模型。

          它猶如一棟規(guī)模宏大的建筑:

          以 kobject、kset、attribute 等作為基本的建筑材料,

          構(gòu)造出支撐驅(qū)動(dòng)世界的 bus、device、driver 三大組件,

          最后通過 sysfs 在各種基礎(chǔ)的建筑材料之間建立彼此的互聯(lián)層次關(guān)系,并向外界提供了與建筑內(nèi)設(shè)施進(jìn)行互動(dòng)的文件接口。

          點(diǎn)擊查看大圖

          device model 有什么作用?

          可以將 device 的硬件描述 和 driver 進(jìn)行分離,提升 driver 的代碼復(fù)用率;

          可以對(duì) device 進(jìn)行分類;

          可以遍歷 device 和 driver;

          可以更好地呈現(xiàn)設(shè)備的拓?fù)潢P(guān)系;

          可以通過 sysfs 訪問設(shè)備;

          可以讓設(shè)備支持熱插拔;

          ...

          為了控制篇幅,本文將重點(diǎn)放在與驅(qū)動(dòng)工程師關(guān)系最緊密的 bus、device、driver 3 個(gè) 組件。

          二、device model 的 3 個(gè)核心概念

          device model 里有 3 個(gè)核心的概念:

          • bus

          • device

          • driver

          什么是 bus?

          bus 代表一種總線,例如 I2C、SPI、USB 等。

          bus 是 Linux 設(shè)備驅(qū)動(dòng)模型這種建筑的核心框架,系統(tǒng)中的設(shè)備和驅(qū)動(dòng)都依附在其周圍。

          啟動(dòng)系統(tǒng)后,可以通過 /sys/bus 可以查看系統(tǒng)里當(dāng)前有哪些總線。

          bus 由 struct bus_type 來描述:

          struct bus_type {
           const char *name;
           const char *dev_name;
           struct device *dev_root;
           const struct attribute_group **bus_groups;
           const struct attribute_group **dev_groups;
           const struct attribute_group **drv_groups;

           int (*match)(struct device *dev, struct device_driver *drv);
           int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
           int (*probe)(struct device *dev);
           int (*remove)(struct device *dev);
           void (*shutdown)(struct device *dev);

           ...
           struct subsys_private *p;
           struct lock_class_key lock_key;
          };

          不需要一下子了解各個(gè)成員的作用,用到的時(shí)候再說明。

          重點(diǎn)關(guān)注成員:

          • int (*match)(struct device *dev, struct device_driver *drv),用于判斷掛在該 bus 上的設(shè)備和驅(qū)動(dòng)是否匹配的回調(diào)函數(shù);

          • int (*probe)(struct device *dev),如果 bus 具有探測設(shè)備的能力,則會(huì)提供該回調(diào)函數(shù);

          • struct subsys_private *p,用于管理 bus 上的設(shè)備和驅(qū)動(dòng)的數(shù)據(jù)結(jié)構(gòu);

          注冊(cè) bus 的 api:

          int bus_register(struct bus_type *bus);

          什么是 device ?

          device 代表了某個(gè)設(shè)備。

          由 struct device 來描述:

          struct device {
           struct device *parent;
           struct device_private *p;
           struct kobject kobj;
           const char *init_name;
           const struct device_type *type;
           struct mutex mutex;
           struct bus_type *bus;
           struct device_driver *driver;
           void *platform_data;
           void *driver_data;
              ...
          }

          重點(diǎn)關(guān)注成員:

          • struct kobject kobj,內(nèi)核對(duì)象;

          • struct bus_type *bus,設(shè)備所在的總線;

          • struct device_driver *driver,和設(shè)備綁定在一起的驅(qū)動(dòng),如果還沒綁定,則為 NULL;

          注冊(cè) device 的 api:

          int device_register(struct device *dev)

          什么是 driver?

          driver 代表了設(shè)備驅(qū)動(dòng)。

          由 struct device_driver 來描述:

          struct device_driver {
           const char *name;
           struct bus_type *bus;

           struct module *owner;
           const char *mod_name; /* used for built-in modules */

           bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
           enum probe_type probe_type;

           const struct of_device_id *of_match_table;
           const struct acpi_device_id *acpi_match_table;

           int (*probe) (struct device *dev);
           int (*remove) (struct device *dev);
           void (*shutdown) (struct device *dev);
           int (*suspend) (struct device *dev, pm_message_t state);
           int (*resume) (struct device *dev);
           const struct attribute_group **groups;

           const struct dev_pm_ops *pm;

           struct driver_private *p;
          };

          重點(diǎn)關(guān)注成員:

          • struct bus_type *bus;
          • int (*probe) (struct device *dev);

          值得一提的是,總線控制器也是一種設(shè)備。

          例如 I2C 總線控制器這個(gè)設(shè)備,對(duì)應(yīng)的驅(qū)動(dòng)為 I2C controller driver。

          而掛在 I2C 總線上的設(shè)備,對(duì)應(yīng)的驅(qū)動(dòng)為 I2C device driver。

          注冊(cè) driver 的 api:

          int driver_register(struct device_driver *drv);

          三、bus、device、driver 是如何關(guān)聯(lián)的?

          device model 最核心的工作就是維護(hù)這三類抽象的實(shí)例,以及建立它們之間的關(guān)聯(lián)關(guān)系。

          bus 如何管理 device 和 driver ?

          在 struct bus_type 中有一個(gè) struct subsys_private *p 指針,它負(fù)責(zé)管理掛在 bus 上的所有設(shè)備和驅(qū)動(dòng),其定義如下:

          struct subsys_private {
           struct kset subsys;
           struct kset *devices_kset;
           struct list_head interfaces;
           struct mutex mutex;

           struct kset *drivers_kset;
           struct klist klist_devices;
           struct klist klist_drivers;
           struct blocking_notifier_head bus_notifier;
           unsigned int drivers_autoprobe:1;
           struct bus_type *bus;

           struct kset glue_dirs;
           struct class *class;
          };

          點(diǎn)擊查看大圖

          兩個(gè) klist 成員以鏈表的形式將該總線上所有的驅(qū)動(dòng)與設(shè)備鏈接到一起。

          struct kset *drivers_kset 和 struct kset *devices_kset 是在向系統(tǒng)注冊(cè)當(dāng)前新總線時(shí)動(dòng)態(tài)生成的容納該總線上所有驅(qū)動(dòng)與設(shè)備的 kset。

          在內(nèi)核里,用 kobject 來表示一個(gè)對(duì)象,kset 則是 kobject set 的縮寫,即內(nèi)核對(duì)象集合。

          內(nèi)核用 kobject 和 kset 等數(shù)據(jù)結(jié)構(gòu)作為原材料,以實(shí)現(xiàn)面向?qū)ο蟮姆绞綐?gòu)建了 device model 的框架。

          最后,device 和 device_driver 的 bus 成員也會(huì)指向總線:

          device 和 driver 的綁定

          無論是通過 device_register() 注冊(cè)一個(gè) device 到 bus 上,

          還是通過 driver_register() 注冊(cè)一個(gè) device_driver 到 bus 上,

          都會(huì)導(dǎo)致 bus 嘗試執(zhí)行 device 和 driver 的綁定行為。

          1. device_register() 觸發(fā)的綁定

          注冊(cè) device 時(shí):

          int device_register(struct device *dev);
           device_add(dev);
            bus_probe_device(dev);
             __device_attach(dev, true);

          __device_attach(dev, true) 會(huì)為 device 遍歷 bus 上的所有 driver:

          bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
           driver_match_device(drv, dev);
            drv->bus->match ? drv->bus->match(dev, drv) : 1;
           driver_probe_device(drv, dev);

          driver_match_device() 通過 bus 里的 match 函數(shù)來判斷是否 device 和 driver 是否匹配,

          是否 match 的判斷標(biāo)準(zhǔn)一般是通過 of_match_table 或者是 id_table 作為衡量的標(biāo)準(zhǔn),

          以 i2c bus 的 match 函數(shù)為例

          static int i2c_device_match(struct device *dev, struct device_driver *drv)
          {
           struct i2c_client *client = i2c_verify_client(dev);
           struct i2c_driver *driver;


           /* Attempt an OF style match */
           if (i2c_of_match_device(drv->of_match_table, client))
            return 1;

           /* Then ACPI style match */
           if (acpi_driver_match_device(dev, drv))
            return 1;

           driver = to_i2c_driver(drv);

           /* Finally an I2C match */
           if (i2c_match_id(driver->id_table, client))
            return 1;

           return 0;
          }

          一旦 match 成功,就會(huì)調(diào)用 driver_probe_device() 以觸發(fā)探測設(shè)備的行為:

          int driver_probe_device(struct device_driver *drv, struct device *dev);
           really_probe(dev, drv);
            if (dev->bus->probe) {
             ret = dev->bus->probe(dev);
            } else if (drv->probe) {
             ret = drv->probe(dev);
            }

          如果 bus 具有探測設(shè)備的能力的話,例如 pci bus, 則會(huì)使用 bus->probe() 探測設(shè)備,

          否則,使用 driver->probe() 探測設(shè)備,driver 的 probe 操作跟具體的硬件設(shè)備掛鉤。

          2. 由 driver_register() 觸發(fā)的綁定

          int driver_register(struct device_driver *drv);
           bus_add_driver(drv);
            driver_attach(drv);

          driver_attach(drv) 會(huì)為 driver 遍歷 bus 上的所有 device:

          int driver_attach(struct device_driver *drv);
           bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
            __driver_attach();
             driver_match_device(drv, dev);
             driver_probe_device(drv, dev);

          和 device_register() 一樣,最終都會(huì)調(diào)用 driver_match_device(drv, dev),

          進(jìn)而通過 bus 里的 match 函數(shù)來判斷是否 device 和 driver 是否匹配。

          同樣地,一旦 match 成功,就會(huì)調(diào)用 driver_probe_device() 以觸發(fā)探測設(shè)備的行為,后續(xù)的操作和注冊(cè)設(shè)備時(shí)是一模一樣的。

          3. device 和 drvier 的綁定關(guān)系

          前面說了綁定是如何被觸發(fā)的,現(xiàn)在來明確一下綁定的具體操作。

          對(duì)于能成功匹配的 device 和 driver,兩者之間的關(guān)系是 N 對(duì) 1,即可以有多個(gè) device 和 1 個(gè) driver 綁定在一起。

          點(diǎn)擊查看大圖

          對(duì)于 device:

          其 driver 成員指向已綁定的 device_driver。

          int driver_probe_device(struct device_driver *drv, struct device *dev)
           really_probe(dev, drv)
          ;
            dev->driver = drv;

          對(duì)于 driver:

          在 device_driver 里鏈表 klist_devices 保存了該 driver 上已綁定的所有 device。

          int driver_probe_device(struct device_driver *drv, struct device *dev)
           really_probe(dev, drv)
          ;
            driver_bound(dev);
             klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

          在 /driver/base/driver.c 中,提供了一些 api,用于遍歷處理 driver 上綁定的所有 device:

          • int driver_for_each_device()
          • struct device *driver_find_device()

          四、bus、device、driver 最簡單示例

          下面的例子,

          構(gòu)造了一個(gè)名為 "simple_bus" 的 bus 實(shí)例。

          simple_bus.c:注冊(cè)了一條名為 "sb" 的 bus,并且提供了注冊(cè) device 和 driver 的 api。

          static int sb_match(struct device *dev, struct device_driver *driver)
          {
           return !strncmp(dev_name(dev), driver->name, strlen(driver->name));
          }

          struct bus_type sb_bus_type = {
           .name = "sb",
           .match = sb_match,
          };

          static ssize_t version_show(struct bus_type *bus, char *buf)
          {
           return snprintf(buf, PAGE_SIZE, "%s\n", Version);
          }

          static BUS_ATTR_RO(version);

          static void sb_dev_release(struct device *dev)
          { }

          int register_sb_device(struct sb_device *sbdev)
          {
              sbdev->dev.bus = &sb_bus_type;
           sbdev->dev.release = sb_dev_release;
              dev_set_name(&sbdev->dev, sbdev->name);
              return device_register(&sbdev->dev);
          }
          EXPORT_SYMBOL(register_sb_device);

          void unregister_sb_device(struct sb_device *sbdev)
          {
           device_unregister(&sbdev->dev);
          }
          EXPORT_SYMBOL(unregister_sb_device);

          static int sb_drv_probe(struct device *dev)
          {
           printk(KERN_INFO"sb_drv probe %s\n", dev_name(dev));
           return 0;
          }

          int register_sb_driver(struct sb_driver *sdrv)
          {
           sdrv->driver.bus = &sb_bus_type;
           sdrv->driver.probe = &sb_drv_probe;
           return driver_register(&sdrv->driver);
          }
          EXPORT_SYMBOL(register_sb_driver);

          void unregister_sb_driver(struct sb_driver *driver)
          {
           driver_unregister(&driver->driver);
          }
          EXPORT_SYMBOL(unregister_sb_driver);

          static int __init sb_bus_init(void)
          {
           int ret;

           ret = bus_register(&sb_bus_type);
           if (ret) {
            printk(KERN_ERR "Unable to register sb bus, failure was %d\n",ret);
            return ret;
           }
           if (bus_create_file(&sb_bus_type, &bus_attr_version))
            printk(KERN_ERR "Unable to create version attribute\n");
           return 0;
          }

          static void sb_bus_exit(void)
          {
           bus_unregister(&sb_bus_type);
          }

          module_init(sb_bus_init);
          module_exit(sb_bus_exit);

          xxx_chip.c:注冊(cè)4個(gè)名為 "chipX" 的 device

          struct xxx_chip {
           char devname[20];
           struct sb_device sdev;
          };

          int chipdev_num = 4;
          struct xxx_chip *chipdev;

          static void chip_register_dev(struct xxx_chip *dev, int index)
          {
           snprintf(dev->devname, sizeof(dev->devname), "chip%d", index);
           dev->sdev.name = dev->devname;
           dev_set_drvdata(&dev->sdev.dev, dev);
           register_sb_device(&dev->sdev);
          }

          int chip_init(void)
          {
              int i;

              chipdev = kmalloc(chipdev_num*sizeof (struct xxx_chip), GFP_KERNEL);

              memset(chipdev, 0, chipdev_num*sizeof (struct xxx_chip));
              for (i = 0; i < chipdev_num; i++) {
            chip_register_dev(chipdev + i, i);
           }

              return 0;
          }

          void chip_cleanup(void)
          {
              int i;
              for (i = 0; i < chipdev_num; i++) {
            unregister_sb_device(&chipdev[i].sdev);
           }
              kfree(chipdev);
          }

          module_init(chip_init);
          module_exit(chip_cleanup);

          xxx_chip_drv.c:注冊(cè)1個(gè)名為 "chip" 的 driver

          static struct sb_driver sculld_driver = {
           .driver = {
            .name = "chip",
           },
          };

          int xxx_chipdrv_init(void)
          {
              return register_sb_driver(&sculld_driver);
          }

          void xxx_chipdrv_cleanup(void)
          {
              unregister_sb_driver(&sculld_driver);
          }

          module_init(xxx_chipdrv_init);
          module_exit(xxx_chipdrv_cleanup);

          運(yùn)行效果:

          root@buildroot:~# insmod simple_bus.ko 
          root@buildroot:~# tree /sys/bus/sb
          /sys/bus/sb
          ├── devices
          ├── drivers
          ├── drivers_autoprobe
          ├── drivers_probe
          ├── uevent
          └── version

          root@buildroot:~# insmod xxx_chip.ko 
          root@buildroot:~# tree /sys/bus/sb
          /sys/bus/sb
          ├── devices
          │   ├── chip0 -> ../../../devices/chip0
          │   ├── chip1 -> ../../../devices/chip1
          │   ├── chip2 -> ../../../devices/chip2
          │   └── chip3 -> ../../../devices/chip3
          ├── drivers
          ├── drivers_autoprobe
          ├── drivers_probe
          ├── uevent
          └── version

          root@buildroot:~# insmod xxx_chip_drv.ko
          sb_drv probe chip0
          sb_drv probe chip1
          sb_drv probe chip2
          sb_drv probe chip3

          root@buildroot:~# tree /sys/bus/sb
          /sys/bus/sb
          ├── devices
          │   ├── chip0 -> ../../../devices/chip0
          │   ├── chip1 -> ../../../devices/chip1
          │   ├── chip2 -> ../../../devices/chip2
          │   └── chip3 -> ../../../devices/chip3
          ├── drivers
          │   └── chip
          │       ├── bind
          │       ├── chip0 -> ../../../../devices/chip0
          │       ├── chip1 -> ../../../../devices/chip1
          │       ├── chip2 -> ../../../../devices/chip2
          │       ├── chip3 -> ../../../../devices/chip3
          │       ├── uevent
          │       └── unbind
          ├── drivers_autoprobe
          ├── drivers_probe
          ├── uevent
          └── version

          通過打印信息可知,device 和 driver 經(jīng)由 bus 判斷是否 match 之后,執(zhí)行了 driver 的 probe() 函數(shù),符合我們前面的分析。

          五、小結(jié)

          Linux 的 device model 是個(gè)非常復(fù)雜的系統(tǒng)。

          從一個(gè)比較高的層次來看,主要由總線、設(shè)備和驅(qū)動(dòng)構(gòu)成。

          內(nèi)核為了實(shí)現(xiàn)這些組件間的相關(guān)關(guān)系,定義了 kobject 和 kset 這樣的基礎(chǔ)底層數(shù)據(jù)結(jié)構(gòu),然后通過 sysfs 文件系統(tǒng)向用戶空間展示發(fā)生在內(nèi)核空間中的各組件間的互聯(lián)層次關(guān)系,并以文件系統(tǒng)接口的方式為用戶空間程序提供了訪問內(nèi)核對(duì)象屬性信息的簡易方法。

          為了控制篇幅,本文并沒有涉及到 kojbect 和 sysfs。

          如果你感興趣的話,去挖掘一下以下內(nèi)容:

          • device model 的底層數(shù)據(jù)結(jié)構(gòu) kojbect、kset 是如何工作的?

          • 內(nèi)核是如何使用 device model 去構(gòu)建 i2c、spi、usb 等驅(qū)動(dòng)框架?

          • device model 和 sysfs 是如何協(xié)同工作的?

          • sysfs 里如何創(chuàng)建屬性文件以訪問設(shè)備驅(qū)動(dòng)?

          • sysfs 里的 class 有什么作用?

          六、相關(guān)參考

          《Linux 設(shè)備驅(qū)動(dòng)》

          • 第 14 章 Linux 設(shè)備模型

          《深入 Linux 設(shè)備驅(qū)動(dòng)程序內(nèi)核機(jī)制》

          • 第 9 章 Linux 設(shè)備驅(qū)動(dòng)模型

          《Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解》

          • 第 5 章 Linux文件系統(tǒng)與設(shè)備文件
          • 第 12 章 Linux設(shè)備驅(qū)動(dòng)的軟件架構(gòu)思想

          Linux/Documentation/driver-model

          • bus.txt
          • class.txt
          • device.txt
          • driver.txt
          • overview.txt

          思考技術(shù),也思考人生

          要學(xué)習(xí)技術(shù),更要學(xué)習(xí)如何生活

          最近在看的書:

          《指數(shù)基金投資指南》

          作者銀行螺絲釘,專注于低估值指數(shù)基金投資,系統(tǒng)性地講解各類指數(shù)基金,以及投資指數(shù)基金的有效策略。

          點(diǎn)擊查看大圖

          收獲了什么?

          • 溫習(xí)了一些關(guān)于基金定投的基礎(chǔ)知識(shí);

          你和我各有一個(gè)蘋果,如果我們交換蘋果的話,我們還是只有一個(gè)蘋果。但當(dāng)你和我各有一個(gè)想法,我們交換想法的話,我們就都有兩個(gè)想法了。

          覺得文章對(duì)你有價(jià)值,不妨 在看 + 分享

          推薦閱讀:

          專輯 | Linux 驅(qū)動(dòng)開發(fā)

          專輯 | 每天一點(diǎn) C

          專輯 | Linux 系統(tǒng)編程





          推薦閱讀:
          專輯|Linux文章匯總
          專輯|程序人生
          專輯|C語言
          我的知識(shí)小密圈

          關(guān)注公眾號(hào),后臺(tái)回復(fù)「1024」獲取學(xué)習(xí)資料網(wǎng)盤鏈接。

          歡迎點(diǎn)贊,關(guān)注,轉(zhuǎn)發(fā),在看,您的每一次鼓勵(lì),我都將銘記于心~



          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国内精品人妻无码久久久影院蜜桃 | 无码成人一区二区三区四区五区 | 婷婷视频| 日本亲子乱婬免费播放 | 欧美日韩一级黄色片 |