In Android, I'm using a class (UsbIso.java) to transfer data in an isochronous way from a USB device attached. Since Android doesn't natively support isochronous transfers, I had to use the USBFS native Linux mechanism through the JNA library to make the proper ioctl calls.
In Android devices with 32-bit architecture (armeabi, armeabi-v7a) everything works properly. In Android devices with 64-bit architecture (arm64-v8a) the ioctl call to reap URB (USBDEVFS_REAPURB, inside the reapRequest method, see related code below) returns error 14, bad address. I guess that this is caused either by the USBDEVFS_REAPURB parameter or by the PointerByReference parameter, which points to a non-valid virtual address, but I have no clue about how to solve it.
The related code in the UsbIso.java class that causes this error is this:
public Request reapRequest (boolean wait) throws IOException {
PointerByReference urbPointer = new PointerByReference();
int func = wait ? USBDEVFS_REAPURB : USBDEVFS_REAPURBNDELAY;
int rc;
try {
rc = libc.ioctl(fileDescriptor, func, urbPointer); // <-- Error 14, bad address
} catch (LastErrorException e) {
if (e.getErrorCode() == EAGAIN && !wait) {
return null;
}
}
...
}
You are using source code optimized for 32-bit:
// Note: The layout and size of the USBFS structures matches that of Linux Kernel 3.2 and 3.14
// for ARM 32 bit. For other environments (X86, 64 bit, future Linux kernels), it might be
// necessary to adjust some values.
While JNA would normally adjust a structure mapping for 32- vs. 64- bit, this code thinks JNA is too slow and manually maps those offsets:
// This class is modeled after struct usbdevfs_urb in <linuxKernel>/include/linux/usbdevice_fs.h
// At first I implemented the URB structure directly using com.sun.jna.Structure, but that was extremely slow.
// Therefore byte offsets are now used to access the fields of the structure.
If you look at the structure mapping for usbdevfs_urb
there are 3 pointer fields that need adjusting from a 4-byte offset to an 8-byte offset. For example, the 5th field buffer
changes from 4 bytes to 8 bytes, so this code breaks:
public void setBuffer (Pointer buffer) {
urbBuf.putInt(12, (int)Pointer.nativeValue(buffer)); }
public void setBufferLength (int bufferLength) {
urbBuf.putInt(16, bufferLength); }
In particular, the putInt(12, (int) ...)
should probably be putLong(12, ...)
and the 16 in the next call should be 20 (and so on adding 4 to the remaining offsets.)
The last two fields are also 8 byte vs. 4 byte, so the setUserContext()
and getUserContext()
need to deal with long
rather than int
and the urbBaseSize
needs incrementing from 44 to 52 (+4 for the buffer
, +4 for the userContext
.
I see a few other int
variables representing memory addresses that would need to become long
s. There may be other changes required that I've missed.