BUS_SPACE(9)

名称
bus_space, bus_space_barrier, bus_space_copy_region_1, bus_space_copy_region_2, bus_space_copy_region_4, bus_space_copy_region_8, bus_space_free, bus_space_map, bus_space_read_1, bus_space_read_2, bus_space_read_4, bus_space_read_8, bus_space_read_multi_1, bus_space_read_multi_2, bus_space_read_multi_4, bus_space_read_multi_8, bus_space_read_region_1, bus_space_read_region_2, bus_space_read_region_4, bus_space_read_region_8, bus_space_set_region_1, bus_space_set_region_2, bus_space_set_region_4, bus_space_set_region_8, bus_space_subregion, bus_space_unmap, bus_space_write_1, bus_space_write_2, bus_space_write_4, bus_space_write_8, bus_space_write_multi_1, bus_space_write_multi_2, bus_space_write_multi_4, bus_space_write_multi_8, bus_space_write_region_1, bus_space_write_region_2, bus_space_write_region_4, bus_space_write_region_8 - バス空間操作関数群

書式
#include <machine/bus.h> int bus_space_map(bus_space_tag_t space, bus_addr_t address, bus_size_t size, int flags, bus_space_handle_t *handlep) void bus_space_unmap(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t size) int bus_space_subregion(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, bus_size_t size, bus_space_handle_t *nhandlep) int bus_space_alloc(bus_space_tag_t space, bus_addr_t reg_start, bus_addr_t reg_end, bus_size_t size, bus_size_t alignment, bus_size_t boundary, int flags, bus_addr_t *addrp, bus_space_handle_t *handlep) void bus_space_free(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t size) u_int8_t bus_space_read_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset) u_int16_t bus_space_read_2(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset) u_int32_t bus_space_read_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset) u_int64_t bus_space_read_8(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset) void bus_space_write_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int8_t value) void bus_space_write_2(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int16_t value) void bus_space_write_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int32_t value) void bus_space_write_8(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int64_t value) void bus_space_barrier(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, bus_size_t length, int flags) void bus_space_read_region_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int8_t *datap, bus_size_t count) void bus_space_read_region_2(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int16_t *datap, bus_size_t count) void bus_space_read_region_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int32_t *datap, bus_size_t count) void bus_space_read_region_8(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int64_t *datap, bus_size_t count) void bus_space_write_region_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int8_t *datap, bus_size_t count) void bus_space_write_region_2(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int16_t *datap, bus_size_t count) void bus_space_write_region_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int32_t *datap, bus_size_t count) void bus_space_write_region_8(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int64_t *datap, bus_size_t count) void bus_space_copy_region_1(bus_space_tag_t space, bus_space_handle_t srchandle, bus_size_t srcoffset, bus_space_handle_t dsthandle, bus_size_t dstoffset, bus_size_t count) void bus_space_copy_region_2(bus_space_tag_t space, bus_space_handle_t srchandle, bus_size_t srcoffset, bus_space_handle_t dsthandle, bus_size_t dstoffset, bus_size_t count) void bus_space_copy_region_4(bus_space_tag_t space, bus_space_handle_t srchandle, bus_size_t srcoffset, bus_space_handle_t dsthandle, bus_size_t dstoffset, bus_size_t count) void bus_space_copy_region_8(bus_space_tag_t space, bus_space_handle_t srchandle, bus_size_t srcoffset, bus_space_handle_t dsthandle, bus_size_t dstoffset, bus_size_t count) void bus_space_set_region_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int8_t value, bus_size_t count) void bus_space_set_region_2(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int16_t value, bus_size_t count) void bus_space_set_region_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int32_t value, bus_size_t count) void bus_space_set_region_8(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int64_t value, bus_size_t count) void bus_space_read_multi_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int8_t *datap, bus_size_t count) void bus_space_read_multi_2(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int16_t *datap, bus_size_t count) void bus_space_read_multi_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int32_t *datap, bus_size_t count) void bus_space_read_multi_8(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int64_t *datap, bus_size_t count) void bus_space_write_multi_1(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int8_t *datap, bus_size_t count) void bus_space_write_multi_2(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int16_t *datap, bus_size_t count) void bus_space_write_multi_4(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int32_t *datap, bus_size_t count) void bus_space_write_multi_8(bus_space_tag_t space, bus_space_handle_t handle, bus_size_t offset, u_int64_t *datap, bus_size_t count)

解説

bus_space 関数群は、デバイスドライバのバスのメモリやレジスタ領域への アーキテクチャ非依存なアクセスを可能にするためのものです。 この文書で述べている関数と型は ~ <machine/bus.h> ヘッダファイルをインクルードすることによって使用できます。

