#include "win_usb.h" #include #include #include #include #include #include #include //#include #include #include #include "enum.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)|| defined(OEM_ZHONGJING) #include "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 #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) || defined(OEM_ZHONGJING) 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 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 USBDEV& dev) : ref_(1), udev_(dev), is_ok_(false) , dev_desc_(NULL), handle_(NULL), online_(true), timout_ms_(1000) { memset(&guid_, 0, sizeof(guid_)); } 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); } void usb_device::vid_pid_from_name(const char* name, int* vid, int* pid) { // name: \\?\usb#vid_3072&pid_0239#01234567aabbccddee#{a5dcbf10-6530-11d2-901f-00c04fb951ed} 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 && vid) *vid = usb_device::from_hex_string(s.c_str() + pos + 4); pos = s.find("pid_"); if (pos != std::string::npos && pid) *pid = usb_device::from_hex_string(s.c_str() + pos + 4); } 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::name_without_guid(const char* name) { std::string no_guid(name); size_t pos = no_guid.find("#{"); if (pos != std::string::npos) no_guid.erase(pos); return no_guid; } std::string usb_device::usb_scan_name(const char* reg_key) { std::wstring path(L"SYSTEM\\CurrentControlSet\\Control\\Class\\" + ansi2unicode(reg_key, CP_UTF8)); HKEY key = NULL; int err = RegOpenKeyW(HKEY_LOCAL_MACHINE, path.c_str(), &key); std::string ret(""); if (key) { char val[256] = { 0 }; DWORD len = _countof(val) - 1, type = REG_SZ; if (RegQueryValueExW(key, L"CreateFileName", NULL, &type, (LPBYTE)val, &len) == ERROR_SUCCESS) { val[len] = 0; ret = u2utf8((wchar_t*)val); } RegCloseKey(key); } return ret; } 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; } usb_device& usb_device::operator=(const GUID& guid) { guid_ = guid; return *this; } std::string usb_device::name(void) { return udev_.name; } USBDEV& usb_device::dev(void) { return udev_; } GUID usb_device::guid(void) { return guid_; } 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 udev_.port; } uint8_t usb_device::address(void) { if (!dev_desc_) init(); return udev_.addr; } std::string usb_device::reg_path(void) { return udev_.driver_key; } bool usb_device::init(void) { PUSBDEVICEINFO info = NULL; GUID guid; clear(); usb_monitor::enum_usb_device(&usb_monitor::find_parent_hub, &udev_, true); //VLOG_MINI_3(LOG_LEVEL_ALL, "Device '%s' at hub '%s + %d'\r\n", udev_.name.c_str(), udev_.hub.c_str(), udev_.port); if (!udev_.hub.empty()) { HANDLE h = CreateFileA(udev_.hub.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (h != INVALID_HANDLE_VALUE) { info = enumerate_hub_port(h, udev_.port); CloseHandle(h); } } #define COPY_MEMBER(d, s, m) \ (d)->m = (s)->m if (info) { udev_.addr = info->ConnectionInfo->DeviceAddress; 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[j].altsetting; } delete[] cfg_desc_[i]->usb_if; } delete cfg_desc_[i]; } cfg_desc_.clear(); is_ok_ = false; } 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 = udev_.vid; desc->idProduct = udev_.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(udev_.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(""); if (udev_.driver_key.length()) root = usb_device::usb_scan_name(udev_.driver_key.c_str()); if (root.empty()) { //VLOG_MINI_1(LOG_LEVEL_WARNING, "Cannot find '\\\\.\\Usbscan' name for '%s', try run in Administrator!\r\n", udev_.name.c_str()); root = udev_.name; fmt = "\\%04d"; } else { // VLOG_MINI_2(LOG_LEVEL_WARNING, "Nice: '%s' for '%s'.\r\n", root.c_str(), udev_.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(); if (*length == 0 && oc->over_lapped()->Internal != ERROR_SUCCESS) { ret = LIBUSB_ERROR_IO; //VLOG_MINI_2(LOG_LEVEL_WARNING, "Bulk-Transfer of endpoint 0x%02x failed with code 0x%08X\n", endpoint, oc->over_lapped()->Internal); } else ret = LIBUSB_SUCCESS; } else ret = LIBUSB_ERROR_TIMEOUT; } else { if (endpoint & BULKIN_FLAG) { //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "Read-bulk = %d, set error as LIBUSB_ERROR_PIPE\r\n", io); } else { //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "Write-bulk = %d, set error as LIBUSB_ERROR_PIPE\r\n", io); } 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 { //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "DeviceIoControl(INT-EP) = %d, set error as LIBUSB_ERROR_PIPE\r\n", io); 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(); } int usb_monitor::enum_usb_device(bool(__stdcall* found_usb)(LPUSBDEV dev, void* param), void* param, bool hub) { GUID hid = hub ? GUID_DEVINTERFACE_USB_HUB : GUID_DEVINTERFACE_USB_DEVICE; HDEVINFO dev_info = NULL; int ind = 0; SP_DEVICE_INTERFACE_DATA id = { 0 }; SP_DEVINFO_DATA sdd = { 0 }; dev_info = SetupDiGetClassDevsW(&hid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (!dev_info) return GetLastError(); 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)) { USBDEV dev; DWORD len = size; dev.name = u2utf8(buf->DevicePath); usb_device::vid_pid_from_name(dev.name.c_str(), &dev.vid, &dev.pid); sdd.cbSize = sizeof(sdd); SetupDiEnumDeviceInfo(dev_info, ind - 1, &sdd); if (SetupDiGetDeviceRegistryPropertyW(dev_info, &sdd, SPDRP_DRIVER, NULL, (PBYTE)buf->DevicePath, len, &len)) // driver key dev.driver_key = u2utf8(buf->DevicePath); len = size; if (SetupDiGetDeviceRegistryPropertyW(dev_info, &sdd, SPDRP_DEVICEDESC, NULL, (PBYTE)buf->DevicePath, len, &len)) // device description dev.desc = u2utf8(buf->DevicePath); len = size; if (SetupDiGetDeviceRegistryPropertyW(dev_info, &sdd, SPDRP_ADDRESS, NULL, (PBYTE)buf->DevicePath, len, &len)) // device description dev.port = *(int*)buf->DevicePath; if (!found_usb(&dev, param)) { delete[](char*)buf; break; } } delete[](char*)buf; } id.cbSize = sizeof(id); } SetupDiDestroyDeviceInfoList(dev_info); return ERROR_SUCCESS; } bool __stdcall usb_monitor::find_all_usb_devices(LPUSBDEV dev, void* param/*std::vector* */) { std::vector* devs = (std::vector*)param; devs->push_back(*dev); return true; } bool __stdcall usb_monitor::usb_dev_by_name(LPUSBDEV dev, void* param/*LPUSBDEV*/) { LPUSBDEV devt = (LPUSBDEV)param; if (stricmp(usb_device::name_without_guid(devt->name.c_str()).c_str(), usb_device::name_without_guid(dev->name.c_str()).c_str()) == 0) { devt->driver_key = dev->driver_key; devt->desc = dev->desc; devt->vid = dev->vid; devt->pid = dev->pid; devt->port = dev->port; return false; } return true; } bool __stdcall usb_monitor::find_parent_hub(LPUSBDEV hub, void* param/*LPUSBDEV*/) { LPUSBDEV t = (LPUSBDEV)param; if (t->driver_key.empty() || t->port == 0) return false; HANDLE h = CreateFileA(hub->name.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); bool go = true; char buf[512] = { 0 }; DWORD bytes = 0; if (h == INVALID_HANDLE_VALUE) return true; USB_NODE_CONNECTION_DRIVERKEY_NAME* name = (USB_NODE_CONNECTION_DRIVERKEY_NAME*)buf; name->ConnectionIndex = t->port; name->ActualLength = (sizeof(buf) - sizeof(*name)) / 2 - 1; if (DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, name, sizeof(*name), name, name->ActualLength, &bytes, NULL)) { if (u2utf8(name->DriverKeyName) == t->driver_key) { t->hub = hub->name; go = false; } } CloseHandle(h); return go; } bool usb_monitor::is_desired_usb_device(int vid, int pid) { return true; // vid == PRODUCT_VID || vid == PRODUCT_VENDOR_HG1; } 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 lock(lock_); int ev = arrive ? LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED : LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT; std::string noguid(usb_device::name_without_guid(dev->name().c_str())); if (arrive) { bool found = false; for (size_t i = 0; i < devices_.size(); ++i) { // if (devices_[i]->name() == dev->name()) if (stricmp(usb_device::name_without_guid(devices_[i]->name().c_str()).c_str(), noguid.c_str()) == 0) { 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 { devices_[i]->clear(); devices_[i]->dev() = dev->dev(); // use NEW connection info ... 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 { bool discard = false; for (size_t i = 0; i < devices_.size(); ++i) { // if (devices_[i]->name() == dev->name()) if (stricmp(usb_device::name_without_guid(devices_[i]->name().c_str()).c_str(), noguid.c_str()) == 0) { dev->release(); dev = devices_[i]; dev->add_ref(); if (dev->is_online()) { dev->set_online(false); discard = false; } else { discard = true; } break; } } if (discard) { //VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "%s is already offline and received LEAVE again, discard this event.\n", dev->name().c_str()); return; } } 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; } std::string utf8(u2utf8(dev->dbcc_name)); if (wp == DBT_DEVICEQUERYREMOVE) return cur_dev_name_ != utf8; //VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "event '%08x' of device %s\n", wp, utf8.c_str()); int vid = 0, pid = 0; usb_device::vid_pid_from_name(utf8.c_str(), &vid, &pid); if (!usb_monitor::is_desired_usb_device(vid, pid)) return 0; USBDEV udev; udev.name = utf8; usb_monitor::enum_usb_device(&usb_monitor::usb_dev_by_name, &udev); if (wp == DBT_DEVICEARRIVAL && udev.driver_key.empty()) { //VLOG_MINI_1(LOG_LEVEL_FATAL, "Failed: driver key for '%s' is not found!\r\n", utf8.c_str()); } usb_device* ud = new usb_device(udev); *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_and_trigger_event(void) { std::vector old(found_usb_devs_); found_usb_devs_.clear(); usb_monitor::enum_usb_device(&usb_monitor::find_all_usb_devices, &found_usb_devs_); for (size_t i = 0; i < found_usb_devs_.size(); ++i) { if (!usb_monitor::is_desired_usb_device(found_usb_devs_[i].vid, found_usb_devs_[i].pid)) { found_usb_devs_.erase(found_usb_devs_.begin() + i); i--; } } for (size_t i = 0; i < found_usb_devs_.size(); ++i) { std::vector::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]); 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]); 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 lock(lock_); cbs_.push_back(obj); } return obj; } void usb_monitor::unreg_callback(usb_callback* cb) { std::lock_guard 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; // mannual triggered at beginning ... find_usb_and_trigger_event(); 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)IMAGE_CLASS_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 (IsWindow(wnd_monitor_)) { PostMessage(wnd_monitor_, WM_QUIT, 0, 0); Sleep(100); } if (handle_msg_.get()) { PostThreadMessageW(handle_msg_id_, WM_QUIT, 0, 0); if (handle_msg_->joinable()) handle_msg_->join(); handle_msg_.reset(); } { std::lock_guard lock(lock_); for (auto& v : devices_) v->release(); devices_.clear(); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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)->address(); }