|
現: 2025-01-13 (月) 07:09:11 yuji  |
| + | * libusb(汎用USBライブラリ) [#sa54dfbe] |
| + | USBインターフェースを自作のハードウェア等で使用する場合,出来るだけ簡単な方法で扱いたい。~ |
| + | libusbはUSB機器をこのような用途で使うためのC言語用の汎用USBライブラリ。~ |
| | | |
| + | デバイス毎に専用のデバイスドライバーを使わず,汎用のUSBデバイスドライバーを使うので,USBインターフェースを使った自作のハードウェアを作成して,アプリケーションでそのデバイスを使うのが楽になる。~ |
| + | |
| + | libusbはLinux,BSD,Solaris,OS X,Windows,AndroidなどのOSで使用出来る。~ |
| + | |
| + | 現在では開発は終了しているバージョンが0.1と,開発が続けられているバージョン1.0があり,それぞれの互換性は無い。~ |
| + | |
| + | しかし,バージョン0.1も使われている。~ |
| + | |
| + | ** いろいろなlibusbとWindows版 [#hb8533d7] |
| + | libusbはLinuxマシンでのlibusb 0.1が開発され,その後それがWindowsにも移植された。~ |
| + | |
| + | そこからさらに派生したいくつか種類がある。~ |
| + | |
| + | また,2005年にMicrosoftはよく知られているUSBのクラスドライバーに加えて,汎用的なUSBドライバーが必要であることに気づいたようだ。~ |
| + | そこでMicrosoftもlibusbのようなWinUSBと呼ばれる独自のデバイスドライバーとライブラリを開発した。~ |
| + | |
| + | Windowsマシンではこのような経緯により,USBデバイスに汎用USBデバイスドライバーとライブラリを使う場合には,しばしば混乱したり動作に問題が出たりしている。~ |
| + | |
| + | *** libusb 0.1 [#d5bb344c] |
| + | [[libusb 0.1>http://libusb.org/]]はLinuxマシンでUSB機器と接続して通信するための,汎用デバイスドライバーとライブラリとして開発された。~ |
| + | クロスプラットフォーム(Linuxやmac OS X,Windows)で動作する。WindowsではWindows 98SE/ME,2000,XP,2003,Vista,Windows 7に対応している。~ |
| + | |
| + | アプリケーションから多くのUSBデバイスへ容易にアクセスできるようになる。~ |
| + | |
| + | メンテナンスは[[こちらのサイト>https://sourceforge.net/projects/libusb/]]のlibusb-0.1 (LEGACY)で行われている。~ |
| + | 現在はメンテナンスのみになっており''0.1.12''が最終版でソフトウェアの更新は行われていない。~ |
| + | |
| + | libusb 0.1ではUSBの転送モードの一つであるアイソクロナス転送もサポートしている。~ |
| + | |
| + | また,フィルター・デバイスドライバーも用意されている。~ |
| + | |
| + | ファイル構成。~ |
| + | - デバイスドライバー~ |
| + | libusb0.sys (win 32bit), libusb0_x64.sys (win 64bit) |
| + | - ライブラリ~ |
| + | ダイナミックライブラリ:libusb.so(linux),libusb0.dll(win 32bit), libusb0_x64.dll(win 64bit) |
| + | スタティックライブラリ:libusb.a(linux, win gcc), libusb.lib(win msc) |
| + | - ヘッダーファイル~ |
| + | usb.h(linux), lusb0_usb.h(windows) |
| + | |
| + | バイナリ提供もある。~ |
| + | |
| + | デバイスドライバーにはデジタル署名がされていない。なのでWindows 10以降ではデバイスドライバーのインストールは難儀になる。~ |
| + | |
| + | *** libusb 1.0 [#z90855cf] |
| + | libusb 0.1に変わって開発されている[[libusb 1.0>https://libusb.info/]]は,現在LinuxやFreeBSD,mac OS Xで標準的に使われている汎用USBライブラリ。~ |
| + | また,Windowsマシンにも移植されている。~ |
| + | |
| + | メンテナンスは[[こちら>https://github.com/libusb/libusb]]で行われている。最新バージョンは''1.0.28''。~ |
| + | |
| + | [[libusb 1.0 API>https://libusb.sourceforge.io/api-1.0/]]でAPI仕様が公開されているが,上記のlibusb 0.1とは互換性は無い。~ |
| + | このため,[[libusb-compat-0.1 wrapper>https://github.com/libusb/libusb-compat-0.1]](libusb-compat-0.1)が開発され,libusb 1.0で0.1系のAPIも使用できるようにはしている。~ |
| + | |
| + | libusb 1.0では独自にデバイスドライバーを開発せず,Windows版ではWinUSB(Microsoftが開発した汎用USBデバイスドライバー)をそのまま使用し,Linuxなどではlibusb 0.1でのデバイスドライバーを使用している。~ |
| + | |
| + | Windows版でWinUSBのデバイスドライバーを使っているので,機能的にはWinUSBに限定されてしまうので,libusb-1.0のAPI全てには対応できていない。特にアイソクロナス転送が使用できない。~ |
| + | |
| + | ファイル構成。~ |
| + | - デバイスドライバー~ |
| + | WinUSB.sys (32bit,64bit) (Microsoftの汎用USBデバイスドライバー) |
| + | - ライブラリ~ |
| + | ダイナミックライブラリ:libusb-1.0.so(linux), libusb-1.0.dll(win gcc, msc 32/64bit) |
| + | スタティックライブラリ:libusb-1.0.a(linux, win gcc 32/64bit), libusb-1.0.lib(msc 32/64bit) |
| + | - ヘッダーファイル~ |
| + | libusb.h |
| + | |
| + | Windows用のデバイスドライバーは,標準でWindowsに付属している。~ |
| + | Linux等のデバイスドライバーは,標準で用意されているlibusb 0.1。~ |
| + | |
| + | *** libusb-win32 [#f3c7f557] |
| + | [[libusb-win32>http://libusb-win32.sourceforge.net]]は,libusb 0.1と完全互換性があるWindows専用のlibusb。~ |
| + | |
| + | libusb 0.1は開発が終了してしまったので,Windows 10以降のマシンで0.1系のAPIを使う場合は,このlibusb-win32も良く使われているようだ。~ |
| + | |
| + | [[こちらで>https://github.com/mcuee/libusb-win32]]メンテナンスされている。最新版はバージョン''1.4.0.0''。~ |
| + | |
| + | ファイル名などもlibusb 0.1と同じ構成になっている。~ |
| + | また,フィルター・デバイスドライバーも用意されている。~ |
| + | |
| + | ファイル構成。~ |
| + | - デバイスドライバー~ |
| + | libusb0.sys (32/64bit) |
| + | - ライブラリ~ |
| + | ダイナミックライブラリ:libusb0_x86.dll(32bit), libusb0.dll(64bit) |
| + | スタティックライブラリ:libusb.lib(msc 32/64bit), libusb.a(gcc) |
| + | - ヘッダーファイル~ |
| + | lusb0_usb.h |
| + | |
| + | デバイスドライバーはデジタル署名されているので,Windows10でも問題なくインストールできる。~ |
| + | |
| + | *** libusbK [#b2e96079] |
| + | [[libusbK>https://sourceforge.net/projects/libusbk/]]はベースのlibusb-win32を拡張してWinUSBのAPIをエミュレートする機能を実装した,WinUSB互換の汎用USBデバイスドライバーとライブラリ。~ |
| + | |
| + | Windows版libusb 1.0(WinUSBデバイスドライバーを使用する)では対応していないアイソクロナス転送や,その他の便利なlibusb-win32に実装されている機能が使用できる。~ |
| + | |
| + | 最新版はバージョン''3.1.0.0''。[[こちらから>https://github.com/mcuee/libusbk/releases]]ダウンロードできる。~ |
| + | |
| + | ファイル構成。~ |
| + | - デバイスドライバー~ |
| + | libusbK.sys (32bit,64bit) |
| + | - ライブラリ~ |
| + | ダイナミックライブラリ:libusbK.dll(32/64bit),libusb0_dll(32/64bit) |
| + | スタティックライブラリ:libusbK.lib(32/64bit),libusb0.lib(32/64bit) |
| + | - ヘッダーファイル~ |
| + | libusbK.h |
| + | |
| + | libusbKでは0.1系と1.0系の両方のAPIを使用できるようになっている。~ |
| + | |
| + | デバイスドライバーはデジタル署名されているので,Windows10でも問題なくインストールできる。 |
| + | |
| + | *** WinUSB [#i4fa9577] |
| + | Microsoftが開発したlibusbのようなWindowsの汎用USBデバイスドライバーとライブラリ。~ |
| + | WDF(Windows Driver Foundation)である。~ |
| + | |
| + | Windows Vistaが発売されてからリリースされたが,Windows XPでも使えた。Windows RTでも使用できる。~ |
| + | |
| + | WinUSBはカーネルモードで動作する''WinUSB.sys''デバイスドライバーと,''WinUSB.dll''というユーザーモード・ライブラリで構成されている。~ |
| + | |
| + | アプリケーションはWinUSB API(WinUSB.dll)を使ってUSBデバイスにアクセスする。~ |
| + | |
| + | WinUSBでサポートされる機能は,デバイスI/O制御要求,リモートウェイクアップ,セレクティブサスペンド,バルク転送,コントロール転送,インタラプト転送がある。~ |
| + | |
| + | WinUSBはアイソクロナス転送をサポートしていないため,Webcamのようなストリームデバイスでは難が出る。~ |
| + | |
| + | デバイスドライバーはデジタル署名されていて,標準でWindowsにバンドルされている。~ |
| + | ただWindowsマシン専用となる。~ |
| + | |
| + | ビルドには[[Windows Driver Kit(WDK)>https://learn.microsoft.com/ja-jp/windows-hardware/drivers/download-the-wdk]]などが必要。~ |
| + | |
| + | ファイル構成。~ |
| + | - デバイスドライバー~ |
| + | WinUSB.sys (32bit,64bit) |
| + | - ライブラリ~ |
| + | ダイナミックライブラリ:WinUSB.dll (msc 32bit,64bit) |
| + | スタティックライブラリ:WinUSB.lib (msc 32bit,64bit) |
| + | - ヘッダーファイル~ |
| + | winusb.h (msc) |
| + | |
| + | ** その他の汎用USBライブラリ [#b0907ac8] |
| + | *** usbdk [#a6a05caf] |
| + | [[usbdk>https://github.com/daynix/UsbDk]](USB Development Kit)は,オープンソースで開発されているWindows用のUSBライブラリ。~ |
| + | ドライバやINFの作成と無関係に,USBシステムに直接アクセスするためのユーザーモード・ライブラリのようである。~ |
| + | |
| + | libusb 1.0から見て,以前のlibusbフィルタドライバーの役目をするらしい。~ |
| + | |
| + | ビルドには,Visual StudioとWindows Driver Kit(WDK)やWindows SDKが必要。 |
| + | |
| + | [[インストーラー>https://github.com/daynix/UsbDk/releases]]も用意されている。~ |
| + | |
| + | *** USBD.DLL [#f0754886] |
| + | [[USBD.DLL>http://www.otto.to/~kasiwano/]]は極力シンプル化したUSB通信を行うための汎用USBデバイスドライバー。~ |
| + | USBデバイスやパイプをオープンした後は,Windowsのファイルアクセスと同様に読み書きができる。~ |
| + | |
| + | 電子工作分野では人気であったが,最近は更新されていない。~ |
| + | |
| + | Windows 7 64bitで使用するには,非公式の64bit版を自己デジタル署名することで一応使用することはできる。~ |
| + | |
| + | [[汎用USBドライバの使い方>http://www.picfun.com/usb07.html]]で使い方が説明されている。~ |
| + | |
| + | *** USB Serial (CDC) [#ga151e7f] |
| + | USB Serial (CDC)(Universal Serial Bus Communications Device Class)は,Microsoft製の仮想シリアルの汎用USBデバイスドライバー。~ |
| + | |
| + | Windows 10では,Usbser.sys/Usbser.infが最初からインストールされている。~ |
| + | なので,USB機器がCDCデバイスクラスに属している場合は,このデバイスドライバーが自動的にロードされるようになっている。~ |
| + | |
| + | この場合,独自のINFファイルを作成する必要はない。~ |
| + | |
| + | Windows 10以前の場合は,INFファイルを作成する必要がある。~ |
| + | |
| + | * ユーティリティツール [#p63ca7c7] |
| + | Windowsマシンでlibusb系のデバイスドライバーをインストールするときに使用できる支援ツール。~ |
| + | |
| + | ** Zadig [#le4110cf] |
| + | [[Zadig>https://zadig.akeo.ie/]]は,USB汎用デバイスドライバーのファイルにUSB機器情報をセットして,デバイスドライバーとしてインストール出来るようにする(INFファイルの作成など)ための支援ツール。~ |
| + | WinUSB,libusb-win32,libusbKなどをサポートしている。~ |
| + | |
| + | デバイスから情報を読み取って,汎用USBデバイスドライバーのlibusb系のデバイスドライバーをインストールして使用できるようにする目的で利用するツール。~ |
| + | |
| + | バージョン2.9で対応している汎用USBデバイスドライバーは,~ |
| + | - libusb-win32~ |
| + | v1.4.0.0~ |
| + | - WinUSB~ |
| + | v6.1.7600.16385~ |
| + | - libusbK~ |
| + | v3.1.0.0~ |
| + | - USB Serial (CDC)~ |
| + | |
| + | になっている。 |
| + | |
| + | * libusbの使い方 [#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 1.0/1.1/2.0/3.0でも仕様書に書いてある。~ |
| + | |
| + | libusbではlibusb_device_descriptorという構造体に格納される。~ |
| + | この構造体のフィールドは[[ここ>http://libusb.sourceforge.net/api-1.0/structlibusb__device__descriptor.html]]で参照できる。~ |
| + | |
| + | libusb_device_descriptorの項目で,デバイスを特定するのに使えるのは以下がある。~ |
| + | なお,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) |
| + | |
| + | // 以下処理 |
| + | ... |
| + | } |
| + | |
| + | 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); |
| + | |
| + | // デバイスをOpen |
| + | 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文字列として扱える。~ |
| + | |
| + | 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()といった関数が使える。~ |
| + | |
| + | //** コンパイルと実行 [#t952ecaf] |
| + | //以下はDebianに自作のV-USB機器を指したときの例。~ |
| + | // $ gcc -O -Wall libusb-sample.c -lusb-1.0 |
| + | // $ ./a.out |
| + | // Template |
| + | // |
| + | // $ |
| + | // |
| + | //「Template」というのが,V-USBでデバイスを作ったときにデフォルトで利用されるiProductの値。~ |
| + | //iProductが設定されているデバイスは意外と少ない。 |