Linuxドライバのプラットフォームバスの詳細説明

Linuxドライバのプラットフォームバスの詳細説明

1. プラットフォームバスの紹介

1.1. Linuxドライバの分離と階層化

1.1.1. Linuxドライバの分離

まず、Linux ドライバーの分離についてお話ししましょう。Linux オペレーティング システムは、さまざまな種類の CPU での実行をサポートしています。CPU ごとにデバイス ドライバーが異なるため、Linux カーネルには大量のコードが蓄積されています。これらのコードには、同じデバイスについてほぼ同じ記述があるため、カーネル コードが非常に冗長になっています。 I2C 経由で MPU6050 を制御する CPU を例に挙げます。

図から、各プラットフォームにはホスト ドライバー セットとデバイス ドライバー セットがあることがわかります。各プラットフォームの I2C コントローラーは異なるため、各プラットフォームには独自のホスト ドライバーが必要です。ただし、誰もが使用する MPU6050 は同じであるため、デバイス ドライバー コード セットを共有することは完全に可能です。改善されたフレームワークは次のとおりです。

もちろん、これは I2C の下にある MPU6050 デバイスにのみ適用されます。実際の状況では、多くのデバイスが I2C の下にマウントされることになります。この考え方に基づいて、次のようなフレームワークを作成できます。

実際の開発では、I2C ホスト ドライバーは半導体メーカーが作成し、デバイス ドライバーもデバイス メーカーが作成します。デバイスがどの I2C インターフェイスに接続されているか、I2C の速度はどのくらいかなどのデバイス情報を提供するだけで済みます。これは、デバイス ドライバーからデバイス情報を削除することと同等であり、デバイス ドライバーも標準的な方法を使用してデバイス情報を取得します (デバイス ツリーからデバイス情報を取得するなど)。これは、ドライバーが駆動のみを担当し、デバイス (情報) がデバイスのみを担当するのと同じです。必要なのは、この 2 つを一致させる方法を見つけることだけです。この一致作業を行うのはバスであり、これが Linux のバス ドライバー デバイス モデルを構成します。構造図は以下のとおりです。

1.2. プラットフォーム主導型モデル

上記では、デバイス ドライバーの分離について説明し、バス ドライバー デバイス モデルを取得しました。このバスは、通常、I2C、SPI、USB などのバスと呼ばれています。しかし問題は、一部のデバイスは特定のバスを経由する必要がないことです。ここでプラットフォーム バスが登場します。

ここで注目すべきは、プラットフォーム バスは USB、SPI、I2C などのバスとは異なる仮想バスであるということです。 SoC と、LED、タイマー、ブザーなどの一部の周辺機器はメモリ アドレス指定空間を通じてアドレス指定されるため、CPU はこれらのデバイスと通信するためにバスを必要とせず、ハードウェア内にそのようなバスは存在しないため、仮想と呼ばれます。ただし、カーネルはこれらのデバイスを統合的に管理する必要があるため、メモリを介して直接アドレス指定されるこれらのデバイス用に仮想プラットフォーム バスが作成され、メモリを介して直接アドレス指定されるすべてのデバイスがこの仮想バスにマップされます。

プラットフォームバスの利点:

1. プラットフォーム バスを介して、プラットフォーム バスに搭載されているすべてのデバイスを通過できます。

2. デバイスとドライバーを分離します。プラットフォームバスを介して、デバイスとドライバーが別々に登録されます。プローブ機能があるため、デバイスに一致するドライバーをいつでも検出できます。一致が成功すると、ドライバーはカーネルに登録されます。

3. 1 つのドライバーを同じ種類の複数のデバイスで使用できます。この機能は、ドライバー登録プロセス中にデバイスを横断する操作があるために実現されます。

2. プラットフォームフレームワーク

2.1、プラットフォームバス

Linux カーネルは、バスを表すために bus_type 構造体を使用します。私たちが使用する I2C、SPI、USB はすべてこの構造体を使用して定義されます。構造は次のとおりです。

/* include/linux/device.h */
 
