Commit 8e113595 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

V4L/DVB (12379): uvcvideo: Multiple streaming interfaces support

Restructure the UVC descriptors parsing code to handle multiple streaming
interfaces. The driver now creates a uvc_video_chain instance for each chain
detected in the UVC control interface descriptors, and tries to register one
video device per streaming endpoint.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 35f02a68
......@@ -729,7 +729,7 @@ static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
}
}
struct uvc_control *uvc_find_control(struct uvc_video_device *video,
struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
__u32 v4l2_id, struct uvc_control_mapping **mapping)
{
struct uvc_control *ctrl = NULL;
......@@ -742,17 +742,17 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video,
v4l2_id &= V4L2_CTRL_ID_MASK;
/* Find the control. */
__uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next);
__uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next)
return ctrl;
list_for_each_entry(entity, &video->iterms, chain) {
list_for_each_entry(entity, &chain->iterms, chain) {
__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next)
return ctrl;
}
list_for_each_entry(entity, &video->extensions, chain) {
list_for_each_entry(entity, &chain->extensions, chain) {
__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next)
return ctrl;
......@@ -765,7 +765,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video,
return ctrl;
}
int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_queryctrl *v4l2_ctrl)
{
struct uvc_control *ctrl;
......@@ -775,7 +775,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
__u8 *data;
int ret;
ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping);
ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
if (ctrl == NULL)
return -EINVAL;
......@@ -793,9 +793,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
ret = uvc_query_ctrl(video->dev, UVC_GET_DEF, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, data,
ctrl->info->size);
ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
chain->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size);
if (ret < 0)
goto out;
v4l2_ctrl->default_value =
......@@ -831,25 +831,25 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
}
if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
ret = uvc_query_ctrl(video->dev, UVC_GET_MIN, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, data,
ctrl->info->size);
ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
chain->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size);
if (ret < 0)
goto out;
v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
ret = uvc_query_ctrl(video->dev, UVC_GET_MAX, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, data,
ctrl->info->size);
ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
chain->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size);
if (ret < 0)
goto out;
v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
ret = uvc_query_ctrl(video->dev, UVC_GET_RES, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, data,
ctrl->info->size);
ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
chain->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size);
if (ret < 0)
goto out;
v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data);
......@@ -886,9 +886,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
* (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
* control lock.
*/
int uvc_ctrl_begin(struct uvc_video_device *video)
int uvc_ctrl_begin(struct uvc_video_chain *chain)
{
return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0;
return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0;
}
static int uvc_ctrl_commit_entity(struct uvc_device *dev,
......@@ -938,34 +938,34 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
return 0;
}
int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback)
int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback)
{
struct uvc_entity *entity;
int ret = 0;
/* Find the control. */
ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback);
ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback);
if (ret < 0)
goto done;
list_for_each_entry(entity, &video->iterms, chain) {
ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
list_for_each_entry(entity, &chain->iterms, chain) {
ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0)
goto done;
}
list_for_each_entry(entity, &video->extensions, chain) {
ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
list_for_each_entry(entity, &chain->extensions, chain) {
ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0)
goto done;
}
done:
mutex_unlock(&video->ctrl_mutex);
mutex_unlock(&chain->ctrl_mutex);
return ret;
}
int uvc_ctrl_get(struct uvc_video_device *video,
int uvc_ctrl_get(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl)
{
struct uvc_control *ctrl;
......@@ -974,13 +974,13 @@ int uvc_ctrl_get(struct uvc_video_device *video,
unsigned int i;
int ret;
ctrl = uvc_find_control(video, xctrl->id, &mapping);
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
return -EINVAL;
if (!ctrl->loaded) {
ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector,
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
chain->dev->intfnum, ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
if (ret < 0)
......@@ -1005,7 +1005,7 @@ int uvc_ctrl_get(struct uvc_video_device *video,
return 0;
}
int uvc_ctrl_set(struct uvc_video_device *video,
int uvc_ctrl_set(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl)
{
struct uvc_control *ctrl;
......@@ -1013,7 +1013,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
s32 value = xctrl->value;
int ret;
ctrl = uvc_find_control(video, xctrl->id, &mapping);
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
return -EINVAL;
......@@ -1028,8 +1028,8 @@ int uvc_ctrl_set(struct uvc_video_device *video,
memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
0, ctrl->info->size);
} else {
ret = uvc_query_ctrl(video->dev, UVC_GET_CUR,
ctrl->entity->id, video->dev->intfnum,
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
ctrl->entity->id, chain->dev->intfnum,
ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
......@@ -1058,7 +1058,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
* Dynamic controls
*/
int uvc_xu_ctrl_query(struct uvc_video_device *video,
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control *xctrl, int set)
{
struct uvc_entity *entity;
......@@ -1068,7 +1068,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
int ret;
/* Find the extension unit. */
list_for_each_entry(entity, &video->extensions, chain) {
list_for_each_entry(entity, &chain->extensions, chain) {
if (entity->id == xctrl->unit)
break;
}
......@@ -1107,7 +1107,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
(!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR)))
return -EINVAL;
if (mutex_lock_interruptible(&video->ctrl_mutex))
if (mutex_lock_interruptible(&chain->ctrl_mutex))
return -ERESTARTSYS;
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
......@@ -1120,8 +1120,8 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
goto out;
}
ret = uvc_query_ctrl(video->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
xctrl->unit, video->dev->intfnum, xctrl->selector,
ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
xctrl->unit, chain->dev->intfnum, xctrl->selector,
data, xctrl->size);
if (ret < 0)
goto out;
......@@ -1137,7 +1137,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
xctrl->size);
mutex_unlock(&video->ctrl_mutex);
mutex_unlock(&chain->ctrl_mutex);
return ret;
}
......
......@@ -276,8 +276,20 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
return NULL;
}
static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
{
struct uvc_streaming *stream;
list_for_each_entry(stream, &dev->streams, list) {
if (stream->header.bTerminalLink == id)
return stream;
}
return NULL;
}
/* ------------------------------------------------------------------------
* Descriptors handling
* Descriptors parsing
*/
static int uvc_parse_format(struct uvc_device *dev,
......@@ -1160,101 +1172,36 @@ static int uvc_parse_control(struct uvc_device *dev)
}
/* ------------------------------------------------------------------------
* USB probe and disconnect
* UVC device scan
*/
/*
* Unregister the video devices.
*/
static void uvc_unregister_video(struct uvc_device *dev)
{
struct uvc_streaming *streaming;
list_for_each_entry(streaming, &dev->streams, list) {
if (streaming->vdev == NULL)
continue;
if (streaming->vdev->minor == -1)
video_device_release(streaming->vdev);
else
video_unregister_device(streaming->vdev);
streaming->vdev = NULL;
}
}
static int uvc_register_video(struct uvc_device *dev,
struct uvc_streaming *stream)
{
struct video_device *vdev;
struct uvc_entity *term;
int ret;
if (uvc_trace_param & UVC_TRACE_PROBE) {
uvc_printk(KERN_INFO, "Found a valid video chain (");
list_for_each_entry(term, &dev->video.iterms, chain) {
printk("%d", term->id);
if (term->chain.next != &dev->video.iterms)
printk(",");
}
printk(" -> %d).\n", dev->video.oterm->id);
}
/* Initialize the streaming interface with default streaming
* parameters.
*/
ret = uvc_video_init(stream);
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to initialize the device "
"(%d).\n", ret);
return ret;
}
/* Register the device with V4L. */
vdev = video_device_alloc();
if (vdev == NULL)
return -1;
/* We already hold a reference to dev->udev. The video device will be
* unregistered before the reference is released, so we don't need to
* get another one.
*/
vdev->parent = &dev->intf->dev;
vdev->minor = -1;
vdev->fops = &uvc_fops;
vdev->release = video_device_release;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
/* Set the driver data before calling video_register_device, otherwise
* uvc_v4l2_open might race us.
*/
stream->vdev = vdev;
video_set_drvdata(vdev, stream);
if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
stream->vdev = NULL;
video_device_release(vdev);
return -1;
}
return 0;
}
/*
* Scan the UVC descriptors to locate a chain starting at an Output Terminal
* and containing the following units:
*
* - one Output Terminal (USB Streaming or Display)
* - one or more Output Terminals (USB Streaming or Display)
* - zero or one Processing Unit
* - zero, one or mode single-input Selector Units
* - zero, one or more single-input Selector Units
* - zero or one multiple-input Selector Units, provided all inputs are
* connected to input terminals
* - zero, one or mode single-input Extension Units
* - one or more Input Terminals (Camera, External or USB Streaming)
*
* A side forward scan is made on each detected entity to check for additional
* extension units.
* The terminal and units must match on of the following structures:
*
* ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0)
* ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ...
* ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n)
*
* +---------+ +---------+ -> OTT_*(0)
* TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ...
* +---------+ +---------+ -> OTT_*(n)
*
* The Processing Unit and Extension Units can be in any order. Additional
* Extension Units connected to the main chain as single-unit branches are
* also supported. Single-input Selector Units are ignored.
*/
static int uvc_scan_chain_entity(struct uvc_video_device *video,
static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
struct uvc_entity *entity)
{
switch (UVC_ENTITY_TYPE(entity)) {
......@@ -1268,20 +1215,20 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return -1;
}
list_add_tail(&entity->chain, &video->extensions);
list_add_tail(&entity->chain, &chain->extensions);
break;
case UVC_VC_PROCESSING_UNIT:
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- PU %d", entity->id);
if (video->processing != NULL) {
if (chain->processing != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple "
"Processing Units in chain.\n");
return -1;
}
video->processing = entity;
chain->processing = entity;
break;
case UVC_VC_SELECTOR_UNIT:
......@@ -1292,13 +1239,13 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
if (entity->selector.bNrInPins == 1)
break;
if (video->selector != NULL) {
if (chain->selector != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
"Units in chain.\n");
return -1;
}
video->selector = entity;
chain->selector = entity;
break;
case UVC_ITT_VENDOR_SPECIFIC:
......@@ -1307,7 +1254,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT %d\n", entity->id);
list_add_tail(&entity->chain, &video->iterms);
list_add_tail(&entity->chain, &chain->iterms);
break;
case UVC_TT_STREAMING:
......@@ -1320,14 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return -1;
}
if (video->sterm != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming "
"entities in chain.\n");
return -1;
}
list_add_tail(&entity->chain, &video->iterms);
video->sterm = entity;
list_add_tail(&entity->chain, &chain->iterms);
break;
default:
......@@ -1339,7 +1279,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return 0;
}
static int uvc_scan_chain_forward(struct uvc_video_device *video,
static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
struct uvc_entity *entity, struct uvc_entity *prev)
{
struct uvc_entity *forward;
......@@ -1350,28 +1290,51 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
found = 0;
while (1) {
forward = uvc_entity_by_reference(video->dev, entity->id,
forward = uvc_entity_by_reference(chain->dev, entity->id,
forward);
if (forward == NULL)
break;
if (UVC_ENTITY_TYPE(forward) != UVC_VC_EXTENSION_UNIT ||
forward == prev)
if (forward == prev)
continue;
if (forward->extension.bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has "
"more than 1 input pin.\n", entity->id);
return -1;
}
switch (UVC_ENTITY_TYPE(forward)) {
case UVC_VC_EXTENSION_UNIT:
if (forward->extension.bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
"has more than 1 input pin.\n",
entity->id);
return -EINVAL;
}
list_add_tail(&forward->chain, &chain->extensions);
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (->");
printk(" XU %d", forward->id);
found = 1;
}
break;
case UVC_OTT_VENDOR_SPECIFIC:
case UVC_OTT_DISPLAY:
case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
case UVC_TT_STREAMING:
if (UVC_ENTITY_IS_ITERM(forward)) {
uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
"terminal %u.\n", forward->id);
return -EINVAL;
}
list_add_tail(&forward->chain, &video->extensions);
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (-> XU");
list_add_tail(&forward->chain, &chain->oterms);
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (->");
printk(" %d", forward->id);
found = 1;
printk(" OT %d", forward->id);
found = 1;
}
break;
}
}
if (found)
......@@ -1380,7 +1343,7 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
return 0;
}
static int uvc_scan_chain_backward(struct uvc_video_device *video,
static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
struct uvc_entity *entity)
{
struct uvc_entity *term;
......@@ -1405,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT");
video->selector = entity;
chain->selector = entity;
for (i = 0; i < entity->selector.bNrInPins; ++i) {
id = entity->selector.baSourceID[i];
term = uvc_entity_by_id(video->dev, id);
term = uvc_entity_by_id(chain->dev, id);
if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
"input %d isn't connected to an "
......@@ -1419,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" %d", term->id);
list_add_tail(&term->chain, &video->iterms);
uvc_scan_chain_forward(video, term, entity);
list_add_tail(&term->chain, &chain->iterms);
uvc_scan_chain_forward(chain, term, entity);
}
if (uvc_trace_param & UVC_TRACE_PROBE)
......@@ -1433,100 +1396,129 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
return id;
}
static int uvc_scan_chain(struct uvc_video_device *video)
static int uvc_scan_chain(struct uvc_video_chain *chain,
struct uvc_entity *oterm)
{
struct uvc_entity *entity, *prev;
int id;
entity = video->oterm;
entity = oterm;
list_add_tail(&entity->chain, &chain->oterms);
uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
video->sterm = entity;
id = entity->output.bSourceID;
while (id != 0) {
prev = entity;
entity = uvc_entity_by_id(video->dev, id);
entity = uvc_entity_by_id(chain->dev, id);
if (entity == NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found reference to "
"unknown entity %d.\n", id);
return -1;
return -EINVAL;
}
if (entity->chain.next || entity->chain.prev) {
uvc_trace(UVC_TRACE_DESCR, "Found reference to "
"entity %d already in chain.\n", id);
return -EINVAL;
}
/* Process entity */
if (uvc_scan_chain_entity(video, entity) < 0)
return -1;
if (uvc_scan_chain_entity(chain, entity) < 0)
return -EINVAL;
/* Forward scan */
if (uvc_scan_chain_forward(video, entity, prev) < 0)
return -1;
if (uvc_scan_chain_forward(chain, entity, prev) < 0)
return -EINVAL;
/* Stop when a terminal is found. */
if (!UVC_ENTITY_IS_UNIT(entity))
if (UVC_ENTITY_IS_TERM(entity))
break;
/* Backward scan */
id = uvc_scan_chain_backward(video, entity);
id = uvc_scan_chain_backward(chain, entity);
if (id < 0)
return id;
}
if (video->sterm == NULL) {
uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
"chain.\n");
return -1;
return 0;
}
static unsigned int uvc_print_terms(struct list_head *terms, char *buffer)
{
struct uvc_entity *term;
unsigned int nterms = 0;
char *p = buffer;
list_for_each_entry(term, terms, chain) {
p += sprintf(p, "%u", term->id);
if (term->chain.next != terms) {
p += sprintf(p, ",");
if (++nterms >= 4) {
p += sprintf(p, "...");
break;
}
}
}
return 0;
return p - buffer;
}
static const char *uvc_print_chain(struct uvc_video_chain *chain)
{
static char buffer[43];
char *p = buffer;
p += uvc_print_terms(&chain->iterms, p);
p += sprintf(p, " -> ");
uvc_print_terms(&chain->oterms, p);
return buffer;
}
/*
* Scan the device for video chains and register video devices.
*
* The driver currently supports a single video device per control interface
* only. The terminal and units must match the following structure:
*
* ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
* TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*
*
* The Extension Units, if present, must have a single input pin. The
* Processing Unit and Extension Units can be in any order. Additional
* Extension Units connected to the main chain as single-unit branches are
* also supported.
* Chains are scanned starting at their output terminals and walked backwards.
*/
static int uvc_scan_device(struct uvc_device *dev)
{
struct uvc_video_chain *chain;
struct uvc_entity *term;
int found = 0;
/* Check if the control interface matches the structure we expect. */
list_for_each_entry(term, &dev->entities, list) {
struct uvc_streaming *stream;
if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
if (!UVC_ENTITY_IS_OTERM(term))
continue;
memset(&dev->video, 0, sizeof dev->video);
mutex_init(&dev->video.ctrl_mutex);
INIT_LIST_HEAD(&dev->video.iterms);
INIT_LIST_HEAD(&dev->video.extensions);
dev->video.oterm = term;
dev->video.dev = dev;
if (uvc_scan_chain(&dev->video) < 0)
/* 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;
list_for_each_entry(stream, &dev->streams, list) {
if (stream->header.bTerminalLink ==
dev->video.sterm->id) {
uvc_register_video(dev, stream);
found = 1;
break;
}
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&chain->iterms);
INIT_LIST_HEAD(&chain->oterms);
INIT_LIST_HEAD(&chain->extensions);
mutex_init(&chain->ctrl_mutex);
chain->dev = dev;
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 (!found) {
if (list_empty(&dev->chains)) {
uvc_printk(KERN_INFO, "No valid video chain found.\n");
return -1;
}
......@@ -1534,6 +1526,133 @@ static int uvc_scan_device(struct uvc_device *dev)
return 0;
}
/* ------------------------------------------------------------------------
* Video device registration and unregistration
*/
/*
* Unregister the video devices.
*/
static void uvc_unregister_video(struct uvc_device *dev)
{
struct uvc_streaming *stream;
list_for_each_entry(stream, &dev->streams, list) {
if (stream->vdev == NULL)
continue;
if (stream->vdev->minor == -1)
video_device_release(stream->vdev);
else
video_unregister_device(stream->vdev);
stream->vdev = NULL;
}
}
static int uvc_register_video(struct uvc_device *dev,
struct uvc_streaming *stream)
{
struct video_device *vdev;
int ret;
/* Initialize the streaming interface with default streaming
* parameters.
*/
ret = uvc_video_init(stream);
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to initialize the device "
"(%d).\n", ret);
return ret;
}
/* Register the device with V4L. */
vdev = video_device_alloc();
if (vdev == NULL) {
uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
ret);
return -ENOMEM;
}
/* We already hold a reference to dev->udev. The video device will be
* unregistered before the reference is released, so we don't need to
* get another one.
*/
vdev->parent = &dev->intf->dev;
vdev->minor = -1;
vdev->fops = &uvc_fops;
vdev->release = video_device_release;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
/* Set the driver data before calling video_register_device, otherwise
* uvc_v4l2_open might race us.
*/
stream->vdev = vdev;
video_set_drvdata(vdev, stream);
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
ret);
stream->vdev = NULL;
video_device_release(vdev);
return ret;
}
return 0;
}
/*
* Register all video devices in all chains.
*/
static int uvc_register_terms(struct uvc_device *dev,
struct uvc_video_chain *chain, struct list_head *terms)
{
struct uvc_streaming *stream;
struct uvc_entity *term;
int ret;
list_for_each_entry(term, terms, chain) {
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
continue;
stream = uvc_stream_by_id(dev, term->id);
if (stream == NULL) {
uvc_printk(KERN_INFO, "No streaming interface found "
"for terminal %u.", term->id);
continue;
}
stream->chain = chain;
ret = uvc_register_video(dev, stream);
if (ret < 0)
return ret;
}
return 0;
}
static int uvc_register_chains(struct uvc_device *dev)
{
struct uvc_video_chain *chain;
int ret;
list_for_each_entry(chain, &dev->chains, list) {
ret = uvc_register_terms(dev, chain, &chain->iterms);
if (ret < 0)
return ret;
ret = uvc_register_terms(dev, chain, &chain->oterms);
if (ret < 0)
return ret;
}
return 0;
}
/* ------------------------------------------------------------------------
* USB probe, disconnect, suspend and resume
*/
/*
* Delete the UVC device.
*
......@@ -1555,7 +1674,7 @@ void uvc_delete(struct kref *kref)
struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
struct list_head *p, *n;
/* Unregister the video device. */
/* Unregister the video devices. */
uvc_unregister_video(dev);
usb_put_intf(dev->intf);
usb_put_dev(dev->udev);
......@@ -1563,6 +1682,12 @@ void uvc_delete(struct kref *kref)
uvc_status_cleanup(dev);
uvc_ctrl_cleanup_device(dev);
list_for_each_safe(p, n, &dev->chains) {
struct uvc_video_chain *chain;
chain = list_entry(p, struct uvc_video_chain, list);
kfree(chain);
}
list_for_each_safe(p, n, &dev->entities) {
struct uvc_entity *entity;
entity = list_entry(p, struct uvc_entity, list);
......@@ -1603,6 +1728,7 @@ static int uvc_probe(struct usb_interface *intf,
return -ENOMEM;
INIT_LIST_HEAD(&dev->entities);
INIT_LIST_HEAD(&dev->chains);
INIT_LIST_HEAD(&dev->streams);
kref_init(&dev->kref);
atomic_set(&dev->users, 0);
......@@ -1644,10 +1770,14 @@ static int uvc_probe(struct usb_interface *intf,
if (uvc_ctrl_init_device(dev) < 0)
goto error;
/* Scan the device for video chains and register video devices. */
/* Scan the device for video chains. */
if (uvc_scan_device(dev) < 0)
goto error;
/* Register video devices. */
if (uvc_register_chains(dev) < 0)
goto error;
/* Save our data pointer in the interface data. */
usb_set_intfdata(intf, dev);
......
......@@ -40,7 +40,7 @@
* table for the controls that can be mapped directly, and handle the others
* manually.
*/
static int uvc_v4l2_query_menu(struct uvc_video_device *video,
static int uvc_v4l2_query_menu(struct uvc_video_chain *chain,
struct v4l2_querymenu *query_menu)
{
struct uvc_menu_info *menu_info;
......@@ -49,7 +49,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video,
u32 index = query_menu->index;
u32 id = query_menu->id;
ctrl = uvc_find_control(video, query_menu->id, &mapping);
ctrl = uvc_find_control(chain, query_menu->id, &mapping);
if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU)
return -EINVAL;
......@@ -451,7 +451,7 @@ static int uvc_v4l2_open(struct file *file)
}
}
handle->video = &stream->dev->video;
handle->chain = stream->chain;
handle->stream = stream;
handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle;
......@@ -498,7 +498,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
struct uvc_video_device *video = handle->video;
struct uvc_video_chain *chain = handle->chain;
struct uvc_streaming *stream = handle->stream;
long ret = 0;
......@@ -525,7 +525,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
/* Get, Set & Query control */
case VIDIOC_QUERYCTRL:
return uvc_query_v4l2_ctrl(video, arg);
return uvc_query_v4l2_ctrl(chain, arg);
case VIDIOC_G_CTRL:
{
......@@ -535,12 +535,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
memset(&xctrl, 0, sizeof xctrl);
xctrl.id = ctrl->id;
ret = uvc_ctrl_begin(video);
if (ret < 0)
ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
ret = uvc_ctrl_get(video, &xctrl);
uvc_ctrl_rollback(video);
ret = uvc_ctrl_get(chain, &xctrl);
uvc_ctrl_rollback(chain);
if (ret >= 0)
ctrl->value = xctrl.value;
break;
......@@ -555,21 +555,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
xctrl.id = ctrl->id;
xctrl.value = ctrl->value;
ret = uvc_ctrl_begin(video);
if (ret < 0)
uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
ret = uvc_ctrl_set(video, &xctrl);
ret = uvc_ctrl_set(chain, &xctrl);
if (ret < 0) {
uvc_ctrl_rollback(video);
uvc_ctrl_rollback(chain);
return ret;
}
ret = uvc_ctrl_commit(video);
ret = uvc_ctrl_commit(chain);
break;
}
case VIDIOC_QUERYMENU:
return uvc_v4l2_query_menu(video, arg);
return uvc_v4l2_query_menu(chain, arg);
case VIDIOC_G_EXT_CTRLS:
{
......@@ -577,20 +577,20 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
ret = uvc_ctrl_begin(video);
if (ret < 0)
ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_get(video, ctrl);
ret = uvc_ctrl_get(chain, ctrl);
if (ret < 0) {
uvc_ctrl_rollback(video);
uvc_ctrl_rollback(chain);
ctrls->error_idx = i;
return ret;
}
}
ctrls->error_idx = 0;
ret = uvc_ctrl_rollback(video);
ret = uvc_ctrl_rollback(chain);
break;
}
......@@ -601,14 +601,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
ret = uvc_ctrl_begin(video);
ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_set(video, ctrl);
ret = uvc_ctrl_set(chain, ctrl);
if (ret < 0) {
uvc_ctrl_rollback(video);
uvc_ctrl_rollback(chain);
ctrls->error_idx = i;
return ret;
}
......@@ -617,31 +617,31 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ctrls->error_idx = 0;
if (cmd == VIDIOC_S_EXT_CTRLS)
ret = uvc_ctrl_commit(video);
ret = uvc_ctrl_commit(chain);
else
ret = uvc_ctrl_rollback(video);
ret = uvc_ctrl_rollback(chain);
break;
}
/* Get, Set & Enum input */
case VIDIOC_ENUMINPUT:
{
const struct uvc_entity *selector = video->selector;
const struct uvc_entity *selector = chain->selector;
struct v4l2_input *input = arg;
struct uvc_entity *iterm = NULL;
u32 index = input->index;
int pin = 0;
if (selector == NULL ||
(video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (index != 0)
return -EINVAL;
iterm = list_first_entry(&video->iterms,
iterm = list_first_entry(&chain->iterms,
struct uvc_entity, chain);
pin = iterm->id;
} else if (pin < selector->selector.bNrInPins) {
pin = selector->selector.baSourceID[index];
list_for_each_entry(iterm, video->iterms.next, chain) {
list_for_each_entry(iterm, chain->iterms.next, chain) {
if (iterm->id == pin)
break;
}
......@@ -662,14 +662,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
u8 input;
if (video->selector == NULL ||
(video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (chain->selector == NULL ||
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
*(int *)arg = 0;
break;
}
ret = uvc_query_ctrl(video->dev, UVC_GET_CUR,
video->selector->id, video->dev->intfnum,
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
chain->selector->id, chain->dev->intfnum,
UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
if (ret < 0)
return ret;
......@@ -685,18 +685,18 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
if (video->selector == NULL ||
(video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (chain->selector == NULL ||
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (input != 1)
return -EINVAL;
break;
}
if (input == 0 || input > video->selector->selector.bNrInPins)
if (input == 0 || input > chain->selector->selector.bNrInPins)
return -EINVAL;
return uvc_query_ctrl(video->dev, UVC_SET_CUR,
video->selector->id, video->dev->intfnum,
return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
chain->selector->id, chain->dev->intfnum,
UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
}
......@@ -1019,10 +1019,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
}
case UVCIOC_CTRL_GET:
return uvc_xu_ctrl_query(video, arg, 0);
return uvc_xu_ctrl_query(chain, arg, 0);
case UVCIOC_CTRL_SET:
return uvc_xu_ctrl_query(video, arg, 1);
return uvc_xu_ctrl_query(chain, arg, 1);
default:
if ((ret = v4l_compat_translate_ioctl(file, cmd, arg,
......
......@@ -128,7 +128,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
if (data == NULL)
return -ENOMEM;
if ((video->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF)
if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF)
return -EIO;
ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
......
......@@ -80,9 +80,11 @@ struct uvc_xu_control {
#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0)
#define UVC_ENTITY_IS_ITERM(entity) \
(((entity)->type & 0x8000) == UVC_TERM_INPUT)
(UVC_ENTITY_IS_TERM(entity) && \
((entity)->type & 0x8000) == UVC_TERM_INPUT)
#define UVC_ENTITY_IS_OTERM(entity) \
(((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
(UVC_ENTITY_IS_TERM(entity) && \
((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
/* ------------------------------------------------------------------------
......@@ -402,10 +404,24 @@ struct uvc_video_queue {
struct list_head irqqueue;
};
struct uvc_video_chain {
struct uvc_device *dev;
struct list_head list;
struct list_head iterms; /* Input terminals */
struct list_head oterms; /* Output terminals */
struct uvc_entity *processing; /* Processing unit */
struct uvc_entity *selector; /* Selector unit */
struct list_head extensions; /* Extension units */
struct mutex ctrl_mutex;
};
struct uvc_streaming {
struct list_head list;
struct uvc_device *dev;
struct video_device *vdev;
struct uvc_video_chain *chain;
atomic_t active;
struct usb_interface *intf;
......@@ -446,18 +462,6 @@ struct uvc_streaming {
__u8 last_fid;
};
struct uvc_video_device {
struct uvc_device *dev;
struct list_head iterms; /* Input terminals */
struct uvc_entity *oterm; /* Output terminal */
struct uvc_entity *sterm; /* USB streaming terminal */
struct uvc_entity *processing;
struct uvc_entity *selector;
struct list_head extensions;
struct mutex ctrl_mutex;
};
enum uvc_device_state {
UVC_DEV_DISCONNECTED = 1,
};
......@@ -480,8 +484,7 @@ struct uvc_device {
__u32 clock_frequency;
struct list_head entities;
struct uvc_video_device video;
struct list_head chains;
/* Video Streaming interfaces */
struct list_head streams;
......@@ -500,7 +503,7 @@ enum uvc_handle_state {
};
struct uvc_fh {
struct uvc_video_device *video;
struct uvc_video_chain *chain;
struct uvc_streaming *stream;
enum uvc_handle_state state;
};
......@@ -618,9 +621,9 @@ extern int uvc_status_suspend(struct uvc_device *dev);
extern int uvc_status_resume(struct uvc_device *dev);
/* Controls */
extern struct uvc_control *uvc_find_control(struct uvc_video_device *video,
extern struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
__u32 v4l2_id, struct uvc_control_mapping **mapping);
extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_queryctrl *v4l2_ctrl);
extern int uvc_ctrl_add_info(struct uvc_control_info *info);
......@@ -630,23 +633,23 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
extern int uvc_ctrl_resume_device(struct uvc_device *dev);
extern void uvc_ctrl_init(void);
extern int uvc_ctrl_begin(struct uvc_video_device *video);
extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback);
static inline int uvc_ctrl_commit(struct uvc_video_device *video)
extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
static inline int uvc_ctrl_commit(struct uvc_video_chain *chain)
{
return __uvc_ctrl_commit(video, 0);
return __uvc_ctrl_commit(chain, 0);
}
static inline int uvc_ctrl_rollback(struct uvc_video_device *video)
static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain)
{
return __uvc_ctrl_commit(video, 1);
return __uvc_ctrl_commit(chain, 1);
}
extern int uvc_ctrl_get(struct uvc_video_device *video,
extern int uvc_ctrl_get(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl);
extern int uvc_ctrl_set(struct uvc_video_device *video,
extern int uvc_ctrl_set(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl);
extern int uvc_xu_ctrl_query(struct uvc_video_device *video,
extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control *ctrl, int set);
/* Utility functions */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment