Android 识别 Camera 报 -110 错误

1
2
版本:Android O、P
Linux 4.9

问题描述

罗技 C270 摄像头插入 Android 单板后报 uvcvideo: Failed to query( GET_ DEF) UVC control2 on unit2:-110 异常错误。

1
2
3
4
5
6
7
8
9
10
usb1-3: new high-speed USB device number 9 using ehci-platform
uvcvideo: Found UVC 1.00 device <unnamed> (046d:0825)
input: UVC Camera (046d:0825) as /devices/platform/soc/f9890000.ehci/usb1/1-3/1-3:1.0/input/input8
usb1-3: HotpLugThread timed out on ep0in len=0/2
uvcvideo: Failed to query (GET_DEF) UVC control2 on unit2:-110 (exp.2).
usb1-3: HotpLugThread timed
uvcvideo: Failed to query (GET_DEF) UVC control5 on unit2:-110 (exp.1).
usb1-3: HotpLugThread timed out onep0in len=0/1
uvcvideo: Failed to query (GET_CUR) UVC control2 on unit1:-110 (exp.1).
usb1-3: set resolution quirk: cval->res = 384

问题原因

1、从 log 分析,Camera 插入单板, USB 枚举完成后,上层开始获取 Camera 支持的功能。如 “GET_ DEF UVC control2 on unit2”,”GET_ DEF UVC control5 on unit2”,就是在获取 video 相关的功能。但获取上述功能描述都失败了,上报 了-110 的错误码,即 timeout / 超时。

2、跟踪代码

log 打印位置在 “../drivers/media/usb/uvc/uvc_video.c +85” 的 uvc_query_ctrl() 函数内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
u8 intfnum, u8 cs, void *data, u16 size)
{
int ret;
u8 error;
u8 tmp;

ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
UVC_CTRL_CONTROL_TIMEOUT);
if (likely(ret == size))
return 0;

uvc_printk(KERN_ERR,
"Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n",
uvc_query_name(query), cs, unit, ret, size);

if (ret != -EPIPE)
return ret;

-110 是 __uvc_query_ctrl() 函数的返回值,ret 。

追踪 __uvc_query_ctrl() 函数,位于 “../drivers/media/usb/uvc/uvc_video.c +33”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static int __uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
u8 intfnum, u8 cs, void *data, u16 size,
int timeout)
{
u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;

pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
: usb_sndctrlpipe(dev->udev, 0);
type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;

return usb_control_msg(dev->udev, pipe, query, type, cs << 8,
unit << 8 | intfnum, data, size, timeout);
}

返回值则来自 usb_control_msg() 函数,追踪 __uvc_query_ctrl() 函数,位于 “../drivers/usb/core/message.c +135”。

1
2
3
4
5
6
7
8
dr->wIndex = cpu_to_le16(index);
dr->wLength = cpu_to_le16(size);

ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);

/* Linger a bit, prior to the next control message. */
if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
msleep(200);

返回值则来自 usb_internal_control_msg() 函数,追踪 usb_internal_control_msg() 函数,位于 “../drivers/usb/core/message.c +85”。

1
2
3
4
5
6
7
8
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
len, usb_api_blocking_completion, NULL);

retv = usb_start_wait_urb(urb, timeout, &length);
if (retv < 0)
return retv;
else
return length;

返回值则来自 usb_start_wait_urb() 函数,追踪 usb_start_wait_urb() 函数,位于 “../drivers/usb/core/message.c +48”。

1
2
3
4
5
6
7
8
retval = usb_submit_urb(urb, GFP_NOIO);
if (unlikely(retval))
goto out;

expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
if (!wait_for_completion_timeout(&ctx.done, expire)) {
usb_kill_urb(urb);
retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);

到这里,我们终于找到了 -110 的真身了,就是 wait_for_completion_timeout(&ctx.done, expire) 的状态 ctx.status 如果是 -ENOENT ,则上报 -ETIMEDOUT 即 -110 。具体意思就是系统等待 usb_submit_urb() (urb,USB 协议对传输数据封装的结构体)的完成,但是等待超时了。

一般 USB timeout 会设置为 5000ms,所以我倒追 timeout 值,它就是 __uvc_query_ctrl 函数的

UVC_CTRL_CONTROL_TIMEOUT,定义在 “../drivers/media/usb/uvc/uvcvideo.h +179”

1
#define UVC_CTRL_CONTROL_TIMEOUT        500

发现只有短短的 500ms,真的少。

3、USB 协议分析仪抓包

因不方便贴出 USB 协议分析仪抓取的协议包图片,所以用文字描述代替。

​ | 类型 | | 命令 | 内容 | 时间

SET_INTERFAEC | audio | class | SET_CUR | SAMPLING_FREQ_CONTROL | 1.310sec

SET_INTERFAEC | video | class | GET_DEF | PU_BRIGHTNESS_CONTROL | 1.752ms

类型指该 packet 是谁发出的,命令与内容就类似 JSON 的 key:value,设置当前的某个选项,时间就是花费的时间。具体如 audio 的 packet,设置当前的采样率,花费1.3秒。

4、上层对 Camera 的 audio 和 video 设置都要使用控制传输(Control Transaction),即使用 ep0 端点获取或设置 device 的 descriptor、interface。在上层, audio 和 video 是并行的,但 USB 管道(ep0)只有一条,数据传输都是要排着队的。从协议包来看,Camera 对 audio packet 一直回 NAK,堵塞控制传输管道1.3秒,导致 video 上层超时,进而报 -110 timeout。

5、虽然目前该问题没影响到 Camera 的功能,但也不是不可能的。在特殊情况下,存在 get descriptor 或 set interface 失败,而导致 host 不知道获取失败的 descriptor 对应的能力,例如丢失了 mjpeg 1280*720 30fps 的 descriptor,那 host 就默认device 不支持 mjpeg 1280*720 30fps。

解决方法

1、参考 kernel urb transaction 的超时一般设置是 5000ms。

2、考虑对其他设备的兼容性。

3、对 kernel v4l2 代码的信任。

决定将 UVC_CTRL_CONTROL_TIMEOUT 由 500 改为 1500。

1
#define UVC_CTRL_CONTROL_TIMEOUT        1500

Android 识别 Camera 报 -110 错误
http://xxxdk.xyz/xxx/2019/10/Android-识别-Camera-报-110-错误/
作者
sni
发布于
2019年10月25日
许可协议