Skip to content

Commit a02b612

Browse files
committed
Windows reference code: new approach for composite devices
1 parent 923311f commit a02b612

File tree

5 files changed

+140
-111
lines changed

5 files changed

+140
-111
lines changed

reference/windows/USB/usb_device.cpp

Lines changed: 121 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,27 @@
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

3036
void 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

448544
usb_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) { }

reference/windows/USB/usb_device.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,17 +241,19 @@ class usb_device {
241241
struct interface_handle {
242242
int interface_num;
243243
int first_interface_num;
244-
std::wstring device_path;
245244
HANDLE device_handle;
246245
WINUSB_INTERFACE_HANDLE intf_handle;
247246
int device_open_count;
248247

249248
interface_handle(int intf_num, int first_num, std::wstring&& path);
250249
};
251250

252-
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);
251+
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);
253252
void set_product_names(const std::string& manufacturer, const std::string& product, const std::string& serial_number);
254-
void build_handles(const std::wstring& device_path, std::map<int, std::wstring>&& children);
253+
void build_handles(const std::wstring& device_path);
254+
std::wstring get_interface_device_path(int interface_num);
255+
int get_child_device_path(const std::wstring& child_id, int interface_num, std::wstring& device_path);
256+
static int extract_interface_number(const std::vector<std::wstring>& hardware_ids);
255257

256258
int control_transfer_core(const usb_control_request& request, uint8_t* data, int timeout);
257259
usb_composite_function* get_function(int intf_number);
@@ -286,6 +288,8 @@ class usb_device {
286288
std::vector<usb_interface> interfaces_;
287289
std::vector<usb_composite_function> functions_;
288290
std::vector<interface_handle> interface_handles_;
291+
std::map<int, std::wstring> interface_device_paths_;
292+
bool is_composite_;
289293

290294
friend class usb_registry;
291295
friend class usb_istreambuf;

reference/windows/USB/usb_error.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ std::string usb_error::full_message(const char* message, int code) {
3636
LPSTR messageBuffer = nullptr;
3737

3838
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
39-
NULL, code, 0, (LPSTR)&messageBuffer, 0, NULL);
39+
nullptr, code, 0, (LPSTR)&messageBuffer, 0, nullptr);
4040
while (size > 0 && (messageBuffer[size - 1] == L'\r' || messageBuffer[size - 1] == '\n'))
4141
size--;
4242

0 commit comments

Comments
 (0)