複数のアーキテクチャで共通に使用されるデバイスはたくさんありますが、 各アーキテクチャでの制約からそれぞれ異なったアクセス方法になっています。 例えば、あるシステムではデバイスが I/O 空間にマップされ、またある システムではメモリ空間にマップされます。またあるシステムでは、 アーキテクチャ上の制限でレジスタへのアクセス方法を変更しなくては ならないこともあります (例えば、非連続のレジスタ空間の作成)。 時には、単一のシステム/アーキテクチャで単一のドライバから同じ タイプのデバイスに複数の方法でアクセスしなくてはならないこともあります。 bus_space 関数は、最終的に、単一のドライバソースファイルで複数の システムアーキテクチャのデバイスを扱い、さらに、単一のドライバオブジェクト ファイルで単一アーキテクチャ上の複数のバスタイプのデバイスを扱うことが できるようにすることが目標です。

全てのバスでこの文書で述べる全ての関数を実装する必要はありませんが、 機能が表面上そのバスでサポートされているようにした方がよいです。 実装していない関数は、できればコンパイル時にエラーとなるように しておいた方がよいでしょう。

この文書で述べているインタフェース定義は、全て関数宣言の形としてあり、 関数でなくてはならないような扱いとなっています。これらインタフェースは 宣言ありの (型チェックをした) 形で実装するべきですが、適宜マクロとして 実装してもよいです。アーキテクチャ依存の型、変数、関数は、 ~ <machine/bus.h> で明記し、アーキテクチャ非依存の型や関数と混同されてしまうことを避ける べきです。また、できれば、アーキテクチャ依存であることが明確にわかる ような名称をつけるべきです。

概念と指針

バス空間はバススペースタグで表現され、バススペースタグはアーキテクチャ 依存のコードでのみ生成されます。ひとつのマシンは複数の異なったタイプの バス空間 (例えばメモリ空間と I/O 空間) を持ち得、その場合複数の 異なったバススペースタグを持つことになります。 ひとつのマシン上のそれぞれのバスあるいはデバイスは 2 つ以上の バススペースタグを持ち得ます。例えば、ISA デバイスは ISA メモリスペースタグと ISA I/O スペースタグを持ちます。また、 アーキテクチャによっては、同じ型の空間に対して複数のタグを 持つものもあります。例えば、異なった複数のホストバスインタフェースチップを 持つものがあるからです。

バス空間の範囲は、バスアドレスとバスサイズで表現されます。バスアドレスは バス空間の範囲の開始点を表します。バスサイズはバス空間の大きさをバイト数で 表します。バイト単位でアドレッシングできないバスでは、バス空間の範囲を 適切な境界値に沿ったアドレスと、正しい丸めサイズで扱う必要があります。

バス空間領域へのアクセスを容易にするため、バススペースハンドルを 使用します。バススペースハンドルは通常、バス空間の特定の範囲を マッピングすることにより生成されます。このハンドルはまた、 バススペースの範囲を割り当ててマップした時点にも、生成されます。 実際の位置は、アプリケーションの関数からの呼び出しで指定された範囲内に 対して、実装依存で決定されます。

全てのバス空間アクセス関数は、バススペースタグと、少なくともひとつの ハンドル引数と、少なくともひとつのオフセット引数 (バスサイズ) が 必要です。 バススペースタグは空間を指定し、ハンドルはその空間内の一領域を指定し、 オフセットはその領域内の実際にアクセスする位置へのオフセットを指定します。 オフセットはバイトで指定しますが、境界制限を課するバスもあります。 指定のハンドルからの相対値としてアクセスするデータに使用する オフセットは、アクセスするデータが全てそのハンドルの示す マップされた領域におさまっている必要があります。領域外へのアクセスは エラーになります。

アーキテクチャによってはメモリデバイスのアクセス速度をよくするために バッファを使用しているものがあるので、バス空間の読み書きのストリームに “バリア” を作成する機構があります。読込、書出、読込/書出 の 3 種類の バリアがあります。読込バリア以前に開始した読込みは、読込バリア開始後の 読込みの前に必ず完了させておく必要があります。(書出バリアに関しても 同様です。) 読込/書出バリア以前に開始した全ての読込みおよび書出しは、 バリア開始後のいかなる読込みおよび書出しより以前に完了させておかなければ なりません。ドライバを正しく記述するには、適切な位置にバリアを含み、 バリア操作の制約を考慮した読込み/書出し順にします。

bus_space 関数群を使って、移植性の高いドライバを書きたい場合、そのシステムで可能と 仮定する事項はできるだけ少なくします。特に、アクセスするバス空間アドレスは 自然な境界に整列させなくてはならないと考えるべきです (つまり、 オフセット値に加算されるベースアドレスはアクセスサイズの 倍数になっている)。また、ポインタに対してはシステムが境界チェックを 行う (つまり、読み書きするオブジェクトへのポインタは正しい境界整列の データを指していなくてはならない) と考えるべきです。

