2019/07/02

デバイスドライバを書いてみよう05 Linuxドライバとデバイスを連携しよう

前回でアプリケーションとドライバのやり取りは大方説明した。
今回はデバイスとドライバの通信部分も作って、最終的にアプリケーションからデバイスを操作したい。

と言っても、実際にサンプルを提示するのがとても難しい・・・
一般的なデバイスで、仕様も公開されてて、反応動作がわかりやすくて・・・みたいなものを探してたんだが、そんなものはなかった。
GPIOが付いているIoT基盤などだったら例示しやすいが・・・
何度か描き進めようかと思ったが断念して、を繰り返して苦痛なので、今回はサンプルというより大体こんな感じでやるんだよ、といった形で進めたい。



デバイスとの通信


以前の記事でメモリマップドI/Oの話をした。



メモリ内にデバイスのレジスタがマップされていて、そのレジスタに対応するメモリアドレスに情報を書き込むと、デバイスに情報を送信することになる。また、同じくそのメモリアドレスを読み込むと、デバイスの情報を受信することになる。
基本的にはデバイスとドライバはこの仕組みで通信を行う。

ただ、I/Oに対応したメモリは物理アドレスでの指定となっていて、アプリケーションはおろか、ドライバでもそのままではアクセスできない。
そのため、手順としては

物理アドレスにアクセスできるよう、メモリマップを行う
→そこにread/writeを行うことで、デバイスと通信をする

といった形となる。


物理アドレスのマップ


メモリの物理アドレスへアクセスするには、プログラムからアクセスできるアドレスへマップする必要がある。実際の例を挙げて説明する。

例えば、Raspberry PiにGPIOという入出力ピンが付いている。これに通電する(3.3V)か遮断する(0V)かをプログラムで制御して、そのピンに繋がっているモータを回したりする。
Raspberry Piは基板上にGPIOが常設されていて、これも一種の物理デバイスとなっており、デバイスドライバで制御している。データシートという仕様書に物理アドレス○○に△△を書き込んだらGPIOのX番に通電する、といったことが記載されている。

例えばGPIOの0番に通電する場合を考えてみる。
物理アドレス「ADDR_GPIO_0_HIGH」に「00010000(1 << 4)」を書き込んだらGPIOの0番に通電する、という仕様だとすると、実際のコードは下記のようになる。
int address = (int)ioremap_nocache(ADDR_GPIO_0_HIGH, 4);
iowrite32(1 << 4, address); iounmap((void*)address);

読み込みも同様にioread32(address) で参照するといい。

ioremap_nocache関数で、実際の物理アドレスをアクセスできるアドレスにマップしている。そのアドレスへ書き込みを行い、iounmap関数で後片付けを行う。ADDR_GPIO_0_HIGHから4byteをマップして、その範囲を読み込み、書き込みができるようにしている。

実際のGPIO操作はもう少し複雑で事前に出力ピンを指定したりしないといけないのだが、基本的には上記の操作でデバイスと通信を行うことができる。


余談


これまで説明したような手順でデバイスと通信を行うことができる。どのようにアクセスするかはデバイスベンダーが公開しているデータシートを参考にして、アドレスをマップする。このようなコードを、これまでで説明したopen/closeやread/write関数に書いて、アプリケーションからデバイスを制御することを可能にする。

ただ、これまでの記事で「アプリケーションは物理アドレスへアクセスできない」みたいな意のことを書いたが・・・あれは嘘だ。厳密にはmmap関数などを使用すると、ユーザランドからもマップしアクセスできてしまう。
まぁ、でも所詮読み書きが多少できるだけで、カーネルランドからアクセスした方がより多くの便利な機能を使うことができる。

まとめ


まとめると、一般的なデバイスベンダーは

1、デバイスドライバ内でopen/closeやread/writeの内容を実装する。
2、OSが用意しているread/writeなどを呼び出し1の機能を使用できるSDKを実装する。

とすることで、アプリケーションからデバイスを制御する方法を提供している。


ioctrlなどまだまだデバイスドライバに重要な機能があるが、それはまたいずれ記事にしたいと思う。

0 件のコメント:

コメントを投稿