Linux カーネル ドライバーは、Linux システムの最も重要なコンポーネントの 1 つです。これらは、オペレーティング システムがハードウェアを正しく識別して使用できるように、ハードウェア デバイスと通信する役割を果たします。ただし、Linux カーネル ドライバーの開発は簡単な作業ではありません。この記事では、Linux カーネル ドライバーの実装方法を詳しく掘り下げ、読者に包括的な理解とガイダンスを提供します。
Linux ドライバーでは、USB ドライバーの最下層は USB ホスト コントローラー ハードウェアです。その上で実行されるのが USB ホスト コントローラー ドライバーです。ホスト コントローラーの上は USB コア層、上位層は USBデバイス ドライバー層 (U ディスク、マウス、USB からシリアル ポートなどのデバイス ドライバーをホスト コンピューターに挿入します)。
したがって、ホスト側の階層では、実装する USB ドライバには、USB ホスト コントローラ ドライバと、USB デバイスに挿入される USB デバイスを制御する USB デバイス ドライバの 2 つのカテゴリがあり、前者は USB デバイスの通信を制御することになります。ホストと一緒に。 Linux カーネル USB コアは、USB ドライバー管理とプロトコル処理の主要な作業を担当します。ホスト コントローラー ドライバーとデバイス ドライバーの間の USB コアは非常に重要であり、その機能には次のものが含まれます: いくつかのデータ構造、マクロ、関数を定義することにより、デバイス ドライバーに上向きのプログラミング インターフェイスを提供し、USB ホスト コントローラーにプログラミング インターフェイスを提供しますドライバーの下位管理、グローバル変数によるシステム全体の USB デバイス情報の維持、完全なデバイスのホットプラグ制御、バス データ送信制御など。
Linux カーネルの USB デバイス側ドライバーは、UDC ドライバー、ガジェット API、およびガジェット ドライバーの 3 つのレベルに分かれています。 UDC ドライバーはハードウェアに直接アクセスし、USB デバイスとホスト間の基礎となる通信を制御し、ハードウェア関連の操作のためのコールバック関数を上位層に提供します。現在のガジェット API は、UDC ドライバーのコールバック関数の単純なラッパーです。ガジェット ドライバーは、特に USB デバイス機能の実装を制御し、デバイスが「ネットワーク接続」、「プリンター」、「USB 大容量ストレージ」などの機能を表示できるようにします。ガジェット API を使用して UDC を制御し、上記の機能を実装します。ガジェット API は、下位層の UDC ドライバーを上位層のガジェット ドライバーから分離するため、Linux システムで USB デバイス側ドライバーを作成するときに、関数の実装を基礎となる通信から分離することができます。
これら 4 つのレベルの簡単な説明は次のとおりです:
通常、デバイスには 1 つ以上の構成があります
多くの場合、構成には 1 つ以上のインターフェイスが含まれます。
インターフェイスにエンドポイントがないか、複数のエンドポイントが存在します。
\2) 構成: struct usb_host_config (一度に有効にできる構成は 1 つだけです)
3) USB インターフェイス: struct usb_interface (USB コアはそれを USB デバイス ドライバーに渡し、USB デバイス ドライバーはその後の制御を担当します。USB インターフェイスは基本的な機能を表し、各 USB ドライバーはインターフェイスを制御します。物理ハードウェア デバイスには複数のドライバーが必要な場合があります。)
4) エンドポイント: struct usb_host_endpoint、それに含まれる実際のエンドポイント情報は別の構造体: struct usb_endpoint_descriptor (エンドポイント記述子、すべての USB 固有のデータが含まれます)。
USB 通信の最も基本的な形式は、エンドポイントと呼ばれるものを介します。 USB エンドポイントは、一方向 (ホストからデバイス (出力エンドポイントと呼ばれる) またはデバイスからホストへ (入力エンドポイントと呼ばれる)) にのみデータを転送できます。エンドポイントは一方向のパイプと考えることができます。
USB エンドポイントには 4 つの異なるタイプがあり、それぞれ異なるデータ送信方法を備えています:
1) コントロール
コントロール エンドポイントは、USB デバイスのさまざまな部分へのアクセスを制御するために使用され、通常はデバイスの構成、デバイス情報の取得、デバイスへのコマンドの送信、またはデバイス ステータス レポートの取得に使用されます。これらのエンドポイントは通常、より小さいです。すべての USB デバイスには「エンドポイント 0」と呼ばれる制御エンドポイントがあり、接続時に USB コアがデバイスを構成するために使用します。 USB プロトコルは、コントロール エンドポイントがデバイスにデータを送信するのに十分な帯域幅が常に残されていることを保証します。
2) 割り込み INTERRUPT
USB ホストがデバイスからデータを要求するたびに、割り込みエンドポイントは少量のデータを固定速度で転送します。これは、USB キーボードとマウスの主要なデータ転送方法です。また、USB デバイスにデータを転送してデバイスを制御するためにも使用されます。通常、大量のデータの転送には使用されません。 USB プロトコルは、割り込みエンドポイントがデバイスにデータを送信するのに十分な帯域幅が常に残っていることを保証します。
3) 一括バッチ
バルク エンドポイントは、大量のデータを転送するために使用されます。これらのエンドポイントは通常、割り込みエンドポイントよりもはるかに大きく、データ損失が許されない状況でよく使用されます。 USB プロトコルは、転送が特定の時間枠内に完了することを保証しません。 BULK パケット全体を送信するのに十分なスペースがバス上にない場合、BULK パケットは複数のパケットに分割されて送信されます。これらのエンドポイントは、プリンター、USB 大容量ストレージ、および USB ネットワーク デバイスで一般的に使用されます。
4) 等時性
アイソクロナス エンドポイントも大量のデータをバッチで転送しますが、このデータは配信されることが保証されません。これらのエンドポイントは、データ損失に対処でき、データの継続的なフローの維持に依存するデバイスで使用されます。オーディオやビデオ機器など。
コントロール エンドポイントとバルク エンドポイントは非同期データ転送に使用されますが、割り込みエンドポイントとアイソクロナス エンドポイントは周期的です。これは、これらのエンドポイントが一定時間に継続的にデータを送信するように設定されており、USB コアがそれらに対応する帯域幅を予約していることを意味します。
USB デバイス ドライバーが usb_submit_urb を呼び出して urb 要求を送信すると、 int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb) が呼び出されて、この urb が urb_list の末尾に追加されます。 (hcd: ホスト コントローラー ドライバー、対応するデータ構造体 struct usb_hcd)
すべての USB 通信は要求 -> 応答モードであり、USB デバイスはホストにデータを積極的に送信しません。データの書き込み: USB デバイス ドライバーは USB デバイスに urb リクエストを送信します。USB デバイスはデータを返す必要はありません。データの読み取り: USB デバイス ドライバーは USB デバイスに urb リクエストを送信し、USB デバイスはデータを返す必要があります。
USB デバイス ドライバーは、urb を介してすべての USB デバイスと通信します。 urb は、struct urb 構造体 (include/linux/usb.h) で記述されます。
urb は、特定の USB デバイスの特定のエンドポイントに非同期でデータを送受信します。 USB デバイス ドライバーは、ドライバーのニーズに応じて、複数の URB を 1 つのエンドポイントに割り当てたり、複数の異なるエンドポイントに 1 つの URB を再利用したりできます。デバイスの各エンドポイントは URB キューを処理するため、キューが空になる前に複数の URB を同じエンドポイントに送信できます。
UURB の一般的なライフサイクルは次のとおりです:
(1) 作成済み;
(2) 特定の USB デバイスに割り当てられた特定のエンドポイント;
(3) USB コアに提出;
(4) USB コアによって特定のデバイスの特定の USB ホスト コントローラー ドライバーに送信されます。
(5) USB ホスト コントローラー ドライバーによって処理され、デバイスに送信されます。
(6) 上記の動作が完了すると、USB ホストコントローラドライバは USB デバイスドライバに通知します。
urb は、それを送信したドライバーによっていつでもキャンセルできます。デバイスが削除された場合、urb は USB コアによってキャンセルできます。 urbs は動的に作成され、内部参照カウントを含むため、最後のユーザーが解放したときに自動的に解放されます。
8.1 アーブを送信
urb が適切に作成され、初期化されたら、USB コアに送信して USB デバイスに送信できます。これは、関数 sb_submit_urb.
を呼び出すことで実現されます。int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
パラメータ:
struct urb *urb: 送信された urb
へのポインタ
gfp_t mem_flags: kmalloc 呼び出しに渡されるのと同じパラメータを使用して、タイムリーにメモリ バッファを割り当てる方法を USB コアに指示します。
GFP_ATOMIC
この値は、次の条件が満たされる限り使用する必要があります:
\1) 呼び出し元は、URB 終了ハンドラー、割り込みハンドラー、下半分、タスクレット、またはタイマー コールバック関数内にあります。
\2) 呼び出し元はスピン ロックまたは読み取り/書き込みロックを保持しています。セマフォが保持されている場合、この値は不要であることに注意してください。
\3) 現在の状態 -> 状態は TASK_RUNNING ではありません。ドライバが自ら現在の状態を変更しない限り、状態は常に TASK_RUNNING である必要があります。
ドライバーはブロック I/O 処理にあります。また、すべてのストレージ タイプのエラー処理にも使用する必要があります。
前述のカテゴリに当てはまらないその他すべての状況
8.2 アーブ終了処理ルーチン
usb_submit_urb が正常に呼び出され、urb の制御が USB コアに転送された場合、関数は 0 を返します。それ以外の場合は、負のエラー コードが返されます。関数呼び出しが成功した場合、urb が正常に呼び出されたときに終了処理ルーチンが呼び出されます。終了しました。一度呼び出されます。この関数が呼び出されると、USB コアは URB を完了し、制御をデバイス ドライバーに返します。
urb を終了して終了処理ルーチンを呼び出す状況は 3 つだけです:
(1)urb がデバイスに正常に送信され、デバイスが正しい確認を返した場合、urb のステータス変数は 0. に設定されます。
(2) エラーが発生し、urb 構造体のステータス変数にエラー値が記録されます。
(3) USB コアからの URB リンク解除これは、ドライバーが usb_unlink_urb または usb_kill_urb を呼び出して、送信された URB をキャンセルするように USB コアに指示したとき、または URB がドライバーに送信されてデバイスがシステムから削除されたときに発生します。
##\9. 検出と切断
9.1 検出機能の解析
プローブ コールバック関数では、USB デバイス ドライバーは、USB デバイスの管理に使用するローカル構造を初期化し、必要なすべてのデバイス情報をローカル構造に保存する必要があります (通常、この時点で行う方が簡単であるため)。デバイス、USB ドライバーを使用すると、通常、デバイスのエンドポイント アドレスとバッファ サイズが検出されます。
この記事では、カーネルドライバーのフレームワーク、カーネルモジュールの記述、デバイスドライバーの登録と登録解除など、Linuxカーネルドライバーの実装方法を詳しく紹介します。この記事を読むことで、読者は Linux カーネル ドライバーの実装原理をより深く理解し、自身の開発作業にさらに多くの参照と支援を提供できると考えています。
以上がLinux USB ドライバーのワークフローの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。