#pragma once // implements USB as libusb on windows // // Date: 2022-04-01 #include #include #include #include #include #include #include #include "libusb-1.0/libusb.h" // HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{6bdd1fc6-810f-11d0-bec7-08002be2092f} #define IMAGE_CLASS_GUID "6BDD1FC6-810F-11D0-BEC7-08002BE2092F" #define MONITOR_WINDOW_OWNER L"monitor_wnd_owner" typedef struct _usb_dev { // all members are in utf8 std::string name; // \\?\usb#vid_0bda&pid_0129#20100201396000000#{a5dcbf10-6530-11d2-901f-00c04fb951ed} std::string driver_key; // {6bdd1fc6-810f-11d0-bec7-08002be2092f}\\0016 std::string desc; // USB Composite Device std::string hub; // parent hub int vid; int pid; int port; // port on the hub, also be ConnectionIndex int addr; // connection address struct _usb_dev() { name = driver_key = desc = hub = ""; vid = pid = port = 0; addr = -1; } bool operator==(const struct _usb_dev& r) { return name == r.name; } }USBDEV, * LPUSBDEV; class ovl_cls// : public refer { volatile long ref_; OVERLAPPED ovl_; DWORD io_bytes_; uint32_t type_; public: ovl_cls(uint32_t type); protected: ~ovl_cls(); public: long add_ref(void) { return InterlockedIncrement(&ref_); } long release(void) { long r = InterlockedDecrement(&ref_); if (r == 0) delete this; return r; } LPOVERLAPPED over_lapped(void); LPDWORD io_bytes(void); void reset(void); bool is_waited(void); void notify(void); uint32_t type(void); }; class ovl_mgr { std::mutex lock_; std::vector ovls_; public: ovl_mgr(); ~ovl_mgr(); public: ovl_cls* get_ovl(uint32_t type); void notify_all(void); }; class usb_device // consider as libusb_device { volatile long ref_; GUID guid_; USBDEV udev_; bool is_ok_; bool online_; ovl_mgr ovl_mgr_; libusb_device_handle *handle_; // as file handle returned by CreateFile libusb_device_descriptor *dev_desc_; std::vector cfg_desc_; typedef struct _usb_pipe { UCHAR address; int type; int index; HANDLE pipe; }USBPIPE; std::vector pipes_; DWORD timout_ms_; HANDLE open_usb(const char* usb_name, DWORD access = GENERIC_READ | GENERIC_WRITE, DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE); int set_timeout(HANDLE h); public: usb_device(const USBDEV& dev); static void vid_pid_from_name(const char* name, int *vid, int *pid); // device name like '\\?\usb#vid_3072&pid_0239#01234567aabbccddee#{a5dcbf10-6530-11d2-901f-00c04fb951ed}' static DWORD from_hex_string(const char* hex_str); static std::string name_without_guid(const char* name); static bool find_vid_pid_in_hub(const char* utf8_hub_path_name, int vid, int pid, int *addr/*if *addr is not -1 or NULL, search the device with vid:pid and set the address in addr if it was not null, or-else chekc the device at *addr is vid:pid or not*/, std::string* reg_key/*{6bdd1fc6-810f-11d0-bec7-08002be2092f}\\0007*/); static std::string usb_scan_name(const char* reg_key/*{6bdd1fc6-810f-11d0-bec7-08002be2092f}\\0007*/); // return \\.\Usbscan1 ... long add_ref(void); long release(void); protected: ~usb_device(); public: usb_device& operator=(const GUID& guid); std::string name(void); USBDEV& dev(void); GUID guid(void); bool is_ok(void); bool is_open(void); bool is_online(void); void set_online(bool online); uint8_t port(void); uint8_t address(void); std::string reg_path(void); bool init(void); void clear(void); int get_descriptor(libusb_device_descriptor* desc); int get_config_descriptor(int index, libusb_config_descriptor** desc); int open(libusb_device_handle** dev_handle); int close(void); int set_timeout(unsigned milliseconds); HANDLE find_pipe(UCHAR addr, int type, int* index = NULL); int transfer_bulk(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout); int transfer_control(uint8_t type, uint8_t req, uint16_t val, uint16_t ind, unsigned char* data, uint16_t len, unsigned timeout); int transfer_interrupt(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout); int cancel_io(void); }; class usb_callback { libusb_hotplug_callback_fn usb_cb_; void* usb_cb_param_; public: usb_callback(libusb_hotplug_callback_fn cb, void* param); ~usb_callback(); public: void notify(libusb_context* ctx, usb_device* dev, int ev); }; class usb_monitor // consider as libusb_context { std::vector cbs_; std::mutex lock_; std::vector devices_; std::shared_ptr handle_msg_; DWORD handle_msg_id_; HWND wnd_monitor_; std::string cur_dev_name_; volatile bool run_; std::vector found_usb_devs_; static LRESULT CALLBACK monitor_wnd_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); static void register_monitor_wnd(const wchar_t* cls); void notify_usb_event(usb_device*& dev, bool arrive); int on_usb_pnp(WPARAM wp, LPARAM lp); void find_usb_and_trigger_event(void); public: usb_monitor(); ~usb_monitor(); static usb_monitor* usb_monitor_; static UINT find_usb_timer_; // set when RegisterDeviceNotification failed static int enum_usb_device(bool(__stdcall* found_usb)(LPUSBDEV dev, void* param), void* param, bool hub = false); static bool __stdcall find_all_usb_devices(LPUSBDEV dev, void* param/*std::vector* */); static bool __stdcall usb_dev_by_name(LPUSBDEV dev, void* param/*LPUSBDEV*/); static bool __stdcall find_parent_hub(LPUSBDEV hub, void* param/*LPUSBDEV*/); static bool is_desired_usb_device(int vid, int pid); public: usb_callback* reg_callback(libusb_hotplug_callback_fn cb, void* param); void unreg_callback(usb_callback* cb); void thread_run_device_event_wnd(void); // called in a seperate thread from libusb void thread_handle_device_change_msg(void); // a seperate thread for WM_DEVICECHANGE cannot be blocked void quit(void); }; //1: \\?\usb#vid_0bda&pid_0129#20100201396000000#{a5dcbf10-6530-11d2-901f-00c04fb951ed} // DeviceIoControl error: 31 // USB\VID_0BDA&PID_0129\20100201396000000 // class: USB // desc: Realtek USB 2.0 Card Reader // HID: USB\VID_0BDA&PID_0129&REV_3960 // name: \Device\USBPDO-3 // bus : 1 // addr: 9 // guid: {36fc9e60-c465-11cf-8056-444553540000} // path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(9) // //2: \\?\usb#vid_06cb&pid_00ea#9bec419959f6#{a5dcbf10-6530-11d2-901f-00c04fb951ed} // Open error: 5 // USB\VID_06CB&PID_00EA\9BEC419959F6 // class: Biometric // desc: Synaptics UWP WBDI // HID: USB\VID_06CB&PID_00EA&REV_0000 // name: \Device\USBPDO-5 // bus : 1 // addr: 12 // guid: {53d29ef7-377c-4d14-864b-eb3a85769359} // path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(12) // //3: \\?\usb#vid_3072&pid_0239#01234567aabbccddee#{a5dcbf10-6530-11d2-901f-00c04fb951ed} // VID: 3072 // PID: 0239 // USB: 4.4 // USB\VID_3072&PID_0239\01234567AABBCCDDEE // class: Image // desc: HUAGOSCAN G200 TWAIN // HID: USB\VID_3072&PID_0239&REV_0404 // name: \Device\USBPDO-7 // bus : 1 // addr: 8 // guid: {6bdd1fc6-810f-11d0-bec7-08002be2092f} // path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(8) // //4: \\?\usb#vid_17ef&pid_6100#5&4adfeed&0&6#{a5dcbf10-6530-11d2-901f-00c04fb951ed} // VID: 0000 // PID: 0000 // USB: 0.0 // USB\VID_17EF&PID_6100\5&4ADFEED&0&6 // class: USB // desc: USB Composite Device // HID: USB\VID_17EF&PID_6100&REV_0201 // name: \Device\USBPDO-1 // bus : 1 // addr: 6 // guid: {36fc9e60-c465-11cf-8056-444553540000} // path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(6) // //5: \\?\usb#vid_8087&pid_0026#5&4adfeed&0&14#{a5dcbf10-6530-11d2-901f-00c04fb951ed} // DeviceIoControl error: 50 // USB\VID_8087&PID_0026\5&4ADFEED&0&14 // class: Bluetooth // desc: 英特尔(R) 无线 Bluetooth(R) // HID: USB\VID_8087&PID_0026&REV_0002 // name: \Device\USBPDO-4 // bus : 1 // addr: 14 // guid: {e0cbf06c-cd8b-4647-bb8a-263b43f0f974} // path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(14) // //6: \\?\usb#vid_17ef&pid_608d#5&4adfeed&0&7#{a5dcbf10-6530-11d2-901f-00c04fb951ed} // Open error: 31 // USB\VID_17EF&PID_608D\5&4ADFEED&0&7 // class: HIDClass // desc: USB 输入设备 // HID: USB\VID_17EF&PID_608D&REV_0100 // name: \Device\USBPDO-2 // bus : 1 // addr: 7 // guid: {745a17a0-74d3-11d0-b6fe-00a0c90f57da} // path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(7) // //Value Code Meaning //USBD_STATUS_CRC // //0xC0000001 //CRC error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_BTSTUFF // //0xC0000002 //BTS error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_DATA_TOGGLE_MISMATCH // //0xC0000003 //Data toggle mismatch. // //USBD_STATUS_STALL_PID // //0xC0000004 //The device returned a stall packet identifier(defined for backward compatibility with the USB 1.0) // //USBD_STATUS_DEV_NOT_RESPONDING // //0xC0000005 //The device is not responding(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_PID_CHECK_FAILURE // //0xC0000006 //The device returned a packet identifier check failure(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_UNEXPECTED_PID // //0xC0000007 //The device returned an unexpected packet identifier error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_DATA_OVERRUN // //0xC0000008 //The device returned a data overrun error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_DATA_UNDERRUN // //0xC0000009 //The device returned a data underrun error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_RESERVED1 // //0xC000000A //Reserved. // //USBD_STATUS_RESERVED2 // //0xC000000B //Reserved. // //USBD_STATUS_BUFFER_OVERRUN // //0xC000000C //The device returned a buffer overrun error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_BUFFER_UNDERRUN // //0xC000000D //The device returned a buffer underrun error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_NOT_ACCESSED // //0xC000000F //The USB stack could not access the device(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_FIFO // //0xC0000010 //The device returned a FIFO error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_XACT_ERROR // //0xC0000011 //The device returned a transaction error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_BABBLE_DETECTED // //0xC0000012 //The device returned a babble detected error(defined for backward compatibility with the USB 1.0). // //USBD_STATUS_DATA_BUFFER_ERROR // //0xC0000013 //Hardware status codes that range from 0x00000001 to 0x000000FF (defined for backward compatibility with the USB 1.0 stack). // //USBD_STATUS_NO_PING_RESPONSE // //0xC0000014 No response was received from the device for a ping packet sent by the host. //USBD_STATUS_INVALID_STREAM_TYPE // //0xC0000015 The stream type is invalid for the endpoint. //USBD_STATUS_INVALID_STREAM_ID // //0xC0000016 The stream identifier is invalid. //USBD_STATUS_ENDPOINT_HALTED // //0xC0000030 //A transfer was submitted to an endpoint that is stalled. // //USBD_STATUS_INVALID_URB_FUNCTION // //0x80000200 //Invalid URB function. // //USBD_STATUS_INVALID_PARAMETER // //0x80000300 //Invalid parameter. // //USBD_STATUS_ERROR_BUSY // //0x80000400 //The client driver caused an error by attempting to close an endpoint, interface, or configuration handle with outstanding transfers. // //USBD_STATUS_REQUEST_FAILED // //0x80000500 //The hub driver cannot complete a URB request. // //USBD_STATUS_INVALID_PIPE_HANDLE // //0x80000600 //Invalid pipe handle. // //USBD_STATUS_NO_BANDWIDTH // //0x80000700 //There was not enough bandwidth to open a requested endpoint. // //USBD_STATUS_INTERNAL_HC_ERROR // //0x80000900 //Unspecified host controller error. // //USBD_STATUS_ERROR_SHORT_TRANSFER // //0x80000900 //The transfer ended with a short packet, but the USBD_SHORT_TRANSFER_OK bit is not set for the pipe. // //USBD_STATUS_BAD_START_FRAME // //0xC0000A00 //The requested start frame is not within a range of USBD_ISO_START_FRAME_RANGE frames of the current USB frame.Whenever this error occurs, the system sets the stall bit on the pipe. // //USBD_STATUS_ISOCH_REQUEST_FAILED // //0xC0000B00 //The host controller returns this error whenever all packets in an isochronous transfer complete with an error. // //USBD_STATUS_FRAME_CONTROL_OWNED // //0xC0000C00 //The hub driver returns this error whenever the frame length control for the host controller is being used by a driver other than the host controller driver. // //USBD_STATUS_FRAME_CONTROL_NOT_OWNED // //0xC0000D00 //The hub driver returns this error if the caller does not own frame length controland attempts to release or modify the host controller frame length. // //USBD_STATUS_NOT_SUPPORTED // //0xC0000E00 //The request was not supported. // //USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR // //0xC0000F00 //Invalid configuration descriptor. // //USBD_STATUS_INSUFFICIENT_RESOURCES // //0xC0001000 //Insufficient resources. // //USBD_STATUS_SET_CONFIG_FAILED // //0xC0002000 //An attempt to change the device configuration failed. // //USBD_STATUS_BUFFER_TOO_SMALL // //0xC0003000 //The buffer is too small. // //USBD_STATUS_INTERFACE_NOT_FOUND // //0xC0004000 //The interface was not found. // //USBD_STATUS_INAVLID_PIPE_FLAGS // //0xC0005000 //Invalid pipe flags. // //USBD_STATUS_TIMEOUT // //0xC0006000 //The request timed out. // //USBD_STATUS_DEVICE_GONE // //0xC0007000 //The device is no longer present in the system. // //USBD_STATUS_STATUS_NOT_MAPPED // //0xC0008000 //The device bus address is not mapped to system memory. // //USBD_STATUS_HUB_INTERNAL_ERROR // //0xC0009000 //The hub driver returns this error whenever it intercepted a URB that was targeted at some other device. // //USBD_STATUS_CANCELED // //0xC0010000 //The USB stack reports this error whenever it completed a transfer because of an AbortPipe request from the client driver. // //USBD_STATUS_ISO_NOT_ACCESSED_BY_HW // //0xC0020000 //The host controller did not access the transfer descriptor(TD) that is associated with this packet.The USB stack reports this error in the packet status field of an isochronous transfer packet. // //USBD_STATUS_ISO_TD_ERROR // //0xC0030000 //The host controller reported an error in the transfer descriptor(TD).The USB stack reports this error in the packet status field of an isochronous transfer packet. // //USBD_STATUS_ISO_NA_LATE_USBPORT // //0xC0040000 //The client driver submitted the packet on time, but the packet failed to reach the miniport driver on time.The USB stack reports this error in the packet status field of an isochronous transfer packet. // //USBD_STATUS_ISO_NOT_ACCESSED_LATE // //0xC0050000 //The client driver did not submit the packet on time.The USB stack reports this error in the packet status field of an isochronous transfer packet. // //USBD_STATUS_BAD_DESCRIPTOR // //0xC0100000 //Invalid descriptor. // //USBD_STATUS_BAD_DESCRIPTOR_BLEN // //0xC0100001 //Invalid descriptor length. // //USBD_STATUS_BAD_DESCRIPTOR_TYPE // //0xC0100002 //Invalid descriptor type. // //USBD_STATUS_BAD_INTERFACE_DESCRIPTOR // //0xC0100003 //Invalid interface descriptor. // //USBD_STATUS_BAD_ENDPOINT_DESCRIPTOR // //0xC0100004 //Invalid endpoint descriptor. // //USBD_STATUS_BAD_INTERFACE_ASSOC_DESCRIPTOR // //0xC0100005 //Invalid interface association descriptor. // //USBD_STATUS_BAD_CONFIG_DESC_LENGTH // //0xC0100006 //Invalid configuration descriptor length. // //USBD_STATUS_BAD_NUMBER_OF_INTERFACES // //0xC0100007 //Invalid number of interfaces. // //USBD_STATUS_BAD_NUMBER_OF_ENDPOINTS // //0xC0100008 //Invalid number of endpoints. // //USBD_STATUS_BAD_ENDPOINT_ADDRESS // //0xC0100009 //Invalid endpoint address.