上野家のホームページ
ナーマル,マリン,ココ
[
新規
|
一覧
|
検索
|
最新
|
ヘルプ
]
開発/libusb
のソース
資料室
開発
/
libusb
のソース
[
差分
|
バックアップ
|
リロード
]
[ ]
差分
を表示
開発/libusb
へ行く。
* libusb [#sa54dfbe] USBインターフェースを自作のハードウェア等で使用する場合,出来るだけ簡単な方法で扱いたい。~ libusbはUSB機器をこのような用途で使うためのC言語用の汎用USBライブラリ。~ デバイス毎に専用のデバイスドライバーを使わず,汎用のUSBデバイスドライバーを使うので,USBインターフェースを使った自作のハードウェアを作成して使うのが楽になる。~ libusbはLinux,BSD,Solaris,OS X,Windows,AndroidなどのOSで使用出来る。~ 現在では開発は終了しているバージョンが0.1系と,開発が続けられているバージョン1.0系があり,それぞれの互換性は無い。~ しかし,バージョン0.1系も現在でもよく使われている。~ ** いろいろなWindows版libusb [#hb8533d7] libusbは,Linuxでのlibusb 0.1がWindowsに移植されたのが最初で,そこから派生したいくつか種類がある。~ *** libusb 0.1 [#d5bb344c] Windows移植されたlibusb 0.1でWindows(98SE/ME,2000,XP,2003,Vista,Win7,・・・)に対応している。~ メンテナンスは[[こちらのサイト>https://sourceforge.net/projects/libusb/]]で行われている。~ 現在はメンテナンスのみになっており,0.1.12が最終版でソフトウェアの更新は行われていない。~ USBの転送モードのアイソクロナス転送もサポートしている。~ フィルター・デバイスドライバーも用意されている。~ アプリケーションから多くのUSBデバイスへアクセスできる。~ ファイル構成。~ - デバイスドライバー~ libusb0.sys (32bit), libusb0_x64.sys (64bit) - ライブラリ~ ダイナミックライブラリ:libusb0.dll(32bit), libusb0_x64.dll(64bit) スタティックライブラリ:libusb.lib(msc), libusb.a(gcc) - ヘッダーファイル~ lusb0_usb.h バイナリ提供もある。~ Windowsでは,Windows 98seからWindows 7まで動作する。~ デバイスドライバーはデジタル署名がされていない。~ *** libusb 1.0 Windows [#z90855cf] [[libusb 1.0 Windows>https://libusb.info/]]は使用するデバイスドライバーを,独自デバイスドライバーを使わず代わりにWinUSB(Microsoftが開発した汎用USBデバイスドライバー)にしたもの。~ デバイスドライバーにWinUSBを使うため,使える機能はWinUSBの機能に限定される。~ このため,アイソクロナス転送は使用できない。~ ファイル構成。~ - デバイスドライバー~ MicrosoftのWinUSBデバイスドライバーを使用。 - ライブラリ~ ダイナミックライブラリ:libusb-1.0.dll(msc 32bit,64bit), libusb-1.0.dll(gcc 32bit,64bit) スタティックライブラリ:libusb-1.0.lib(msc 32bit,64bit), libusb-1.0.a(gcc 32bit,64bit) - ヘッダーファイル~ libusb.h デバイスドライバーは標準でWindowsに付属している。~ *** libusb-win32 [#f3c7f557] libusb-win32は,libusb 0.1完全互換性があるWindowsマシン用のlibusb。~ [[こちらで>http://libusb-win32.sourceforge.net]]もメンテナンスされているようだ。最新版はバージョン1.2.7.3。~ ファイル名などもlibusb 0.1系と同じ構成になっている。~ 現在Windowsマシンにlibusbをインストールする場合は,このlibusb-win32がよく使われている。~ フィルター・デバイスドライバーも用意されている。~ ファイル構成。~ - デバイスドライバー~ libusb0.sys (32bit,64bit)~ - ライブラリ~ ダイナミックライブラリ:libusb0_x86.dll(32bit), libusb0.dll(64bit) スタティックライブラリ:libusb.lib(msc 32bit,64bit), libusb.a(gcc) - ヘッダーファイル~ lusb0_usb.h デバイスドライバーはデジタル署名されているので,Windows10でも問題なくインストールできる。~ *** libusbK [#b2e96079] libusbKは,libusb-win32を拡張してWinUSBのAPIをエミュレートする機能を実装したWinUSB 100%互換のデバイスドライバーと汎用USBライブラリ。~ libusb 1.0 Windows(WinUSBを使用する)では対応していない,アイソクロナス転送やその他の便利な機能(libusb-win32に実装されている)が使用できる。~ 最新版はバージョン3.1.0.0。~ ファイル構成。~ - デバイスドライバー~ libusbK.sys (32bit,64bit)~ - ライブラリ~ ダイナミックライブラリ:libusbK.dll(32bit,64bit),libusb0_dll(32bit,64bit) スタティックライブラリ:libusbK.lib(32bit,64bit),libusb0.lib(32bit,64bit) - ヘッダーファイル~ libusbK.h このため,0.1系と1.0系の両方のAPIを使用できるようになっている。~ デバイスドライバーはデジタル署名されているので,Windows10でも問題なくインストールできる。 *** WinUSB [#i4fa9577] Microsoftが開発したlibusbのような汎用USBデバイスドライバーとライブラリ。~ WDF(Windows Driver Foundation)である。~ Windows Vistaが発売されてからリリースされたが,Windows XPでも使える。~ WinUSBは,カーネルモードで動作するWinUSB.sysというデバイスドライバーと,WinUSB.dllというユーザーモードコンポーネントで構成されている。~ アプリケーションは,WinUSB APIを使ってUSBデバイスにアクセスする。~ WinUSBでサポートされる機能は,デバイス I/O 制御要求,リモートウェイクアップ,セレクティブ サスペンド,バルク転送,コントロール転送,インタラプト転送である。~ WinUSBはアイソクロナス転送をサポートしていないため,Webcamのようなストリームデバイスの操作に難が出る。~ デバイスドライバーはデジタル署名されていて,標準でWindowsにバンドルされている。~ Windows RTでも使用できる。もちろん,Windowsマシン専用となる。~ ビルドには,Windows Driver Kit(WDK)が必要。~ ** その他の汎用USBライブラリ [#b0907ac8] *** usbdk [#a6a05caf] Windows用のライブラリ。~ ドライバやINFの作成と無関係に,USBシステムに直接アクセスするためのライブラリのようである。~ libusb 1.0から見て,以前のlibusbフィルタドライバーの役目をするらしい。~ ビルドには,Windows Driver Kit(WDK)が必要。 *** USBD.DLL [#f0754886] 極力シンプル化したUSB通信を行うためのデバイスドライバー。~ USBデバイスやパイプをオープンした後は,Windowsのファイルアクセスと同様に読み書きができる。~ 電子工作分野では人気であったが,最近は更新されていない。~ Windows 7 64bitで使用するには,非公式の64bit版を自己デジタル署名することで一応使用することはできる。~ *** USB Serial (CDC) [#ga151e7f] UARTのようなシリアルデバイス専用の汎用USBデバイスドライバー。~ Windows 10では,Usbser.infが最初からインストールされている。~ なのでUSB機器がCDCデバイスクラスに属している場合は,自動的にロードされるようになっている。~ この場合,独自のINFファイルを作成する必要はない。~ Windows 10以前の場合は,INFファイルを作成する必要がある。~ * ユーティリティツール [#p63ca7c7] Windowsマシンでlibusb系のデバイスドライバーをインストールするときに使用できる支援ツール。~ ** Zadig [#le4110cf] [[Zadig>https://zadig.akeo.ie/]]は,USB汎用デバイスドライバーのファイルにUSB機器情報をセットして,デバイスドライバーとしてインストール出来るようにする(INFファイルの作成など)ための支援ツール。~ WinUSB,Libusb-Win32(libusb0.sys),libusbkなどをサポートしている。~ デバイスメーカーからのネイティブなデバイスドライバーから情報を読み取って,汎用USBデバイスドライバーのlibusb系のデバイスドライバーをインストールして使用できるようにする目的で利用するツール。~ 対応している汎用USBデバイスドライバーは,~ - libusb-win32~ v1.4.0.0~ - WinUSB~ v6.1.7600.16385~ - libusbK~ v3.1.0.0~ - USB Serial (CDC)~ になっている。 * サンプルプログラム [#q107cd4b] ** サンプルプログラム1 [#ud5f008e] プロジェクトのフォルダに,64bit版のlibusb-1.0.libとlibusb-1.0.dll,libusb.hを用意して,ビルドする。~ ''main.cpp''~ #code(c++,nonumber){{ #include <stdio.h> #include "libusb.h" #pragma comment(lib,"libusb-1.0.lib") void die(char* s) { printf("Error: "); puts(s); exit(-1); libusb_exit(NULL); } int main() { int ret; //初期化 if ( libusb_init(NULL) < 0 ) die("libusb_init"); printf("Init\n"); //デバッグレベルの設定(通信失敗時などにメッセージが出るようになる) libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING); printf("Set debug\n"); //デバイスをVIDとPIDを指定してオープン libusb_device_handle *devh = libusb_open_device_with_vid_pid(NULL, 0x04D8, 0x0053); if ( devh == NULL) die("libusb_open_device_with_vid_pid"); printf("libusb_open_device_with_vid_pid\n"); //デバイスのインターフェースの使用権を要求 //これをしないと、以降通信をしようとしても //libusb: error [winusbx_submit_bulk_transfer] unable to match endpoint to an open interface - cancelling transfer //が出てしまいうまく行かない。 ret = libusb_claim_interface(devh,0); if ( ret == 0 ) { printf("OK\n"); } else if ( ret == LIBUSB_ERROR_NOT_FOUND ) die("libusb_claim_interface : LIBUSB_ERROR_NOT_FOUND"); else if ( ret == LIBUSB_ERROR_BUSY ) die("libusb_claim_interface : LIBUSB_ERROR_BUSY"); else if ( ret == LIBUSB_ERROR_OVERFLOW ) die("llibusb_claim_interface : LIBUSB_ERROR_NO_DEVICE"); else die("libusb_claim_interface : UNKNOWN %d"); printf("libusb_claim_interface\n"); //--- unsigned char data[64]={0x81}; int data_len=1; //--- // EP=1にバルク送信。タイムアウト1000ms ret = libusb_bulk_transfer(devh, LIBUSB_ENDPOINT_OUT | 1, data, sizeof(data), &data_len, 1000); if ( ret == 0 ) { printf("Received : %d Bytes\n", data_len); for ( int i = 0; i<data_len; i++ ) { printf("%02X ", data[i]); } } else if ( ret == LIBUSB_ERROR_TIMEOUT ) die("libusb_bulk_transfer : LIBUSB_ERROR_TIMEOUT"); else if ( ret == LIBUSB_ERROR_PIPE ) die("libusb_bulk_transfer : LIBUSB_ERROR_PIPE"); else if ( ret == LIBUSB_ERROR_OVERFLOW ) die("libusb_bulk_transfer : LIBUSB_ERROR_OVERFLOW"); else if ( ret == LIBUSB_ERROR_NO_DEVICE ) die("libusb_bulk_transfer : LIBUSB_ERROR_NO_DEVICE"); else die("libusb_bulk_transfer : UNKNOWN"); printf("\nSent\n"); //--- // EP=1からバルク受信。タイムアウト1000ms ret = libusb_bulk_transfer(devh, LIBUSB_ENDPOINT_IN|1, data, sizeof(data), &data_len, 1000); if(ret == 0) { printf("Received : %d Bytes\n",data_len); for(int i=0;i<data_len;i++ ) { printf("%02X ",data[i]); } } else if ( ret == LIBUSB_ERROR_TIMEOUT ) die("libusb_bulk_transfer : LIBUSB_ERROR_TIMEOUT"); else if ( ret == LIBUSB_ERROR_PIPE ) die("libusb_bulk_transfer : LIBUSB_ERROR_PIPE"); else if ( ret == LIBUSB_ERROR_OVERFLOW ) die("libusb_bulk_transfer : LIBUSB_ERROR_OVERFLOW"); else if ( ret == LIBUSB_ERROR_NO_DEVICE ) die("libusb_bulk_transfer : LIBUSB_ERROR_NO_DEVICE"); else die("libusb_bulk_transfer : UNKNOWN"); printf("\nReceived\n"); //片付け libusb_close(devh); libusb_exit(NULL); printf("Done\n"); return 0; } }} ** サンプルプログラム2 [#ve4b73da] ''main.cpp'' #code(c++,nonumber){{ #include <stdio.h> #include "libusb.h" #pragma comment(lib,"libusb-1.0.lib") #define VENDERID 0x04D8 #define PRODUCTID 0x0053 #define EP1 0x1 int main() { libusb_device_handle *devh = NULL; unsigned char send_data[64] = {0}; // マイコン側の受信配列と同じか以下の大きさに合わせること unsigned char receive_data[64] = {0}; // マイコン側の送信配列と同じか以下の大きさに合わせること int data_len; int r; try { //初期化 r = libusb_init(NULL); if ( r < 0 ) throw(libusb_error_name(r)); //デバッグ設定 libusb_set_debug(NULL, LIBUSB_LOG_LEVEL_WARNING); //デバイスハンドル取得 devh = libusb_open_device_with_vid_pid(NULL, VENDERID, PRODUCTID); if ( devh == NULL ) throw("Device Not Found\n"); //使用権要求 r = libusb_claim_interface(devh, 0); if ( r != 0 ) throw(libusb_error_name(r)); //送信 int dummy = 0; send_data[0] = 0x81; r = libusb_bulk_transfer(devh, LIBUSB_ENDPOINT_OUT | EP1, send_data, sizeof(send_data), &dummy, 1000); if ( r != 0 ) throw(libusb_error_name(r)); //受信 r = libusb_bulk_transfer(devh, LIBUSB_ENDPOINT_IN | EP1, receive_data, sizeof(receive_data), &data_len, 1000); if ( r != 0 ) throw(libusb_error_name(r)); //受信データの表示 printf("Received : %d Bytes\n", data_len); for ( int i = 0; i < data_len; i++ ) printf("%02X ", receive_data[i]); printf("\n"); } catch ( const char* e ) { //例外処理 printf(e); } //開放処理 if ( devh != NULL ) libusb_close(devh); libusb_exit(NULL); return 0; } }} * プログラム全体の流れ [#r1ea7c08] libusbではUSBデバイスを操作するアプリケーションは以下のような流れになる。~ - libusbを初期化(セッションを開く) - 接続しているデバイスの一覧を取得 - 一覧の中から関心のあるデバイスを見つけて開く - 開いたデバイスで何かする(メッセージをやり取りする) - 開いたデバイスを閉じる - デバイスの一覧を破棄する - セッションを閉じる それぞれの具体的な説明。 ** libusbの初期化(セッションを開く) [#ha612d59] 初期化には,libusb_init()関数を使う。~ libusb_context *context; libusb_init (&context); libusbでは,単一のアプリケーションで複数のUSBとのやり取りを扱えるように,1つのプロセス中で複数のlibusbをリンクできるようになっている。~ その際,それぞれの状態はlibusb_contextという構造体型の変数で管理される。~ この変数によって区別されるlibusbの1つの実体が「セッション」であり,セッションを識別する変数のほうは「コンテキスト」と呼ばれている。~ libusb_init()関数にもコンテキスト(へのポインタ(がある場所))を引数として指定する。~ ただし,ふつうはアプリケーションでlibusbを同時にいくつも使わないだろうから,そういうときのためにデフォルトのコンテキストが用意されている。~ libusb_init()の引数にNULLを指定すれば,このデフォルトのコンテキストでセッションが開かれる。~ libusb_init (NULL); ** 接続しているデバイスの一覧を取得 [#y86edac2] libusbのセッションを初期化したら,USBポートに接続しているデバイスを探す。~ そのために,まずはデバイスの一覧を取得する。これにはlibusb_get_device_list()関数を使う。~ libusb_get_device_list()関数の第1引数にはコンテキストを指定する。~ 第2引数には,デバイス一覧を格納する配列を指定する。~ libusbでは,個々のUSBデバイスをlibusb_deviceという構造体型で表すので,そのポインタを格納する配列を指定することになる。~ libusb_get_device_list()関数の返り値は,取得したデバイスの数。取得に失敗したら-1が返る。~ 以上より,デフォルトのコンテキストではこんな感じにデバイスの一覧を取得すればよい。~ libusb_device **list; int cnt = libusb_get_device_list(NULL, &list); ** デバイス一覧から関心のあるデバイスを見つけて開く [#r63a178b] libusb_get_device_list()関数で取得したデバイスの一覧のうち,アプリケーションで操作したいUSBデバイスだけを探して開くために,デバイス一覧を走査する。~ 走査にあたって目的のデバイスを特定する際には,USBの仕様で定められている「デバイスのデスクリプタ」の情報を使う。~ 「デバイスのデスクリプタ」は,USBデバイスに関する一般的な情報を示すもので,USB 2.0(1.0および1.1も包含されてる)でもUSB 3.0でも仕様書のセクション9.6.1で規定されている。~ libusbではlibusb_device_descriptorという構造体で表されている。~ この構造体のフィールドは下記で参照できる。 http://libusb.sourceforge.net/api-1.0/structlibusb__device__descriptor.html libusb_device_descriptorのフィールドうち,デバイスを特定するのに使えそうなのは下記の4つ。~ なお,USB-IFというのは''USB Implementers Forum''のことで,要するにUSBの規格を策定している団体。~ |フィールド |説明 |h |idVendor |USB-IFから割り当てられている識別子(Vendor ID) | |idProduct |Vendor IDを持っているベンダーが製品に割り当てるID(Product ID)| |iManufacturer|ベンダーについて説明した文字列 | |iProduct |製品について説明した文字列 | デバイスを特定するには,取得したデバイス一覧に含まれている各デバイスについてデスクリプタを取り出し,上記のフィールドの値を読み出してそれを調べればよい。~ デスクリプタの取り出しにはlibusb_get_device_descriptorという関数が用意されているので,だいたい以下のような流れでやっていく。~ struct libusb_device_descriptor desc; libusb_device_handle *handle; for (i = 0; i < cnt; i++) { libusb_device *dev = list[i]; libusb_get_device_descriptor(device, &desc); libusb_open(devive, &handle) /* handle と desc を使って何かする */ ... } 上記には,libusb_device_handle型のhandleという変数へのポインタが出てくるが,これはデバイスハンドラと呼ばれており,デバイスに対する実際のI/O操作などの対象になる。~ デバイス一覧として得られるのがlibusb_device型の変数へのポインタたちで,これを直接いじれそうな気がするかもしれないが,こちらは内部構造を参照できない。~ デスクリプタを参照する際も,libusb_device型の変数へのポインタをlibusb_open()して得られるlibusb_device_handle型の変数が必要になる。~ じゃあ,libusb_device型の変数は何のためにあるかというと,これにはデバイスの参照カウンタという役割がある。~ 参照カウンタは,デバイス一覧を取得した時点で「1」が割り当てられ,そのあとはプログラマーが増やしたり減らしたりできる。~ 参照カウンタがゼロになったデバイスは,そのセッションで破棄される。~ ちなみに,Vendor IDとProduct IDを指定して一気にデバイスハンドラを得られるlibusb_open_device_with_vid_pid()という関数もある。~ これにコンテキストとVendor IDとProduct IDを引数に指定して実行すれば,返り値としてlibusb_device_handle型の変数へのポインタが得られる。~ int vendorId, productId; libusb_device_handle *handle; handle = libusb_open_device_with_vid_pid(NULL, vendorId, productId) ** 開いたデバイスで何かする(メッセージをやり取りする) [#d30a964b] デバイスハンドラとデスクリプタを手に入れたら,いよいよそのデバイスを使ってやりたいことを書く。~ たとえば,iProductを標準出力に表示するなら全体はこんな感じになる。~ #include <stdio.h> #include <libusb-1.0/libusb.h> main () { libusb_device **list; struct libusb_device_descriptor desc; libusb_device_handle *handle; int i, ret; unsigned char text[512]; /* 初期化 */ libusb_init (NULL); /* デバイスの一覧を取得 */ int cnt = libusb_get_device_list(NULL, &list); // /* 一覧を走査して関心のあるデバイスを開く */ for (i = 0; i < cnt; i++) { libusb_device *dev = list[i]; libusb_get_device_descriptor(device, &desc); ret = libusb_open(devive, &handle) /* handle と desc を使って何かする */ if (ret == 0) { libusb_get_string_descriptor_ascii( handle, desc.iProduct, text, sizeof(text)); printf ("%s\n", text); } } /* クロージング */ ... } libusb_get_string_descriptor_ascii()は,文字列のデスクリプタをASCII表現で取り出してくれるありがたいlibusbの関数。~ USBの仕様では,iProductやiManufacturerのような文字列のデスクリプタはUTF-16LEで格納されることになっており,他バイト文字も使えるようになっているんだけど,そうはいってもASCIIが利用されるわけで,そのままC言語で出力したりデバイス特定のために文字列比較したりするのが単純にめんどくさい。~ この関数を使えば,取得したバイト列をそのままASCII文字列として扱える。~ あと上記で注意しないといけないのは,libusb_open()の戻り値でエラーハンドリングしていること。~ もっとちゃんとしたエラー処理をするほうがよいだろうけど,とりあえずlibusb_open()はデバイスが接続されていて権限やメモリの割り当てに成功した場合(つまり返り値が0の場合)にのみデバイスハンドルを有効にするので,ここだけはエラー処理しないとデバイスハンドラを使う関数(上記の例では libusb_get_string_descriptor_ascii())でだいたいセグフォしてしまう。~ ** クロージング [#z1616410] 終了は基本的に以下の手順に従う。 - 開いたデバイスを閉じる - デバイスの一覧をクリアする - セッションを閉じる ただし,実行する関数はlibusb_free_device_list()とlibusb_exit()の2つでよいらしい。~ これは,libusb_free_device_list()の第2引数に1を設定することで上記の1と2を同時にやってくれるから,ということらしいのだけど,第2引数に1以外を設定してもこの程度のアプリケーションでは特に問題もなく終了するっぽいので,よくわからない。~ libusb_device型の変数の参照カウンタが減らず,生きたままになることで何か問題が起きるケースがあるのだろうけれど…。~ なお,libusb_exit()にはコンテキストを指定する必要がある(これにより他のコンテキストのセッションは終了されない)。~ これはもちろんデフォルトのコンテキストを使っているならNULLでいい。~ 全体はこんな感じになる。 #include <stdio.h> #include <libusb-1.0/libusb.h> main () { libusb_device **list; struct libusb_device_descriptor desc; libusb_device_handle *handle; int i, ret; unsigned char text[512]; /* 初期化 */ libusb_init (NULL); /* デバイスの一覧を取得 */ int cnt = libusb_get_device_list(NULL, &list); // /* 一覧を走査して関心のあるデバイスを開く */ for (i = 0; i < cnt; i++) { libusb_device *dev = list[i]; libusb_get_device_descriptor(device, &desc); ret = libusb_open(devive, &handle) /* handle と desc を使って何かする */ if (ret == 0) { libusb_get_string_descriptor_ascii( handle, desc.iProduct, text, sizeof(text)); printf ("%s\n", text); } } /* クロージング */ libusb_free_device_list(list, 1); libusb_exit(NULL); return 0; } 「handleとdescを使って何かする」の部分でもっといろいろやるには,libusb_control_transfer()やlibusb_bulk_transfer()といった関数を使う。~ これらの関数の使い方はUSBデバイスのほうの実装によるので,ここでは省略。~ ** コンパイルと実行 [#t952ecaf] 以下はDebianに自作のV-USB機器を指したときの例。~ $ gcc -O -Wall libusb-sample.c -lusb-1.0 $ ./a.out Template $ 「Template」というのが,V-USBでデバイスを作ったときにデフォルトで利用されるiProductの値。~ iProductが設定されているデバイスは意外と少ない。
開発/libusb のバックアップソース(No. All)
現: 2025-01-13 (月) 08:09:11
yuji
Counter: 32, today: 2, yesterday: 0
Copyright©2008 Yuji Ueno All Rights Reserved.
ログイン
ユーザ名:
パスワード:
IDとパスワードを記憶
パスワード紛失
メインメニュー
ホーム
でぶlog
資料室
最新ページ一覧
全ページ一覧
ヘルプ
» 関連ページ
» Wikiソース
» 編集履歴
» バックアップ一覧
» 添付ファイル一覧
フォーラム
お問い合わせ