ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
デバイスドライバの記述 Oracle Solaris 10 8/11 Information Library (日本語) |
USB デバイスの管理では、ホットプラグ、システム電源管理 (チェックポイントと復元再開)、およびデバイス電源管理を考慮する必要があります。クライアントドライバは必ず、次の図に示す基本的な状態マシンを実装するべきです。詳細については、/usr/include/sys/usb/usbai.h を参照してください。
図 20-4 USB デバイスの状態マシン
この状態マシンと 4 つの状態は、ドライバ固有の状態で拡張できます。0x80 から 0xff のデバイス状態を定義して使用できるのは、クライアントドライバだけです。
USB デバイスはホットプラグをサポートします。USB デバイスの挿入や取り外しはいつでも行えます。クライアントドライバは、オープン状態のデバイスの取り外しと再挿入を処理する必要があります。オープン状態のデバイスを処理するには、ホットプラグコールバックを使用します。クローズ状態のデバイスの挿入と取り外しは、attach(9E) および detach(9E) エントリポイントによって処理されます。
USBA 2.0 フレームワーク では次のイベント通知がサポートされています。
デバイスが電源を入れたまま取り外された場合、クライアントドライバはコールバックを受け取ります。
デバイスが電源を入れたまま取り外されたあとで元に戻された場合、クライアントドライバはコールバックを受け取ります。このイベントコールバックが発生する可能性があるのは、デバイスのドライバインスタンスがオフラインになっていない状態でユーザーがデバイスを元のポートに戻した場合です。ドライバインスタンスがオープン状態に保たれていれば、ドライバインスタンスがオフラインになることはありません。
クライアントドライバは、attach(9E) ルーチン内で usb_register_hotplug_cbs(9F) を呼び出してイベントコールバック用の登録を行う必要があります。ドライバは、detach(9E) ルーチン内で usb_unregister_hotplug_cbs(9F) を呼び出したあとで、設定解除を行う必要があります。
電源を入れたまま USB デバイスを挿入した場合のイベントシーケンスは、次のとおりです。
ハブドライバ hubd(7D) が、ポート接続状態が変化するまで待ちます。
hubd ドライバがポート接続を検出します。
hubd ドライバがデバイス内の情報を列挙し、子デバイスノードを作成し、クライアントドライバを接続します。互換名の定義については、「クライアントドライバのバインド」を参照してください。
クライアントドライバがデバイスを管理します。ドライバの状態は ONLINE です。
電源を入れたまま USB デバイスを取り外した場合のイベントシーケンスは、次のとおりです。
ハブドライバ hubd(7D) が、ポート接続状態が変化するまで待ちます。
hubd ドライバがポート切り離しを検出します。
hubd ドライバが、切り離しイベントを子クライアントドライバに送信します。子クライアントドライバが hubd ドライバまたは usb_mid(7D) マルチインタフェースドライバである場合、子クライアントドライバはそのイベントを子に伝播します。
クライアントドライバが、カーネルスレッドコンテキストで切り離しイベント通知を受け取ります。カーネルスレッドコンテキストでは、ドライバの切り離しハンドラのブロックが可能となります。
クライアントドライバの状態が DISCONNECTED に遷移します。device not responding という完了理由で未処理の入出力転送が失敗します。新しい入出力転送やデバイスノードのオープン試行もすべて失敗します。クライアントドライバがパイプを閉じる必要はありません。ドライバは、デバイスがふたたび接続された場合に復元する必要のあるデバイスやドライバのコンテキストを保存しておく必要があります。
hubd ドライバは、OS デバイスノードとその子を下から順にオフラインにしようとします。
hubd ドライバがデバイスノードをオフラインにしようとした時点でデバイスノードが開いていなかった場合、次のイベントが発生します。
クライアントドライバの detach(9E) エントリポイントが呼び出されます。
デバイスノードが破棄されます。
そのポートが新しいデバイスから使用可能になります。
ホットプラグのイベントシーケンスが最初から始まります。hubd ドライバが、ポート接続状態が変化するまで待ちます。
hubd ドライバがデバイスノードをオフラインにしようとした時点でデバイスノードが開いていた場合、次のイベントが発生します。
hubd ドライバが、定期的オフライン再試行キューにオフライン要求を置きます。
そのポートは、新しいデバイスから使用不可能なままになります。
hubd ドライバがデバイスノードをオフラインにしようとした時点ではデバイスノードが開いていたが、その後ユーザーがそのデバイスノードを閉じた場合、hubd ドライバによるデバイスノードの定期的なオフライン化が成功し、次のイベントが発生します。
クライアントドライバの detach(9E) エントリポイントが呼び出されます。
デバイスノードが破棄されます。
そのポートが新しいデバイスから使用可能になります。
ホットプラグのイベントシーケンスが最初から始まります。hubd ドライバが、ポート接続状態が変化するまで待ちます。
ユーザーがそのデバイスを使用するアプリケーションをすべて閉じると、そのポートがまた使用可能になります。アプリケーションが終了しない場合やデバイスがクローズされない場合、そのポートは使用不可能なままになります。
デバイスのデバイスノードがまだ開いている間に以前に取り外されたデバイスが同じポートに再挿入された場合、次のイベントが発生します。
ハブドライバ hubd(7D) がポート接続を検出します。
hubd ドライバがバスアドレスとデバイス設定を復元します。
hubd ドライバがオフライン再試行要求を取り消します。
hubd ドライバが、接続イベントをクライアントドライバに送信します。
クライアントドライバが接続イベントを受け取ります。
クライアントドライバが、新しいデバイスが以前接続されていたデバイスと同じものかどうかを判定します。クライアントドライバはこの判定を行うために、まずデバイス記述子を比較します。クライアントドライバは、シリアル番号や構成記述子クラウドも比較する可能性があります。
クライアントドライバが、現在のデバイスが以前接続されていたデバイスと同じものでないと判定した場合、次のイベントが発生する可能性があります。
クライアントドライバがコンソールに警告メッセージを発行する可能性があります。
ユーザーがデバイスを再度取り外す可能性があります。ユーザーがデバイスを再度取り外した場合、電源を入れたまま取り外した場合のイベントシーケンスが最初から始まります。hubd ドライバがポート切り離しを検出します。ユーザーがデバイスを再度取り外さなかった場合、次のイベントが発生します。
クライアントドライバの状態が DISCONNECTED のままになり、要求やオープンがすべて失敗します。
そのポートは使用不可能なままになります。ユーザーはポートを解放するために、デバイスを閉じて取り外す必要があります。
ポートが解放されると、ホットプラグのイベントシーケンスが最初から始まります。hubd ドライバが、ポート接続状態が変化するまで待ちます。
クライアントドライバが、現在のデバイスが以前接続されていたデバイスと同じものである判定した場合、次のイベントが発生する可能性があります。
クライアントドライバが状態を復元し、通常の動作を継続する可能性があります。このポリシーに従うかどうかは、クライアントドライバしだいです。クライアントドライバが処理を継続するべきである場合の例として、オーディオスピーカーが挙げられます。
再接続されたデバイスを使用し続けても安全である場合には、ホットプラグのイベントシーケンスが最初から始まります。hubd ドライバが、ポート接続状態が変化するまで待ちます。デバイスがふたたびサービスを提供できる状態になります。
この節では、デバイス電源管理とシステム電源管理について説明します。
デバイス電源管理は、個々の USB デバイスをその入出力動作やアイドル状態かどうかに応じて管理します。
システム電源管理は、チェックポイントと復元再開を使用することで、システムの状態をファイル内にチェックポイントし、システムを完全に停止します (チェックポイントは「システム保存停止」と呼ばれることもあります)。システムの電源を再度入れると、システムは保存停止される前の状態に復元再開されます。
次の概要は、USB デバイスの電源管理のためにドライバ内で行う必要のある処理を箇条書きにしたものです。電源管理の詳細については、この概要のあとで説明します。
attach(9E) 内で電源管理部品を作成します。usb_create_pm_components(9F) のマニュアルページを参照してください。
power(9E) エントリポイントを実装します。
デバイスにアクセスする前に、pm_busy_component(9F) と pm_raise_power(9F) を呼び出します。
デバイスへのアクセスが終了したら pm_idle_component(9F) を呼び出します。
USBA 2.0 フレームワーク は、USB インタフェースの電源管理仕様で規定されている 4 つの電源レベルをサポートしています。USB 電源レベルとオペレーティングシステム電源レベルとの対応関係については、/usr/include/sys/usb/usbai.h を参照してください。
デバイスの状態が USB_DEV_OS_PWR_OFF に遷移すると、hubd ドライバはポートを保存停止します。デバイスの状態が USB_DEV_OS_PWR_1 以上に遷移すると、hubd ドライバはポートを復元再開します。ポート保存停止とシステム保存停止は異なります。ポート保存停止の場合、停止されるのは USB ポートだけです。システム保存停止については、「システム電源管理」で定義します。
クライアントドライバでは、デバイスでのリモートウェイクアップを有効化できます。usb_handle_remote_wakeup(9F) のマニュアルページを参照してください。hubd ドライバは、あるポート上でリモートウェイクアップを検出すると、ウェイクアップ処理を実行したあと、pm_raise_power(9F) を呼び出して子に通知します。
次の図は、電源管理のさまざまな部分の間の関係を示したものです。
図 20-5 USB 電源管理
ドライバは、図 20-5 の下部で説明した 2 つの電源管理方式のいずれかを実装できます。パッシブ方式は、デバイス転送中に電源管理を行わないので、アクティブ方式よりも単純です。
この節では、アクティブ電源管理方式を実装するために使用する必要のある関数について説明します。
ドライバの attach(9E) エントリポイントで次の処理を行います。
usb_create_pm_components(9F) を呼び出します。
必要に応じて、第 2 引数に USB_REMOTE_WAKEUP_ENABLE を指定して usb_handle_remote_wakeup(9F) を呼び出すことで、デバイスでのリモートウェイクアップを有効にします。
pm_busy_component(9F) を呼び出します。
pm_raise_power(9F) を呼び出して電源レベルを USB_DEV_OS_FULL_PWR にします。
デバイスとの通信を行ってデバイスを初期化します。
pm_idle_component(9F) を呼び出します。
ドライバの detach(9E) エントリポイントで次の処理を行います。
pm_busy_component(9F) を呼び出します。
pm_raise_power(9F) を呼び出して電源レベルを USB_DEV_OS_FULL_PWR にします。
attach(9E) エントリポイントで usb_handle_remote_wakeup (9F) 関数を呼び出した場合、ここで第 2 引数に USB_REMOTE_WAKEUP_DISABLE を指定して usb_handle_remote_wakeup(9F) を呼び出します。
デバイスとの通信を行ってデバイスを完全に停止させます。
pm_lower_power(9F) を呼び出して電源レベルを USB_DEV_OS_PWR_OFF にします。
これが、クライアントドライバ内で pm_lower_power(9F) を呼び出す唯一の場合です。
pm_idle_component(9F) を呼び出します。
あるドライバスレッド内でデバイスへの入出力を開始する必要がある場合、そのスレッドで次のタスクを実行します。
pm_busy_component(9F) を呼び出します。
pm_raise_power(9F) を呼び出して電源レベルを USB_DEV_OS_FULL_PWR にします。
入出力転送を開始します。
ドライバ内で入出力転送が完了したという通知を受け取ったら、pm_idle_component(9F) を呼び出します。
ドライバの power(9E) エントリポイントでは、遷移先の電源レベルが有効かどうかをチェックします。また、複数のスレッドが同時に power(9E) への呼び出しを行うことを考慮しなければならない可能性もあります。
デバイスのアイドル状態が一定時間続いたか、またはシステムが停止しようとしている場合、デバイスの状態を USB_DEV_OS_PWR_OFF にするために power(9E) ルーチンが呼び出される可能性があります。この状態は、図 20-4 で示した PWRED_DWN 状態に対応しています。 デバイスの状態が USB_DEV_OS_PWR_OFF に遷移する場合、power(9E) ルーチンで次の処理を行います。
開かれたパイプをすべてアイドル状態にします。たとえば、割り込みパイプのポーリングを停止します。
保存する必要のあるデバイスやドライバのコンテキストをすべて保存します。
power(9E) の呼び出しが完了したあと、デバイスが接続されているポートが保存停止されます。
デバイス起動のリモートウェイクアップまたはシステム起動のウェイクアップが受信されると、デバイスの電源を入れるために power(9E) ルーチンが呼び出される可能性があります。ウェイクアップ通知が発生するのは、アイドル時間が長く続いたかシステムが保存停止したために、デバイスの電源レベルが低下したあとです。デバイスの状態が USB_DEV_OS_PWR_1 に遷移する場合、power(9E) ルーチンで次の処理を行います。
デバイスやドライバの必要なコンテキストをすべて復元します。
指定された電源レベルに適したアクティビティーをパイプ上で再開します。たとえば、割り込みパイプでポーリングを開始します。
デバイスが接続されているポートが以前保存停止されていた場合、power(9E) が呼び出される前にそのポートが復元再開されます。
パッシブ電源管理方式は、前述のアクティブ電源管理方式より単純です。このパッシブ方式では、転送中の電源管理は行われません。このパッシブ方式を実装するには、デバイスを開くときに pm_busy_component(9F) と pm_raise_power(9F) を呼び出します。その後、デバイスを閉じるときに pm_idle_component(9F) を呼び出します。
システム電源管理は、状態保存後のシステム全体の電源切断とシステム電源再投入後の状態復元とで構成されます。この処理は CPR (チェックポイントおよび復元再開) と呼ばれます。CPR に関する USB クライアントドライバの動作は、ほかのクライアントドライバの動作と同じです。デバイスを保存停止する場合は、ドライバの detach(9E) エントリポイントが cmd 引数 DDI_SUSPEND で呼び出されます。デバイスを復元再開する場合は、ドライバの attach(9E) エントリポイントが cmd 引数 DDI_RESUME で呼び出されます。detach(9E) ルーチンで DDI_SUSPEND コマンドを処理する場合、あとで完全な復元再開を行うのに必要な範囲で、デバイス状態やドライバ状態をクリーンアップします (この状態は図 20-4 の SUSPENDED に対応しています)。attach(9E) ルーチンで DDI_RESUME コマンドを処理する場合、システムとデバイスの同期が取れるように、必ずデバイスの電源レベルをフルにします。
USB デバイスの場合、保存停止/復元再開はホットプラグの切り離し/再接続 (「USB デバイスのホットプラグ」を参照) と同様に処理されます。CPR とホットプラグの重要な違いは、CPR では、デバイスが保存停止可能な状態にない場合にドライバでのチェックポイント処理が失敗する可能性があるという点です。たとえば、デバイスのエラー回復が進行中である場合、デバイスの保存停止は行えません。デバイスがビジー状態で安全に停止できない場合も、デバイスの保存停止は行えません。
ドライバでは一般に、相互排他を保持した状態で USBA 関数を呼び出するべきではありません。したがって、クライアントドライバ内で競合状態が発生することが避けられない可能性があります。
切り離しや CPR といった非同期イベントの処理コードと通常動作コードの同時実行を許可しないでください。これらのタイプの非同期イベントでは通常、パイプのクリーンアップと設定解除が行われるため、通常動作コードに悪影響が及ぶ可能性があります。
競合状態を管理して通常動作コードを保護する方法の 1 つは、排他アクセス権を持つ同期オブジェクトの取得と解放を行える直列化機能を記述することです。USBA 関数の呼び出し中に同期オブジェクトを安全に保持できるように、直列化機能を記述できます。usbskel サンプルドライバでこの手法が使用されています。usbskel ドライバについては、「サンプル USB デバイスドライバ」を参照してください。