Skip to content

Commit eda2de1

Browse files
committed
Rewrite handling of interfaces to be configuration-specific
We now wait for the (mandatory) SET_CONFIGURATION request to claim interfaces.
1 parent 15b02ab commit eda2de1

File tree

3 files changed

+101
-49
lines changed

3 files changed

+101
-49
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.cgutman.usbip.service;
2+
3+
import android.hardware.usb.UsbConfiguration;
4+
import android.hardware.usb.UsbDevice;
5+
import android.hardware.usb.UsbDeviceConnection;
6+
7+
import org.cgutman.usbip.server.protocol.dev.UsbIpSubmitUrb;
8+
9+
import java.util.HashSet;
10+
import java.util.concurrent.ThreadPoolExecutor;
11+
12+
public class AttachedDeviceContext {
13+
public UsbDevice device;
14+
public UsbDeviceConnection devConn;
15+
public UsbConfiguration activeConfiguration;
16+
public ThreadPoolExecutor requestPool;
17+
public HashSet<UsbIpSubmitUrb> activeMessages;
18+
}

app/src/main/java/org/cgutman/usbip/service/UsbIpService.java

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ public void submitUrbRequest(Socket s, UsbIpSubmitUrb msg) {
577577

578578
// We have to handle certain control requests (SET_CONFIGURATION/SET_INTERFACE) by calling
579579
// Android APIs rather than just submitting the URB directly to the device
580-
if (!UsbControlHelper.handleInternalControlTransfer(dev, devConn, requestType, request, value, index)) {
580+
if (!UsbControlHelper.handleInternalControlTransfer(context, requestType, request, value, index)) {
581581
do {
582582
res = XferUtils.doControlTransfer(devConn, requestType, request, value, index,
583583
(requestType & 0x80) != 0 ? reply.inData : msg.outData, length, 1000);
@@ -613,35 +613,40 @@ public void submitUrbRequest(Socket s, UsbIpSubmitUrb msg) {
613613
else {
614614
// Find the correct endpoint
615615
UsbEndpoint selectedEndpoint = null;
616-
for (int i = 0; i < dev.getInterfaceCount(); i++) {
617-
// Check each interface
618-
UsbInterface iface = dev.getInterface(i);
619-
for (int j = 0; j < iface.getEndpointCount(); j++) {
620-
// Check the endpoint number
621-
UsbEndpoint endpoint = iface.getEndpoint(j);
622-
if (msg.ep == endpoint.getEndpointNumber()) {
623-
// Check the direction
624-
if (msg.direction == UsbIpDevicePacket.USBIP_DIR_IN) {
625-
if (endpoint.getDirection() != UsbConstants.USB_DIR_IN) {
626-
continue;
616+
if (context.activeConfiguration != null) {
617+
for (int i = 0; i < context.activeConfiguration.getInterfaceCount(); i++) {
618+
// Check each interface
619+
UsbInterface iface = context.activeConfiguration.getInterface(i);
620+
for (int j = 0; j < iface.getEndpointCount(); j++) {
621+
// Check the endpoint number
622+
UsbEndpoint endpoint = iface.getEndpoint(j);
623+
if (msg.ep == endpoint.getEndpointNumber()) {
624+
// Check the direction
625+
if (msg.direction == UsbIpDevicePacket.USBIP_DIR_IN) {
626+
if (endpoint.getDirection() != UsbConstants.USB_DIR_IN) {
627+
continue;
628+
}
627629
}
628-
}
629-
else {
630-
if (endpoint.getDirection() != UsbConstants.USB_DIR_OUT) {
631-
continue;
630+
else {
631+
if (endpoint.getDirection() != UsbConstants.USB_DIR_OUT) {
632+
continue;
633+
}
632634
}
635+
636+
// This the right endpoint
637+
selectedEndpoint = endpoint;
638+
break;
633639
}
634-
635-
// This the right endpoint
636-
selectedEndpoint = endpoint;
640+
}
641+
642+
// Check if we found the endpoint on the last interface
643+
if (selectedEndpoint != null) {
637644
break;
638645
}
639646
}
640-
641-
// Check if we found the endpoint on the last interface
642-
if (selectedEndpoint != null) {
643-
break;
644-
}
647+
}
648+
else {
649+
System.err.println("Attempted to transfer to non-control EP before SET_CONFIGURATION!");
645650
}
646651

647652
if (selectedEndpoint == null) {
@@ -735,13 +740,6 @@ public boolean attachToDevice(Socket s, String busId) {
735740
return false;
736741
}
737742

738-
// Claim all interfaces since we don't know which one the client wants
739-
for (int i = 0; i < dev.getInterfaceCount(); i++) {
740-
if (!devConn.claimInterface(dev.getInterface(i), true)) {
741-
System.err.println("Unable to claim interface "+dev.getInterface(i).getId());
742-
}
743-
}
744-
745743
// Create a context for this attachment
746744
AttachedDeviceContext context = new AttachedDeviceContext();
747745
context.devConn = devConn;
@@ -841,11 +839,5 @@ public void abortUrbRequest(Socket s, UsbIpUnlinkUrb msg) {
841839
found ? UsbIpSubmitUrb.USBIP_STATUS_URB_ABORTED :
842840
-22); // EINVAL
843841
}
844-
845-
static class AttachedDeviceContext {
846-
public UsbDevice device;
847-
public UsbDeviceConnection devConn;
848-
public ThreadPoolExecutor requestPool;
849-
public HashSet<UsbIpSubmitUrb> activeMessages;
850-
}
842+
851843
}

app/src/main/java/org/cgutman/usbip/usb/UsbControlHelper.java

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import android.hardware.usb.UsbInterface;
88
import android.os.Build;
99

10+
import org.cgutman.usbip.service.AttachedDeviceContext;
11+
1012
public class UsbControlHelper {
1113

1214
private static final int GET_DESCRIPTOR_REQUEST_TYPE = 0x80;
@@ -71,31 +73,71 @@ public static boolean clearHaltCondition(UsbDeviceConnection devConn, UsbEndpoin
7173
return true;
7274
}
7375

74-
public static boolean handleInternalControlTransfer(UsbDevice dev, UsbDeviceConnection devConn, int requestType, int request, int value, int index) {
76+
public static boolean handleInternalControlTransfer(AttachedDeviceContext deviceContext, int requestType, int request, int value, int index) {
7577
// Mask out possible sign expansions
7678
requestType &= 0xFF;
7779
request &= 0xFF;
7880
value &= 0xFFFF;
7981
index &= 0xFFFF;
8082

8183
if (requestType == SET_CONFIGURATION_REQUEST_TYPE && request == SET_CONFIGURATION_REQUEST) {
82-
for (int i = 0; i < dev.getConfigurationCount(); i++) {
83-
UsbConfiguration config = dev.getConfiguration(i);
84+
System.out.println("Handling SET_CONFIGURATION via Android API");
85+
86+
for (int i = 0; i < deviceContext.device.getConfigurationCount(); i++) {
87+
UsbConfiguration config = deviceContext.device.getConfiguration(i);
8488
if (config.getId() == value) {
85-
devConn.setConfiguration(config);
86-
System.out.println("Handled SET_CONFIGURATION via Android API");
89+
// If we have a current config, we need unclaim all interfaces to allow the
90+
// configuration change to work properly.
91+
if (deviceContext.activeConfiguration != null) {
92+
System.out.println("Unclaiming all interfaces from old configuration: "+deviceContext.activeConfiguration.getId());
93+
for (int j = 0; j < deviceContext.activeConfiguration.getInterfaceCount(); j++) {
94+
UsbInterface iface = deviceContext.activeConfiguration.getInterface(j);
95+
deviceContext.devConn.releaseInterface(iface);
96+
}
97+
}
98+
99+
if (!deviceContext.devConn.setConfiguration(config)) {
100+
// This can happen for certain types of devices where Android itself
101+
// has set the configuration for us. Let's just hope that whatever the
102+
// client wanted is also what Android selected :/
103+
System.err.println("Failed to set configuration! Proceeding anyway!");
104+
}
105+
106+
// This is now the active configuration
107+
deviceContext.activeConfiguration = config;
108+
109+
System.out.println("Claiming all interfaces from new configuration: "+deviceContext.activeConfiguration.getId());
110+
for (int j = 0; j < deviceContext.activeConfiguration.getInterfaceCount(); j++) {
111+
UsbInterface iface = deviceContext.activeConfiguration.getInterface(j);
112+
if (!deviceContext.devConn.claimInterface(iface, true)) {
113+
System.err.println("Unable to claim interface: "+iface.getId());
114+
}
115+
}
116+
87117
return true;
88118
}
89119
}
120+
121+
System.err.printf("SET_CONFIGURATION specified invalid configuration: %d\n", value);
90122
}
91123
else if (requestType == SET_INTERFACE_REQUEST_TYPE && request == SET_INTERFACE_REQUEST) {
92-
for (int i = 0; i < dev.getInterfaceCount(); i++) {
93-
UsbInterface iface = dev.getInterface(i);
94-
if (iface.getId() == index && iface.getAlternateSetting() == value) {
95-
devConn.setInterface(iface);
96-
System.out.println("Handled SET_INTERFACE via Android API");
97-
return true;
124+
System.out.println("Handling SET_INTERFACE via Android API");
125+
126+
if (deviceContext.activeConfiguration != null) {
127+
for (int i = 0; i < deviceContext.activeConfiguration.getInterfaceCount(); i++) {
128+
UsbInterface iface = deviceContext.activeConfiguration.getInterface(i);
129+
if (iface.getId() == index && iface.getAlternateSetting() == value) {
130+
if (!deviceContext.devConn.setInterface(iface)) {
131+
System.err.println("Unable to set interface: "+iface.getId());
132+
}
133+
return true;
134+
}
98135
}
136+
137+
System.err.printf("SET_INTERFACE specified invalid interface: %d %d\n", index, value);
138+
}
139+
else {
140+
System.err.println("Attempted to use SET_INTERFACE before SET_CONFIGURATION!");
99141
}
100142
}
101143

0 commit comments

Comments
 (0)