以下の bus_space 関数群の説明は、正しい引数で呼び出されることが前提です。間違った引数や 範囲外の引数 (例えば指定したハンドルが生成時にマップされた領域の外の データをアクセスしようとしたりすること) で呼び出した場合、どのような 結果となるかは未定義です。そのような場合、意図した (panic による) あるいは 意図しない (別の理由による致命的トラップによる) システム停止を引き起こす かも知れませんし、すぐには致命的とならない不正なオペレーションを引き起こす こともあります。返値が void のものや、バス空間から読み出したデータを 返す関数 (つまり明示的にエラーコードを返さない関数) は、失敗がありません。 それらは、不正な引数を与えられたときのみ失敗します。その場合の動作は 未定義です。

型
ドライバからの bus_space 関数群の使用の便宜のため、 ~ <machine/bus.h> にいくつかの型が定義されています。

bus_addr_t

bus_addr_t 型はバスアドレスを表現するのに使用します。この型は、そのアーキテクチャで 使用できるバスアドレスの最大値を保持できる、符号なし整数である 必要があります。この型は主にバス空間のマップとマップ解除に使用します。

bus_size_t

bus_size_t 型はバス空間の領域のサイズを表すのに使用します。この型は、そのアーキテクチャで 使用できる最大のバスアドレス領域サイズを保持できる、符号なし整数である 必要があります。この型は、実質全ての bus_space 関数群で使用し、領域をマッピングする時にはサイズを、空間アクセス操作時には 領域へのオフセットを表します。

bus_space_tag_t

bus_space_tag_t 型は、マシン内でのバス空間を特定するために使用します。これの内容は アーキテクチャ依存で、アーキテクチャ非依存コードでは非透過として 扱います。この型は全ての bus_space 関数群で、操作対象の空間を区別するために使用します。

bus_space_handle_t

bus_space_handle_t 型は、バス空間のある領域の 1 マッピングを表します。これの内容は アーキテクチャ依存で、アーキテクチャ非依存コードでは非透過として 扱います。この型はバス空間アクセス操作を行う時に使用します。

バス空間のマップとマップ解除

バス空間は、使用前にマップし、使用後にマップ解除しなくてはなりません。 bus_space_map() と bus_space_unmap() でこれらを行います。

ドライバによっては、既にマップした空間の一部の領域を他のドライバや ドライバ内のモジュールに渡さなくてはならない場合があります。 bus_space_subregion() によって、そのような一部領域を作成することができます。

bus_space_map(space, address, size, flags, handlep)

bus_space_map() 関数は、 space, address, size 引数で指定されるバス空間をマップします。成功すると 0 を返し、 マップした領域へのアクセスに使用するハンドルを handlep で指すバス空間ハンドルに設定します。失敗すると、非 0 を返し、 handlep で指すバス空間ハンドルは不定の状態となります。

flags 引数は空間のマップされ方を制御します。有効なフラグは下記です:

BUS_SPACE_MAP_CACHEABLE
    その空間へのアクセスをキャッシュするか、先読みする (あるいは両方) ように マップするよう試みます。このフラグが指定されない場合、キャッシュや 先読みされないように空間のマップを実装すべきです。

    このフラグは従来との互換性のため全ての実装で 1 として下さい。

BUS_SPACE_MAP_LINEAR
    空間を、通常のメモリアクセス (例えばポインタ参照や構造体アクセス) で 扱えるよう、内容が連続するようにマップを試みます。 ソフトウエアがメモリデバイスへ、フレームバッファのように直接アクセスしたい 場合に有用です。このフラグが指定されて、連続的なマップができない場合は、 bus_space_map() 呼び出しはエラーとなります。このフラグが指定されなければ、システムは 空間をもっともやりやすいようにマップします。

フラグの組合せによっては意味のないものもあれば、空間によってはサポート されないものもあります。例えば、 BUS_SPACE_MAP_CACHEABLE を I/O ポート空間に使うことは多くのシステムで意味がありません。また。 BUS_SPACE_MAP_CACHEABLE 無しで BUS_SPACE_MAP_LINEAR を指定しても機能しないシステムもあります。システムハードウエアあるいは ファームウエアが空間をどのようにマップするかのヒントを提供している場合 (例えば PCI メモリマッピングレジスタの "先読み可能" ビット)、最大限 互換性を取るためにこれらのヒントに従うべきです。システムによっては、 完全にできないマップリクエスト (例えばキャッシュ付のマッピングのみを 提供しているシステムでキャッシュ無効のマッピングをリクエストする) はリクエストがエラーになります。

いくつかの実装では、特定の、あるいは全てのバス空間について使用状況を 追跡し、割当の重複を許しません。ISA や VME のように、スロット毎の スペースアドレッシングをしないバス空間や、それらと共存する空間 (たとえば、ISA のメモリ、I/O 空間と共存する EISA や PCI の メモリ、I/O 空間) では、このように実装することを奨めます。

