mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
uvcvideo: uvc_scan_fallback() for webcams with broken chain
[ Upstream commit e950267ab8 ]
Some devices have invalid baSourceID references, causing uvc_scan_chain()
to fail, but if we just take the entities we can find and put them
together in the most sensible chain we can think of, turns out they do
work anyway. Note: This heuristic assumes there is a single chain.
At the time of writing, devices known to have such a broken chain are
- Acer Integrated Camera (5986:055a)
- Realtek rtl157a7 (0bda:57a7)
Signed-off-by: Henrik Ingo <henrik.ingo@avoinelama.fi>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
e627116c0c
commit
bd2de45031
@@ -1595,6 +1595,114 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain)
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev)
|
||||||
|
{
|
||||||
|
struct uvc_video_chain *chain;
|
||||||
|
|
||||||
|
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
||||||
|
if (chain == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&chain->entities);
|
||||||
|
mutex_init(&chain->ctrl_mutex);
|
||||||
|
chain->dev = dev;
|
||||||
|
v4l2_prio_init(&chain->prio);
|
||||||
|
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fallback heuristic for devices that don't connect units and terminals in a
|
||||||
|
* valid chain.
|
||||||
|
*
|
||||||
|
* Some devices have invalid baSourceID references, causing uvc_scan_chain()
|
||||||
|
* to fail, but if we just take the entities we can find and put them together
|
||||||
|
* in the most sensible chain we can think of, turns out they do work anyway.
|
||||||
|
* Note: This heuristic assumes there is a single chain.
|
||||||
|
*
|
||||||
|
* At the time of writing, devices known to have such a broken chain are
|
||||||
|
* - Acer Integrated Camera (5986:055a)
|
||||||
|
* - Realtek rtl157a7 (0bda:57a7)
|
||||||
|
*/
|
||||||
|
static int uvc_scan_fallback(struct uvc_device *dev)
|
||||||
|
{
|
||||||
|
struct uvc_video_chain *chain;
|
||||||
|
struct uvc_entity *iterm = NULL;
|
||||||
|
struct uvc_entity *oterm = NULL;
|
||||||
|
struct uvc_entity *entity;
|
||||||
|
struct uvc_entity *prev;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start by locating the input and output terminals. We only support
|
||||||
|
* devices with exactly one of each for now.
|
||||||
|
*/
|
||||||
|
list_for_each_entry(entity, &dev->entities, list) {
|
||||||
|
if (UVC_ENTITY_IS_ITERM(entity)) {
|
||||||
|
if (iterm)
|
||||||
|
return -EINVAL;
|
||||||
|
iterm = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UVC_ENTITY_IS_OTERM(entity)) {
|
||||||
|
if (oterm)
|
||||||
|
return -EINVAL;
|
||||||
|
oterm = entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iterm == NULL || oterm == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Allocate the chain and fill it. */
|
||||||
|
chain = uvc_alloc_chain(dev);
|
||||||
|
if (chain == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (uvc_scan_chain_entity(chain, oterm) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
prev = oterm;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add all Processing and Extension Units with two pads. The order
|
||||||
|
* doesn't matter much, use reverse list traversal to connect units in
|
||||||
|
* UVC descriptor order as we build the chain from output to input. This
|
||||||
|
* leads to units appearing in the order meant by the manufacturer for
|
||||||
|
* the cameras known to require this heuristic.
|
||||||
|
*/
|
||||||
|
list_for_each_entry_reverse(entity, &dev->entities, list) {
|
||||||
|
if (entity->type != UVC_VC_PROCESSING_UNIT &&
|
||||||
|
entity->type != UVC_VC_EXTENSION_UNIT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (entity->num_pads != 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (uvc_scan_chain_entity(chain, entity) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
prev->baSourceID[0] = entity->id;
|
||||||
|
prev = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uvc_scan_chain_entity(chain, iterm) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
prev->baSourceID[0] = iterm->id;
|
||||||
|
|
||||||
|
list_add_tail(&chain->list, &dev->chains);
|
||||||
|
|
||||||
|
uvc_trace(UVC_TRACE_PROBE,
|
||||||
|
"Found a video chain by fallback heuristic (%s).\n",
|
||||||
|
uvc_print_chain(chain));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
kfree(chain);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan the device for video chains and register video devices.
|
* Scan the device for video chains and register video devices.
|
||||||
*
|
*
|
||||||
@@ -1617,15 +1725,10 @@ static int uvc_scan_device(struct uvc_device *dev)
|
|||||||
if (term->chain.next || term->chain.prev)
|
if (term->chain.next || term->chain.prev)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
chain = uvc_alloc_chain(dev);
|
||||||
if (chain == NULL)
|
if (chain == NULL)
|
||||||
return -ENOMEM;
|
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;
|
term->flags |= UVC_ENTITY_FLAG_DEFAULT;
|
||||||
|
|
||||||
if (uvc_scan_chain(chain, term) < 0) {
|
if (uvc_scan_chain(chain, term) < 0) {
|
||||||
@@ -1639,6 +1742,9 @@ static int uvc_scan_device(struct uvc_device *dev)
|
|||||||
list_add_tail(&chain->list, &dev->chains);
|
list_add_tail(&chain->list, &dev->chains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (list_empty(&dev->chains))
|
||||||
|
uvc_scan_fallback(dev);
|
||||||
|
|
||||||
if (list_empty(&dev->chains)) {
|
if (list_empty(&dev->chains)) {
|
||||||
uvc_printk(KERN_INFO, "No valid video chain found.\n");
|
uvc_printk(KERN_INFO, "No valid video chain found.\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user