Linux加载USB摄像头uvcvideo.ko报错:No valid video chain found
摘要:使用正点原子阿尔法开发板(Linux 4.1.15内核)连接某宝购买的免驱USB摄像头时,出现uvcvideo.ko报错"no valid video chain found"。问题原因是摄像头USB描述符中OUTPUT_TERMINAL的bSourceID错误指向空节点而非EXTENSION_UNIT节点。通过修改uvc_probe()函数,强制指定bSourceID为
简述
硬件:正点原子阿尔法开发板;某宝购买的免驱USB摄像头。

开发板环境:出厂固件,内核版本:linux-imx-4.1.15-2.1.0-g06f53e4-v2.1
问题现象:
1. 开发板插入USB摄像头能正常识别,但是uvcvideo.ko 报错No valid video chain found
2. 摄像头接入Ubuntu能正常使用

注: cannot get freq at ep 0x82这里是音频使用的endpoint报错,后面有空再研究。
原因及解决方案
原因
摄像头USB设备描述符中,OUTPUT_TERMINAL的 bSourceID 本该指向上一个描述符节点EXTENSION_UNIT,但是实际指向了空的节点,目测是厂商留下了的bug。最终导致建立不了video chain。
在豆包中再次得到确认:(现在AI可真好用)


最简单的解决方案
添加如下代码:
在uvc_probe()函数能够获取USB的PID和VID,判断是此USB摄像头,直接指定bSourceID到上一个正确的节点,我这里的id是3,指向EXTENSION_UNIT节点

我这里是自己使用,故直接偷懒:

=========================================================================
video chain解析过程
video chain解析过程其实是对USB描述符的VideoControl接口描述符进行遍历
设备描述符(简
Device Descriptor:
Configuration Descriptor:
...
Interface Descriptor:
...
iInterface 5 USB Camera
VideoControl Interface Descriptor:
bDescriptorSubtype 1 (HEADER)
VideoControl Interface Descriptor:
bDescriptorSubtype 2 (INPUT_TERMINAL)
bTerminalID 1 # 当前单元 / 终端id
wTerminalType 0x0201 Camera Sensor
VideoControl Interface Descriptor:
bDescriptorSubtype 5 (PROCESSING_UNIT)
bUnitID 2 # 当前单元 / 终端id
bSourceID 1 # 等于INPUT_TERMINAL的bTerminalID
VideoControl Interface Descriptor:
bDescriptorSubtype 6 (EXTENSION_UNIT)
bUnitID 3 # 当前单元 / 终端id
baSourceID( 0) 2 # 等于PROCESSING_UNIT的bUnitID
VideoControl Interface Descriptor:
bDescriptorSubtype 3 (OUTPUT_TERMINAL)
bTerminalID 5 # 当前单元 / 终端id
bSourceID 4 # 应为3(bUnitID),并指向EXTENSION_UNIT单元
可以看出每一个单元的【bSourceID】都指向上一个【单元/终端】,在代码中也是这样反向遍历的。
遍历过程:
检查到UVC设备后
uvc_probe()
| -> uvc_scan_device() # 扫描VideoControl接口
| -> UVC_ENTITY_IS_OTERM # 寻找 VideoControl中的output terminal终端
| -> uvc_scan_chain() # 反过来查找并添加到video chain链表
| -> uvc_register_chains() # 注册到 /dev/videoX
最终获取到的video chain链表:
OUTPUT_TERMINAL -> EXTENSION_UNIT -> PROCESSING_UNIT
-> INPUT_TERMINAL
源码
static int uvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct uvc_device *dev;
int ret;
if (id->idVendor && id->idProduct)
uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
"(%04x:%04x)\n", udev->devpath, id->idVendor,
id->idProduct);
else
uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
udev->devpath);
/* Allocate memory for the device and initialize it. */
if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&dev->entities);
INIT_LIST_HEAD(&dev->chains);
INIT_LIST_HEAD(&dev->streams);
atomic_set(&dev->nstreams, 0);
atomic_set(&dev->nmappings, 0);
mutex_init(&dev->lock);
dev->udev = usb_get_dev(udev);
dev->intf = usb_get_intf(intf);
dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
dev->quirks = (uvc_quirks_param == -1)
? id->driver_info : uvc_quirks_param;
if (udev->product != NULL)
strlcpy(dev->name, udev->product, sizeof dev->name);
else
snprintf(dev->name, sizeof dev->name,
"UVC Camera (%04x:%04x)",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
/* Parse the Video Class control descriptor. */
if (uvc_parse_control(dev) < 0) {
uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
"descriptors.\n");
goto error;
}
uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
dev->uvc_version >> 8, dev->uvc_version & 0xff,
udev->product ? udev->product : "<unnamed>",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
if (dev->quirks != id->driver_info) {
uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
"parameter for testing purpose.\n", dev->quirks);
uvc_printk(KERN_INFO, "Please report required quirks to the "
"linux-uvc-devel mailing list.\n");
}
/* Register the media and V4L2 devices. */
#ifdef CONFIG_MEDIA_CONTROLLER
dev->mdev.dev = &intf->dev;
strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
if (udev->serial)
strlcpy(dev->mdev.serial, udev->serial,
sizeof(dev->mdev.serial));
strcpy(dev->mdev.bus_info, udev->devpath);
dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
dev->mdev.driver_version = LINUX_VERSION_CODE;
if (media_device_register(&dev->mdev) < 0)
goto error;
dev->vdev.mdev = &dev->mdev;
#endif
if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
goto error;
/* Initialize controls. */
if (uvc_ctrl_init_device(dev) < 0)
goto error;
/* Scan the device for video chains. */
if (uvc_scan_device(dev) < 0)
goto error;
/* Register video device nodes. */
if (uvc_register_chains(dev) < 0)
goto error;
/* Save our data pointer in the interface data. */
usb_set_intfdata(intf, dev);
/* Initialize the interrupt URB. */
if ((ret = uvc_status_init(dev)) < 0) {
uvc_printk(KERN_INFO, "Unable to initialize the status "
"endpoint (%d), status interrupt will not be "
"supported.\n", ret);
}
uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
usb_enable_autosuspend(udev);
return 0;
error:
uvc_unregister_video(dev);
return -ENODEV;
}
static int uvc_scan_device(struct uvc_device *dev)
{
struct uvc_video_chain *chain;
struct uvc_entity *term;
list_for_each_entry(term, &dev->entities, list) {
// 不是输出终端: 跳过
if (!UVC_ENTITY_IS_OTERM(term))
continue;
/* If the terminal is already included in a chain, skip it.
* This can happen for chains that have multiple output
* terminals, where all output terminals beside the first one
* will be inserted in the chain in forward scans.
*/
if (term->chain.next || term->chain.prev)
continue;
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&chain->entities);
mutex_init(&chain->ctrl_mutex);
chain->dev = dev;
v4l2_prio_init(&chain->prio);
term->flags |= UVC_ENTITY_FLAG_DEFAULT;
if (uvc_scan_chain(chain, term) < 0) {
kfree(chain);
continue;
}
uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
uvc_print_chain(chain));
list_add_tail(&chain->list, &dev->chains);
}
if (list_empty(&dev->chains)) {
uvc_printk(KERN_INFO, "No valid video chain found. euan\n");
return -1;
}
return 0;
}
static int uvc_scan_chain(struct uvc_video_chain *chain,
struct uvc_entity *term)
{
struct uvc_entity *entity, *prev;
uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
entity = term;
prev = NULL;
while (entity != NULL) {
/* Entity must not be part of an existing chain */
if (entity->chain.next || entity->chain.prev) {
uvc_trace(UVC_TRACE_DESCR, "Found reference to "
"entity %d already in chain.\n", entity->id);
return -EINVAL;
}
/* Process entity */
if (uvc_scan_chain_entity(chain, entity) < 0)
return -EINVAL;
/* Forward scan */
if (uvc_scan_chain_forward(chain, entity, prev) < 0)
return -EINVAL;
/* Backward scan */
prev = entity;
if (uvc_scan_chain_backward(chain, &entity) < 0)
return -EINVAL;
}
return 0;
}
驱动路径
/lib/modules/4.1.15-g06f53e4/kernel/drivers/media/usb/uvc/uvcvideo.ko
更多推荐


所有评论(0)