- Published on
WinUSB で USB デバイスと通信 3 - WinUSB API を使ってデバイスとコミュニケーションを行う
- Authors
- Name
- Daisuke Kobayashi
- https://twitter.com
今回は WinUSB API を使ってターゲットデバイスとコミュニケーションを行います.
下記のファイルをプロジェクトでインクルード or リンクする
- winusb.h: WinDDK\BuildNumber\inc\ddk
- winusbio.h: WinDDK\BuildNumber\inc\ddk
- usb.h: WinDDK\BuildNumber\inc\api
- usb100.h: WinDDK\BuildNumber\inc\api
- usb200.h: WinDDK\BuildNumber\inc\api
- setupapi.lib: WinDDK\BuildNumber\lib\OSVersion\CPUArch
- winusb.lib: WinDDK\BuildNumber\lib\OSVersion\CPUArch
手順:
- デバイスインターフェース GUID を使って,デバイスへのハンドルを取得する
- ハンドルを使って WinUSB を初期化する
- WinUSB API を使ってデバイスをコンフィギュアする
- WinUSB API を使ってエンドポイントとコミュニケーションする
// winusb.h
#ifndef __WIN_USB_H__
#define __WIN_USB_H__
// Include Windows headers
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <string>
// Include WinUSB headers
#include <winusb.h>
#include <usb100.h>
#include <setupapi.h>
const int WINUSB_BULK_IN_PIPE_BUFSIZE = 1024;
BOOL GetDevicePath(LPGUID interface_guid, LPTSTR device_path, size_t buf_len);
HANDLE OpenDevice(LPGUID interface_guid, BOOL sync);
class WinUSB {
public:
WinUSB(LPGUID interface_guid);
~WinUSB();
BOOL Send(const std::string& buf);
BOOL Recv(std::string& buf);
private:
LPGUID interface_guid_;
HANDLE device_handle_;
WINUSB_INTERFACE_HANDLE win_usb_handle_;
unsigned char bulk_in_pipe_;
unsigned char bulk_out_pipe_;
unsigned char interrupt_pipe_;
int device_speed_;
};
#endif
// winusb.cpp
#include "stdafx.h"
#include "win_usb.h"
BOOL GetDevicePath(LPGUID interface_guid, LPTSTR device_path, size_t buf_len)
{
BOOL result = FALSE;
HDEVINFO device_info;
SP_DEVICE_INTERFACE_DATA interface_data;
PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
ULONG length;
ULONG required_length = 0;
HRESULT hr;
device_info = SetupDiGetClassDevs(interface_guid, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
result = SetupDiEnumDeviceInterfaces(device_info, NULL, interface_guid, 0,
&interface_data);
SetupDiGetDeviceInterfaceDetail(device_info, &interface_data, NULL, 0,
&required_length, NULL);
detail_data = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED,
required_length);
if (detail_data == NULL) {
SetupDiDestroyDeviceInfoList(device_info);
return FALSE;
}
detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
length = required_length;
result = SetupDiGetDeviceInterfaceDetail(device_info, &interface_data,
detail_data, length,
&required_length, NULL);
if (result == FALSE) {
LocalFree(detail_data);
return FALSE;
}
hr = StringCchCopy(device_path, buf_len, detail_data->DevicePath);
if (FAILED(hr)) {
SetupDiDestroyDeviceInfoList(device_info);
LocalFree(detail_data);
}
LocalFree(detail_data);
return result;
}
HANDLE OpenDevice(LPGUID interface_guid, bool sync)
{
HANDLE device_handle = NULL;
char device_path[_MAX_PATH + 1];
BOOL retval = GetDevicePath(interface_guid, (LPTSTR)device_path,
sizeof(device_path) / sizeof(device_path[0]));
device_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);
return device_handle;
}
WinUSB::WinUSB(LPGUID interface_guid)
{
BOOL result;
USB_INTERFACE_DESCRIPTOR iface_descriptor;
WINUSB_PIPE_INFORMATION pipe_info;
ULONG length;
device_handle_ = OpenDevice(interface_guid, true);
result = WinUsb_Initialize(device_handle_, &win_usb_handle_);
if (result) {
length = sizeof(unsigned char);
result = WinUsb_QueryDeviceInformation(win_usb_handle_, DEVICE_SPEED,
&length, &device_speed_);
}
if (result) {
result = WinUsb_QueryInterfaceSettings(win_usb_handle_, 0,
&iface_descriptor);
}
if (result) {
for (int i = 0; i < iface_descriptor.bNumEndpoints; i++) {
result = WinUsb_QueryPipe(win_usb_handle_, 0, (unsigned char)i,
&pipe_info);
if (pipe_info.PipeType == UsbdPipeTypeBulk &&
USB_ENDPOINT_DIRECTION_IN(pipe_info.PipeId)) {
bulk_in_pipe_ = pipe_info.PipeId;
} else if (pipe_info.PipeType == UsbdPipeTypeBulk &&
USB_ENDPOINT_DIRECTION_OUT(pipe_info.PipeId)) {
bulk_out_pipe_ = pipe_info.PipeId;
} else if (pipe_info.PipeType == UsbdPipeTypeInterrupt) {
interrupt_pipe_ = pipe_info.PipeId;
} else {
result = FALSE;
break;
}
}
}
}
WinUSB::~WinUSB()
{
WinUsb_Free(win_usb_handle_);
CloseHandle(device_handle_);
}
BOOL WinUSB::Send(const std::string& buf)
{
ULONG byte_written;
BOOL result = WinUsb_WritePipe(win_usb_handle_, bulk_out_pipe_,
(unsigned char*)buf.data(),
(ULONG)buf.size(), &byte_written, NULL);
return result;
}
BOOL WinUSB::Recv(std::string& buf)
{
ULONG bytes_read;
char recvbuf[WINUSB_BULK_IN_PIPE_BUFSIZE];
BOOL result;
std::string tempbuf;
while (1) {
::ZeroMemory(recvbuf, sizeof(recvbuf));
result = WinUsb_ReadPipe(win_usb_handle_, bulk_in_pipe_,
(unsigned char*)recvbuf, sizeof(recvbuf) - 1,
&bytes_read, NULL);
if (bytes_read) {
tempbuf += recvbuf;
} else {
break;
}
}
std::string(tempbuf.data(), tempbuf.size()).swap(buf);
return result;
}
あとは WinUSB クラスを前回の INF ファイルで指定した GUID をコンストラクタに 渡してインスタンス化し, Send, Recv で WinSys と通信できます.