@@ -44,6 +44,10 @@ public class WindowsUSBDevice extends USBDeviceImpl {
4444
4545 private static final System .Logger LOG = System .getLogger (WindowsUSBDevice .class .getName ());
4646
47+ private static final int RETRY_LATER = 0 ;
48+ private static final int TRY_NEXT_CHILD = 1 ;
49+ private static final int SUCCESS = 2 ;
50+
4751 private final WindowsAsyncTask asyncTask ;
4852 private List <InterfaceHandle > interfaceHandles ;
4953 // device paths by interface number (first interface of function)
@@ -100,7 +104,32 @@ public synchronized void close() {
100104 showAsOpen = false ;
101105 }
102106
103- public synchronized void claimInterface (int interfaceNumber ) {
107+ public void claimInterface (int interfaceNumber ) {
108+ // When a device is plugged in, a notification is sent. For composite devices, it is a notification
109+ // that the composite device is ready. Each composite function will be registered separately and
110+ // the related information will be available with a delay. So for composite functions, several
111+ // retries might be needed until the device path is available.
112+ var numRetries = 30 ; // 30 x 100ms
113+ while (true ) {
114+ if (claimInteraceSynchronized (interfaceNumber ))
115+ return ; // success
116+
117+ numRetries -= 1 ;
118+ if (numRetries == 0 )
119+ throw new USBException ("claiming interface failed (function has no device path, might be missing WinUSB driver)" );
120+
121+ // sleep and retry
122+ try {
123+ LOG .log (DEBUG , "Sleeping for 100ms..." );
124+ //noinspection BusyWait
125+ Thread .sleep (100 );
126+ } catch (InterruptedException e ) {
127+ Thread .currentThread ().interrupt ();
128+ }
129+ }
130+ }
131+
132+ private synchronized boolean claimInteraceSynchronized (int interfaceNumber ) {
104133 checkIsOpen ();
105134
106135 getInterfaceWithCheck (interfaceNumber , false );
@@ -117,7 +146,7 @@ public synchronized void claimInterface(int interfaceNumber) {
117146 if (firstIntfHandle .deviceHandle == null ) {
118147 var devicePath = getInterfaceDevicePath (firstIntfHandle .interfaceNumber );
119148 if (devicePath == null )
120- throw new USBException ( "claiming interface failed (function has no device path, might be missing WinUSB driver)" );
149+ return false ; // retry later
121150
122151 LOG .log (DEBUG , "opening device {0}" , devicePath );
123152
@@ -160,6 +189,7 @@ public synchronized void claimInterface(int interfaceNumber) {
160189
161190 firstIntfHandle .deviceOpenCount += 1 ;
162191 setClaimed (interfaceNumber , true );
192+ return true ;
163193 }
164194
165195 @ Override
@@ -208,6 +238,15 @@ public synchronized void releaseInterface(int interfaceNumber) {
208238 WinUSB .WinUsb_Free (firstIntfHandle .winusbHandle );
209239 firstIntfHandle .winusbHandle = null ;
210240
241+ var path = (String )getUniqueId ();
242+ if (firstIntfHandle .interfaceNumber != 0 ) {
243+ if (devicePaths != null )
244+ path = devicePaths .get (firstIntfHandle .interfaceNumber );
245+ else
246+ path = null ;
247+ }
248+ LOG .log (DEBUG , "closing device {0}" , path );
249+
211250 Kernel32 .CloseHandle (firstIntfHandle .deviceHandle );
212251 firstIntfHandle .deviceHandle = null ;
213252 }
@@ -487,46 +526,29 @@ private String getInterfaceDevicePath(int interfaceNumber) {
487526 return devicePath ;
488527 }
489528
490- // When a device is plugged in, a notification is sent. For composite devices, it is a notification
491- // that the information for the composite device is ready. Each composite function will be registered
492- // separately and the related information will be available with a delay. So for composite functions,
493- // several retries might be needed until the device path is available.
494- var numRetries = 30 ; // 30 x 100ms
495- while (true ) {
496- try (var deviceInfoSet = DeviceInfoSet .ofPath (parentDevicePath )) {
497- if (!deviceInfoSet .isCompositeDevice ())
498- throwException ("internal error: interface belongs to a separate function but device is not composite" );
499-
500- var childrenInstanceIDs = deviceInfoSet .getStringListProperty (Children );
501- if (childrenInstanceIDs == null ) {
502- LOG .log (DEBUG , "missing children instance IDs for device {0}" , parentDevicePath );
503-
504- } else {
505- LOG .log (DEBUG , "children instance IDs: {0}" , childrenInstanceIDs );
506-
507- for (var instanceId : childrenInstanceIDs ) {
508- var res = fetchChildDevicePath (instanceId , interfaceNumber );
509- if (res == 1 )
510- return devicePaths .get (interfaceNumber ); // success
511- if (res == -1 )
512- break ; // retry later
513- }
514- }
529+ try (var deviceInfoSet = DeviceInfoSet .ofPath (parentDevicePath )) {
530+ if (!deviceInfoSet .isCompositeDevice ())
531+ throwException ("internal error: interface belongs to a composite function but device is not composite" );
515532
516- numRetries -= 1 ;
517- if (numRetries == 0 )
518- return null ; // failure
533+ var childrenInstanceIDs = deviceInfoSet .getStringListProperty (Children );
534+ if (childrenInstanceIDs == null ) {
535+ LOG .log (DEBUG , "missing children instance IDs for device {0}" , parentDevicePath );
536+ return null ;
519537
520- // sleep and retry
521- try {
522- LOG .log (DEBUG , "Sleeping for 100ms..." );
523- //noinspection BusyWait
524- Thread .sleep (100 );
525- } catch (InterruptedException e ) {
526- Thread .currentThread ().interrupt ();
538+ } else {
539+ LOG .log (DEBUG , "children instance IDs: {0}" , childrenInstanceIDs );
540+
541+ for (var instanceId : childrenInstanceIDs ) {
542+ var res = fetchChildDevicePath (instanceId , interfaceNumber );
543+ if (res == SUCCESS )
544+ return devicePaths .get (interfaceNumber );
545+ if (res == RETRY_LATER )
546+ return null ; // retry later
527547 }
528548 }
529549 }
550+
551+ return null ; // retry later
530552 }
531553
532554 private int fetchChildDevicePath (String instanceId , int interfaceNumber ) {
@@ -536,28 +558,28 @@ private int fetchChildDevicePath(String instanceId, int interfaceNumber) {
536558 var hardwareIds = deviceInfoSet .getStringListProperty (HardwareIds );
537559 if (hardwareIds == null ) {
538560 LOG .log (DEBUG , "child device {0} has no hardware IDs" , instanceId );
539- return 0 ; // continue with next child
561+ return TRY_NEXT_CHILD ;
540562 }
541563
542564 var extractedNumber = extractInterfaceNumber (hardwareIds );
543565 if (extractedNumber == -1 ) {
544566 LOG .log (DEBUG , "child device {0} has no interface number" , instanceId );
545- return 0 ; // continue with next child
567+ return TRY_NEXT_CHILD ;
546568 }
547569
548570 if (extractedNumber != interfaceNumber )
549- return 0 ; // continue with next child
571+ return TRY_NEXT_CHILD ;
550572
551573 var devicePath = deviceInfoSet .getDevicePathByGUID (instanceId );
552574 if (devicePath == null ) {
553575 LOG .log (INFO , "Child device {0} has no device path" , instanceId );
554- return - 1 ; // retry later
576+ return RETRY_LATER ;
555577 }
556578
557579 if (devicePaths == null )
558580 devicePaths = new HashMap <>();
559581 devicePaths .put (interfaceNumber , devicePath );
560- return 1 ; // success
582+ return SUCCESS ;
561583 }
562584 }
563585
0 commit comments