マップされた領域で、バス上にデバイスのない範囲があるかも知れません。 この範囲内の空間へのアクセスした場合、結果はバスによります。

bus_space_unmap(space, handle, size)

bus_space_unmap() 関数は、 bus_space_map() でマップされたバス空間領域をマップ解除します。 領域のマップ解除時に指定する size は、その領域をマップした時に bus_space_map() に与えたサイズと同じにします。

bus_space_unmap() がハンドルに対して呼び出された後は、ハンドルは無効となります。(たとえ ハンドルのコピーを作成しておいても、それらはやはり無効です。)

この関数がエラーになることはありません。もしエラーとなるなら (例えば引数エラーによる)、それはパニックすべきソフトウエアバグがある ということです。この場合、 bus_space_unmap() は復帰しません。

bus_space_subregion(space, handle, offset, size, nhandlep)

bus_space_subregion() は、すでにマップされているバス空間の領域の子領域へのハンドルを生成する ことを容易にする関数です。新しいハンドルで表される子領域は、 handle の領域から offset のバイト位置からはじまり、 size の大きさで、全体が親領域に収まっていなくてはなりません。

bus_space_subregion() は、成功時には 0 を返し、 nhandlep の指すバス空間ハンドルの内容を埋めます。 エラー時には非 0 を返し、 nhandlep の指すバス空間ハンドルは不定となります。どちらの場合も、 handle の表すハンドルは有効のままで、変更されません。

bus_space_subregion() で生成したハンドルを使い終ったならば、破棄すべきです。このハンドルには、 bus_space_unmap() を使用してはなりません。使用すると、その空間のリソース管理に混乱をきたし、 どのような影響がでるかわかりません。 あるハンドルに対して bus_space_unmap() や bus_space_free() を呼び出すと、そのハンドルの子領域は全て無効化されます。

バス空間の割当と解放

デバイスによっては、そのデバイスで使用するために、オペレーティングシステムに バス空間の割当を要求したり、許可したりします。デバイスにとって空間が 不要になった場合、オペレーティングシステムは他のデバイスが使用できるように 解放すべきです。 bus_space_alloc() と bus_space_free() でこの機能を提供します。

bus_space_alloc(space, reg_start, reg_end, size, alignment, boundary, flags, addrp, handlep)

bus_space_alloc() は、 size で指定される大きさの、指定の制約を満たすバス空間を割り当て、マップします。 成功すれは 0 を返し、 addrp の指すバスアドレスに割り当てた領域のバス空間アドレスを設定し、 handlep の指すバス空間ハンドルにその領域へのアクセスするためのハンドルを設定 します。 エラーの場合は非 0 を返し、 addrp の指すバスアドレスと handlep の指すバス空間アドレスの内容は不定です。

割り当ての制約事項は reg_start, reg_end, alignment, および boundary パラメータで与えます。割り当てた領域は reg_start からか、もしくはその後ろからはじまり、 reg_end もしくはその前までとなります。 alignment 制約条件は 2 の冪乗でなければならず、また割り当てられた領域はその 2 の冪乗数の 偶数倍からの開始となります。 boundary 制約条件が 0 でない場合、割り当てられる領域は 領域の先頭アドレス / boundary と 領域の最後尾のアドレス / boundary とが同じ値となります。 制約条件を満たせない場合、 bus_space_alloc() はエラーとなります。実現不可能な制約条件の組合せはエラーです ( 例えば、 size が boundary よりも大きい場合) 。

flags パラメータは bus_space_map のパラメータと同じ名称のパラメータと同様で、同じフラグ値を使用し、 同様の意味に解釈されます。

bus_space_alloc() で生成したハンドルは必ず bus_space_free() で解放します。 それらに bus_space_unmap() を使用すると、その振舞いはどうなるかわかりません。 bus_space_subregion() 関数は bus_space_alloc() で生成したハンドルに使用することができます。

bus_space_free(space, handle, size)

bus_space_free() 関数は、 bus_space_alloc() でマップし、割り当てたバス空間の領域を、マップ解除し、解放します。 マップ解除時には、 size はその領域を割り当てた時に bus_space_alloc() に渡したサイズと同じ値を指定します。

ハンドルに対して bus_space_free() が呼び出されると、そのハンドル無効となります。(そのハンドルの複製が作成 してあったとしても、それらも同様に無効となります。)

この関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる場合、 ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、 bus_space_free() は復帰しません。

単一のデータアイテムの読み書き

バス空間への最も簡単なアクセス方法は、単一のデータアイテムを読み書きする ことです。 bus_space_read_N() と bus_space_write_N() 関数ファミリは、1, 2, 4, 8 バイトのデータアイテムを、それらのサイズでの アクセスの可能なバス上で読み書きする機能を提供します。

