CP2112をFreeBSDで使う

ArduinoでI2Cをいじって遊んでいたところ、そもそも最初のトリガーをEthernetかなにかで送らねばならず。
結局Raspberry Piにするか、もしくはArduino Yunにするのかと思っていたところ、いいものが見つかった。

サンハヤト: USB・I2C(SMBus)変換モジュール MM-CP2112B

CP2112というチップが載ったちんまいボードで、USBからI2C 1本とGPIO 8本が使える素敵な子。
しかもFreeBSDはCP2112のドライバを標準で積んでいて、kldloadするだけ。

買おうと思ったら、なぜか押し入れから発掘されたので、早速試してみた。(いつ買ったんだろうね??)

i2cデバイスは /dev/iic0 とかとして出現する。
制御には /usr/sbin/i2c コマンドが用意されている。(man i2c(8))
試してみよう。

# kldload cp2112
# kldload iic
# <ここでデバイスを挿す>
# dmesg
...
ugen0.6:  at usbus0
cp2112hid0 on uhub0
cp2112hid0:  on usbus0
cp2112hid0: part number 0x0c, version 0x03
gpio0:  on cp2112hid0
gpiobus0:  on gpio0
gpioc0:  on gpio0
iichb0:  on cp2112hid0
iicbus0:  on iichb0
iichb0: unsolicited interrupt, ignored
iic0:  on iicbus0
# i2c -s -f /dev/iic0
Hardware may not support START/STOP scanning; trying less-reliable read method.
Scanning I2C devices on /dev/iic0: 10 

アドレス 0x10 のデバイスがあっさり検出された。
データを書いてみる。が、世の中そう甘くない。

# printf "\000" | i2c -a 0x10 -d w -c 1
ioctl: error sending start condition

ちなみに -a 0x10 はアドレス指定、-d wは書き込みモード指定、-c 1 は書き込むバイト数、だ。

解決のヒントは先のスキャンで出ている Hardware may not support START/STOP scanning というメッセージで、CP2112のドライバが一部のIOCTLに対応していないことにある。
manによるところの、

Some I2C bus hardware does not provide control over the individual start, repeat-start, and stop operations.

というやつに該当するのだ。(該当部分の処理)
なので、これもmanの通りに tr モードを指定すればいい。

# printf "\000" | i2c -a 0x10 -d w -m tr -w 0 -c 1

これで成功。readも似たようなものなので、やりたい人はmanを読んでほしい。

※アドレスを間違えると i2c: ioctl(I2CRDWR) failed: No such file or directory と言われる。
FileでもDirectoryでも無いよ!と叫びたいが、エラーコードは ENOENT なので、メッセージが悪いと言えよう。

ちなみに。
CP2112さんを引っこ抜くと、panicする。とても辛い。


※これはVMware Fusionでの実験結果。実機でも同様。

本稿を書くにあたって、Vadim Zaigrin氏の Working with I2C in FreeBSD on Raspberry Pi を参考にさせてもらった。
実はこの方と同じ原因(ドライバが一部のIOCTLに非対応) なのだが、割と最近(May 2019)になってtr モードが追加されて、標準ツールでも使えるようになっていた。

もちろん、依然としてC言語で制御もできる。
i2cコマンドの実装や、Vadim Zaigrin氏の記事を参考にしてほしい。