Published on

WinUSB で USB デバイスと通信 3 - WinUSB API を使ってデバイスとコミュニケーションを行う

Authors

今回は 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

手順:

  1. デバイスインターフェース GUID を使って,デバイスへのハンドルを取得する
  2. ハンドルを使って WinUSB を初期化する
  3. WinUSB API を使ってデバイスをコンフィギュアする
  4. 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 と通信できます.