1010#include " usb_device.hpp"
1111#include " usb_error.hpp"
1212#include " usb_iostream.hpp"
13+ #include " device_info_set.h"
1314#include " scope.hpp"
1415#include " config_parser.hpp"
1516#include " usb_registry.hpp"
1617
18+ #include < devpkey.h>
19+
20+ #include < chrono>
1721#include < cstdio>
22+ #include < regex>
23+ #include < thread>
1824
19- usb_device::usb_device (usb_registry* registry, std::wstring&& device_path, int vendor_id, int product_id, const std::vector<uint8_t >& config_desc, std::map< int , std::wstring>&& children )
20- : registry_(registry), vendor_id_(vendor_id), product_id_(product_id), is_open_(false ), device_path_(std::move(device_path)) {
25+ usb_device::usb_device (usb_registry* registry, std::wstring&& device_path, int vendor_id, int product_id, const std::vector<uint8_t >& config_desc, bool is_composite )
26+ : registry_(registry), vendor_id_(vendor_id), product_id_(product_id), is_open_(false ), device_path_(std::move(device_path)), is_composite_(is_composite) {
2127
2228 config_parser parser{};
2329 parser.parse (config_desc.data (), static_cast <int >(config_desc.size ()));
2430 interfaces_ = std::move (parser.interfaces );
2531 functions_ = std::move (parser.functions );
2632
27- build_handles (device_path_, std::move (children) );
33+ build_handles (device_path_);
2834}
2935
3036void usb_device::set_product_names (const std::string& manufacturer, const std::string& product, const std::string& serial_number) {
@@ -33,19 +39,14 @@ void usb_device::set_product_names(const std::string& manufacturer, const std::s
3339 serial_number_ = serial_number;
3440}
3541
36- void usb_device::build_handles (const std::wstring& device_path, std::map< int , std::wstring>&& children ) {
42+ void usb_device::build_handles (const std::wstring& device_path) {
3743 for (const usb_interface& intf : interfaces_) {
3844 int intf_number = intf.number ();
3945 auto function = get_function (intf_number);
4046
4147 std::wstring path;
42- if (function->first_interface () == intf_number) {
43- if (children.size () > 0 ) {
44- path = std::move (children[intf_number]);
45- } else {
46- path = device_path;
47- }
48- }
48+ if (intf_number == 0 )
49+ path = device_path;
4950
5051 interface_handles_.push_back (interface_handle (intf_number, function->first_interface (), std::move (path)));
5152 }
@@ -132,8 +133,13 @@ void usb_device::claim_interface(int interface_number) {
132133
133134 // open device if needed
134135 if (first_intf_handle->device_handle == nullptr ) {
135- std::wcerr << " opening device " << first_intf_handle->device_path << std::endl;
136- first_intf_handle->device_handle = CreateFileW (first_intf_handle->device_path .c_str (),
136+ auto device_path = get_interface_device_path (first_intf_handle->interface_num );
137+ if (device_path.empty ())
138+ throw usb_error (" failed to claim interface (function has no device path, might be missing WinUSB driver)" );
139+
140+ std::wcerr << " opening device " << device_path << std::endl;
141+
142+ first_intf_handle->device_handle = CreateFileW (device_path.c_str (),
137143 GENERIC_WRITE | GENERIC_READ,
138144 FILE_SHARE_WRITE | FILE_SHARE_READ,
139145 nullptr ,
@@ -143,21 +149,19 @@ void usb_device::claim_interface(int interface_number) {
143149 if (first_intf_handle->device_handle == INVALID_HANDLE_VALUE)
144150 usb_error::throw_error (" cannot open USB device" );
145151
146- registry_->add_to_completion_port (first_intf_handle->device_handle );
147- }
148-
149- // open interface
150- std::wcerr << " opening interface for device " << first_intf_handle->device_path << std::endl;
151- if (!WinUsb_Initialize (first_intf_handle->device_handle , &intf_handle->intf_handle )) {
152- auto err = GetLastError ();
153- if (first_intf_handle->device_open_count == 0 ) {
152+ // open interface
153+ if (!WinUsb_Initialize (first_intf_handle->device_handle , &first_intf_handle->intf_handle )) {
154+ auto err = GetLastError ();
154155 CloseHandle (first_intf_handle->device_handle );
155156 first_intf_handle->device_handle = nullptr ;
157+ throw usb_error (" cannot open USB device (2)" , err);
156158 }
157- throw usb_error (" cannot open USB device" , err);
159+
160+ registry_->add_to_completion_port (first_intf_handle->device_handle );
158161 }
159162
160163 first_intf_handle->device_open_count += 1 ;
164+ intf_handle->intf_handle = first_intf_handle->intf_handle ;
161165 intf->set_claimed (true );
162166}
163167
@@ -175,14 +179,13 @@ void usb_device::release_interface(int interface_number) {
175179 interface_handle* intf_handle = get_interface_handle (interface_number);
176180 interface_handle* first_intf_handle = get_interface_handle (intf_handle->first_interface_num );
177181
178- // close interface
179- WinUsb_Free (intf_handle->intf_handle );
180182 intf_handle->intf_handle = nullptr ;
181183 intf->set_claimed (false );
182184
183185 // close device if needed
184186 first_intf_handle->device_open_count -= 1 ;
185187 if (first_intf_handle->device_open_count == 0 ) {
188+ WinUsb_Free (first_intf_handle->intf_handle );
186189 CloseHandle (first_intf_handle->device_handle );
187190 first_intf_handle->device_handle = nullptr ;
188191 }
@@ -442,9 +445,102 @@ void usb_device::cancel_transfer(usb_direction direction, int endpoint_number, O
442445 usb_error::throw_error (" Error on cancelling transfer" );
443446}
444447
448+ std::wstring usb_device::get_interface_device_path (int interface_num) {
449+ if (interface_num == 0 )
450+ return device_path_;
451+
452+ auto it = interface_device_paths_.find (interface_num);
453+ if (it != interface_device_paths_.end ())
454+ return it->second ;
455+
456+ int num_retries = 30 ; // 30 x 100ms
457+
458+ while (num_retries > 0 ) {
459+
460+ auto dev_info_set = device_info_set::of_path (device_path_);
461+
462+ if (!dev_info_set.is_composite_device ())
463+ throw usb_error (" internal error: interface belongs to a separate function but device is not composite" );
464+
465+ auto children_instance_ids = dev_info_set.get_device_property_string_list (DEVPKEY_Device_Children);
466+ if (children_instance_ids.empty ()) {
467+ std::wcerr << " missing children IDs for device " << device_path_ << std::endl;
468+
469+ } else {
470+
471+ std::wcerr << " children IDs: " ;
472+ for (auto it = children_instance_ids.begin (); it < children_instance_ids.end (); it++) {
473+ if (it != children_instance_ids.begin ())
474+ std::wcerr << " , " ;
475+ std::wcerr << *it;
476+ }
477+ std::wcerr << std::endl;
478+
479+ std::wstring child_path;
480+ for (auto & child_id : children_instance_ids) {
481+ auto res = get_child_device_path (child_id, interface_num, child_path);
482+ if (res == 1 )
483+ return child_path;
484+ if (res == -1 )
485+ break ;
486+ }
487+ }
488+
489+ std::cerr << " Sleeping for 100ms..." << std::endl;
490+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
491+ num_retries -= 1 ;
492+ }
493+
494+ return {};
495+ }
496+
497+ int usb_device::get_child_device_path (const std::wstring& child_id, int interface_num, std::wstring& device_path) {
498+
499+ auto dev_info_set = device_info_set::of_instance (child_id);
500+
501+ auto hardware_ids = dev_info_set.get_device_property_string_list (DEVPKEY_Device_HardwareIds);
502+ if (hardware_ids.empty ()) {
503+ std::wcerr << " child device " << child_id << " has no hardware IDs" << std::endl;
504+ return 0 ;
505+ }
506+
507+ auto intf_num = extract_interface_number (hardware_ids);
508+ if (intf_num == -1 ) {
509+ std::wcerr << " child device " << child_id << " has no interface number" << std::endl;
510+ return 0 ;
511+ }
512+
513+ if (intf_num != interface_num)
514+ return 0 ;
515+
516+ device_path = dev_info_set.get_device_path_by_guid (child_id);
517+ if (device_path.empty ()) {
518+ std::wcerr << " child device " << child_id << " has no device path" << std::endl;
519+ return -1 ;
520+ }
521+
522+ std::wcerr << " child device: interface=" << intf_num << " , device path=" << device_path << std::endl;
523+ interface_device_paths_[interface_num] = device_path;
524+ return 1 ;
525+ }
526+
527+ static const std::wregex multiple_interface_id_pattern (L" USB\\\\ VID_[0-9A-Fa-f]{4}&PID_[0-9A-Fa-f]{4}&MI_([0-9A-Fa-f]{2})" );
528+
529+ int usb_device::extract_interface_number (const std::vector<std::wstring>& hardware_ids) {
530+ // Also see https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers#multiple-interface-usb-devices
531+
532+ for (auto & id : hardware_ids) {
533+ auto matches = std::wsmatch{};
534+ if (std::regex_search (id, matches, multiple_interface_id_pattern))
535+ return std::stoul (matches[1 ].str (), nullptr , 16 );
536+ }
537+
538+ return -1 ;
539+ }
540+
445541
446542// --- interface_handle
447543
448544usb_device::interface_handle::interface_handle (int intf_num, int first_num, std::wstring&& path)
449- : interface_num(intf_num), first_interface_num(first_num), device_path(std::move(path)),
545+ : interface_num(intf_num), first_interface_num(first_num),
450546 device_handle(nullptr ), intf_handle(nullptr ), device_open_count(0 ) { }
0 commit comments