Skip to content

Commit af26a4f

Browse files
committed
Accurately fill the UsbIpDevice if we're attached by reading the device descriptor
1 parent 3f4eb76 commit af26a4f

File tree

4 files changed

+185
-8
lines changed

4 files changed

+185
-8
lines changed

src/org/cgutman/usbip/server/UsbIpServer.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,20 @@ private boolean handleRequest(InputStream in, OutputStream out) throws IOExcepti
4040
else if (inMsg.code == ProtoDefs.OP_REQ_IMPORT) {
4141
ImportDeviceRequest imReq = (ImportDeviceRequest)inMsg;
4242
ImportDeviceReply imReply = new ImportDeviceReply();
43-
imReply.devInfo = handler.getDeviceByBusId(imReq.busid);
44-
if (imReply.devInfo == null) {
45-
imReply.status = ProtoDefs.ST_NA;
43+
44+
res = handler.attachToDevice(imReq.busid);
45+
if (res) {
46+
imReply.devInfo = handler.getDeviceByBusId(imReq.busid);
47+
if (imReply.devInfo == null) {
48+
res = false;
49+
}
50+
}
51+
52+
if (res) {
53+
imReply.status = ProtoDefs.ST_OK;
4654
}
4755
else {
48-
res = handler.attachToDevice(imReq.busid);
56+
imReply.status = ProtoDefs.ST_NA;
4957
}
5058
outMsg = imReply;
5159
}

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

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.cgutman.usbip.server.protocol.dev.UsbIpDevicePacket;
2020
import org.cgutman.usbip.server.protocol.dev.UsbIpSubmitUrb;
2121
import org.cgutman.usbip.server.protocol.dev.UsbIpSubmitUrbReply;
22+
import org.cgutman.usbip.usb.DescriptorReader;
23+
import org.cgutman.usbip.usb.UsbDeviceDescriptor;
2224

2325
import android.annotation.SuppressLint;
2426
import android.app.PendingIntent;
@@ -82,19 +84,105 @@ public IBinder onBind(Intent intent) {
8284
// Not currently bindable
8385
return null;
8486
}
87+
88+
// Here we're going to enumerate interfaces and endpoints
89+
// to eliminate possible speeds until we've narrowed it
90+
// down to only 1 which is our speed real speed. In a typical
91+
// USB driver, the host controller knows the real speed but
92+
// we need to derive it without HCI help.
93+
private final static int FLAG_POSSIBLE_SPEED_LOW = 0x01;
94+
private final static int FLAG_POSSIBLE_SPEED_FULL = 0x02;
95+
private final static int FLAG_POSSIBLE_SPEED_HIGH = 0x04;
96+
private int detectSpeed(UsbDevice dev, UsbDeviceDescriptor devDesc) {
97+
int possibleSpeeds = FLAG_POSSIBLE_SPEED_LOW |
98+
FLAG_POSSIBLE_SPEED_FULL |
99+
FLAG_POSSIBLE_SPEED_HIGH;
100+
101+
for (int i = 0; i < dev.getInterfaceCount(); i++) {
102+
UsbInterface iface = dev.getInterface(i);
103+
for (int j = 0; j < iface.getEndpointCount(); j++) {
104+
UsbEndpoint endpoint = iface.getEndpoint(j);
105+
if ((endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) ||
106+
(endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_ISOC)) {
107+
// Low speed devices can't implement bulk or iso endpoints
108+
possibleSpeeds &= ~FLAG_POSSIBLE_SPEED_LOW;
109+
}
110+
111+
if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_CONTROL) {
112+
if (endpoint.getMaxPacketSize() > 8) {
113+
// Low speed devices can't use control transfer sizes larger than 8 bytes
114+
possibleSpeeds &= ~FLAG_POSSIBLE_SPEED_LOW;
115+
}
116+
if (endpoint.getMaxPacketSize() < 64) {
117+
// High speed devices can't use control transfer sizes smaller than 64 bytes
118+
possibleSpeeds &= ~FLAG_POSSIBLE_SPEED_HIGH;
119+
}
120+
}
121+
else if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_INT) {
122+
if (endpoint.getMaxPacketSize() > 8) {
123+
// Low speed devices can't use interrupt transfer sizes larger than 8 bytes
124+
possibleSpeeds &= ~FLAG_POSSIBLE_SPEED_LOW;
125+
}
126+
if (endpoint.getMaxPacketSize() > 64) {
127+
// Full speed devices can't use interrupt transfer sizes larger than 64 bytes
128+
possibleSpeeds &= ~FLAG_POSSIBLE_SPEED_FULL;
129+
}
130+
}
131+
else if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
132+
// A bulk endpoint alone can accurately distiniguish between
133+
// full and high speed devices
134+
if (endpoint.getMaxPacketSize() == 512) {
135+
// High speed devices can only use 512 byte bulk transfers
136+
possibleSpeeds = FLAG_POSSIBLE_SPEED_HIGH;
137+
}
138+
else {
139+
// Otherwise it must be full speed
140+
possibleSpeeds = FLAG_POSSIBLE_SPEED_FULL;
141+
}
142+
}
143+
else if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_ISOC) {
144+
// If the transfer size is 1024, it must be high speed
145+
if (endpoint.getMaxPacketSize() == 1024) {
146+
possibleSpeeds = FLAG_POSSIBLE_SPEED_HIGH;
147+
}
148+
}
149+
}
150+
}
151+
152+
if (devDesc != null) {
153+
if (devDesc.bcdUSB < 0x200) {
154+
// High speed only supported on USB 2.0 or higher
155+
possibleSpeeds &= ~FLAG_POSSIBLE_SPEED_HIGH;
156+
}
157+
}
158+
159+
// Return the lowest speed that we're compatible with
160+
System.out.printf("Speed heuristics for device %d left us with 0x%x\n",
161+
dev.getDeviceId(), possibleSpeeds);
162+
163+
if ((possibleSpeeds & FLAG_POSSIBLE_SPEED_LOW) != 0) {
164+
return UsbIpDevice.USB_SPEED_LOW;
165+
}
166+
else if ((possibleSpeeds & FLAG_POSSIBLE_SPEED_FULL) != 0) {
167+
return UsbIpDevice.USB_SPEED_FULL;
168+
}
169+
else if ((possibleSpeeds & FLAG_POSSIBLE_SPEED_HIGH) != 0) {
170+
return UsbIpDevice.USB_SPEED_HIGH;
171+
}
172+
else {
173+
// Something went very wrong in speed detection
174+
return UsbIpDevice.USB_SPEED_UNKNOWN;
175+
}
176+
}
85177

