Skip to content

Commit

Permalink
Windows: give up earlier on waiting for child device
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelbl committed Sep 29, 2023
1 parent b5a4143 commit 43f4192
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ public class WindowsUSBDevice extends USBDeviceImpl {

private static final System.Logger LOG = System.getLogger(WindowsUSBDevice.class.getName());

private static final int RETRY_LATER = 0;
private static final int TRY_NEXT_CHILD = 1;
private static final int SUCCESS = 2;

private final WindowsAsyncTask asyncTask;
/**
* Indicates if the device is a composite device
Expand Down Expand Up @@ -125,7 +121,7 @@ public void claimInterface(int interfaceNumber) {

numRetries -= 1;
if (numRetries == 0)
throw new USBException("claiming interface failed (function has no device path, might be missing WinUSB driver)");
throw new USBException("claiming interface failed (function has no device path / interface GUID, might be missing WinUSB driver)");

// sleep and retry
try {
Expand Down Expand Up @@ -534,11 +530,9 @@ private String getInterfaceDevicePath(int interfaceNumber) {
LOG.log(DEBUG, "children instance IDs: {0}", childrenInstanceIDs);

for (var instanceId : childrenInstanceIDs) {
var res = fetchChildDevicePath(instanceId, interfaceNumber);
if (res == SUCCESS)
return devicePaths.get(interfaceNumber);
if (res == RETRY_LATER)
return null; // retry later
devicePath = getChildDevicePath(instanceId, interfaceNumber);
if (devicePath != null)
return devicePath;
}
}
}
Expand All @@ -552,35 +546,35 @@ private String getCachedInterfaceDevicePath(int interfaceNumber) {
return devicePaths.get(interfaceNumber);
}

private int fetchChildDevicePath(String instanceId, int interfaceNumber) {
private String getChildDevicePath(String instanceId, int interfaceNumber) {
try (var deviceInfoSet = DeviceInfoSet.ofInstance(instanceId)) {

// get hardware IDs (to extract interface number)
var hardwareIds = deviceInfoSet.getStringListProperty(HardwareIds);
if (hardwareIds == null) {
LOG.log(DEBUG, "child device {0} has no hardware IDs", instanceId);
return TRY_NEXT_CHILD;
return null;
}

var extractedNumber = extractInterfaceNumber(hardwareIds);
if (extractedNumber == -1) {
LOG.log(DEBUG, "child device {0} has no interface number", instanceId);
return TRY_NEXT_CHILD;
return null;
}

if (extractedNumber != interfaceNumber)
return TRY_NEXT_CHILD;
return null;

var devicePath = deviceInfoSet.getDevicePathByGUID(instanceId);
if (devicePath == null) {
LOG.log(INFO, "Child device {0} has no device path", instanceId);
return RETRY_LATER;
LOG.log(INFO, "Child device {0} has no device path / interface GUID", instanceId);
throw new USBException("claiming interface failed (function has no device path / interface GUID, might be missing WinUSB driver)");
}

if (devicePaths == null)
devicePaths = new HashMap<>();
devicePaths.put(interfaceNumber, devicePath);
return SUCCESS;
return devicePath;
}
}

Expand Down
83 changes: 44 additions & 39 deletions reference/windows/USB/usb_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,26 @@ void usb_device::close() {
}

void usb_device::claim_interface(int interface_number) {
// When a device is plugged in, a notification is sent. For composite devices, it is a notification
// that the composite device is ready. Each composite function will be registered separately and
// the related information will be available with a delay. So for composite functions, several
// retries might be needed until the device path is available.
int num_retries = 30; // 30 x 100ms
while (true) {
if (try_claim_interface(interface_number))
return; // success

num_retries -= 1;
if (num_retries == 0)
throw usb_error("claiming interface failed (function has no device interface GUID/path, might be missing WinUSB driver)");

// sleep and retry
std::cerr << "Sleeping for 100ms..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}

bool usb_device::try_claim_interface(int interface_number) {

if (!is_open())
throw usb_error("USB device is not open");
Expand All @@ -135,7 +155,7 @@ void usb_device::claim_interface(int interface_number) {
if (first_intf_handle->device_handle == nullptr) {
auto device_path = get_interface_device_path(first_intf_handle->interface_num);
if (device_path.empty())
throw usb_error("failed to claim interface (function has no device path, might be missing WinUSB driver)");
return false;

std::wcerr << "opening device " << device_path << std::endl;

Expand Down Expand Up @@ -169,6 +189,7 @@ void usb_device::claim_interface(int interface_number) {

first_intf_handle->device_open_count += 1;
intf->set_claimed(true);
return true;
}

void usb_device::release_interface(int interface_number) {
Expand Down Expand Up @@ -465,72 +486,56 @@ std::wstring usb_device::get_interface_device_path(int interface_num) {
if (it != interface_device_paths_.end())
return it->second;

int num_retries = 30; // 30 x 100ms

while (num_retries > 0) {

auto dev_info_set = device_info_set::of_path(device_path_);

auto children_instance_ids = dev_info_set.get_device_property_string_list(DEVPKEY_Device_Children);
if (children_instance_ids.empty()) {
std::wcerr << "missing children IDs for device " << device_path_ << std::endl;
auto dev_info_set = device_info_set::of_path(device_path_);

} else {
auto children_instance_ids = dev_info_set.get_device_property_string_list(DEVPKEY_Device_Children);

std::wcerr << "children IDs: ";
for (auto it = children_instance_ids.begin(); it < children_instance_ids.end(); it++) {
if (it != children_instance_ids.begin())
std::wcerr << ", ";
std::wcerr << *it;
}
std::wcerr << std::endl;

std::wstring child_path;
for (auto& child_id : children_instance_ids) {
auto res = get_child_device_path(child_id, interface_num, child_path);
if (res == 1)
return child_path;
if (res == -1)
break;
}
}
std::wcerr << "children IDs: ";
for (auto it = children_instance_ids.begin(); it < children_instance_ids.end(); it++) {
if (it != children_instance_ids.begin())
std::wcerr << ", ";
std::wcerr << *it;
}
std::wcerr << std::endl;

std::cerr << "Sleeping for 100ms..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
num_retries -= 1;
std::wstring child_path;
for (auto& child_id : children_instance_ids) {
child_path = get_child_device_path(child_id, interface_num);
if (!child_path.empty())
return child_path;
}

return {};
return {}; // retry later
}

int usb_device::get_child_device_path(const std::wstring& child_id, int interface_num, std::wstring& device_path) {
std::wstring usb_device::get_child_device_path(const std::wstring& child_id, int interface_num) {

auto dev_info_set = device_info_set::of_instance(child_id);

auto hardware_ids = dev_info_set.get_device_property_string_list(DEVPKEY_Device_HardwareIds);
if (hardware_ids.empty()) {
std::wcerr << "child device " << child_id << " has no hardware IDs" << std::endl;
return 0; // continue with next child
return {}; // continue with next child
}

auto intf_num = extract_interface_number(hardware_ids);
if (intf_num == -1) {
std::wcerr << "child device " << child_id << " has no interface number" << std::endl;
return 0; // continue with next child
return {}; // continue with next child
}

if (intf_num != interface_num)
return 0; // continue with next child
return {}; // continue with next child

device_path = dev_info_set.get_device_path_by_guid(child_id);
auto device_path = dev_info_set.get_device_path_by_guid(child_id);
if (device_path.empty()) {
std::wcerr << "child device " << child_id << " has no device path" << std::endl;
return -1; // retry later
throw usb_error("claiming interface failed (function has no device interface GUID/path, might be missing WinUSB driver)");
}

std::wcerr << "child device: interface=" << intf_num << ", device path=" << device_path << std::endl;
interface_device_paths_[interface_num] = device_path;
return 1; // success
return device_path; // success
}

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})");
Expand Down
3 changes: 2 additions & 1 deletion reference/windows/USB/usb_device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,9 @@ class 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);
void set_product_names(const std::string& manufacturer, const std::string& product, const std::string& serial_number);
void build_handles(const std::wstring& device_path);
bool try_claim_interface(int interface_number);
std::wstring get_interface_device_path(int interface_num);
int get_child_device_path(const std::wstring& child_id, int interface_num, std::wstring& device_path);
std::wstring get_child_device_path(const std::wstring& child_id, int interface_num);
static int extract_interface_number(const std::vector<std::wstring>& hardware_ids);

int control_transfer_core(const usb_control_request& request, uint8_t* data, int timeout);
Expand Down

0 comments on commit 43f4192

Please sign in to comment.