1

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; 
            }
        }
...

}
moictab
  • 959
  • 6
  • 27

2 Answers2

1

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 longs. There may be other changes required that I've missed.

Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
1

As stated in the last answer of this other question by Peter Stoiber, there exists a class which solves this problem: https://github.com/Peter-St/Android-UVC-Camera/tree/master/app/src/main/java/humer/uvc_camera/UsbIso64

moictab
  • 959
  • 6
  • 27