構造体バスタイプ{
    const char *name; /* バス名*/
    定数char *デバイス名; 
    構造体デバイス *dev_root;
    構造体デバイス属性 *dev_attrs;
    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)(構造体デバイス *dev、構造体 kobj_uevent_env *env);
    int (*probe)(構造体デバイス *dev);
    int (*remove)(構造体デバイス *dev);
    void (*shutdown)(構造体デバイス *dev);
 
    int (*オンライン)(構造体デバイス *dev);
    int (*オフライン)(構造体デバイス *dev);
    int (*suspend)(構造体デバイス *dev、pm_message_t状態);
    int (*resume)(構造体デバイス *dev);
    定数構造体dev_pm_ops *pm;
    定数構造体iommu_ops *iommu_ops;
    構造体subsys_private *p;
    構造体 lock_class_key ロックキー;
};

プラットフォーム バスは、bus_type 型の定数です。この変数は Linux カーネルによって値が割り当てられており、その構造体メンバーに対応する関数もカーネルに記述されているため、定数と呼ばれます。

定義は次のとおりです。

/* ドライバー/ベース/プラットフォーム.c */
 
構造体バスタイププラットフォームバスタイプ = {
    .name = "プラットフォーム",
    .dev_groups = プラットフォーム開発グループ、
    .match = platform_match, /* マッチング関数 */
    .uevent = プラットフォーム_uevent、
    .pm = &platform_dev_pm_ops,
};

platform_bus_type の platform_match は、ドライバーとデバイスを一致させるための前述の関数です。この関数は次のように定義されます。

/* ドライバー/ベース/プラットフォーム.c */
 
静的 int プラットフォームマッチ (構造体デバイス *dev、構造体デバイスドライバ *drv)
{
    構造体platform_device *pdev = to_platform_device(dev);
    構造体platform_driver *pdrv = to_platform_driver(drv);
 
    /*driver_override が設定されている場合は、一致するドライバーにのみバインドします*/
    (pdev->driver_override) の場合
        !strcmp(pdev->driver_override, drv->name) を返します。
 
    /* デバイス ツリー OF タイプ マッチング ドライバー ベース クラスの of_match_table 内の互換性マッチング テーブルが、デバイス ツリー内の各デバイス ノードの互換性属性と比較されます。これらが同じであれば、マッチングが成功したことを意味します */
    if (of_driver_match_device(dev, drv))
        1 を返します。
 
    /* ACPI マッチング */
    acpi_driver_match_device(dev, drv) の場合
        1 を返します。
 
    /* id_table はプラットフォーム ドライバーの id_table 配列と一致し、多くの ID 情報が格納されます */
    (pdrv->id_table) の場合
        platform_match_id(pdrv->id_table, pdev) != NULL を返します。
 
    /* 名前のマッチングは、プラットフォーム ドライバーとデバイス内の名前情報を直接大まかに比較します */
    戻り値 (strcmp(pdev->name, drv->name) == 0);
}

このマッチング関数がいつ、どこで使用されるかは、未定のままにしておくほうがよいでしょう。

2.2 プラットフォームドライバー

2.2.1、プラットフォーム ドライバーの定義

プラットフォーム ドライバーは platform_driver 構造体によって表され、その内容は次のとおりです。

/* include/linux/platform_device.h */
 
構造体プラットフォームドライバ{
    int (*probe)(struct platform_device *); /* プラットフォーム ドライバーは、プラットフォーム デバイスを一致させた後にこのプローブ関数を実行します */
    int (*削除)(構造体platform_device *);
    void (*シャットダウン)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t 状態);
    int (*resume)(構造体platform_device *);
    struct device_driver ドライバー; /* ドライバー基本クラス*/
    const struct platform_device_id *id_table; /* id_table テーブル*/
    ブール型 prevent_deferred_probe;
};

platform_driver の const struct platform_device_id *id_table は id_table テーブルであり、プラットフォーム バスが id_table テーブル マッチング方法を使用してドライバーとデバイスをマッチングするときに使用されます。この id_table テーブルは実際には配列であり、その中の各要素の型は platform_device_id です。platform_device_id は、次の内容を持つ構造体です。

構造体プラットフォームデバイスID{
    char name[プラットフォーム名_サイズ];
    kernel_ulong_t ドライバーデータ;
};

