Android with native library - interface a CDC usb device - can't use fd in native library

806 views Asked by At

I'am trying to make a android tablet communicate with a USB device (STM32 Nucleo). I developped a sketch available on github :

  • the mobile application is developped with Xamarin and VS Studio. It acts as usb host
  • the STM32 Nucleo application is developped with STMCube and uses it's USB stack to act as a CDC device.
  • I developped a small master/slave protocol to echange "register" id/value. The device is the slave.

I managed to make it work using the Android.hardware.usb API. But I must use a native C shared object library for communication as it used on other platform, and that's where I am having trouble now. I embbeded the library using swig and build it with VS Studio. I tried two ways to make the library communicate with the device :

  • From the Android side get permission and fd and pass it to the library that does standard read/write opĂ©rations.
  • From the Android do the same as above, but also pass with fd the endpoints numbers that uses the linux usbdevice_fs API to call bulk transfers; more o less like presented at that question.

Both methods fail and returns an error about the fd that does not exist. I checked the fd and and enpoints values, they're same as the Android. I launched the android usb device monitor, I can't find the created fd. I can't use any android shell like termux to list the process /proc tree. But still I can use tehm in the Android side.

From the community I checked that passing fd to the native library is the right method. I don't know what to do now, is there some more permission to ask ?

Below is how I retrieve the fd :

               _devHandle.usbdev = _devHandle.usbManager.DeviceList[name];
            // Ask for permission to access the created device
            if (!_devHandle.usbManager.HasPermission(_devHandle.usbdev))
            {
                PendingIntent pi = PendingIntent.GetBroadcast((ContextWrapper)_context, 0, new Intent(ACTION_USB_PERMISSION), 0);
                _devHandle.usbManager.RequestPermission(_devHandle.usbdev, pi);
                /* We check the permission was given
                 */
                if (!_devHandle.usbManager.HasPermission(_devHandle.usbdev))
                {
                    // Loose !
                    Log.Debug("pandavcom", "FAILED : did not have persmission to open device" + _devHandle.usbdev.DeviceName);
                    return;
                }
            }
            // Now open the port, with  the USB Manager : we get the fd/enpoints and pass it to library, no more
            _devHandle.connection = _devHandle.usbManager.OpenDevice(_devHandle.usbdev);
            if (_devHandle.connection != null)
            {
                if (OpenInterface(_devHandle.usbdev, _devHandle.connection, ref _devHandle.usbIface, ref _devHandle.ep_in, ref _devHandle.ep_out) == 0)
                {
                    _devHandle.fd = _devHandle.connection.FileDescriptor;
                    Log.Debug("pandavcom", "opened device endpoint" + _devHandle.usbdev.DeviceName + "with descriptor: " + _devHandle.fd);
                }
                else
                {
                    Log.Debug("pandavcom", "FAILED : open device endpoint" + _devHandle.usbdev.DeviceName);
                }
            }

capture of the mobile application failing to use the native library

1

There are 1 answers

0
Selso Liberado On

So I made it working, thanks tho the guys of the libusb mailing list. Actually the mad resistor libusb could only send data to device. Now with the officiel libusb there's a way to do it, that is still recent.

So the process is :

  • get with android the device permission and the file descriptor

  • pass the file descriptor to your native code and execute this step to initliaze libusb on the fd :

     libusb_context *ctx;
     enum libusb_error rc;
     rc = libusb_set_option(&ctx, LIBUSB_OPTION_WEAK_AUTHORITY, NULL);
     if (rc != LIBUSB_SUCCESS) {
         __android_log_print(ANDROID_LOG_ERROR, TAG,"libusb_init failed: %d\n", ret);
         return -1;
     }
     ret = libusb_init(&ctx);
     if (ret < 0) {
         __android_log_print(ANDROID_LOG_INFO, TAG,
                             "libusb_init failed: %d\n", ret);
         return 1;
     }
     libusb_device_handle *devh = NULL;
     ret = libusb_wrap_sys_device(NULL, (intptr_t)FD, &devh);
     if (ret < 0) {
         __android_log_print(ANDROID_LOG_INFO, TAG,
                             "libusb_wrap_sys_device failed: %d\n", ret);
         return 2;
     }
     else if (devh == NULL) {
         __android_log_print(ANDROID_LOG_INFO, TAG,
                             "libusb_wrap_sys_device returned invalid handle\n");
         return 3;
     }
    

    (FD = FileDescriptor from Android.USBManager)

The code above was sent over the mailing. An example of integration is available on my github project. I'm still validating this solution on more Android tablet.