アトミック バス操作
SPB に接続された周辺機器の特定のハードウェア機能を使用するには、SPB コントローラー (つまり、周辺機器ドライバー) のクライアントが、アトミック バス操作としてデバイスとの間で、一連のデータ転送を実行する必要がある場合があります。 転送シーケンスはアトミックですが、それはシーケンスが完了するまで、他のクライアントはバス上のデバイス間でデータを転送できないためです。
クライアントがアトミック バス操作として転送シーケンスを実行する一般的な方法は、IOCTL_SPB_EXECUTE_SEQUENCE 要求をターゲット デバイスに送信することです。 この要求では、クライアントは、単純な読み取りと書き込みの転送の一覧としてシーケンスを指定します。 一覧の長さは任意です。 読み取りと書き込みは、一覧の順序で実行され、読み取りまたは書き込みごとに任意のバイト数を転送できます。 ほとんどの SPB コントローラーでは、IOCTL_SPB_EXECUTE_SEQUENCE 要求がサポートされています。
SPB コントローラー ロック
アトミック転送シーケンスの実行であまり一般的でない方法は、SPB コントローラー ロックを使用することです。 クライアントは、ロックを取得する IOCTL_SPB_LOCK_CONTROLLER 要求と、ロックを解放する IOCTL_SPB_UNLOCK_CONTROLLER 要求を送信します。 クライアントがコントローラー ロックを保持している場合、クライアントがデバイスに送信する単純な読み取りと書き込み (IRP_MJ_READ および IRP_MJ_WRITE) 要求のシーケンスは、バス上でアトミック操作として実行されます。
ほとんどの SPB 接続周辺機器はコントローラー ロックを必要とせず、ほとんどの SPB コントローラー ドライバーはこれらのロックのサポートを実装していません。 ただし、一部のクライアントでは、通常とは異なる機能を持つデバイスにアクセスするためにコントローラー ロックを使用する必要がある場合があります。
たとえば、デバイスは、バス上でアトミックの読み取り/変更/書き込み操作によってのみアクセスできるデバイス関数を実装できます。 このような操作を実行するために、クライアントは次の 4 つの I/O 要求を (示されている順序で) 送信します。
- IOCTL_SPB_LOCK_CONTROLLER – コントローラー ロックを取得します。
- IRP_MJ_READ – ターゲット デバイスからデータのブロックを読み取ります。
- IRP_MJ_WRITE – 変更したデータをデバイスに書き戻します。
- IOCTL_SPB_UNLOCK_CONTROLLER – コントローラーのロックを解除します。
前の一覧の読み取り操作の後、クライアントはデバイスから読み取られたデータを解釈し、データを変更してからデバイスに書き戻します。
ただし、コントローラー ロックを必要とする機能を備えた SPB 接続デバイスはあまりありません。 アトミック バス操作を必要とするほとんどのデバイスでは、IOCTL_SPB_EXECUTE_SEQUENCE 要求で十分です。
SPB コントローラー ロックと SPB コネクション ロックを混同しないでください。 2 つのクライアントが同じ SPB 接続周辺機器へのアクセスを共有する場合、どちらのクライアントもコネクション ロックを使用してデバイスへの排他アクセスを一時的に取得できます。 詳細については、「SPB 接続ロック」を参照してください。
ハードウェア バス信号
IOCTL_SPB_EXECUTE_SEQUENCE 要求を処理するために、SPB コントローラー ドライバーは、転送シーケンス中にバス上で適切な信号を生成するようにコントローラー ハードウェアを構成します。 バスに接続されている周辺機器は、これらの信号に依存して、アトミック バス操作が進行中であることを検出する場合があります。 SPB コントローラーがアトミック バス操作として転送シーケンスを実行する際に使用するハードウェア信号のセットは、バスの種類によって異なります。
I2C バスの場合、コントローラはバス上で開始ビットを送信してシーケンスを開始し、ストップ ビットを送信してシーケンスを終了します。 開始ビットと停止ビットの間では、デバイスとの間のデータ転送のシーケンスは、1 つのアトミック バス操作として実行されます。 シーケンス内の最後の転送を除き、各転送の後に I2C 再起動操作 (停止ビットが先行しない繰り返し開始ビット) が続きます。
SPI バスの場合、コントローラーはチップ選択ラインをターゲット デバイスにアサートしてシーケンスを開始し、チップ選択ラインをデアサートしてシーケンスを終了します。 バス経由の一連のデータ転送中、チップ選択ラインを継続的にアサートすることで、転送は 1 つのアトミック バス操作として実行されます。
I2C デバイスの例
I2C バス上の一般的な周辺機器は、いくつかの内部デバイス機能を実装する場合があります。 これらの関数の一部にアクセスするために、クライアントは IOCTL_SPB_EXECUTE_SEQUENCE 要求を使用する場合があります。
たとえば、I2C 周辺機器には、次の 2 つの内部レジスタが含まれている場合があります。
- クライアントがアクセスするデバイス関数の内部アドレスを書き込む関数アドレス レジスタ。
- クライアントが指定された関数アドレスからデータを読み取る、またはデータを書き込むデータ レジスタ。
この例の I2C 周辺機器は、開始ビットの後にデバイスに書き込まれた最初のバイトを、関数アドレス レジスタに読み込む関数アドレスとして解釈します。 シーケンスが終了する前にデバイス間で転送された追加のバイト (ストップ ビットで示される) は、デバイスによってデータ レジスタを介して転送されるデータとして扱われます。
書き込み操作を実行するために、クライアントは書き込み (IRP_MJ_WRITE) 要求を送信しますが、書き込みバッファーの最初のバイトは関数アドレスであり、バッファー内の残りのバイトが関数アドレスに書き込まれるデータです。
デバイスからの読み取りはより複雑です。 この例の I2C デバイスでは、バスでストップ ビットが検出されたときに、関数アドレス レジスタを既定値の 0 に自動的にリセットする "高速読み取り" 機能がサポートされているものとします。 この機能を使用すると、最初に関数アドレス レジスタに書き込む必要なく、クライアントは関数アドレス 0 からデータを読み取ることができます。 この機能により、ほとんどの読み取りが関数アドレス 0 からの読み取りであり、比較的短い場合は特に、デバイスの読み取り操作の速度を向上させることができます。
ただし、0 以外の関数アドレスからデータ ブロックを読み取るために、クライアントはデータ レジスタからデータ ブロックを読み取る前に、関数アドレス レジスタにバイトを書き込む必要があります。 クライアントは、これらの書き込みおよび読み取り転送をアトミック バス操作として実行し、バス コントローラーが関数アドレス レジスタへの書き込みの後、またデータ レジスタからの読み取りの前にストップ ビットを送信しないようにする必要があります。 それ以外の場合、ストップ ビットは、0 以外の関数アドレスからではなく、関数アドレス 0 からデータを読み取ります。
次の一覧では、デバイスの 0 以外の関数アドレスにあるデータに対して読み取り/変更/書き込み操作を実行するために、クライアントが I2C デバイスに送信する一連の I/O 要求について説明します。
- IOCTL_SPB_EXECUTE_SEQUENCE - I/O 転送シーケンスを実行して、デバイスからデータを読み取ります。 このシーケンスの最初の転送は、関数アドレス レジスタへのバイト書き込みです。 シーケンス内の 2 番目の転送は、選択した関数アドレスからのバイト数の読み取りです。 これらの 2 つの転送は、バス上でアトミックに実行されます。
- IRP_MJ_WRITE - デバイスにデータを書き込みます。 この要求の書き込みバッファーの最初のバイトは、関数アドレス レジスタに書き込む値です。 バッファー内の残りのバイトは、選択した関数アドレスに書き込むデータです。
代わりに、この読み取り/変更/書き込み操作を実行するために、他のパターンの要求が使用される場合があります。 たとえば、手順 2 の IRP_MJ_WRITE 要求は、2 つのデータ転送 (両方とも書き込み) を指定する IOCTL_SPB_EXECUTE_SEQUENCE 要求に置き換えることができます。 シーケンス内の最初の転送では、関数アドレス レジスタにバイトを読み込みます。 2 番目の転送では、選択した関数アドレスにデータ バイトが書き込まれます。 この要求は、手順 2 の IRP_MJ_WRITE 要求とは異なり、クライアントが関数アドレス バイトとデータ バイトを同じ書き込みバッファーに結合する必要はありません。
このデバイスの関数アドレス 0 に対して読み取り/変更/書き込みを実行するには、前の一覧の手順 1 の IOCTL_SPB_EXECUTE_SEQUENCE 要求を単純読み取り (IRP_MJ_READ) 要求に置き換えることができます。