platform_driver では、driver はドライバー ベース クラスであり、ドライバーの最も基本的な属性に相当します。さまざまなバスの属性は、platform_driver 構造体に格納されます。

ドライバー基本クラス構造体 device_driver の内容は次のとおりです。

/* include/linux/device.h */
 
構造体デバイスドライバ{
    const char *name; /* プラットフォームバスを使用してデバイスとドライバーを一致させる4番目の方法は、2つの名前フィールドを直接大まかに一致させることです*/
    構造体バスタイプ *バス;
    構造体モジュール *owner;
    定数char *mod_name; 
    ブール値suppress_bind_attrs; 
    const struct of_device_id *of_match_table; /* デバイスツリーを使用するときにドライバーが使用するマッチングテーブル*/
    定数構造体acpi_device_id *acpi_match_table;
    int (*probe) (構造体デバイス *dev);
    int (*remove) (構造体デバイス *dev);
    void (*shutdown) (構造体デバイス *dev);
    int (*suspend) (構造体デバイス *dev、pm_message_t 状態);
    int (*resume) (構造体デバイス *dev);
    const 構造体 attribute_group **グループ;
    定数構造体dev_pm_ops *pm;
    構造体driver_private *p;
};

ドライバーの Of_match_table もマッチング テーブルです。このマッチング テーブルは、プラットフォーム バスがデバイス ツリーを使用してドライバーとデバイスをマッチングするために使用されます。これも配列です。配列要素はすべて of_device_id 型です。この型の構造は次のとおりです。

/* include/linux/mod_devicetable.h */
 
デバイスID構造体{
    文字名[32];
    char型[32];
    charcompatible[128]; /* デバイスツリーマッチングを使用する場合、デバイスノードのcompatible属性値がof_match_table内の各項目のcompatibleと比較されます。それらが等しい場合、デバイスとドライバーが正常に一致していることを意味します*/
    定数void *データ;
};

2.2.2、プラットフォーム ドライバーの登録

platform_driver 構造体でプラットフォーム ドライバーを定義した後、platform_driver_register 関数を使用してプラットフォーム ドライバーを Linux カーネルに登録します。関数の一般的なフローは次のとおりです。

プラットフォーム ドライバー レジスタ (drv)
    -> __プラットフォーム_ドライバー_レジスタ
        -> drv->driver.probe = platform_drv_probe; /* プラットフォーム ドライバーのドライバー ベース クラス drier のプローブ関数に platform_drv_probe 関数を割り当てます */
        -> driver_registe (&drv->driver) /* ドライバーベースクラスのドライバーを Linux カーネルに登録する */
            -> ...... 
                -> drv->driver->probe /* 最後に、ドライバベースクラスドライバのプローブ関数を実行します。
                                                            実際、それは上記のplatform_drv_probe関数です*/
                    -> プラットフォーム_drv_プローブ
                        -> drv->probe /* platform_drv_probe 関数はプラットフォーム ドライバー drv のプローブ関数を実行します*/

上記の分析では、driver_register (&drv->driver) から drv->driver->probe までのステップを省略記号に置き換えました。次に、これを分析してみましょう。

driver_register(&drv->driver)
    -> bus_add_driver /* バスにドライバーを追加 */
        -> ドライバーアタッチ 
            -> bus_for_each_dev /* バス下にある各デバイスを検索する、つまりトラバーサル操作*/ 
                -> __driver_attach /* 各デバイスがこの関数を呼び出します*/
                    -> driver_match_device /* 一致するかどうか確認 */
                        -> バスの下の一致関数を呼び出す -> driver_probe_device /* 一致が成功した後にこの関数を実行する */
                        -> 本当に_プローブ 
                            -> drv->probe /* drv の下でプローブ関数を実行する*/

driver_register 関数のフローを見ると、バス マッチ関数がここで走査されて使用されることがわかります。これにより、前に残した「バス マッチ関数はどこで使用されるのか」という疑問に答えることができます。マッチが成功すると、ドライバーのプローブ関数に入ります。

