FreeBSD でデバイスドライバ書いて DMA を動かすためのいろいろ。とりあえず参考になりそうなのは、
- FreeBSD Architecture Handbook: Bus Memory Mapping
- bus_dma (9)
- Writing and Adapting Device Drivers for FreeBSD
- How to write a device driver in FreeBSD (PDF)
このあたり。実例は /usr/src/sys/dev の下あたりをみればいくらでもあるけれど、とりあえず参考にしたのは /usr/src/sys/dev/hifn/hifn7751.c とか。
bus_alloc_resources() とかで BAR の値を読み出したり、DMAC を制御するためのレジスタを mmap() できるようにするあたりは今回はとりあえず省略して、DMA のアクセス先になるメインメモリ上の領域を確保するための手順をまとめておくことにします。
- bus_dma_tag_create()
デバイスのDMACに関するいろいろを設定する。たとえば以下のようなこと。これを DMA tag という。- 何バイト単位でDMAできるか(1バイト単位か、4バイト単位か、など)
- DMA可能なアドレスの範囲 (16MB, 4GB, それ以上、など)
- DMA用のメモリの合計の最大サイズ
- 上記メモリを最大いくつのセグメントに分割してよいか、セグメントサイズの上限はいくらか
DMA tag を作るだけではメモリは確保されません。
- bus_dmamap_create()
タグの情報に基づいて map を生成します。ひとつのタグに対応する複数の map を生成できます。map だけで、実際のメモリは確保されないっぽい。 - bus_dmamem_alloc()
ここでmalloc() か contigmalloc() (確保する領域のサイズ次第ですね) が呼ばれます。確保されるサイズは tag を作るときに指定した最大サイズ。物理アドレスは分散している可能性がありますが、カーネル仮想アドレス空間では連続した領域になります (たぶん)。 - bus_dmamap_load()
最後にこれでデバイス側から見えるアドレスのリストを受け取ります。bus_dmamap_load() ではすぐにリストがもらえなくて、callback 関数を指定しておいて、おわると結果が帰ってくる感じです。
片付けるときは逆順です。
- bus_dmamap_unload()
- bus_dmamem_free()
- bus_dmamp_destroy()
- bus_dma_tag_destroy()