86178
private UsbDeviceInfo getInfoForDevice(UsbDevice dev) {
87179
UsbDeviceInfo info = new UsbDeviceInfo();
88180
UsbIpDevice ipDev = new UsbIpDevice();
89181

90-
// TODO: Fill bcdDevice, bConfigurationValue, bNumConfigurations,
91-
// and speed from USB configuration descriptor
92-
93182
ipDev.path = dev.getDeviceName();
94183
ipDev.busid = String.format("%d", dev.getDeviceId());
95184
ipDev.busnum = 0;
96185
ipDev.devnum = dev.getDeviceId();
97-
ipDev.speed = UsbIpDevice.USB_SPEED_UNKNOWN;
98186

99187
ipDev.idVendor = (short) dev.getVendorId();
100188
ipDev.idProduct = (short) dev.getProductId();
@@ -121,6 +209,19 @@ private UsbDeviceInfo getInfoForDevice(UsbDevice dev) {
121209
info.interfaces[i].bInterfaceProtocol = (byte) iface.getInterfaceProtocol();
122210
}
123211

212+
AttachedDeviceContext context = connections.get(dev.getDeviceId());
213+
UsbDeviceDescriptor devDesc = null;
214+
if (context != null) {
215+
// Since we're attached already, we can directly query the USB descriptors
216+
// to fill some information that Android's USB API doesn't expose
217+
devDesc = DescriptorReader.readDeviceDescriptor(context.devConn);
218+
219+
ipDev.bcdDevice = devDesc.bcdDevice;
220+
ipDev.bNumConfigurations = devDesc.bNumConfigurations;
221+
}
222+
223+
ipDev.speed = detectSpeed(dev, devDesc);
224+
124225
return info;
125226
}
126227

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.cgutman.usbip.usb;
2+
3+
import android.hardware.usb.UsbDeviceConnection;
4+
5+
public class DescriptorReader {
6+
7+
private static final int GET_DESCRIPTOR_REQUEST_TYPE = 0x80;
8+
private static final int GET_DESCRIPTOR_REQUEST = 0x06;
9+
10+
private static final int DEVICE_DESCRIPTOR_TYPE = 1;
11+
12+
public static UsbDeviceDescriptor readDeviceDescriptor(UsbDeviceConnection devConn) {
13+
byte[] descriptorBuffer = new byte[UsbDeviceDescriptor.DESCRIPTOR_SIZE];
14+
15+
16+
int res = devConn.controlTransfer(GET_DESCRIPTOR_REQUEST_TYPE,
17+
GET_DESCRIPTOR_REQUEST,
18+
(DEVICE_DESCRIPTOR_TYPE << 8) | 0x00, // Devices only have 1 descriptor
19+
0, descriptorBuffer, descriptorBuffer.length, 0);
20+
if (res != UsbDeviceDescriptor.DESCRIPTOR_SIZE) {
21+
return null;
22+
}
23+
24+
return new UsbDeviceDescriptor(descriptorBuffer);
25+
}
26+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.cgutman.usbip.usb;
2+
3+
import java.nio.ByteBuffer;
4+
import java.nio.ByteOrder;
5+
6+
public class UsbDeviceDescriptor {
7+
8+
public byte bLength;
9+
public byte bDescriptorType;
10+
public short bcdUSB;
11+
public byte bDeviceClass;
12+
public byte bDeviceSubClass;
13+
public byte bDeviceProtocol;
14+
public byte bMaxPacketSize;
15+
public short idVendor;
16+
public short idProduct;
17+
public short bcdDevice;
18+
public byte iManufacturer;
19+
public byte iProduct;
20+
public byte iSerialNumber;
21+
public byte bNumConfigurations;
22+
23+
public static final int DESCRIPTOR_SIZE = 18;
24+
25+
public UsbDeviceDescriptor(byte[] data) {
26+
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
27+
bLength = bb.get();
28+
bDescriptorType = bb.get();
29+
bcdUSB = bb.getShort();
30+
bDeviceClass = bb.get();
31+
bDeviceSubClass = bb.get();
32+
bDeviceProtocol = bb.get();
33+
bMaxPacketSize = bb.get();
34+
idVendor = bb.getShort();
35+
idProduct = bb.getShort();
36+
bcdDevice = bb.getShort();
37+
iManufacturer = bb.get();
38+
iProduct = bb.get();
39+
iSerialNumber = bb.get();
40+
bNumConfigurations = bb.get();
41+
}
42+
}

0 commit comments

Comments
 (0)