bus_space_read_1(space, handle, offset)
bus_space_read_2(space, handle, offset)
bus_space_read_4(space, handle, offset)
bus_space_read_8(space, handle, offset)

bus_space_read_N() 関数ファミリは、それぞれ 1, 2, 4, 8 バイトのデータアイテムを、 space で指定されるバス空間の handle で指定される領域から offset で指定されるオフセットから読み込みます。 読み込まれる箇所は、 handle で指定されるバス空間領域におさまっていなければなりません。

移植性のため、 handle にオフセットを加えて指定する領域の開始アドレスは、読み込まれる データアイテムのサイズの整数倍になるようにします。 システムによっては、この条件に従わない場合は誤ったデータが読み込まれたり、 システムクラッシュを招いたりします。

bus_space_read_N() 関数群での読み込みは、 bus_space_barrier() 関数を使って順番を強制しない場合、他の保留されている読み込みや書き出し操作の 影響を受けて実行される順序が変更される可能性があります。

これらの関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる 場合、ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、これらは復帰しません。

bus_space_write_1(space, handle, offset, value)
bus_space_write_2(space, handle, offset, value)
bus_space_write_4(space, handle, offset, value)
bus_space_write_8(space, handle, offset, value)

bus_space_write_N() 関数ファミリは、それぞれ 1, 2, 4, 8 バイトのデータアイテムを、 space で指定されるバス空間の handle で指定される領域から offset で指定されるオフセットへ書き出します。

移植性のため、 handle にオフセットを加えて指定する領域の開始アドレスは、書き出される データアイテムのサイズの整数倍になるようにします。 システムによっては、この条件に従わない場合は誤ったデータが書き出されたり、 システムクラッシュを招いたりします。

bus_space_write_N() 関数群での書き出しは、 bus_space_barrier() 関数を使って順番を強制しない場合、他の保留されている読み込みや書き出し操作の 影響を受けて実行される順序が変更される可能性があります。

これらの関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる 場合、ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、これらは復帰しません。

バリア

操作のたびにバスをアクティブにすることを避け、高パフォーマンス バッファリングが実装できるようにするには、ドライバが、必要な場合に読み込みや 書き込みの順序を明示的に指定できるようにしなくてはなりません。 bus_space_barrier() 関数はこのような機能を提供します。

bus_space_barrier(space, handle, offset, length, flags)

bus_space_barrier() 関数は、指定した子領域へのバス空間の読み込み/書き出し操作の順序を 強制します。子領域は space で識別される空間の handle で識別される領域の offset と length パラメータで記述されます。

flags 引数はどのタイプの操作を順序づけるかを制御します。 有効なフラグ以下:

BUS_SPACE_BARRIER_READ
    同期読み込み操作。

BUS_SPACE_BARRIER_WRITE
    同期書き出し操作。

これらフラグは (or をとることによって) 同時に指定して、読み込みと書き出しの 両方の順序付けを指示することができます。

あるバリア操作以前に領域に対して実行された、指定の型に該当する操作は、 バリア後の指定の型の操作よりも前に完了することが保証されます。

例えば: 2 つの、1 バイトのポートを持ったデバイスがあると仮定します。 片方は書き出し専用の入力ポート (オフセット 0)、もう一方は読み込み専用の 出力ポート (オフセット 1) です。このデバイスに対する操作は以下のように なります: 複数バイトのデータは入力ポートに書き出され、その後 デバイスによってスタックに置かれ、出力ポートからの読み込みでその 先頭が読み込まれます。2 バイトのデータをこのデバイスに書き出し、その後 この 2 バイトを読み込むことを正しく行う手順は以下のようになります:

    /*
     * t と h はマップされたデバイスの空間のタグとハンドルです。
     */
    bus_space_write_1(t, h, 0, data0);
    bus_space_barrier(t, h, 0, 1, BUS_SPACE_BARRIER_WRITE);  /* 1 */
    bus_space_write_1(t, h, 0, data1);
    bus_space_barrier(t, h, 0, 2,
        BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);     /* 2 */
    ndata1 = bus_space_read_1(t, h, 1);
    bus_space_barrier(t, h, 1, 1, BUS_SPACE_BARRIER_READ);   /* 3 */
    ndata0 = bus_space_read_1(t, h, 1);
    /* data0 == ndata0, data1 == ndata1 */

最初のバリアは、最初の書き出しが 2 つめの書き込出しが発行されるまでに 完了することを確実にします。これによって、入力ポートへの 2 つの書き出しは 順序通りに行われ、1 回の書き込みにくっついてしまうことはありません。 データバイトがデバイスへ正しく、順序通り書き出されることを保証します。

2 番目のバリアは、いかなる入力ポートからの読み込みも出力ポートへの 書き出しが完了してから発行されることを確実にします。それによってデータの 読み込みの前に全ての書き出しが完了することを確実にします。実際に 最初にデバイスから読み込まれる 1 バイトは、最後に書き出したものとなります。

