Search code examples
androidcaudiojava-native-interfaceusb

ioctl returns ENOENT trying to post URB request to Isoc device endpoint (accessed from android's JNI)


Looks like I missing some spice in Android/JNI...

I'm trying to access custom composite USB device, connected to regular (not rooted) Android device over OTG USB. Device exposes several interfaces, Audio Streaming and CDC terminal among of them, enumerates fine and Audio is played when used by OS.

When I try to access this device from my app, with CDC no problems at all. I can successfully claimInterface in SDK's context and access its endpoints from separate thread, started in JNI environment.

char read_buf[64];
struct usbdevfs_bulktransfer  ctrl;
memset(&ctrl, 0, sizeof(ctrl));
int fd = ((thread_arg_t*)arg)->fd;
ctrl.ep = ((thread_arg_t*)arg)->ep_in;
ctrl.len = sizeof(read_buf);
ctrl.data = read_buf;
ctrl.timeout = 0;

...

int ret = ioctl(fd, USBDEVFS_BULK, &ctrl);

So, it reads data, coming from CDC part of USB device pretty well...

But when it comes to Audio interfaces, something definitely goes wrong... I can claimInterface exclusive access to interface, exposing its Isoc endpoints, and it returns true, but when I try to stream audio data via appropriate endpoint from my separate thread, ioctl returns ENOENT error in errno:

int fd = ((thread_arg_t*)arg)->fd;
struct usbdevfs_urb *urb = (struct usbdevfs_urb*)malloc(sizeof(struct usbdevfs_urb) + sizeof(struct usbdevfs_iso_packet_desc));

memset(urb, 0, sizeof(struct usbdevfs_urb) + sizeof(struct usbdevfs_iso_packet_desc));
uint8_t empty_buffer[192] = {0};

urb->type                = USBDEVFS_URB_TYPE_ISO;
urb->endpoint            = ((thread_arg_t*)arg)->ep_out;
urb->status              = -1;
urb->flags               = USBDEVFS_URB_ISO_ASAP;
urb->buffer              = empty_buffer;
urb->buffer_length       = 192;
urb->actual_length       = 0;
urb->start_frame         = 0;
urb->number_of_packets   = 1;
urb->error_count         = 0;
urb->signr               = 0;
urb->usercontext         = empty_buffer

struct usbdevfs_iso_packet_desc *iso_desc = &urb->iso_frame_desc[0];
iso_desc->length        = 192;
iso_desc->actual_length = 0;
iso_desc->status        = -1;

.....

int ret = ioctl(fd, USBDEVFS_SUBMITURB, urb);

Any clues what I'm missing?


Solution

  • the outcome of hours of debugging is not good... problem is inside Android's API. When you claimInterface, asking Android to force binded driver disconnection, it will disconnect the driver from your interface ONLY in some particular case... (according to sources). And if it doesn't, you woun't have exclusive use of interface. The only solution I could employ is explicit call of ioctl():

    usbdevfs_ioctl command;
    command.ifno        = 0;
    command.ioctl_code  = USBDEVFS_DISCONNECT;
    command.data        = NULL;
    ioctl(fd, USBDEVFS_IOCTL, &command );
    
    command.ifno        = 1;
    command.ioctl_code  = USBDEVFS_DISCONNECT;
    command.data        = NULL;
    ioctl(fd, USBDEVFS_IOCTL, &command );
    

    and it is REALLY necessary to switch interface altsetting then

    struct usbdevfs_setinterface setif;
    setif.altsetting = 1;
    setif.interface = 1;
    ioctl(fd, USBDEVFS_SETINTERFACE, &setif);
    

    Otherwise, endpoints will be unavailable as in initial post (at lease in my USB device)