ioctl returns ENOENT 试图 post 对 Isoc 设备端点的 URB 请求(从 android 的 JNI 访问)

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

看来我在 Android/JNI 中缺少一些香料...

我正在尝试访问自定义复合 USB 设备,通过 OTG USB 连接到常规(未 root)Android 设备。设备公开了几个接口,其中包括音频流和 CDC 终端,枚举正常,音频在 OS 使用时播放。

当我尝试从我的应用程序访问此设备时,使用 CDC 完全没有问题。我可以在 SDK 的上下文中成功声明接口并从在 JNI 环境中启动的单独线程访问其端点。

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);

因此,它可以很好地读取来自 USB 设备的 CDC 部分的数据...

但是当涉及到音频接口时,肯定出了问题...我可以声明对接口的独占访问权限,公开其 Isoc 端点,它 returns 是的,但是当我尝试流式传输音频数据时通过我单独线程的适当端点,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);

有什么线索吗?

几个小时的调试结果并不理想...问题出在 Android 的 API 中。当你声明接口时,要求 Android 强制断开绑定的驱动程序,它只会在某些特定情况下断开驱动程序与你的接口的连接......(根据消息来源)。如果没有,您将无法独占使用界面。我可以采用的唯一解决方案是显式调用 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 );

然后确实有必要切换界面 altsetting

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

否则,端点将像初始时一样不可用 post(至少在我的 USB 设备中)