3 番目のバリアは最初の読み込みが 2 つめの読み込みの発行より前に完了する ことを確実にし、データが正しく、順序通り読み込まれる事を確実にします。

上に例示したバリアは、バス空間の最低限の範囲を指定しています。 ですが、バリアをバス空間のデバイスの範囲全体をカバーするようにするのも 正しい (また、より楽な場合が多い) やりかたです。その場合、オフセット 0 と 領域全体のサイズを指定します。

領域の操作

デバイスによっては、バス空間に領域としてマップされたバッファを使用します。 ドライバでは、これらバッファの内容をメモリへ書き出したり読み込んだり したい場合がよくあります。例えば、システムの上位へ渡すために mbuf へ 書き出したり、mbuf からネットワークへ出力したりする場合です。ドライバが できるだけ効果的にこれらをできるように、 bus_space_read_region_N() と bus_space_write_region_N() 関数ファミリを提供しています。

ドライバでは、バス空間のある領域から他の領域へコピーしたり、バス空間の ある領域全体に単一の値を設定する必要が生じることがあります。 bus_space_copy_region_N() 関数ファミリと、 bus_space_set_region_N() 関数ファミリでこれらの操作をドライバが実行することができます。

bus_space_read_region_1(space, handle, offset, datap, count)
bus_space_read_region_2(space, handle, offset, datap, count)
bus_space_read_region_4(space, handle, offset, datap, count)
bus_space_read_region_8(space, handle, offset, datap, count)

bus_space_read_region_N() 関数ファミリは、 count 個の 1, 2, 4, 8 バイトのデータを space で指定されるバス空間の handle で指定される領域のオフセット offset バイトで始まるバス空間から読み込み、 datap で指定される配列に書き出します。 直前のデータの 1, 2, 4, 8 バイト (どの関数を使っているかによります) 後の 続きのデータが読み込まれます。読み込まれる範囲全体が handle で指定されるバス空間領域に収まっていなくてはなりません。

移植性のため、 handle で指定される領域の開始アドレスにオフセットを加えたものは、読み込むデータ アイテムの倍数にし、データ配列のポインタは正しく整列したものにすべきです。 システムによっては、これらの要求に従わないと、誤ったデータが読み込まれたり、 システムクラッシュを招いたりします。

複数の bus_space_read_region_N() 関数による読み込み操作は順序が変わって実行されることがあります。また、 bus_space_barrier() 関数を使って順序を強制していない場合、 他の保留中の読み込みや書き出し操作の影響で実行順序が変わってしまうことも あります。 bus_space_read_region_N() 関数で別々のバス空間位置を読み込む場合は、その間にバリアを挟むことは できません。

これらの関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる 場合、ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、これらは復帰しません。

bus_space_write_region_1(space, handle, offset, datap, count)
bus_space_write_region_2(space, handle, offset, datap, count)
bus_space_write_region_4(space, handle, offset, datap, count)
bus_space_write_region_8(space, handle, offset, datap, count)

bus_space_write_region_N() 関数ファミリは、それぞれ count 個の 1, 2, 4, 8 バイトのデータアイテムを、 datap で指定される配列から読み込み、 space で指定されるバス空間の handle で指定される領域の offset のバイトオフセットのバス空間へ書き出します。 連続したデータアイテムは、直前のデータアイテムの 1, 2, 4, 8 バイト (どの関数を使っているかによります) 後へ書き出されます。書き出される位置は handle で指定されるバス空間領域に収まっていなくてはなりません。

移植性を高めるため、 handle で指定される領域の開始アドレスにオフセットを加えたものは、書き出される データアイテムの大きさの整数倍とし、データ配列のポインタは正しく整列 したものにするべきです。システムによっては、これらの要求に従わないと、 誤ったデータが書き出されたり、システムクラッシュを招いたりします。

複数の bus_space_write_region_N() 関数による書き出し操作は順序が変わって実行されることがあります。また、 bus_space_barrier() 関数を使って順序を強制していない場合、 他の保留中の読み込みや書き出し操作の影響で実行順序が変わってしまうことも あります。 bus_space_write_region_N() 関数で別々のバス空間位置へ書き出す場合は、その間にバリアを挟むことは できません。

これらの関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる 場合、ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、これらは復帰しません。

bus_space_copy_region_1(space, srchandle, srcoffset, dsthandle, dstoffset, count)
bus_space_copy_region_2(space, srchandle, srcoffset, dsthandle, dstoffset, count)
bus_space_copy_region_4(space, srchandle, srcoffset, dsthandle, dstoffset, count)
bus_space_copy_region_8(space, srchandle, srcoffset, dsthandle, dstoffset, count)

