Skip to content

Commit 543f875

Browse files
committed
Transfer using devusbfs via JNI rather than Android APIs
The Java methods have significant limitations, such as: - Totally broken handling > 16KB transfers on Oreo and earlier - Unable to retrieve the actual error code without horrible hacks
1 parent 8ce0610 commit 543f875

File tree

6 files changed

+94
-78
lines changed

6 files changed

+94
-78
lines changed

app/src/main/java/org/cgutman/usbip/errno/Errno.java

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.cgutman.usbip.jni;
2+
3+
public class UsbLib {
4+
static {
5+
System.loadLibrary("usblib");
6+
}
7+
8+
public static native int doControlTransfer(int fd, byte requestType, byte request, short value,
9+
short index, byte[] data, int length, int timeout);
10+
11+
public static native int doBulkTransfer(int fd, int endpoint, byte[] data, int timeout);
12+
}

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

Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,31 @@
11
package org.cgutman.usbip.usb;
22

3-
import org.cgutman.usbip.errno.Errno;
3+
import org.cgutman.usbip.jni.UsbLib;
44

5-
import android.hardware.usb.UsbConstants;
65
import android.hardware.usb.UsbDeviceConnection;
76
import android.hardware.usb.UsbEndpoint;
87

98
public class XferUtils {
109

1110
public static int doInterruptTransfer(UsbDeviceConnection devConn, UsbEndpoint endpoint, byte[] buff, int timeout) {
1211
// Interrupt transfers are implemented as one-shot bulk transfers
13-
int res = devConn.bulkTransfer(endpoint, buff,
14-
buff.length, timeout);
15-
if (res < 0) {
16-
res = -Errno.getErrno();
17-
if (res != -110) {
18-
// Don't print for ETIMEDOUT
19-
System.err.println("Interrupt Xfer failed: "+res);
20-
}
12+
int res = UsbLib.doBulkTransfer(devConn.getFileDescriptor(), endpoint.getAddress(), buff, timeout);
13+
if (res < 0 && res != -110) {
14+
// Don't print for ETIMEDOUT
15+
System.err.println("Interrupt Xfer failed: "+res);
2116
}
2217

2318
return res;
2419
}
2520

2621
public static int doBulkTransfer(UsbDeviceConnection devConn, UsbEndpoint endpoint, byte[] buff, int timeout) {
27-
int bytesTransferred = 0;
28-
while (bytesTransferred < buff.length) {
29-
byte[] remainingBuffer = new byte[buff.length - bytesTransferred];
30-
31-
if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
32-
// Copy input data into the new buffer
33-
System.arraycopy(buff, bytesTransferred, remainingBuffer, 0, remainingBuffer.length);
34-
}
35-
36-
int res = devConn.bulkTransfer(endpoint, remainingBuffer,
37-
remainingBuffer.length, timeout);
38-
if (res < 0) {
39-
// Failed transfer terminates the bulk transfer
40-
res = -Errno.getErrno();
41-
if (res != -110) {
42-
// Don't print for ETIMEDOUT
43-
System.err.println("Bulk Xfer failed: "+res);
44-
}
45-
return res;
46-
}
47-
48-
if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
49-
// Copy output data into the original buffer
50-
System.arraycopy(remainingBuffer, 0, buff, bytesTransferred, res);
51-
}
52-
53-
bytesTransferred += res;
54-
55-
if (res < endpoint.getMaxPacketSize()) {
56-
// A packet less than the maximum size for this endpoint
57-
// indicates the transfer has ended
58-
break;
59-
}
22+
int res = UsbLib.doBulkTransfer(devConn.getFileDescriptor(), endpoint.getAddress(), buff, timeout);
23+
if (res < 0 && res != -110) {
24+
// Don't print for ETIMEDOUT
25+
System.err.println("Bulk Xfer failed: "+res);
6026
}
61-
62-
return bytesTransferred;
27+
28+
return res;
6329
}
6430

6531
public static int doControlTransfer(UsbDeviceConnection devConn, int requestType,
@@ -75,14 +41,11 @@ public static int doControlTransfer(UsbDeviceConnection devConn, int requestType
7541
System.out.printf("SETUP: %x %x %x %x %x\n",
7642
requestType, request, value, index, length);
7743

78-
int res = devConn.controlTransfer(requestType, request, value,
79-
index, buff, length, interval);
80-
if (res < 0) {
81-
res = -Errno.getErrno();
82-
if (res != -110) {
83-
// Don't print for ETIMEDOUT
84-
System.err.println("Control Xfer failed: "+res);
85-
}
44+
int res = UsbLib.doControlTransfer(devConn.getFileDescriptor(), (byte)requestType, (byte)request,
45+
(short)value, (short)index, buff, length, interval);
46+
if (res < 0 && res != -110) {
47+
// Don't print for ETIMEDOUT
48+
System.err.println("Control Xfer failed: "+res);
8649
}
8750

8851
return res;

app/src/main/jni/errno/errno_jni.c

Lines changed: 0 additions & 11 deletions
This file was deleted.

app/src/main/jni/errno/Android.mk renamed to app/src/main/jni/usblib/Android.mk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ include $(call all-subdir-makefiles)
66
LOCAL_PATH := $(MY_LOCAL_PATH)
77

88
include $(CLEAR_VARS)
9-
LOCAL_MODULE := errno
10-
LOCAL_SRC_FILES := errno_jni.c
9+
LOCAL_MODULE := usblib
10+
LOCAL_SRC_FILES := usblib_jni.c
1111

1212
include $(BUILD_SHARED_LIBRARY)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include <stdlib.h>
2+
#include <unistd.h>
3+
#include <jni.h>
4+
5+
#include <errno.h>
6+
#include <linux/usbdevice_fs.h>
7+
#include <sys/ioctl.h>
8+
9+
JNIEXPORT jint JNICALL
10+
Java_org_cgutman_usbip_jni_UsbLib_doBulkTransfer(
11+
JNIEnv *env, jclass clazz, jint fd, jint endpoint, jbyteArray data, jint timeout)
12+
{
13+
jbyte* dataPtr = data ? (jbyte*)(*env)->GetPrimitiveArrayCritical(env, data, NULL) : NULL;
14+
jsize dataLen = data ? (*env)->GetArrayLength(env, data) : 0;
15+
16+
struct usbdevfs_bulktransfer xfer = {
17+
.ep = endpoint,
18+
.len = dataLen,
19+
.timeout = timeout,
20+
.data = dataPtr,
21+
};
22+
jint res = TEMP_FAILURE_RETRY(ioctl(fd, USBDEVFS_BULK, &xfer));
23+
if (res < 0) {
24+
res = -errno;
25+
}
26+
27+
// If this is an OUT or a failed IN, use JNI_ABORT to avoid a useless memcpy().
28+
if (dataPtr) {
29+
(*env)->ReleasePrimitiveArrayCritical(env, data, dataPtr,
30+
((endpoint & 0x80) && (res > 0)) ? 0 : JNI_ABORT);
31+
}
32+
33+
return res;
34+
};
35+
36+
JNIEXPORT jint JNICALL
37+
Java_org_cgutman_usbip_jni_UsbLib_doControlTransfer(
38+
JNIEnv *env, jclass clazz, jint fd, jbyte requestType, jbyte request, jshort value,
39+
jshort index, jbyteArray data, jint length, jint timeout)
40+
{
41+
jbyte* dataPtr = data ? (jbyte*)(*env)->GetPrimitiveArrayCritical(env, data, NULL) : NULL;
42+
43+
struct usbdevfs_ctrltransfer xfer = {
44+
.bRequestType = requestType,
45+
.bRequest = request,
46+
.wValue = value,
47+
.wIndex = index,
48+
.wLength = length,
49+
.timeout = timeout,
50+
.data = dataPtr,
51+
};
52+
jint res = TEMP_FAILURE_RETRY(ioctl(fd, USBDEVFS_CONTROL, &xfer));
53+
if (res < 0) {
54+
res = -errno;
55+
}
56+
57+
// If this is an OUT or a failed IN, use JNI_ABORT to avoid a useless memcpy().
58+
if (dataPtr) {
59+
(*env)->ReleasePrimitiveArrayCritical(env, data, dataPtr,
60+
((requestType & 0x80) && (res > 0)) ? 0 : JNI_ABORT);
61+
}
62+
63+
return res;
64+
};

0 commit comments

Comments
 (0)