code_twain/device/win_usb/win_usb.cpp

1269 lines
38 KiB
C++
Raw Normal View History

2022-05-03 08:54:08 +00:00
#include "win_usb.h"
#include <initguid.h>
#include <usbiodef.h>
#include <usbioctl.h>
#include <SetupAPI.h>
#include <hidsdi.h>
#include <winusb.h>
#include <rpc.h>
//#include <winioctl.h>
#include <usbscan.h>
#include <Dbt.h>
#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "hid.lib")
#pragma comment(lib, "winusb.lib")
#pragma comment(lib, "rpcrt4.lib")
#include "hginclude/hg_log.h"
#include "scanner_manager.h" // for hg_scanner_mgr::ui_default_callback
#include "usbview/enum.h"
#define MSG_DEVICE_PNP WM_USER + 1 // wParam: (bool)arrive; lParam: usb_device*
#define bzero(a, l) memset(a, 0, l)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// usb_callback ...
usb_callback::usb_callback(libusb_hotplug_callback_fn cb
, void* param)
: usb_cb_(cb), usb_cb_param_(param)
{}
usb_callback::~usb_callback()
{}
void usb_callback::notify(libusb_context* ctx, usb_device* dev, int ev)
{
usb_cb_(ctx, (libusb_device*)dev, (libusb_hotplug_event)ev, usb_cb_param_);
}
std::string u2utf8(const wchar_t* u)
{
//*
return hg_log::u2utf8(u);
/*/
int len = WideCharToMultiByte(CP_UTF8, 0, u, lstrlenW(u), NULL, 0, NULL, NULL);
char *ansi = new char[len + 4];
len = WideCharToMultiByte(CP_UTF8, 0, u, lstrlenW(u), ansi, len, NULL, NULL);
ansi[len--] = 0;
std::string utf8(ansi);
delete[] ansi;
return utf8;
/////*///////////////////////
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// usb_device ...
usb_device::usb_device(const char* name) : ref_(1), name_(name ? name : ""), is_ok_(false)
, dev_desc_(NULL), handle_(NULL), online_(true), timout_ms_(1000)
{
bzero(&guid_, sizeof(guid_));
id_ = usb_device::vid_pid_from_name(name);
}
usb_device::~usb_device()
{
clear();
}
HANDLE usb_device::find_pipe(UCHAR addr, int type)
{
for (size_t i = 0; i < pipes_.size(); ++i)
{
if (pipes_[i].address == addr && pipes_[i].type == type)
return pipes_[i].pipe;
}
return NULL;
}
int usb_device::set_timeout(HANDLE h)
{
USBSCAN_TIMEOUT uto = { 0 };
DWORD cbr = 0;
uto.TimeoutEvent = uto.TimeoutRead = uto.TimeoutWrite = (timout_ms_ + 500) / 1000;
return DeviceIoControl(h, IOCTL_SET_TIMEOUT, &uto, sizeof(uto), NULL, 0, &cbr, NULL) ? LIBUSB_SUCCESS : LIBUSB_ERROR_IO;
}
DEVID usb_device::vid_pid_from_name(const char* name)
{
// name: \\?\usb#vid_3072&pid_0239#01234567aabbccddee#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
DEVID id = { 0 };
std::string s(name ? name : "");
size_t pos = 0;
std::transform(s.begin(), s.end(), s.begin(), tolower);
pos = s.find("vid_");
if (pos != std::string::npos)
id.vid = usb_device::from_hex_string(s.c_str() + pos + 4);
pos = s.find("pid_");
if (pos != std::string::npos)
id.pid = usb_device::from_hex_string(s.c_str() + pos + 4);
return id;
}
DWORD usb_device::from_hex_string(const char* hex_str)
{
DWORD v = 0;
for (int i = 0; hex_str[i]; ++i)
{
DWORD now = 0;
if (hex_str[i] >= '0' && hex_str[i] <= '9')
now = hex_str[i] - '0';
else if (hex_str[i] >= 'a' && hex_str[i] <= 'f')
now = hex_str[i] - 'a' + 10;
else if (hex_str[i] >= 'A' && hex_str[i] <= 'F')
now = hex_str[i] - 'A' + 10;
else
break;
v <<= 4;
v += now;
}
return v;
}
std::string usb_device::driver_key_name(HANDLE file)
{
ULONG nBytes = 0;
USB_HCD_DRIVERKEY_NAME driverKeyName = { 0 };
BOOL success = DeviceIoControl(file, IOCTL_GET_HCD_DRIVERKEY_NAME, &driverKeyName, sizeof(driverKeyName), &driverKeyName, sizeof(driverKeyName), &nBytes, NULL);
std::string ret("");
if (success && driverKeyName.ActualLength > nBytes)
{
PUSB_HCD_DRIVERKEY_NAME buf = (PUSB_HCD_DRIVERKEY_NAME)GlobalAlloc(GPTR, driverKeyName.ActualLength);
nBytes = driverKeyName.ActualLength;
success = DeviceIoControl(file, IOCTL_GET_HCD_DRIVERKEY_NAME, &buf, nBytes, &buf, nBytes, &nBytes, NULL);
if (success)
{
ret = u2utf8(buf->DriverKeyName);
}
GlobalFree(buf);
}
return ret;
}
std::string usb_device::parent_hub_path_name(int vid, int pid, int *addr)
{
std::string ret("");
GUID guid = GUID_DEVINTERFACE_USB_HUB;
HDEVINFO dev = SetupDiGetClassDevsW(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
int port = addr ? *addr : -1;
if (dev)
{
SP_DEVICE_INTERFACE_DATA id = { 0 };
SP_DEVINFO_DATA did;
int ind = 0;
id.cbSize = sizeof(id);
while (SetupDiEnumDeviceInterfaces(dev, NULL, &guid, ind++, &id))
{
PSP_DEVICE_INTERFACE_DETAIL_DATA_W buf = NULL;
DWORD size = 0;
if (!SetupDiGetDeviceInterfaceDetailW(dev, &id, buf, 0, &size, NULL) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
int bytes = size + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + 40;
buf = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new wchar_t[bytes];
memset(buf, 0, bytes * 2);
buf->cbSize = sizeof(*buf);
if (SetupDiGetDeviceInterfaceDetailW(dev, &id, buf, buf->cbSize + size, NULL, NULL))
{
std::string n(u2utf8(buf->DevicePath));
if (find_vid_pid_in_hub(n.c_str(), vid, pid, &port))
ret = n;
}
delete[] buf;
if (ret.length())
break;
}
}
SetupDiDestroyDeviceInfoList(dev);
}
if (addr)
*addr = port;
HG_VLOG_MINI_4(HG_LOG_LEVEL_DEBUG_INFO, "Parent hub for %04X:%04X is: %s (+%d)\r\n", vid, pid, ret.c_str(), port);
return ret;
}
bool usb_device::find_vid_pid_in_hub(const char* utf8_hub_path_name, int vid, int pid, int* addr)
{
bool ret = false;
HANDLE h = CreateFileA(utf8_hub_path_name, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (h == INVALID_HANDLE_VALUE)
return ret;
int bytes = sizeof(USB_NODE_CONNECTION_INFORMATION_EX) + (sizeof(USB_PIPE_INFO) * 30);
PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfoEx = (PUSB_NODE_CONNECTION_INFORMATION_EX)new char[bytes];
memset(connectionInfoEx, 0, bytes);
if (addr && *addr != -1)
{
connectionInfoEx->ConnectionIndex = *addr;
DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, connectionInfoEx,
bytes, connectionInfoEx, bytes, (LPDWORD)&bytes, NULL);
ret = connectionInfoEx->DeviceDescriptor.idVendor == vid && connectionInfoEx->DeviceDescriptor.idProduct == pid;
}
else
{
for (int i = 1; !ret; ++i)
{
DWORD len = bytes;
connectionInfoEx->ConnectionIndex = i;
if (!DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, connectionInfoEx,
bytes, connectionInfoEx, bytes, (LPDWORD)&bytes, NULL))
break;
ret = connectionInfoEx->DeviceDescriptor.idVendor == vid && connectionInfoEx->DeviceDescriptor.idProduct == pid;
}
if (ret && addr)
*addr = connectionInfoEx->ConnectionIndex;
}
delete[] connectionInfoEx;
CloseHandle(h);
return ret;
}
int usb_device::get_device_address(const char* device_name, LPGUID lpguid)
{
int addr = -1;
GUID guid = lpguid ? *lpguid : GUID_DEVINTERFACE_USB_DEVICE;
HDEVINFO dev = NULL;
std::string ret(""), src(device_name);
dev = SetupDiGetClassDevsW(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (dev)
{
SP_DEVICE_INTERFACE_DATA id = { 0 };
SP_DEVINFO_DATA did;
int ind = 0;
size_t pos = src.find("{");
if (pos != std::string::npos)
src.erase(pos);
id.cbSize = sizeof(id);
while (SetupDiEnumDeviceInterfaces(dev, NULL, &guid, ind++, &id))
{
PSP_DEVICE_INTERFACE_DETAIL_DATA_W buf = NULL;
DWORD size = 0;
ret = "";
if (!SetupDiGetDeviceInterfaceDetailW(dev, &id, buf, 0, &size, NULL) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
buf = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new wchar_t[size + 4];
memset(buf, 0, (size + 4) * 2);
buf->cbSize = sizeof(*buf);
if (SetupDiGetDeviceInterfaceDetailW(dev, &id, buf, buf->cbSize + size, NULL, NULL))
{
wchar_t* l = wcsstr(buf->DevicePath, L"{");
if (l)
*l = 0;
ret = u2utf8(buf->DevicePath);
}
delete buf;
}
if (stricmp(ret.c_str(), src.c_str()))
continue;
SP_DEVINFO_DATA dd = { 0 };
dd.cbSize = sizeof(dd);
// for (int i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dd); ++i)
if (SetupDiEnumDeviceInfo(dev, ind - 1, &dd))
{
SetupDiGetDeviceRegistryPropertyA(dev, &dd, SPDRP_ADDRESS, NULL, (PBYTE)&addr, sizeof(addr), NULL);
}
break;
}
SetupDiDestroyDeviceInfoList(dev);
}
return addr;
}
long usb_device::add_ref(void)
{
return InterlockedIncrement(&ref_);
}
long usb_device::release(void)
{
long ref = InterlockedDecrement(&ref_);
if (ref == 0)
delete this;
return ref;
}
bool usb_device::operator==(const char* name)
{
return name_ == name;
}
bool usb_device::operator==(const DEVID& id)
{
return id_.vid == id.vid && id_.pid == id.pid;
}
usb_device& usb_device::operator=(const DEVID& id)
{
id_ = id;
return *this;
}
usb_device& usb_device::operator=(const GUID& guid)
{
guid_ = guid;
return *this;
}
std::string usb_device::name(void)
{
return name_;
}
GUID usb_device::guid(void)
{
return guid_;
}
DEVID usb_device::id(void)
{
return id_;
}
bool usb_device::is_ok(void)
{
return is_ok_;
}
bool usb_device::is_open(void)
{
return handle_ != NULL;
}
bool usb_device::is_online(void)
{
return online_;
}
void usb_device::set_online(bool online)
{
online_ = online;
}
uint8_t usb_device::port(void)
{
if (!dev_desc_)
init();
return port_;
}
bool usb_device::init(void)
{
PUSBDEVICEINFO info = NULL;
GUID guid;
int addr = -1;
clear();
for (size_t i = 0; i < _countof(ovl_); ++i)
ovl_[i].hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
UuidFromStringA((RPC_CSTR)HG_SCANNER_GUID, &guid);
addr = usb_device::get_device_address(name_.c_str(), &guid);
//if (addr != -1)
{
std::string path(usb_device::parent_hub_path_name(id_.vid, id_.pid, &addr));
if (!path.empty())
{
HANDLE h = CreateFileA(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (h != INVALID_HANDLE_VALUE)
{
info = enumerate_hub_port(h, addr);
CloseHandle(h);
}
}
}
#define COPY_MEMBER(d, s, m) \
(d)->m = (s)->m
if (info)
{
port_ = addr;
if (info->ConnectionInfo)
{
dev_desc_ = new libusb_device_descriptor;
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bLength);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bDescriptorType);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bcdUSB);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bDeviceClass);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bDeviceSubClass);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bDeviceProtocol);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bMaxPacketSize0);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, idVendor);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, idProduct);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bcdDevice);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, iManufacturer);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, iProduct);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, iSerialNumber);
COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bNumConfigurations);
}
if (info->ConfigDesc)
{
libusb_config_descriptor* desc = NULL;
PUSB_COMMON_DESCRIPTOR cmn = (PUSB_COMMON_DESCRIPTOR)(info->ConfigDesc + 1);
int if_ind = 0, ep_ind = 0;
while (cmn->bLength)
{
if (USB_CONFIGURATION_DESCRIPTOR_TYPE == cmn->bDescriptorType)
{
PUSB_CONFIGURATION_DESCRIPTOR conf = (PUSB_CONFIGURATION_DESCRIPTOR)cmn;
if (desc)
cfg_desc_.push_back(desc);
desc = new libusb_config_descriptor;
memset(desc, 0, sizeof(*desc));
COPY_MEMBER(desc, conf, bLength);
COPY_MEMBER(desc, conf, bDescriptorType);
COPY_MEMBER(desc, conf, wTotalLength);
COPY_MEMBER(desc, conf, bNumInterfaces);
COPY_MEMBER(desc, conf, bConfigurationValue);
COPY_MEMBER(desc, conf, iConfiguration);
COPY_MEMBER(desc, conf, bmAttributes);
COPY_MEMBER(desc, conf, MaxPower);
desc->usb_if = new libusb_interface[desc->bNumInterfaces];
memset(desc->usb_if, 0, desc->bNumInterfaces * sizeof(desc->usb_if[0]));
ep_ind = if_ind = 0;
}
else if (USB_INTERFACE_DESCRIPTOR_TYPE == cmn->bDescriptorType)
{
PUSB_INTERFACE_DESCRIPTOR ifd = (PUSB_INTERFACE_DESCRIPTOR)cmn;
libusb_interface_descriptor* uid = NULL;
desc->usb_if[if_ind].num_altsetting = 1;
desc->usb_if[if_ind].altsetting = uid = new libusb_interface_descriptor;
COPY_MEMBER(uid, ifd, bLength);
COPY_MEMBER(uid, ifd, bDescriptorType);
COPY_MEMBER(uid, ifd, bInterfaceNumber);
COPY_MEMBER(uid, ifd, bAlternateSetting);
COPY_MEMBER(uid, ifd, bNumEndpoints);
COPY_MEMBER(uid, ifd, bInterfaceClass);
COPY_MEMBER(uid, ifd, bInterfaceSubClass);
COPY_MEMBER(uid, ifd, bInterfaceProtocol);
COPY_MEMBER(uid, ifd, iInterface);
uid->endpoint = new libusb_endpoint_descriptor[desc->usb_if[if_ind].altsetting->bNumEndpoints];
if_ind++;
ep_ind = 0;
}
else if (USB_ENDPOINT_DESCRIPTOR_TYPE == cmn->bDescriptorType)
{
PUSB_ENDPOINT_DESCRIPTOR endp = (PUSB_ENDPOINT_DESCRIPTOR)cmn;
libusb_endpoint_descriptor* uep = (libusb_endpoint_descriptor*)((void*)&desc->usb_if[if_ind - 1].altsetting->endpoint[ep_ind]);
COPY_MEMBER(uep, endp, bLength);
COPY_MEMBER(uep, endp, bDescriptorType);
COPY_MEMBER(uep, endp, bEndpointAddress);
COPY_MEMBER(uep, endp, bmAttributes);
COPY_MEMBER(uep, endp, wMaxPacketSize);
COPY_MEMBER(uep, endp, bInterval);
uep->extra = NULL;
ep_ind++;
}
cmn = (PUSB_COMMON_DESCRIPTOR)((PCHAR)cmn + cmn->bLength);
}
if (desc)
cfg_desc_.push_back(desc);
is_ok_ = !cfg_desc_.empty();
}
free_usb_device_info(info);
}
return is_ok_;
}
void usb_device::clear(void)
{
close();
for (size_t i = 0; i < _countof(ovl_); ++i)
CloseHandle(ovl_[i].hEvent);
if (dev_desc_)
delete dev_desc_;
dev_desc_ = NULL;
for (size_t i = 0; i < cfg_desc_.size(); ++i)
{
if (cfg_desc_[i]->usb_if)
{
for (size_t j = 0; j < cfg_desc_[i]->bNumInterfaces; ++j)
{
if (cfg_desc_[i]->usb_if[j].altsetting->endpoint)
delete[] cfg_desc_[i]->usb_if[j].altsetting->endpoint;
}
delete[] cfg_desc_[i]->usb_if;
}
delete cfg_desc_[i];
}
cfg_desc_.clear();
is_ok_ = false;
}
void usb_device::online_statu_changed(bool online)
{
online_ = online;
}
int usb_device::get_descriptor(libusb_device_descriptor* desc)
{
if (dev_desc_)
memcpy(desc, dev_desc_, sizeof(*desc));
else
{
char cls[128] = { 0 };
SetupDiGetClassDescriptionA(&guid_, cls, _countof(cls) - 1, NULL);
std::transform(cls, cls + lstrlenA(cls), cls, tolower);
if (strcmp(cls, "usb") == 0)
desc->bDeviceClass = libusb_class_code::LIBUSB_CLASS_HUB;
else if (strcmp(cls, "image") == 0)
desc->bDeviceClass = libusb_class_code::LIBUSB_CLASS_IMAGE;
else if (strcmp(cls, "hidclass") == 0)
desc->bDeviceClass = libusb_class_code::LIBUSB_CLASS_HID;
else
desc->bDeviceClass = libusb_class_code::LIBUSB_CLASS_VENDOR_SPEC;
desc->idVendor = id_.vid;
desc->idProduct = id_.pid;
desc->bcdUSB = 0x200; // USB2.0 ?
desc->bcdDevice = 0; // ?
desc->bDescriptorType = libusb_descriptor_type::LIBUSB_DT_DEVICE;
desc->bDeviceProtocol = 0;
desc->bDeviceSubClass = libusb_class_code::LIBUSB_CLASS_IMAGE;
desc->bLength = sizeof(*desc);
desc->bMaxPacketSize0 = 512;
desc->bNumConfigurations = 1; // ?
desc->iManufacturer = 0;
desc->iSerialNumber = 0;
desc->iProduct = 0;
}
return LIBUSB_SUCCESS;
}
int usb_device::get_config_descriptor(int index, libusb_config_descriptor** desc)
{
if (index >= 0 && index < cfg_desc_.size())
*desc = cfg_desc_[index];
else
return LIBUSB_ERROR_INVALID_PARAM;
return LIBUSB_SUCCESS;
}
int usb_device::open(libusb_device_handle** dev_handle)
{
if (handle_)
return LIBUSB_ERROR_BUSY;
if (!dev_desc_)
init();
HANDLE h = CreateFileA(name_.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (h == INVALID_HANDLE_VALUE)
{
*dev_handle = NULL;
return online_ ? LIBUSB_ERROR_IO : LIBUSB_ERROR_NO_DEVICE;
}
USBSCAN_PIPE_CONFIGURATION upc = { 0 };
DWORD cbr = 0;
if (DeviceIoControl(h, IOCTL_GET_PIPE_CONFIGURATION, NULL, 0, &upc, sizeof(upc), &cbr, NULL))
{
for (int i = 0; i < upc.NumberOfPipes; ++i)
{
USBPIPE up = { 0 };
char ind[20] = { 0 };
up.address = upc.PipeInfo[i].EndpointAddress;
up.type = upc.PipeInfo[i].PipeType;
sprintf_s(ind, _countof(ind) - 1, "\\%04d", i);
up.pipe = CreateFileA((name_ + ind).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (up.pipe != INVALID_HANDLE_VALUE)
{
set_timeout(up.pipe);
pipes_.push_back(up);
}
}
}
set_timeout(h);
handle_ = (libusb_device_handle*)h;
*dev_handle = (libusb_device_handle*)this;
return LIBUSB_SUCCESS;
}
int usb_device::close(void)
{
for (size_t i = 0; i < pipes_.size(); ++i)
{
CancelIo(pipes_[i].pipe);
CloseHandle(pipes_[i].pipe);
}
pipes_.clear();
if (handle_)
{
OVERLAPPED ovl = { 0 };
PIPE_TYPE type = ALL_PIPE;
DWORD cbr = 0;
ovl.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
DeviceIoControl(handle_, IOCTL_CANCEL_IO, &type, sizeof(type), NULL, 0, &cbr, &ovl);
WaitForSingleObject(ovl.hEvent, 1000);
CloseHandle(ovl.hEvent);
CancelIo(handle_);
CloseHandle((HANDLE)handle_);
handle_ = NULL;
}
return LIBUSB_SUCCESS;
}
int usb_device::set_timeout(unsigned milliseconds)
{
timout_ms_ = milliseconds;
for (size_t i = 0; i < pipes_.size(); ++i)
{
set_timeout(pipes_[i].pipe);
}
if (handle_)
set_timeout(handle_);
return LIBUSB_SUCCESS;
}
int usb_device::transfer_bulk(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout)
{
typedef BOOL(WINAPI* file_io)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
int ret = LIBUSB_ERROR_PIPE;
HANDLE h = find_pipe(endpoint, USBSCAN_PIPE_BULK);
file_io fio = (endpoint & BULKIN_FLAG) ? ReadFile : (file_io)WriteFile;
LPOVERLAPPED ovl = (endpoint & BULKIN_FLAG) ? &ovl_[OVL_BULK_IN] : &ovl_[OVL_BULK_OUT];
if (h)
{
DWORD io = 0;
BOOL result = FALSE;
HANDLE he = ovl->hEvent;
memset(ovl, 0, sizeof(*ovl));
ovl->hEvent = he;
result = fio(h, data, *length, &io, ovl);
if (result)
{
FlushFileBuffers(h);
*length = io;
ret = LIBUSB_SUCCESS;
}
else
{
io = GetLastError();
if (io == ERROR_IO_PENDING)
{
WaitForSingleObject(he, 500);
GetOverlappedResult(h, &ovl_[OVL_BULK_IN], &io, FALSE);
*length = io;
ret = LIBUSB_SUCCESS;
}
}
}
return ret;
}
int usb_device::transfer_control(uint8_t type, uint8_t req, uint16_t val, uint16_t ind, unsigned char* data, uint16_t len, unsigned timeout)
{
int ret = LIBUSB_ERROR_PIPE;
_IO_BLOCK_EX irp;
if ((HANDLE)handle_ != INVALID_HANDLE_VALUE)
{
DWORD io = 0;
LPOVERLAPPED ovl = ovl_ + OVL_CONTROL;
ResetEvent(ovl->hEvent);
irp.bmRequestType = (type >> 5) & 0x03;
irp.bRequest = req;
irp.fTransferDirectionIn = type >> 7;
irp.pbyData = data;
irp.uIndex = ind;
irp.uLength = len;
irp.uOffset = val;
if (DeviceIoControl((HANDLE)handle_, IOCTL_SEND_USB_REQUEST, &irp, sizeof(irp), data, len, &io, ovl))
ret = LIBUSB_SUCCESS;
else if (GetLastError() == ERROR_IO_PENDING)
{
ret = WaitForSingleObject(ovl->hEvent, timeout) == WAIT_TIMEOUT ? LIBUSB_ERROR_TIMEOUT : LIBUSB_SUCCESS;
}
}
return ret;
}
int usb_device::transfer_interrupt(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout)
{
int ret = LIBUSB_ERROR_PIPE;
HANDLE h = find_pipe(endpoint, USBSCAN_PIPE_INTERRUPT);
DWORD io = 0;
if (h)
{
if (DeviceIoControl(h, IOCTL_WAIT_ON_DEVICE_EVENT, NULL, 0, data, *length, &io, &ovl_[OVL_INTERRUPT]))
{
ret = LIBUSB_SUCCESS;
*length = io;
}
else
{
io = GetLastError();
if (io == ERROR_IO_PENDING)
{
GetOverlappedResult(h, &ovl_[OVL_INTERRUPT], &io, TRUE);
ret = LIBUSB_SUCCESS;
*length = io;
}
}
}
return ret;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// usb_monitor ...
usb_monitor* usb_monitor::usb_monitor_ = NULL;
usb_monitor::usb_monitor() : wnd_monitor_(NULL), handle_msg_id_(0), run_(true)
{
handle_msg_.reset(new std::thread(&usb_monitor::thread_handle_device_change_msg, this));
}
usb_monitor::~usb_monitor()
{
quit();
}
LRESULT CALLBACK usb_monitor::monitor_wnd_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
usb_monitor* monitor = (usb_monitor*)GetPropW(hwnd, MONITOR_WINDOW_OWNER);
if (msg == WM_CREATE)
{
LPCREATESTRUCTW data = (LPCREATESTRUCTW)lp;
SetPropW(hwnd, MONITOR_WINDOW_OWNER, (HANDLE)data->lpCreateParams);
return 0;
}
else if (!monitor || msg != WM_DEVICECHANGE)
return DefWindowProcW(hwnd, msg, wp, lp);
return monitor->on_usb_pnp(wp, lp);
}
void usb_monitor::register_monitor_wnd(const wchar_t* cls)
{
WNDCLASSW wc = { 0 };
wc.lpfnWndProc = &usb_monitor::monitor_wnd_proc;
wc.lpszClassName = cls;
wc.hInstance = GetModuleHandleW(NULL);
RegisterClassW(&wc);
}
void usb_monitor::notify_usb_event(usb_device*& dev, bool arrive)
{
std::lock_guard<std::mutex> lock(lock_);
int ev = arrive ? LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED : LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
DEVID id = dev->id();
if (arrive)
{
bool found = false;
for(size_t i = 0; i < devices_.size(); ++i)
{
if (!(devices_[i]->id() == id))
continue;
if (!devices_[i]->is_online() && devices_[i]->is_open())
{
dev->release();
dev = devices_[i];
dev->add_ref();
dev->set_online(true);
found = true;
break;
}
}
if (!found)
{
devices_.push_back(dev);
dev->add_ref();
}
}
else
{
for (size_t i = 0; i < devices_.size(); ++i)
{
if (!(devices_[i]->id() == id))
continue;
dev->release();
dev = devices_[i];
dev->add_ref();
if (dev->is_open())
{
dev->set_online(false);
}
else
{
devices_.erase(devices_.begin() + i);
dev->release();
}
break;
}
}
for (size_t i = 0; i < cbs_.size(); ++i)
cbs_[i]->notify((libusb_context*)this, dev, ev);
}
int usb_monitor::on_usb_pnp(WPARAM wp, LPARAM lp)
{
int ret = 0;
DEV_BROADCAST_HDR* dbt = (DEV_BROADCAST_HDR*)lp;
if (!dbt || dbt->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
return wp == DBT_DEVICEQUERYREMOVE;
PDEV_BROADCAST_DEVICEINTERFACE_W dev = (PDEV_BROADCAST_DEVICEINTERFACE_W)lp;
if (dev->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
{
return wp == DBT_DEVICEQUERYREMOVE;
}
if (wp == DBT_DEVICEQUERYREMOVE)
return cur_dev_name_ != u2utf8(dev->dbcc_name);
usb_device* ud = new usb_device(u2utf8(dev->dbcc_name).c_str());
*ud = dev->dbcc_classguid;
if (!PostThreadMessageW(handle_msg_id_, MSG_DEVICE_PNP, wp == DBT_DEVICEARRIVAL, (LPARAM)ud))
ud->release();
return ret;
}
void usb_monitor::find_usb(std::vector<std::string>& usb_devs)
{
GUID hid = GUID_DEVINTERFACE_USB_DEVICE; // GUID_DEVINTERFACE_USB_HUB
HDEVINFO dev_info = NULL;
int ind = 0;
SP_DEVICE_INTERFACE_DATA id = { 0 };
// HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\DeviceClasses\{a5dcbf10-6530-11d2-901f-00c04fb951ed}
// HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\USB
// UuidFromStringW((RPC_WSTR)WIN_USB_GUID, &hid);
dev_info = SetupDiGetClassDevsW(&hid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (!dev_info)
return;
id.cbSize = sizeof(id);
while (SetupDiEnumDeviceInterfaces(dev_info, NULL, &hid, ind++, &id))
{
PSP_DEVICE_INTERFACE_DETAIL_DATA_W buf = NULL;
DWORD size = 0;
SetupDiGetDeviceInterfaceDetailW(dev_info, &id, buf, 0, &size, NULL);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
buf = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new wchar_t[size + 4];
memset(buf, 0, (size + 4) * 2);
buf->cbSize = sizeof(*buf);
if (SetupDiGetDeviceInterfaceDetailW(dev_info, &id, buf, buf->cbSize + size, NULL, NULL))
{
usb_devs.push_back(u2utf8(buf->DevicePath));
}
delete[](char*)buf;
}
id.cbSize = sizeof(id);
}
SetupDiDestroyDeviceInfoList(dev_info);
}
usb_callback* usb_monitor::reg_callback(libusb_hotplug_callback_fn cb, void* param)
{
usb_callback* obj = new usb_callback(cb, param);
{
std::lock_guard<std::mutex> lock(lock_);
cbs_.push_back(obj);
}
return obj;
}
void usb_monitor::unreg_callback(usb_callback* cb)
{
std::lock_guard<std::mutex> lock(lock_);
for (int i = 0; i < cbs_.size(); ++i)
{
if (cbs_[i] == cb)
{
cbs_.erase(cbs_.begin() + i);
delete cb;
break;
}
}
}
void usb_monitor::thread_run_device_event_wnd(void)
{
MSG msg = { 0 };
const wchar_t* cls = L"usb_pnp_wnd";
register_monitor_wnd(cls);
wnd_monitor_ = CreateWindowW(cls, L"usb", WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandleW(NULL), this);
if (!IsWindow(wnd_monitor_))
{
// HG_VLOG_MINI_1(HG_LOG_LEVEL_FATAL, "create monitor window failed: %d\n", GetLastError());
if(run_)
Sleep(1000);
return;
}
if (run_)
{
BOOL ret = TRUE;
DEV_BROADCAST_HDR dbh = { 0 };
HDEVNOTIFY notify = NULL;
std::vector<std::string> first;
find_usb(first);
for (size_t i = 0; i < first.size(); ++i)
{
usb_device* dev = new usb_device(first[i].c_str());
notify_usb_event(dev, true);
dev->release();
}
dbh.dbch_size = sizeof(dbh);
dbh.dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
notify = RegisterDeviceNotificationW(wnd_monitor_, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
ret = GetLastError();
// find_usb();
while (run_ && (ret = GetMessageW(&msg, wnd_monitor_, 0, 0)))
{
if (ret == -1)
{
// HG_VLOG_MINI_1(HG_LOG_LEVEL_FATAL, "GetMessageW returned -1 with error: %d\n", GetLastError());
break;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
UnregisterDeviceNotification(notify);
}
DestroyWindow(wnd_monitor_);
UnregisterClassW(cls, GetModuleHandleW(NULL));
}
void usb_monitor::thread_handle_device_change_msg(void)
{
MSG msg = { 0 };
BOOL ret = FALSE;
handle_msg_id_ = GetCurrentThreadId();
while ((ret = GetMessageW(&msg, NULL, 0, 0)))
{
if (ret == -1)
break;
if (msg.message == MSG_DEVICE_PNP)
{
char buf[40] = { 0 };
usb_device* dev = (usb_device*)msg.lParam;
//if(msg.wParam)
// dev->init();
notify_usb_event(dev, msg.wParam);
// dev->release ?
dev->release();
}
else
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
void usb_monitor::quit(void)
{
run_ = false;
if (handle_msg_.get())
{
PostThreadMessageW(handle_msg_id_, WM_QUIT, 0, 0);
if (handle_msg_->joinable())
handle_msg_->join();
handle_msg_.reset();
}
if (IsWindow(wnd_monitor_))
{
PostMessage(wnd_monitor_, WM_QUIT, 0, 0);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// libusb APIs ...
int LIBUSB_CALL libusb_init(libusb_context** ctx)
{
if (ctx)
*ctx = (libusb_context*)new usb_monitor();
else if(!usb_monitor::usb_monitor_)
usb_monitor::usb_monitor_ = new usb_monitor();
return LIBUSB_SUCCESS;
}
void LIBUSB_CALL libusb_exit(libusb_context* ctx)
{
usb_monitor* usb = (usb_monitor*)ctx;
if (usb)
{
usb->quit();
delete usb;
}
}
int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context* ctx,
libusb_hotplug_event events,
libusb_hotplug_flag flags,
int vendor_id, int product_id,
int dev_class,
libusb_hotplug_callback_fn cb_fn,
void* user_data,
libusb_hotplug_callback_handle* callback_handle)
{
if (ctx)
*callback_handle = (libusb_hotplug_callback_handle)((usb_monitor*)ctx)->reg_callback(cb_fn, user_data);
else if (usb_monitor::usb_monitor_)
*callback_handle = (libusb_hotplug_callback_handle)usb_monitor::usb_monitor_->reg_callback(cb_fn, user_data);
return LIBUSB_SUCCESS;
}
void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context* ctx, libusb_hotplug_callback_handle callback_handle)
{
if (ctx)
((usb_monitor*)ctx)->unreg_callback((usb_callback*)callback_handle);
else if (usb_monitor::usb_monitor_)
usb_monitor::usb_monitor_->unreg_callback((usb_callback*)callback_handle);
}
int LIBUSB_CALL libusb_handle_events_timeout(libusb_context* ctx, struct timeval* tv)
{
// NOTE: for the caller is in a separate thread, this call will blocked while process exited, or libusb_exit called
((usb_monitor*)ctx)->thread_run_device_event_wnd();
return LIBUSB_SUCCESS;
}
libusb_device* LIBUSB_CALL libusb_ref_device(libusb_device* dev)
{
if (dev)
((usb_device*)dev)->add_ref();
return dev;
}
void LIBUSB_CALL libusb_unref_device(libusb_device* dev)
{
if (dev)
((usb_device*)dev)->release();
}
int LIBUSB_CALL libusb_get_device_descriptor(libusb_device* dev, struct libusb_device_descriptor* desc)
{
if (!dev)
return LIBUSB_ERROR_INVALID_PARAM;
return ((usb_device*)dev)->get_descriptor(desc);
}
int LIBUSB_CALL libusb_get_config_descriptor(libusb_device* dev, uint8_t config_index, struct libusb_config_descriptor** config)
{
if (!dev)
return LIBUSB_ERROR_INVALID_PARAM;
return ((usb_device*)dev)->get_config_descriptor(config_index, config);
}
void LIBUSB_CALL libusb_free_config_descriptor(struct libusb_config_descriptor* config)
{
// because the configuration descriptor is member of device, nothing to do here ...
}
int LIBUSB_CALL libusb_reset_device(libusb_device_handle* dev_handle)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context* ctx, libusb_device*** list)
{
// to be implemented later ...
return 0;
}
void LIBUSB_CALL libusb_free_device_list(libusb_device** list, int unref_devices)
{
// to be implemented later ...
}
int LIBUSB_CALL libusb_set_auto_detach_kernel_driver(libusb_device_handle* dev_handle, int enable)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
int LIBUSB_CALL libusb_claim_interface(libusb_device_handle* dev_handle, int interface_number)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
int LIBUSB_CALL libusb_release_interface(libusb_device_handle* dev_handle, int interface_number)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
int LIBUSB_CALL libusb_set_configuration(libusb_device_handle* dev_handle, int configuration)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle* dev_handle, int interface_number)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle* dev_handle, int interface_number)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle* dev_handle, int interface_number)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
libusb_device_handle* LIBUSB_CALL libusb_open_device_with_vid_pid(libusb_context* ctx, uint16_t vendor_id, uint16_t product_id)
{
// to be implemented later ...
return NULL;
}
int LIBUSB_CALL libusb_clear_halt(libusb_device_handle* dev_handle, unsigned char endpoint)
{
// to be implemented later ...
return LIBUSB_SUCCESS;
}
const char* LIBUSB_CALL libusb_error_name(int errcode)
{
#define RETURN_IF(e) \
if(errcode == e) \
return #e;
RETURN_IF(LIBUSB_SUCCESS);
RETURN_IF(LIBUSB_ERROR_IO);
RETURN_IF(LIBUSB_ERROR_INVALID_PARAM);
RETURN_IF(LIBUSB_ERROR_ACCESS);
RETURN_IF(LIBUSB_ERROR_NO_DEVICE);
RETURN_IF(LIBUSB_ERROR_NOT_FOUND);
RETURN_IF(LIBUSB_ERROR_BUSY);
RETURN_IF(LIBUSB_ERROR_TIMEOUT);
RETURN_IF(LIBUSB_ERROR_OVERFLOW);
RETURN_IF(LIBUSB_ERROR_PIPE);
RETURN_IF(LIBUSB_ERROR_INTERRUPTED);
RETURN_IF(LIBUSB_ERROR_NO_MEM);
RETURN_IF(LIBUSB_ERROR_NOT_SUPPORTED);
RETURN_IF(LIBUSB_ERROR_OTHER);
return "Unknown error";
}
int LIBUSB_CALL libusb_open(libusb_device* dev, libusb_device_handle** dev_handle)
{
if (!dev)
return LIBUSB_ERROR_INVALID_PARAM;
return ((usb_device*)dev)->open(dev_handle);
}
void LIBUSB_CALL libusb_close(libusb_device_handle* dev_handle)
{
if (dev_handle)
{
((usb_device*)dev_handle)->close();
}
}
int LIBUSB_CALL libusb_control_transfer(libusb_device_handle* dev_handle,
uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
unsigned char* data, uint16_t wLength, unsigned int timeout)
{
if (!dev_handle)
return LIBUSB_ERROR_INVALID_PARAM;
return ((usb_device*)dev_handle)->transfer_control(request_type, bRequest, wValue, wIndex, data, wLength, timeout);
}
int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle* dev_handle,
unsigned char endpoint, unsigned char* data, int length,
int* actual_length, unsigned int timeout)
{
if (!dev_handle)
return LIBUSB_ERROR_INVALID_PARAM;
if (actual_length)
*actual_length = length;
else
actual_length = &length;
return ((usb_device*)dev_handle)->transfer_bulk(endpoint, data, actual_length, timeout);
}
int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle* dev_handle,
unsigned char endpoint, unsigned char* data, int length,
int* actual_length, unsigned int timeout)
{
if (!dev_handle)
return LIBUSB_ERROR_INVALID_PARAM;
if (actual_length)
*actual_length = length;
else
actual_length = &length;
return ((usb_device*)dev_handle)->transfer_interrupt(endpoint, data, actual_length, timeout);
}
int LIBUSB_CALL libusb_set_timeout(libusb_device_handle* h, unsigned int milliseconds)
{
if (!h)
return LIBUSB_ERROR_INVALID_PARAM;
return ((usb_device*)h)->set_timeout(milliseconds);
}
void LIBUSB_CALL libusb_quit(libusb_context* ctx)
{
if (ctx)
((usb_monitor*)ctx)->quit();
}
uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device* device)
{
if(!device)
return 0;
return ((usb_device*)device)->port();
}