#include "win_usb.h" #include #include #include #include #include #include #include //#include #include #include #pragma comment(lib, "setupapi.lib") #pragma comment(lib, "hid.lib") #pragma comment(lib, "winusb.lib") #pragma comment(lib, "rpcrt4.lib") #include "../wrapper/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; /////*/////////////////////// } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // OVERLAPPED ... ovl_cls::ovl_cls() : 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; } 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 (WaitForSingleObject(v->over_lapped()->hEvent, 0) == WAIT_OBJECT_0) { ResetEvent(v->over_lapped()->hEvent); 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) { 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; 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; } 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; 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(); 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_ = 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) { typedef BOOL(WINAPI* file_io)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED); int ret = LIBUSB_ERROR_NOT_SUPPORTED; HANDLE h = find_pipe(endpoint, USBSCAN_PIPE_BULK); file_io fio = (endpoint & BULKIN_FLAG) ? ReadFile : (file_io)WriteFile; if (h) { ovl_cls *oc = ovl_mgr_.get_ovl(); DWORD io = 0; BOOL result = fio(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(), &io, FALSE); *length = io; 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 = 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 ? LIBUSB_ERROR_TIMEOUT : *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; } 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; HANDLE h = find_pipe(endpoint, USBSCAN_PIPE_INTERRUPT); DWORD io = 0; if (h) { ovl_cls* oc = ovl_mgr_.get_ovl(); if (DeviceIoControl(h, IOCTL_WAIT_ON_DEVICE_EVENT, NULL, 0, data, *length, 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(), &io, FALSE); *length = io; ret = LIBUSB_SUCCESS; } else ret = LIBUSB_ERROR_TIMEOUT; } else ret = LIBUSB_ERROR_PIPE; } oc->release(); } 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 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) { 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& 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 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; std::vector 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(); 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) { 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(); }