bus_space_copy_region_N() 関数ファミリは、 count 個の 1, 2, 4, 8 バイトのバス空間のデータアイテムを、 space で指定されるバス空間の srchandle で指定される領域のオフセット srcoffset バイトで始まる範囲から、同じバス空間の dsthandle で指定される領域の、オフセット dstoffset から始まる範囲へコピーします。 連続したデータアイテムは、直前のデータアイテムの 1, 2, 4, 8 バイト (どの関数を使っているかによります) 後から読み込み、書き出されます。 読み込んだり書き出んだりする位置はそれぞれのハンドルで 指定されるバス空間領域に収まっていなくてはなりません。

移植性を高めるため、それぞれのハンドルで 指定される領域の開始アドレスにそれぞれのオフセットを加えたものは、コピーされる データアイテムの大きさの整数倍とすべきです。システムによっては、 これらの要求に従わないと、 誤ったデータがコピーされたり、システムクラッシュを招いたりします。

複数の bus_space_copy_region_N() 関数による読み込み操作や書き出し操作は順序が変わって実行されることが あります。また、 bus_space_barrier() 関数を使って順序を強制していない場合、 他の保留中の読み込みや書き出し操作の影響で実行順序が変わってしまうことも あります。 bus_space_copy_region_N() 関数で別々のバス空間位置へ書き出す場合は、その間にバリアを挟むことは できません。

バス空間の、同一の領域にある子領域間の、重複する部分のあるコピーも、 bus_space_copy_region_N() は正しく実行します。

これらの関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる 場合、ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、これらは復帰しません。

bus_space_set_region_1(space, handle, offset, value, count)
bus_space_set_region_2(space, handle, offset, value, count)
bus_space_set_region_4(space, handle, offset, value, count)
bus_space_set_region_8(space, handle, offset, value, count)

bus_space_set_region_N() 関数ファミリは、与えられる value を count 回、それぞれ 1, 2, 4, 8 バイトのデータアイテムとして space で指定されるバス空間の handle で指定される領域の offset のバイトオフセットのバス空間へ書き出します。 連続したデータアイテムは、直前のデータアイテムの 1, 2, 4, 8 バイト (どの関数を使っているかによります) 後となります。書き出される位置は handle で指定されるバス空間領域に収まっていなくてはなりません。

移植性を高めるため、 handle で指定される領域の開始アドレスにオフセットを加えたものは、書き出される データアイテムの大きさの整数倍とすべきです。 システムによっては、これらの要求に従わないと、 誤ったデータが書き出されたり、システムクラッシュを招いたりします。

複数の bus_space_set_region_N() 関数による書き出し操作は順序が変わって実行されることがあります。また、 bus_space_barrier() 関数を使って順序を強制していない場合、 他の保留中の読み込みや書き出し操作の影響で実行順序が変わってしまうことも あります。 bus_space_set_region_N() 関数で別々のバス空間位置へ書き出す場合は、その間にバリアを挟むことは できません。

これらの関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる 場合、ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、これらは復帰しません。

同一位置からの複数回の読み書き

バス空間の単一の位置を何度も読み書きしてデータ交換を行うよう実装された デバイスがあります。例えば、Ethernet デバイスのパケットバッファ FIFO など です。ドライバがこれらのタイプをできるだけ有効に扱えるように、 bus_space_read_multi_N() と bus_space_write_multi_N() 関数ファミリを提供しています。

bus_space_read_multi_1(space, handle, offset, datap, count)
bus_space_read_multi_2(space, handle, offset, datap, count)
bus_space_read_multi_4(space, handle, offset, datap, count)
bus_space_read_multi_8(space, handle, offset, datap, count)

bus_space_read_multi_N() 関数ファミリは、それぞれ 1, 2, 4, 8 バイトのデータアイテムを count 回、 space で指定されるバス空間の handle で指定される領域の offset で指定されるオフセットから読み込み、 datap で指定される配列へ書き出します。 バス空間の同一位置から、データアイテムが繰り返し読み込まれます。 読み込まれる位置は handle で指定されるバス空間領域に収まっていなくてはなりません。

移植性を高めるため、 handle で指定される領域の開始アドレスにオフセットを加えたものは、読み込む データアイテムの大きさの整数倍とし、データ配列のポインタは正しく整列 したものにするべきです。システムによっては、これらの要求に従わないと、 誤ったデータが読み込まれたり、システムクラッシュを招いたりします。

bus_space_read_multi_N() 関数による読み込み操作は順序が変わって実行されることがあります。また、 bus_space_barrier() 関数を使って順序を強制していない場合、 他の保留中の読み込みや書き出し操作の影響で実行順序が変わってしまうことも あります。 bus_space_read_multi_N() 関数は同じバス空間位置を何度も読み込むので、バス空間位置の繰り返しの 読み出しそれぞれの間には暗黙に読み込みバリアが設定されます。

これらの関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる 場合、ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、これらは復帰しません。

