AVR の USBasp かその手のライタに、I2C ブリッジ機能を持たせるという方向で検討しているのだが、今回は Windows 側 の アプリの話。
実をいうと Windows 10 になってから、開発環境を インストールしていない。一から構築である。普段使いのノーパソの Disk 容量が小さいので、入れるにしても容量が気になる。また、WindowsUpdate のために クリーンインストールを何度かしている。
今回 Windows でもちょっとしたものは、作りたいのだが、最小の環境でというのを考えている。
最小のコンパイル環境 tcc-win32
最小と言えば、これはもう TinyCC - tcc である。
ダウンロードのページを見ると、まだ更新がある。そして tcc-win32 なんてものが ... バイナリまで置いてある。このバイナリの zip は、全部で 386K 。これで、ヘッダファイル、ライブラリが含まれているのである。
この tcc は、フルセットの C 言語 である。__asm__ とかも使え、gcc の拡張も一部入っている。
(今は 分からないが)かつては、Linux を高速にビルドできることを 宣伝していたのだ。
ひょっとして、libusb とか使うアプリも これで作れるのだろうか?
tcc の dll 関係の機能
tcc-win32 をインストールすると、
tcc/lib に
gdi32.def user32.def kernel32.def msvcrt.def
という ファイルがある。これは、 windows の dll の関数一覧で、テキストファイル。
これらは標準で使える API だが、def ファイルを 作成し コンパイルで指定すると dll が使える。(def ファイルではなく、dll を指定しても良い)
-lwinusb
でいける。/tcc/lib に def ファイルを置けば、ライブラリパスを指定しなくて良い。
なお、
tcc -impdef dllファイル
とすることで、 この def ファイルを作ることが出来る。
libusb は使えるのか?
libusb (相当機能)が使えないと 意味がない。これが出来ないのであれば、windows でちょっとテストすることが厳しくなってしまう。
で、作れるのか? libusb-win32 の libusb0-x86.dll を試してみた。
... 結果はリンクして exe は出来る。しかし、正しくリンクできていないらしく、起動でエラー。
制限事項があり、関数のみの dll しか使えないということであった。
他の dll は?
そんなことでは諦めないのである。そう言えば winusb はどうだろう?
というわけで \Windows\system32 にあった winusb.dll をコピーして来て def ファイルを作ってみた。
exe が出来て、起動でエラーが起きない!
ヘッダファイルをなんとかすれば、使えるようになりそうである。user32, kernel32 なんてものが使えているのであるから、多分大丈夫だろう。
そういえば、ws2_32.dll なんてものも system32 にある -- WinSock2 である。
これもいけた。USB とネットワークが使える! (かも)
現時点では、可能性があるというだけだが、期待できそうである。
忘れていた。FTDI は、どうだろう? ftd2xx.dll を試してみた。... ダメであった。
tcc で perl4 のビルド (の調査)
perl4 というのは、今の perl5 の前のバージョンである。かつては、perl3 なども当然あったわけだが、今ダウンロードできるのは、perl4 の最終版のみである。
perl4 は、perl5 のように膨大なライブラリ環境がない。機能も少ないから コンパクトなのである。テスト用のスクリプトなどこの程度で十分である。過去に作った ガーバーファイルのツールも 多分動く。
というわけで perl4 を Windows に移植したいと思っている。
サポートしたい DLL は、windows 10 標準の ws2_32 + winusb 。あと 非標準のいくつか。
GDBM
SQLITE3
ダメだった。思ったより制限がきついようだ。
追記:後で気が付いたのだが、LoadLibrary() は、使えるはずである。直接 リンクできないものは、そうすれば良い。非標準だし、必ずあるという保証もないわけで、むしろ LoadLibrary() を使うべしということにしよう。
winusb
perl は、長期間で考えているので、早急に結論をださなくて良い。気を取り直して winusb を使うことだけ考えよう。
winusb.dll はあった。ドライバ自体をインストールするには、Zadig を使えば良い。足りないのは ... ヘッダファイルである。
tcc の ダウンロードに winapi-full-for-0.9.27.zip がある。
期待したのだが、DDK はあるが、winusb.h はなかった。一応 usb100.h などはある。他に winsock2.h などはあった。
Microsoft の API リファレンス を見れば、作れそうなのではあるが、決定的に足りない定義があるかも知れない。まずは ... Github になにかないか探してみる。
探したら winusb.h があった! psplinkusb というもの、psplinkusb がなにか興味はないのだが、libusb , winusb 両対応で、使い方を対比するのに助かりそうな、コードまである。
・ https://github.com/tyranid/psplinkusb/tree/master/usbhostfs_pc
これで必要なものは揃いそうだ。
tcc ディレクトリへのファイルのコピー
psplinkusb から
winusb.h
winusbio.h
winapi-full-for-0.9.27/ddk から
usb.h
usb100.h
usb200.h
winapi-full-for-0.9.27/winapi から
initguid.h
qos.h
winsock2.h
setupapi.h
commctrl.h
prsht.h
を tcc/include/winapi にコピー
作った
ws2_32.def
winusb.def
に加えて setupapi.def も作り tcc/lib にコピー
WINUSB をリンクするときは、
-lwinusb -lsetupapi
とする。
これで(試す)準備は出来た。
API の調査
WINUSB の API は、全然知らない。libusb は以前は弄ったことがあったが、ほぼ忘れてしまった。一からである。
インクルード
#include<windows.h>
#include<winusb.h>
#include<setupapi.h>
インクルードは、最低これだけ必要。
初期化
dev = OpenDevice(TRUE);
r = WinUsb_Initialize(dev, &winusb);
OpenDevice() でハンドルを得る。それに対して、WinUsb_Initialize() で 初期化をして 新たに ハンドルを得る。オーバライド可の指定をすると、同じハンドルが返ってくる(らしい)
と書いたが、間違いである。setupapi を使って デバイスを特定・オープンし、それに対して WinUsb_Initialize() を行う。これについて、後で詳しく。
終了処理
WinUsb_Free(winusb);
Setupパケットの送信とリプライの受信
r = WinUsb_ControlTransfer(winusb,
setupPacket,
buf,
buf_max,
&buf_len,
NULL);
USBasp などは、だいたいは、これを使って通信するが、他に、Flash の Read/Write では、エンドポイントを使う。
エンドポイントの通信
WinUsb_ReadPipe()
WinUsb_WritePipe()
を使う。
面倒になったが、基本はこれだけみたいなのである。そんなことより、どうやって USB装置を特定するか? libusb でもめんどくさいことをやっていたが WINUSB でも同様らしい。
そこさえクリアできれば、使うのは簡単という気になってきた。
ただ、libusb と共通化できるようにして、しっかりここを作ると、libusb へのラッパーライブラリを作っている気分になる。それはたぶん既にあるだろうから無駄である。もともと、V-USB で作る典型的な装置にしか対応する気がないのである。できるだけ簡略化の方向で。
デバイスのスキャン
・https://ameblo.jp/new3bon/entry-10461467643.html
たぶんここに書いてあることをする。
(要約)
SetupDiEnumDeviceInterfaces() のMemberIndex引数で何番目を指定できる。これをループすれば良い。
やってみた結果、SetupDiEnumDeviceInterfaces() は、すでに使われていて使えないデバイスも含まれているようだ。CreateFileでファイルをオープンしてみて開けたら使えるということで認識することにした。
あとはこのハンドルをWinUsb_Initializeかければいい。
SetupDiEnumDeviceInterfaces() を理解すればよいようだ。
すべての存在するデバイスを指定して、SetupDiEnumDeviceInterfaces()をループで回して、VID/PID や クラス、デバイス名 が一致するものを選ぶ。2個刺している場合もあるわけで、n 番目に見つかったものという指定も必要だ。
Handle = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
すべての存在するデバイスの指定は、これで良いらしい。
SetupDiEnumDeviceInterfaces()のループでは、
・SetupDiGetDeviceInterfaceDetail()でデバイスパスを取得する
・CreateFile() にデバイスパスを指定してハンドルを開く
・WinUsb_Initialize()
そこまで来るとようやく
WinUsb_GetDescriptor(handle, USB_DEVICE_DESCRIPTOR_TYPE,....)
が call できる。お目当てのものでなければ、ループ再開。
戻りは、USB_DEVICE_DESCRIPTOR構造体。この中は、Descriptor の情報そのものっぽい。
CreateFile()は、
handle = CreateFile((LPTSTR)device_path,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
で、もう分からないのでおまじないとして捉える。
ここから先は、コードを書いてみて確認しないとダメそうだ。
だいぶ遠回りしてしまったような気がする。LoadLibrary() を使ってすなおに、libusb を使えば良かったとも思うのだが、WINUSB をどう使うか気になっていたので、それはそれで。open のところまで行くのが、すごくめんどくさいが、libusb でも同じことなのである。ここまで来たからには、WINUSB でと考えている。
とにかく、テストツールを作れる目途が立ったが、結局は libusb を使った Linux 版も作ることになるだろう。それだけではなく、SBC 用ならば、USB を通さず 直接 GPIO でということに。USB-I2C ブリッジを検討していると書いたが、そのコードを Linux に持ってきて、テストツールに加えることになるだろう。
それも考えて、テストツールを作ることになる。実際に作り出すのは、USB-I2C ブリッジの後ということになりそうだ。
Rasberry Pi での I2C 制御 (例) の調査
カーネルで i2c ドライバが 構成されると、/dev/i2cdev-1 といった デバイスファイルが出来る。
これを Open して使うというのが、最も ベーシックな使い方で、
・ioctl(fd, I2C_SLAVE,i2cAddress)
で、装置アドレスを指定
fd に対する read/write で 送受信 (たぶん)。
どうもこれだけのようである。
これにマッピングできる レイヤを作る。--- これで良いようだ。