platform_driver_register 関数のフローを見ると、プラットフォーム ドライバーを Linux カーネルに登録するプロセスには、ドライバーとデバイスのマッチングをトラバースするプロセスが含まれるという結論を導き出すことができます。マッチングが成功すると、最終的にプラットフォーム ドライバーのプローブ関数が実行されます。ドライバー ベース クラスのドライバーのプローブ関数とプロセス内の platform_drv_probe 関数は、この目的を達成するための中継関数にすぎません。

注目すべきは、最終的に実行されるプラットフォーム ドライバー プローブ関数は私たちが作成したものであり、主導権は私たちの手に戻ったということです。

2.3、プラットフォーム機器

2.3.1、プラットフォームデバイスの定義

使用している Linux バージョンがデバイス ツリーをサポートしている場合は、デバイス ツリーにデバイスを記述します。デバイス ツリーをサポートしていない場合は、プラットフォーム デバイスを定義する必要があります。ここで考慮する必要がある点は、バスの下のマッチング関数 match が最初にデバイス ツリーをマッチングし、次に id_table テーブルをマッチングし、最後に name フィールドをマッチングすることです。デバイス ツリーがサポートされている場合は、デバイス ツリー ノードでデバイス情報を直接変更できます。カーネルが起動すると、デバイス ツリー ノードが自動的に走査されます。一致が成功すると、次のステップで使用するための platform_device が自動的に生成されます。デバイス ツリーでない場合、この platform_device は開発者によって記述されます。

ここではデバイス ツリーを使用せず、プラットフォーム デバイスを独自に定義します。プラットフォーム デバイスは、次のように定義される platform_device 構造体によって表されます。

/* include/linux/platform_device.h */
 
構造体プラットフォームデバイス{
    const char *name; /* デバイス名。対応するプラットフォームドライバの名前と同じである必要があります。
                                           そうしないと、デバイスは対応するドライバーと一致しません*/
    整数ID; 
    ブール id_auto;
    構造体デバイス dev;
    u32 リソース数; 
    構造体リソース *リソース;
    const 構造体 platform_device_id *id_entry;
    char *driver_override; /* 一致を強制するドライバー名 */
    /* MFD セル ポインタ */
    構造体 mfd_cell *mfd_cell;
    /* アーキテクチャ固有の追加 */
    構造体 pdev_archdata アーキテクチャデータ;
};

2.4. プラットフォームマッチングプロセス

プラットフォーム バスをドライバーとデバイスに一致させるプロセスについては、実際に上で詳しく説明しました。ここで、それを要約してもう一度確認してみましょう。

前述のように、バス下のドライバーとデバイスのマッチングは、バス下のマッチ関数によって実現されます。異なるバスに対応するマッチ関数は間違いなく異なります。カーネルがそれらを作成するので、これについて心配する必要はありません。使用するプラットフォーム バスに対応する一致関数は、platform_match 関数です。この関数を分析してみましょう。

プラットフォームマッチ
    -> of_driver_match_device /* デバイスツリーのマッチング */
    -> acpi_driver_match_device /* ACPI マッチング */
    -> platform_match_id /* プラットフォームドライバー->id_table 一致*/
    -> strcmp(pdev->name, drv->name) /* 名前が一致*/

上記のマッチング関数の簡単な分析により、マッチング関数は最初にデバイス ツリーをマッチングし、次に id_table テーブルをマッチングし、最後に名前フィールドをブルート フォースでマッチングすることがわかります。デバイス ツリーをサポートする Linux バージョンの場合は、デバイス ツリーのマッチングをすぐに実行できます。デバイス ツリーがサポートされていない場合は、プラットフォーム デバイスを定義してから、id_tabale テーブルまたは名前のマッチングを使用する必要があります。通常は、名前のマッチングが選択されます。

次に、デバイス ツリー条件でのマッチング プロセスを詳しく見てみましょう。

of_driver_match_device /* of 関数は一般的にデバイスツリーに使用され、ヒントも提供します*/
    ->of_match_device (drv->of_match_table、dev)    
        -> マッチノード  
            -> __of_match_node
                -> __of_device_is_compatible
                    -> __of_find_property(device, "compatible", NULL) /* 互換性のあるプロパティ値を取得する*/