bus_space_write_multi_1(space, handle, offset, datap, count)
bus_space_write_multi_2(space, handle, offset, datap, count)
bus_space_write_multi_4(space, handle, offset, datap, count)
bus_space_write_multi_8(space, handle, offset, datap, count)

bus_space_write_multi_N() 関数ファミリは、それぞれ 1, 2, 4, 8 バイトのデータアイテムを count 回、 datap で指定される配列から読み込み、 space で指定されるバス空間の handle で指定される領域の offset で指定されるオフセットのバス空間へ書き出します。 データアイテムはバス空間の同一位置に繰り返し書き出されます。 書き出される位置は handle で指定されるバス空間領域に収まっていなくてはなりません。

移植性を高めるため、 handle で指定される領域の開始アドレスにオフセットを加えたものは、書き出す データアイテムの大きさの整数倍とし、データ配列のポインタは正しく整列 したものにするべきです。システムによっては、これらの要求に従わないと、 誤ったデータが書き出されたり、システムクラッシュを招いたりします。

bus_space_write_multi_N() 関数による書き出し操作は、 bus_space_barrier() 関数を使って順序を強制していない場合、 他の保留中の読み込みや書き出し操作の影響で実行順序が変わってしまうことが あります。 bus_space_write_multi_N() 関数は同じバス空間位置へ何度も書き出すので、バス空間位置への繰り返しの 書き出しそれぞれの間には暗黙に書き出しバリアが設定されます。

これらの関数はエラーにはなりません。(例えば引数エラーによって) エラーとなる 場合、ソフトウエアバグがあるということなので、panic するようにすべきです。 そのような場合、これらは復帰しません。

バス空間関数群の改善事項

bus_space 関数群の定義は最終的なものではありません。調査して変更、改善すべき点が いくつかあり、以下を含みます:

•   間違った書き方をされたドライバに自動的にバリアを挿入し、かつ正しく書かれた ドライバに必要以上の余分なバリアが挿入されてしまうことがないような機構を 導入すること。これはおそらく、間違った書き方をされたドライバに #define を置くことによって実現されます。 残念ながら現時点では、実際にバリアを正しく使用しているドライバは ほとんどありません (もしくは全くありません)。というのは、 バッファリングを行うアーキテクチャでの bus_space の実装は、安全を考慮すると、 bus_space 呼び出し内部でバリアを設定しなくてはなりません。このことは効率の点で 重大な影響があります。

•   bus_space 関数群をユーザランドで使用可能にすること。これによって、アプリケーション (X サーバなど) がより簡単に、移植性の高いやり方でデバイス空間へのアクセスが 可能となります。

•   アーキテクチャ非依存のバスインタフェースドライバ (例えば PCI - VME ブリッジ) でアーキテクチャ依存のコードを使わないでバス空間定義と実装ができるよう、 バス空間タグとハンドルを定義しなおすこと。これが実現した後も、 アーキテクチャ依存での最適化も可能なままであるように実現すべきです。

•   現状空間特有のアクセス手法を取っているバス空間 (たとえば PCI の設定空間など) を、適切であれば bus_space 関数群を使うよう変更する。

•   マップと割り当てを行う時に、バス空間タグを返すバス特有の関数が 使われるように、バス空間のマップと割り当ての方法を再定義する。こうする ことによって、現状よりさらに最適化が可能になり、 bus_space 関数群をユーザ空間用に流用するのが容易になります (というのは、 ユーザ空間でのマップは、もうひとつ別のバス特有のマップ関数を使うことと よく似ているからです)。

互換性

現在のバージョンの bus_space のインタフェース仕様は広く使われるようになった最初の仕様と少し違っています。 以前のものとの両立させて、機能を拡張するために、関数の名称と引数を 変更したものがいくつかあります。 古い、廃止された仕様で書かれたドライバは ~ <machine/bus.h> をインクルードする前に __BUS_SPACE_COMPAT_OLDDEFS プリプロセッサシンボルを定義するとコンパイルできます。

歴史

bus_space 関数群は NetBSD 1.2 で現在とは違う形式 (メモリと I/O 空間は別々の関数セットでアクセスしていた) で導入されました。 これら関数群は NetBSD 1.3 開発期間に “空間” 一般で動作するよう統合され、多数のドライバがそれを使うよう書き換えられ ました。 この文書は NetBSD 1.3 開発期間の終りに書かれ、一貫性のおかしいところを修正し、欠けていた機能を 追加して仕様が新しくなっています。

作者

bus_space インタフェース群は NetBSD 開発コミュニティによって設計、実装されました。 主な貢献者は Chris Demetriou, Jason Thorpe, Charles Hannum ですが、 他の NetBSD 開発者やユーザコミュニティも開発に重大な役割を果たしました。

Chris Demetriou がこのマニュアルページを書きました。

関連項目
bus_dma(9)