ioctl 返回 ENOENT 尝试将 URB 请求发布到 Isoc 设备端点(从 android 的 JNI 访问)

Posted

技术标签:

【中文标题】ioctl 返回 ENOENT 尝试将 URB 请求发布到 Isoc 设备端点(从 android 的 JNI 访问)【英文标题】:ioctl returns ENOENT trying to post URB request to Isoc device endpoint (accessed from android's JNI) 【发布时间】:2018-04-07 14:15:31 【问题描述】:

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

我正在尝试访问自定义复合 USB 设备,通过 OTG USB 连接到常规(未植根)Android 设备。 Device对外暴露了几个接口,其中有Audio Streaming和CDC终端,在OS使用的时候可以很好的枚举和播放Audio。

当我尝试从我的应用程序访问此设备时,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 端点,并且它返回 true,但是当我尝试通过适当的端点从我的单独的端点流式传输音频数据时线程,ioctl 在 errno 中返回 ENOENT 错误:

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

任何线索我错过了什么?

【问题讨论】:

【参考方案1】:

几个小时的调试结果并不好...问题出在 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);

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

【讨论】:

以上是关于ioctl 返回 ENOENT 尝试将 URB 请求发布到 Isoc 设备端点(从 android 的 JNI 访问)的主要内容,如果未能解决你的问题,请参考以下文章

IOCTL返回No such device

WDF 内部 IOCTL 不返回输出

Linux ioctl 返回值由谁解释?

如何修复 ioctl 请求阻止设备的“无效参数”

ethtool ioctl 返回未填充的 ethtool_link_settings

在 Java 中区分 ESTALE 和 ENOENT 的最佳方法