1530 lines
46 KiB
C++
1530 lines
46 KiB
C++
#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")
|
||
#pragma comment(lib, "advapi32.lib") // for Reg...
|
||
|
||
#if defined(OEM_NONE) || defined(OEM_LISCHENG) || defined(OEM_HANWANG)
|
||
#include "../wrapper/hg_log.h"
|
||
#else
|
||
#define VLOG_MINI_1(l, f, d) printf(f, d)
|
||
#define VLOG_MINI_2(l, f, d1, d2) printf(f, d1, d2)
|
||
#define VLOG_MINI_3(l, f, d1, d2, d3) printf(f, d1, d2, d3)
|
||
#define VLOG_MINI_4(l, f, d1, d2, d3, d4) printf(f, d1, d2, d3, d4)
|
||
#endif
|
||
#include "usbview/enum.h"
|
||
#pragma warning(disable: 4996)
|
||
|
||
#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)
|
||
{
|
||
#if defined(OEM_NONE) || defined(OEM_LISCHENG) || defined(OEM_HANWANG)
|
||
return hg_log::u2utf8(u);
|
||
#else
|
||
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;
|
||
#endif
|
||
}
|
||
std::wstring ansi2unicode(const char* ansi, UINT cp = CP_ACP)
|
||
{
|
||
int len = MultiByteToWideChar(cp, 0, ansi, lstrlenA(ansi), NULL, 0);
|
||
wchar_t* buf = new wchar_t[len + 4];
|
||
|
||
memset(buf, 0, (len + 4) * 2);
|
||
len = MultiByteToWideChar(cp, 0, ansi, lstrlenA(ansi), buf, len);
|
||
buf[len--] = 0;
|
||
|
||
std::wstring unic(buf);
|
||
delete[] buf;
|
||
|
||
return unic;
|
||
}
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// OVERLAPPED ...
|
||
ovl_cls::ovl_cls() : ref_(1), io_bytes_(0)
|
||
{
|
||
memset(&ovl_, 0, sizeof(ovl_));
|
||
ovl_.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||
}
|
||
ovl_cls::~ovl_cls()
|
||
{
|
||
CloseHandle(ovl_.hEvent);
|
||
}
|
||
|
||
LPOVERLAPPED ovl_cls::over_lapped(void)
|
||
{
|
||
return &ovl_;
|
||
}
|
||
LPDWORD ovl_cls::io_bytes(void)
|
||
{
|
||
return &io_bytes_;
|
||
}
|
||
void ovl_cls::reset(void)
|
||
{
|
||
HANDLE h = ovl_.hEvent;
|
||
|
||
memset(&ovl_, 0, sizeof(ovl_));
|
||
ovl_.hEvent = h;
|
||
io_bytes_ = 0;
|
||
ResetEvent(h);
|
||
}
|
||
bool ovl_cls::is_waited(void)
|
||
{
|
||
return WaitForSingleObject(ovl_.hEvent, 0) == WAIT_OBJECT_0;
|
||
}
|
||
|
||
ovl_mgr::ovl_mgr()
|
||
{}
|
||
ovl_mgr::~ovl_mgr()
|
||
{
|
||
for (auto& v : ovls_)
|
||
v->release();
|
||
}
|
||
|
||
ovl_cls* ovl_mgr::get_ovl(void)
|
||
{
|
||
std::lock_guard<std::mutex> lock(lock_);
|
||
ovl_cls* o = NULL;
|
||
|
||
for (auto& v : ovls_)
|
||
{
|
||
if (v->is_waited())
|
||
{
|
||
o = v;
|
||
o->add_ref();
|
||
o->reset();
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!o)
|
||
{
|
||
o = new ovl_cls();
|
||
ovls_.push_back(o);
|
||
o->add_ref();
|
||
}
|
||
|
||
return o;
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// 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, int* index)
|
||
{
|
||
for (size_t i = 0; i < pipes_.size(); ++i)
|
||
{
|
||
if (pipes_[i].address == addr && pipes_[i].type == type)
|
||
{
|
||
if (index)
|
||
*index = i;
|
||
return pipes_[i].pipe;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
HANDLE usb_device::open_usb(const char* usb_name, DWORD access, DWORD share)
|
||
{
|
||
std::wstring unic(ansi2unicode(usb_name));
|
||
SECURITY_ATTRIBUTES sa = { 0 };
|
||
sa.nLength = sizeof(sa);
|
||
sa.bInheritHandle = TRUE;
|
||
sa.lpSecurityDescriptor = NULL;
|
||
|
||
return CreateFileW(unic.c_str(), access, share, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 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;
|
||
|
||
//COMMTIMEOUTS to = { 0 };
|
||
//to.ReadIntervalTimeout = timout_ms_;
|
||
//to.ReadTotalTimeoutConstant = timout_ms_;
|
||
//to.ReadTotalTimeoutMultiplier = 1;
|
||
//
|
||
//SetCommTimeouts(h, &to);
|
||
}
|
||
|
||
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;
|
||
VLOG_MINI_4(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;
|
||
}
|
||
std::string usb_device::usb_scan_name(DEVID id, const char* guid)
|
||
{
|
||
// HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}
|
||
HKEY key = NULL;
|
||
std::string path("SYSTEM\\CurrentControlSet\\Control\\Class\\{"), name("");
|
||
char strind[20] = { "0000" }, val[256] = { 0 };
|
||
int ind = 0, err = 0;
|
||
|
||
path += guid;
|
||
path += "}\\";
|
||
err = RegOpenKeyA(HKEY_LOCAL_MACHINE, (path + strind).c_str(), &key);
|
||
while (key)
|
||
{
|
||
DWORD len = _countof(val) - 1,
|
||
type = REG_SZ;
|
||
if (RegQueryValueExA(key, "MatchingDeviceId", NULL, &type, (LPBYTE)val, &len) == ERROR_SUCCESS)
|
||
{
|
||
val[len] = 0;
|
||
DEVID cid = usb_device::vid_pid_from_name(val);
|
||
if (cid == id)
|
||
{
|
||
len = _countof(val) - 1;
|
||
type = REG_SZ;
|
||
if (RegQueryValueExA(key, "CreateFileName", NULL, &type, (LPBYTE)val, &len) == ERROR_SUCCESS)
|
||
{
|
||
val[len] = 0;
|
||
name = val;
|
||
}
|
||
}
|
||
}
|
||
RegCloseKey(key);
|
||
if (!name.empty())
|
||
break;
|
||
sprintf_s(strind, _countof(strind) - 1, "%04d", ++ind);
|
||
RegOpenKeyA(HKEY_LOCAL_MACHINE, (path + strind).c_str(), &key);
|
||
}
|
||
|
||
return name;
|
||
}
|
||
|
||
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();
|
||
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, used = 0;
|
||
|
||
while (used < info->ConfigDesc->SetupPacket.wLength && 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;
|
||
memset(uid, 0, sizeof(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]);
|
||
|
||
memset(uep, 0, sizeof(*uep));
|
||
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);
|
||
ep_ind++;
|
||
}
|
||
used += cmn->bLength;
|
||
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();
|
||
|
||
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 = open_usb(name_.c_str());
|
||
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;
|
||
std::string fmt("\\%d"), root(usb_device::usb_scan_name(id_));
|
||
|
||
if (root.empty())
|
||
{
|
||
VLOG_MINI_1(LOG_LEVEL_WARNING, "Cannot find '\\\\.\\Usbscan' name for '%s', try run in Administrator!\r\n", name_.c_str());
|
||
root = name_;
|
||
fmt = "\\%04d";
|
||
}
|
||
else
|
||
{
|
||
VLOG_MINI_2(LOG_LEVEL_WARNING, "Nice: '%s' for '%s'.\r\n", root.c_str(), name_.c_str());
|
||
}
|
||
if (DeviceIoControl(h, IOCTL_GET_PIPE_CONFIGURATION, NULL, 0, &upc, sizeof(upc), &cbr, NULL))
|
||
{
|
||
int type = PIPE_TYPE::WRITE_DATA_PIPE;
|
||
DeviceIoControl(h, IOCTL_RESET_PIPE, &type, sizeof(type), NULL, NULL, &cbr, NULL);
|
||
for (int i = 0; i < upc.NumberOfPipes; ++i)
|
||
{
|
||
USBPIPE up = { 0 };
|
||
char ind[80] = { 0 };
|
||
up.address = upc.PipeInfo[i].EndpointAddress;
|
||
up.type = upc.PipeInfo[i].PipeType;
|
||
sprintf_s(ind, _countof(ind) - 1, fmt.c_str(), i);
|
||
up.pipe = open_usb((root + ind).c_str());
|
||
if (up.pipe != INVALID_HANDLE_VALUE)
|
||
{
|
||
set_timeout(up.pipe);
|
||
up.index = i;
|
||
pipes_.push_back(up);
|
||
}
|
||
}
|
||
}
|
||
set_timeout(h);
|
||
handle_ = h == INVALID_HANDLE_VALUE ? NULL : (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)
|
||
{
|
||
int ret = LIBUSB_ERROR_NOT_SUPPORTED, ind = 0;
|
||
HANDLE h = find_pipe(endpoint, USBSCAN_PIPE_BULK, &ind);
|
||
|
||
if (h)
|
||
{
|
||
ovl_cls* oc = ovl_mgr_.get_ovl();
|
||
DWORD io = 0;
|
||
BOOL result = FALSE;
|
||
|
||
if (endpoint & BULKIN_FLAG)
|
||
result = ReadFile(h, data, *length, oc->io_bytes(), oc->over_lapped());
|
||
else
|
||
{
|
||
// oc->over_lapped()->Offset = oc->over_lapped()->OffsetHigh = -1;
|
||
result = WriteFile(h, data, *length, oc->io_bytes(), oc->over_lapped());
|
||
}
|
||
|
||
if (result)
|
||
{
|
||
FlushFileBuffers(h);
|
||
*length = *oc->io_bytes();
|
||
ret = LIBUSB_SUCCESS;
|
||
}
|
||
else
|
||
{
|
||
io = GetLastError();
|
||
if (io == ERROR_IO_PENDING)
|
||
{
|
||
if (WaitForSingleObject(oc->over_lapped()->hEvent, timeout) == WAIT_OBJECT_0)
|
||
{
|
||
GetOverlappedResult(h, oc->over_lapped(), oc->io_bytes(), FALSE);
|
||
*length = *oc->io_bytes();
|
||
ret = LIBUSB_SUCCESS;
|
||
}
|
||
else
|
||
ret = LIBUSB_ERROR_TIMEOUT;
|
||
}
|
||
else
|
||
ret = LIBUSB_ERROR_PIPE;
|
||
}
|
||
oc->release();
|
||
}
|
||
|
||
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)
|
||
{
|
||
// return transfer bytes ...
|
||
int ret = 0; // LIBUSB_ERROR_NOT_FOUND;
|
||
_IO_BLOCK_EX irp;
|
||
|
||
if ((HANDLE)handle_ != INVALID_HANDLE_VALUE)
|
||
{
|
||
DWORD io = 0;
|
||
ovl_cls *oc = ovl_mgr_.get_ovl();
|
||
|
||
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, oc->io_bytes(), oc->over_lapped()))
|
||
{
|
||
if (irp.fTransferDirectionIn)
|
||
{
|
||
GetOverlappedResult((HANDLE)handle_, oc->over_lapped(), oc->io_bytes(), FALSE);
|
||
ret = *oc->io_bytes();
|
||
}
|
||
else
|
||
{
|
||
ret = len;
|
||
}
|
||
}
|
||
else if (GetLastError() == ERROR_IO_PENDING)
|
||
{
|
||
ret = WaitForSingleObject(oc->over_lapped()->hEvent, timeout) == WAIT_TIMEOUT ? 0 : *oc->io_bytes();
|
||
}
|
||
else
|
||
{
|
||
//io = GetLastError();
|
||
//if (io == ERROR_ACCESS_DENIED)
|
||
// ret = LIBUSB_ERROR_ACCESS;
|
||
//else if (io == ERROR_SEM_TIMEOUT)
|
||
// ret = LIBUSB_ERROR_TIMEOUT;
|
||
//else
|
||
// ret = LIBUSB_ERROR_PIPE;
|
||
ret = 0;
|
||
}
|
||
oc->release();
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
int usb_device::transfer_interrupt(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout)
|
||
{
|
||
int ret = LIBUSB_ERROR_NOT_SUPPORTED, len = *length;
|
||
HANDLE h = find_pipe(endpoint, USBSCAN_PIPE_INTERRUPT);
|
||
DWORD io = 0;
|
||
|
||
*length = 0;
|
||
if (h)
|
||
{
|
||
ovl_cls* oc = ovl_mgr_.get_ovl();
|
||
if (DeviceIoControl(h, IOCTL_WAIT_ON_DEVICE_EVENT, NULL, 0, data, len, oc->io_bytes(), oc->over_lapped()))
|
||
{
|
||
ret = LIBUSB_SUCCESS;
|
||
*length = *oc->io_bytes();
|
||
}
|
||
else
|
||
{
|
||
io = GetLastError();
|
||
if (io == ERROR_IO_PENDING)
|
||
{
|
||
if (WaitForSingleObject(oc->over_lapped()->hEvent, timeout) == WAIT_OBJECT_0)
|
||
{
|
||
GetOverlappedResult(h, oc->over_lapped(), oc->io_bytes(), FALSE);
|
||
*length = *oc->io_bytes();
|
||
ret = LIBUSB_SUCCESS;
|
||
}
|
||
else
|
||
{
|
||
ret = LIBUSB_ERROR_TIMEOUT;
|
||
|
||
// 此错误可能导致后续新产生的数据被填充进data,故取消该操作 BUG-242
|
||
CancelIo(h);
|
||
}
|
||
}
|
||
else
|
||
ret = LIBUSB_ERROR_PIPE;
|
||
}
|
||
oc->release();
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
// usb_monitor ...
|
||
|
||
usb_monitor* usb_monitor::usb_monitor_ = NULL;
|
||
UINT usb_monitor::find_usb_timer_ = 101;
|
||
|
||
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_TIMER && wp == usb_monitor::find_usb_timer_)
|
||
{
|
||
monitor->find_usb_and_trigger_event();
|
||
|
||
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)
|
||
{
|
||
if (devices_[i]->is_online())
|
||
{
|
||
VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "%s is already in device queue and received ARRIVE again, discard this event.\n", dev->name().c_str());
|
||
dev->release();
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
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())
|
||
{
|
||
if (dev->is_online())
|
||
dev->set_online(false);
|
||
else
|
||
{
|
||
VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "%s is already offline and received LEAVE again, discard this event.\n", dev->name().c_str());
|
||
dev->release();
|
||
return;
|
||
}
|
||
}
|
||
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);
|
||
|
||
VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "event '%08x' of device %s\n", wp, u2utf8(dev->dbcc_name).c_str());
|
||
|
||
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);
|
||
}
|
||
void usb_monitor::find_usb_and_trigger_event(void)
|
||
{
|
||
std::vector<std::string> old(found_usb_devs_);
|
||
|
||
found_usb_devs_.clear();
|
||
find_usb(found_usb_devs_);
|
||
for (size_t i = 0; i < found_usb_devs_.size(); ++i)
|
||
{
|
||
std::vector<std::string>::iterator it = std::find(old.begin(), old.end(), found_usb_devs_[i]);
|
||
if (it == old.end())
|
||
{
|
||
usb_device* dev = new usb_device(found_usb_devs_[i].c_str());
|
||
notify_usb_event(dev, true);
|
||
dev->release();
|
||
}
|
||
else
|
||
old.erase(it);
|
||
}
|
||
|
||
for (size_t i = 0; i < old.size(); ++i)
|
||
{
|
||
usb_device* dev = new usb_device(old[i].c_str());
|
||
notify_usb_event(dev, false);
|
||
dev->release();
|
||
}
|
||
}
|
||
|
||
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_))
|
||
{
|
||
if(run_)
|
||
Sleep(1000);
|
||
|
||
return;
|
||
}
|
||
|
||
if (run_)
|
||
{
|
||
BOOL ret = TRUE;
|
||
DEV_BROADCAST_HDR dbh = { 0 };
|
||
HDEVNOTIFY notify = NULL;
|
||
|
||
find_usb(found_usb_devs_);
|
||
for (size_t i = 0; i < found_usb_devs_.size(); ++i)
|
||
{
|
||
usb_device* dev = new usb_device(found_usb_devs_[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);
|
||
if (!notify && GetLastError() == ERROR_SERVICE_SPECIFIC_ERROR)
|
||
{
|
||
DEV_BROADCAST_DEVICEINTERFACE_A di = { 0 };
|
||
di.dbcc_size = (sizeof(di) + 3) / 4 * 4;
|
||
di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
||
UuidFromStringA((RPC_CSTR)HG_SCANNER_GUID, &di.dbcc_classguid);
|
||
notify = RegisterDeviceNotificationA(wnd_monitor_, &di, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
|
||
VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "RegisterDeviceNotificationA = 0x%08x, error = %d. di.dbcc_size = %d\n", notify, GetLastError(), di.dbcc_size);
|
||
if (!notify)
|
||
SetTimer(wnd_monitor_, usb_monitor::find_usb_timer_, 1000, NULL);
|
||
}
|
||
ret = GetLastError();
|
||
while (run_ && (ret = GetMessageW(&msg, wnd_monitor_, 0, 0)))
|
||
{
|
||
if (ret == -1)
|
||
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)
|
||
{
|
||
KillTimer(wnd_monitor_, usb_monitor::find_usb_timer_);
|
||
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();
|
||
}
|
||
|