上記の分析から、マッチング プロセスでは最終的に、ドライバー ベース クラスの of_match_table 内のcompatible プロパティとデバイス ツリー ノード内のcompatible プロパティが比較されることがわかります。これは、デバイスツリーとプラットフォームバスを接続するメカニズムであり、これにより、デバイスツリーの対応するノードにデバイス情報を書き込み、ドライバーを個別に書き込むという目的、つまり、先ほど説明したドライバー分離を実現します。

3. まとめ

実際の開発プロセスでは、プラットフォーム バス モデルはカーネル内ですでに定義されているため、実際に記述する必要はありません。プラットフォーム バス モデルの分析は、主にドライバーとデバイスをどのように一致させるか、つまり、デバイスを挿入したときに対応するドライバーをどのように見つけるか、またはドライバーを挿入したときに対応するデバイスをどのように見つけるかを把握し、最後にプローブ関数を呼び出すことです。実際、ドライバーが先でデバイスが後であっても、デバイスが先でドライバーが後であっても、最終マッチ後に最初に行うべきことはドライバーのプローブ機能を実行することなので、中間の感情的な絡み合いの紆余曲折を無視して、最終プローブ機能に直接焦点を当てることができます。

Linux ドライバーのプラットフォーム バスの詳細な説明については、これで終わりです。Linux ドライバーのプラットフォーム バスに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

<<:  HTML 名 ID とクラスの違い_PowerNode Java アカデミー

>>:  CSSアニメーションを途中で止めて姿勢を維持する方法

推薦する

CentOS 8 カスタム ディレクトリ インストール nginx (チュートリアルの詳細)

1. ツールとライブラリをインストールする# PCRE は、Perl 互換の正規表現ライブラリを含...

JavaScript オブジェクト (詳細)

目次JavaScript オブジェクト1. 定義2. オブジェクトの分類3. オブジェクトを定義する...

Nginx の高同時実行最適化の実践

1. チューニングの必要性​ 私は、どのように書けばいいのか本当に分からないので、共有するために最適...

Baota LinuxパネルにFTP接続できない問題の解決方法の詳細な説明

Alibaba Cloud Server を使用している場合は、セキュリティ グループ設定でポート ...

MySQLカバーインデックスの利点

一般的な提案は、WHERE 条件のインデックスを作成することですが、これは実際には一方的です。インデ...

Dockerコンテナの接続と通信の実装

ポート マッピングは、Docker を別のコンテナーに接続する唯一の方法ではありません。 Docke...

インスピレーションを得るための7つのクールなダイナミックウェブサイトデザイン

デザインの分野では、毎年さまざまなデザインのトレンドや流行があります。たとえば、近年のレスポンシブデ...

Nginx レイヤー 4 負荷分散構成ガイド

1. レイヤー4負荷分散の概要レイヤー 4 ロード バランシングとは何ですか?いわゆる 4 層負荷分...

コンポーネントベースのフロントエンド開発プロセスの詳細な説明

背景<br />フロントエンドを担当する学生は、ページが多すぎると煩雑になるため、開発プ...

uniappの無痛トークンリフレッシュ方法の詳細な説明

フロントエンドがインターフェースを要求すると、バックエンドでインターフェースが定義されます。ステータ...

MySQLデータのバックアップ方法の選択と考え方

目次1. rsync、cpでファイルをコピーする2. xxxをoutfile構文に選択する3. 遅延...

React プロジェクトにおける axios カプセル化と API インターフェース管理の詳細な説明

目次序文インストール導入環境の切り替え傍受を要求するレスポンスインターセプションAPIの統合管理要約...

Linux でプロセスを効果的に管理するための 8 つのコマンド

序文プロセス管理の役割:サーバーの健全性状態を判定する: プロセスの状態 (メモリ、CPU 占有率な...

Docker 可視化グラフィックツール portainer の詳細な説明

目次1. ポーテナーの紹介2. Portainer アーキテクチャの概要3. Portainerのイ...

Docker Tomcat のアクセス インターフェイスが表示されないのはなぜですか?

質問:オリジン サーバーはターゲット リソースの表現を見つけることができないか、既存の表現を公開した...