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, ...@@ -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) __u32 v4l2_id, struct uvc_control_mapping **mapping)
{ {
struct uvc_control *ctrl = NULL; struct uvc_control *ctrl = NULL;
...@@ -742,17 +742,17 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video, ...@@ -742,17 +742,17 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video,
v4l2_id &= V4L2_CTRL_ID_MASK; v4l2_id &= V4L2_CTRL_ID_MASK;
/* Find the control. */ /* 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) if (ctrl && !next)
return ctrl; 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); __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next) if (ctrl && !next)
return ctrl; 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); __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
if (ctrl && !next) if (ctrl && !next)
return ctrl; return ctrl;
...@@ -765,7 +765,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video, ...@@ -765,7 +765,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video,
return ctrl; 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 v4l2_queryctrl *v4l2_ctrl)
{ {
struct uvc_control *ctrl; struct uvc_control *ctrl;
...@@ -775,7 +775,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, ...@@ -775,7 +775,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
__u8 *data; __u8 *data;
int ret; int ret;
ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
if (ctrl == NULL) if (ctrl == NULL)
return -EINVAL; return -EINVAL;
...@@ -793,9 +793,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, ...@@ -793,9 +793,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
ret = uvc_query_ctrl(video->dev, UVC_GET_DEF, ctrl->entity->id, ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, data, chain->dev->intfnum, ctrl->info->selector,
ctrl->info->size); data, ctrl->info->size);
if (ret < 0) if (ret < 0)
goto out; goto out;
v4l2_ctrl->default_value = v4l2_ctrl->default_value =
...@@ -831,25 +831,25 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, ...@@ -831,25 +831,25 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
} }
if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
ret = uvc_query_ctrl(video->dev, UVC_GET_MIN, ctrl->entity->id, ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, data, chain->dev->intfnum, ctrl->info->selector,
ctrl->info->size); data, ctrl->info->size);
if (ret < 0) if (ret < 0)
goto out; goto out;
v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data); v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data);
} }
if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
ret = uvc_query_ctrl(video->dev, UVC_GET_MAX, ctrl->entity->id, ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, data, chain->dev->intfnum, ctrl->info->selector,
ctrl->info->size); data, ctrl->info->size);
if (ret < 0) if (ret < 0)
goto out; goto out;
v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data); v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data);
} }
if (ctrl->info->flags & UVC_CONTROL_GET_RES) { if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
ret = uvc_query_ctrl(video->dev, UVC_GET_RES, ctrl->entity->id, ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, data, chain->dev->intfnum, ctrl->info->selector,
ctrl->info->size); data, ctrl->info->size);
if (ret < 0) if (ret < 0)
goto out; goto out;
v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data); v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data);
...@@ -886,9 +886,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, ...@@ -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 * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
* control lock. * 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, static int uvc_ctrl_commit_entity(struct uvc_device *dev,
...@@ -938,34 +938,34 @@ 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; 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; struct uvc_entity *entity;
int ret = 0; int ret = 0;
/* Find the control. */ /* 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) if (ret < 0)
goto done; goto done;
list_for_each_entry(entity, &video->iterms, chain) { list_for_each_entry(entity, &chain->iterms, chain) {
ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0) if (ret < 0)
goto done; goto done;
} }
list_for_each_entry(entity, &video->extensions, chain) { list_for_each_entry(entity, &chain->extensions, chain) {
ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0) if (ret < 0)
goto done; goto done;
} }
done: done:
mutex_unlock(&video->ctrl_mutex); mutex_unlock(&chain->ctrl_mutex);
return ret; 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 v4l2_ext_control *xctrl)
{ {
struct uvc_control *ctrl; struct uvc_control *ctrl;
...@@ -974,13 +974,13 @@ int uvc_ctrl_get(struct uvc_video_device *video, ...@@ -974,13 +974,13 @@ int uvc_ctrl_get(struct uvc_video_device *video,
unsigned int i; unsigned int i;
int ret; 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) if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
return -EINVAL; return -EINVAL;
if (!ctrl->loaded) { if (!ctrl->loaded) {
ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, ctrl->entity->id, ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector, chain->dev->intfnum, ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size); ctrl->info->size);
if (ret < 0) if (ret < 0)
...@@ -1005,7 +1005,7 @@ int uvc_ctrl_get(struct uvc_video_device *video, ...@@ -1005,7 +1005,7 @@ int uvc_ctrl_get(struct uvc_video_device *video,
return 0; 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 v4l2_ext_control *xctrl)
{ {
struct uvc_control *ctrl; struct uvc_control *ctrl;
...@@ -1013,7 +1013,7 @@ int uvc_ctrl_set(struct uvc_video_device *video, ...@@ -1013,7 +1013,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
s32 value = xctrl->value; s32 value = xctrl->value;
int ret; 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) if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
return -EINVAL; return -EINVAL;
...@@ -1028,8 +1028,8 @@ int uvc_ctrl_set(struct uvc_video_device *video, ...@@ -1028,8 +1028,8 @@ int uvc_ctrl_set(struct uvc_video_device *video,
memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
0, ctrl->info->size); 0, ctrl->info->size);
} else { } else {
ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
ctrl->entity->id, video->dev->intfnum, ctrl->entity->id, chain->dev->intfnum,
ctrl->info->selector, ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size); ctrl->info->size);
...@@ -1058,7 +1058,7 @@ int uvc_ctrl_set(struct uvc_video_device *video, ...@@ -1058,7 +1058,7 @@ int uvc_ctrl_set(struct uvc_video_device *video,
* Dynamic controls * 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_xu_control *xctrl, int set)
{ {
struct uvc_entity *entity; struct uvc_entity *entity;
...@@ -1068,7 +1068,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, ...@@ -1068,7 +1068,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
int ret; int ret;
/* Find the extension unit. */ /* 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) if (entity->id == xctrl->unit)
break; break;
} }
...@@ -1107,7 +1107,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, ...@@ -1107,7 +1107,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
(!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR))) (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR)))
return -EINVAL; return -EINVAL;
if (mutex_lock_interruptible(&video->ctrl_mutex)) if (mutex_lock_interruptible(&chain->ctrl_mutex))
return -ERESTARTSYS; return -ERESTARTSYS;
memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
...@@ -1120,8 +1120,8 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, ...@@ -1120,8 +1120,8 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
goto out; goto out;
} }
ret = uvc_query_ctrl(video->dev, set ? UVC_SET_CUR : UVC_GET_CUR, ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR,
xctrl->unit, video->dev->intfnum, xctrl->selector, xctrl->unit, chain->dev->intfnum, xctrl->selector,
data, xctrl->size); data, xctrl->size);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1137,7 +1137,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, ...@@ -1137,7 +1137,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
xctrl->size); xctrl->size);
mutex_unlock(&video->ctrl_mutex); mutex_unlock(&chain->ctrl_mutex);
return ret; return ret;
} }
......
...@@ -276,8 +276,20 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, ...@@ -276,8 +276,20 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
return NULL; 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, static int uvc_parse_format(struct uvc_device *dev,
...@@ -1160,101 +1172,36 @@ static int uvc_parse_control(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 * Scan the UVC descriptors to locate a chain starting at an Output Terminal
* and containing the following units: * 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 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 * - zero or one multiple-input Selector Units, provided all inputs are
* connected to input terminals * connected to input terminals
* - zero, one or mode single-input Extension Units * - zero, one or mode single-input Extension Units
* - one or more Input Terminals (Camera, External or USB Streaming) * - one or more Input Terminals (Camera, External or USB Streaming)
* *
* A side forward scan is made on each detected entity to check for additional * The terminal and units must match on of the following structures:
* extension units. *
* 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) struct uvc_entity *entity)
{ {
switch (UVC_ENTITY_TYPE(entity)) { switch (UVC_ENTITY_TYPE(entity)) {
...@@ -1268,20 +1215,20 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, ...@@ -1268,20 +1215,20 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return -1; return -1;
} }
list_add_tail(&entity->chain, &video->extensions); list_add_tail(&entity->chain, &chain->extensions);
break; break;
case UVC_VC_PROCESSING_UNIT: case UVC_VC_PROCESSING_UNIT:
if (uvc_trace_param & UVC_TRACE_PROBE) if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- PU %d", entity->id); printk(" <- PU %d", entity->id);
if (video->processing != NULL) { if (chain->processing != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple " uvc_trace(UVC_TRACE_DESCR, "Found multiple "
"Processing Units in chain.\n"); "Processing Units in chain.\n");
return -1; return -1;
} }
video->processing = entity; chain->processing = entity;
break; break;
case UVC_VC_SELECTOR_UNIT: case UVC_VC_SELECTOR_UNIT:
...@@ -1292,13 +1239,13 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, ...@@ -1292,13 +1239,13 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
if (entity->selector.bNrInPins == 1) if (entity->selector.bNrInPins == 1)
break; break;
if (video->selector != NULL) { if (chain->selector != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
"Units in chain.\n"); "Units in chain.\n");
return -1; return -1;
} }
video->selector = entity; chain->selector = entity;
break; break;
case UVC_ITT_VENDOR_SPECIFIC: case UVC_ITT_VENDOR_SPECIFIC:
...@@ -1307,7 +1254,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, ...@@ -1307,7 +1254,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE) if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT %d\n", entity->id); printk(" <- IT %d\n", entity->id);
list_add_tail(&entity->chain, &video->iterms); list_add_tail(&entity->chain, &chain->iterms);
break; break;
case UVC_TT_STREAMING: case UVC_TT_STREAMING:
...@@ -1320,14 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, ...@@ -1320,14 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return -1; return -1;
} }
if (video->sterm != NULL) { list_add_tail(&entity->chain, &chain->iterms);
uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming "
"entities in chain.\n");
return -1;
}
list_add_tail(&entity->chain, &video->iterms);
video->sterm = entity;
break; break;
default: default:
...@@ -1339,7 +1279,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, ...@@ -1339,7 +1279,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
return 0; 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 *entity, struct uvc_entity *prev)
{ {
struct uvc_entity *forward; struct uvc_entity *forward;
...@@ -1350,29 +1290,52 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, ...@@ -1350,29 +1290,52 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
found = 0; found = 0;
while (1) { while (1) {
forward = uvc_entity_by_reference(video->dev, entity->id, forward = uvc_entity_by_reference(chain->dev, entity->id,
forward); forward);
if (forward == NULL) if (forward == NULL)
break; break;
if (forward == prev)
if (UVC_ENTITY_TYPE(forward) != UVC_VC_EXTENSION_UNIT ||
forward == prev)
continue; continue;
switch (UVC_ENTITY_TYPE(forward)) {
case UVC_VC_EXTENSION_UNIT:
if (forward->extension.bNrInPins != 1) { if (forward->extension.bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has " uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
"more than 1 input pin.\n", entity->id); "has more than 1 input pin.\n",
return -1; 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); list_add_tail(&forward->chain, &chain->oterms);
if (uvc_trace_param & UVC_TRACE_PROBE) { if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found) if (!found)
printk(" (-> XU"); printk(" (->");
printk(" %d", forward->id); printk(" OT %d", forward->id);
found = 1; found = 1;
} }
break;
}
} }
if (found) if (found)
printk(")"); printk(")");
...@@ -1380,7 +1343,7 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, ...@@ -1380,7 +1343,7 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
return 0; 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 *entity)
{ {
struct uvc_entity *term; struct uvc_entity *term;
...@@ -1405,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, ...@@ -1405,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE) if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT"); printk(" <- IT");
video->selector = entity; chain->selector = entity;
for (i = 0; i < entity->selector.bNrInPins; ++i) { for (i = 0; i < entity->selector.bNrInPins; ++i) {
id = entity->selector.baSourceID[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)) { if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
"input %d isn't connected to an " "input %d isn't connected to an "
...@@ -1419,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, ...@@ -1419,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
if (uvc_trace_param & UVC_TRACE_PROBE) if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" %d", term->id); printk(" %d", term->id);
list_add_tail(&term->chain, &video->iterms); list_add_tail(&term->chain, &chain->iterms);
uvc_scan_chain_forward(video, term, entity); uvc_scan_chain_forward(chain, term, entity);
} }
if (uvc_trace_param & UVC_TRACE_PROBE) if (uvc_trace_param & UVC_TRACE_PROBE)
...@@ -1433,107 +1396,263 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, ...@@ -1433,107 +1396,263 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
return id; 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; struct uvc_entity *entity, *prev;
int id; 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); 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; id = entity->output.bSourceID;
while (id != 0) { while (id != 0) {
prev = entity; prev = entity;
entity = uvc_entity_by_id(video->dev, id); entity = uvc_entity_by_id(chain->dev, id);
if (entity == NULL) { if (entity == NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found reference to " uvc_trace(UVC_TRACE_DESCR, "Found reference to "
"unknown entity %d.\n", id); "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 */ /* Process entity */
if (uvc_scan_chain_entity(video, entity) < 0) if (uvc_scan_chain_entity(chain, entity) < 0)
return -1; return -EINVAL;
/* Forward scan */ /* Forward scan */
if (uvc_scan_chain_forward(video, entity, prev) < 0) if (uvc_scan_chain_forward(chain, entity, prev) < 0)
return -1; return -EINVAL;
/* Stop when a terminal is found. */ /* Stop when a terminal is found. */
if (!UVC_ENTITY_IS_UNIT(entity)) if (UVC_ENTITY_IS_TERM(entity))
break; break;
/* Backward scan */ /* Backward scan */
id = uvc_scan_chain_backward(video, entity); id = uvc_scan_chain_backward(chain, entity);
if (id < 0) if (id < 0)
return id; return id;
} }
if (video->sterm == NULL) { return 0;
uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in " }
"chain.\n");
return -1; 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. * Scan the device for video chains and register video devices.
* *
* The driver currently supports a single video device per control interface * Chains are scanned starting at their output terminals and walked backwards.
* 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.
*/ */
static int uvc_scan_device(struct uvc_device *dev) static int uvc_scan_device(struct uvc_device *dev)
{ {
struct uvc_video_chain *chain;
struct uvc_entity *term; 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) { list_for_each_entry(term, &dev->entities, list) {
struct uvc_streaming *stream; if (!UVC_ENTITY_IS_OTERM(term))
continue;
if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term)) /* 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; continue;
memset(&dev->video, 0, sizeof dev->video); chain = kzalloc(sizeof(*chain), GFP_KERNEL);
mutex_init(&dev->video.ctrl_mutex); if (chain == NULL)
INIT_LIST_HEAD(&dev->video.iterms); return -ENOMEM;
INIT_LIST_HEAD(&dev->video.extensions);
dev->video.oterm = term; INIT_LIST_HEAD(&chain->iterms);
dev->video.dev = dev; INIT_LIST_HEAD(&chain->oterms);
if (uvc_scan_chain(&dev->video) < 0) INIT_LIST_HEAD(&chain->extensions);
mutex_init(&chain->ctrl_mutex);
chain->dev = dev;
if (uvc_scan_chain(chain, term) < 0) {
kfree(chain);
continue; 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.\n");
return -1;
}
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) { list_for_each_entry(stream, &dev->streams, list) {
if (stream->header.bTerminalLink == if (stream->vdev == NULL)
dev->video.sterm->id) { continue;
uvc_register_video(dev, stream);
found = 1; if (stream->vdev->minor == -1)
break; 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;
} }
if (!found) { /* We already hold a reference to dev->udev. The video device will be
uvc_printk(KERN_INFO, "No valid video chain found.\n"); * unregistered before the reference is released, so we don't need to
return -1; * 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; 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. * Delete the UVC device.
* *
...@@ -1555,7 +1674,7 @@ void uvc_delete(struct kref *kref) ...@@ -1555,7 +1674,7 @@ void uvc_delete(struct kref *kref)
struct uvc_device *dev = container_of(kref, struct uvc_device, kref); struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
struct list_head *p, *n; struct list_head *p, *n;
/* Unregister the video device. */ /* Unregister the video devices. */
uvc_unregister_video(dev); uvc_unregister_video(dev);
usb_put_intf(dev->intf); usb_put_intf(dev->intf);
usb_put_dev(dev->udev); usb_put_dev(dev->udev);
...@@ -1563,6 +1682,12 @@ void uvc_delete(struct kref *kref) ...@@ -1563,6 +1682,12 @@ void uvc_delete(struct kref *kref)
uvc_status_cleanup(dev); uvc_status_cleanup(dev);
uvc_ctrl_cleanup_device(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) { list_for_each_safe(p, n, &dev->entities) {
struct uvc_entity *entity; struct uvc_entity *entity;
entity = list_entry(p, struct uvc_entity, list); entity = list_entry(p, struct uvc_entity, list);
...@@ -1603,6 +1728,7 @@ static int uvc_probe(struct usb_interface *intf, ...@@ -1603,6 +1728,7 @@ static int uvc_probe(struct usb_interface *intf,
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&dev->entities); INIT_LIST_HEAD(&dev->entities);
INIT_LIST_HEAD(&dev->chains);
INIT_LIST_HEAD(&dev->streams); INIT_LIST_HEAD(&dev->streams);
kref_init(&dev->kref); kref_init(&dev->kref);
atomic_set(&dev->users, 0); atomic_set(&dev->users, 0);
...@@ -1644,10 +1770,14 @@ static int uvc_probe(struct usb_interface *intf, ...@@ -1644,10 +1770,14 @@ static int uvc_probe(struct usb_interface *intf,
if (uvc_ctrl_init_device(dev) < 0) if (uvc_ctrl_init_device(dev) < 0)
goto error; goto error;
/* Scan the device for video chains and register video devices. */ /* Scan the device for video chains. */
if (uvc_scan_device(dev) < 0) if (uvc_scan_device(dev) < 0)
goto error; goto error;
/* Register video devices. */
if (uvc_register_chains(dev) < 0)
goto error;
/* Save our data pointer in the interface data. */ /* Save our data pointer in the interface data. */
usb_set_intfdata(intf, dev); usb_set_intfdata(intf, dev);
......
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
* table for the controls that can be mapped directly, and handle the others * table for the controls that can be mapped directly, and handle the others
* manually. * 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 v4l2_querymenu *query_menu)
{ {
struct uvc_menu_info *menu_info; struct uvc_menu_info *menu_info;
...@@ -49,7 +49,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video, ...@@ -49,7 +49,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video,
u32 index = query_menu->index; u32 index = query_menu->index;
u32 id = query_menu->id; 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) if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU)
return -EINVAL; return -EINVAL;
...@@ -451,7 +451,7 @@ static int uvc_v4l2_open(struct file *file) ...@@ -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->stream = stream;
handle->state = UVC_HANDLE_PASSIVE; handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle; file->private_data = handle;
...@@ -498,7 +498,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -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 video_device *vdev = video_devdata(file);
struct uvc_fh *handle = (struct uvc_fh *)file->private_data; 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; struct uvc_streaming *stream = handle->stream;
long ret = 0; long ret = 0;
...@@ -525,7 +525,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -525,7 +525,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
/* Get, Set & Query control */ /* Get, Set & Query control */
case VIDIOC_QUERYCTRL: case VIDIOC_QUERYCTRL:
return uvc_query_v4l2_ctrl(video, arg); return uvc_query_v4l2_ctrl(chain, arg);
case VIDIOC_G_CTRL: case VIDIOC_G_CTRL:
{ {
...@@ -535,12 +535,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -535,12 +535,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
memset(&xctrl, 0, sizeof xctrl); memset(&xctrl, 0, sizeof xctrl);
xctrl.id = ctrl->id; xctrl.id = ctrl->id;
ret = uvc_ctrl_begin(video); ret = uvc_ctrl_begin(chain);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = uvc_ctrl_get(video, &xctrl); ret = uvc_ctrl_get(chain, &xctrl);
uvc_ctrl_rollback(video); uvc_ctrl_rollback(chain);
if (ret >= 0) if (ret >= 0)
ctrl->value = xctrl.value; ctrl->value = xctrl.value;
break; break;
...@@ -555,21 +555,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -555,21 +555,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
xctrl.id = ctrl->id; xctrl.id = ctrl->id;
xctrl.value = ctrl->value; xctrl.value = ctrl->value;
ret = uvc_ctrl_begin(video); uvc_ctrl_begin(chain);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = uvc_ctrl_set(video, &xctrl); ret = uvc_ctrl_set(chain, &xctrl);
if (ret < 0) { if (ret < 0) {
uvc_ctrl_rollback(video); uvc_ctrl_rollback(chain);
return ret; return ret;
} }
ret = uvc_ctrl_commit(video); ret = uvc_ctrl_commit(chain);
break; break;
} }
case VIDIOC_QUERYMENU: case VIDIOC_QUERYMENU:
return uvc_v4l2_query_menu(video, arg); return uvc_v4l2_query_menu(chain, arg);
case VIDIOC_G_EXT_CTRLS: case VIDIOC_G_EXT_CTRLS:
{ {
...@@ -577,20 +577,20 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -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; struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i; unsigned int i;
ret = uvc_ctrl_begin(video); ret = uvc_ctrl_begin(chain);
if (ret < 0) if (ret < 0)
return ret; return ret;
for (i = 0; i < ctrls->count; ++ctrl, ++i) { for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_get(video, ctrl); ret = uvc_ctrl_get(chain, ctrl);
if (ret < 0) { if (ret < 0) {
uvc_ctrl_rollback(video); uvc_ctrl_rollback(chain);
ctrls->error_idx = i; ctrls->error_idx = i;
return ret; return ret;
} }
} }
ctrls->error_idx = 0; ctrls->error_idx = 0;
ret = uvc_ctrl_rollback(video); ret = uvc_ctrl_rollback(chain);
break; break;
} }
...@@ -601,14 +601,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -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; struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i; unsigned int i;
ret = uvc_ctrl_begin(video); ret = uvc_ctrl_begin(chain);
if (ret < 0) if (ret < 0)
return ret; return ret;
for (i = 0; i < ctrls->count; ++ctrl, ++i) { for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_set(video, ctrl); ret = uvc_ctrl_set(chain, ctrl);
if (ret < 0) { if (ret < 0) {
uvc_ctrl_rollback(video); uvc_ctrl_rollback(chain);
ctrls->error_idx = i; ctrls->error_idx = i;
return ret; return ret;
} }
...@@ -617,31 +617,31 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -617,31 +617,31 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ctrls->error_idx = 0; ctrls->error_idx = 0;
if (cmd == VIDIOC_S_EXT_CTRLS) if (cmd == VIDIOC_S_EXT_CTRLS)
ret = uvc_ctrl_commit(video); ret = uvc_ctrl_commit(chain);
else else
ret = uvc_ctrl_rollback(video); ret = uvc_ctrl_rollback(chain);
break; break;
} }
/* Get, Set & Enum input */ /* Get, Set & Enum input */
case VIDIOC_ENUMINPUT: case VIDIOC_ENUMINPUT:
{ {
const struct uvc_entity *selector = video->selector; const struct uvc_entity *selector = chain->selector;
struct v4l2_input *input = arg; struct v4l2_input *input = arg;
struct uvc_entity *iterm = NULL; struct uvc_entity *iterm = NULL;
u32 index = input->index; u32 index = input->index;
int pin = 0; int pin = 0;
if (selector == NULL || if (selector == NULL ||
(video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (index != 0) if (index != 0)
return -EINVAL; return -EINVAL;
iterm = list_first_entry(&video->iterms, iterm = list_first_entry(&chain->iterms,
struct uvc_entity, chain); struct uvc_entity, chain);
pin = iterm->id; pin = iterm->id;
} else if (pin < selector->selector.bNrInPins) { } else if (pin < selector->selector.bNrInPins) {
pin = selector->selector.baSourceID[index]; 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) if (iterm->id == pin)
break; break;
} }
...@@ -662,14 +662,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -662,14 +662,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{ {
u8 input; u8 input;
if (video->selector == NULL || if (chain->selector == NULL ||
(video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
*(int *)arg = 0; *(int *)arg = 0;
break; break;
} }
ret = uvc_query_ctrl(video->dev, UVC_GET_CUR, ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
video->selector->id, video->dev->intfnum, chain->selector->id, chain->dev->intfnum,
UVC_SU_INPUT_SELECT_CONTROL, &input, 1); UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -685,18 +685,18 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -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) if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret; return ret;
if (video->selector == NULL || if (chain->selector == NULL ||
(video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (input != 1) if (input != 1)
return -EINVAL; return -EINVAL;
break; break;
} }
if (input == 0 || input > video->selector->selector.bNrInPins) if (input == 0 || input > chain->selector->selector.bNrInPins)
return -EINVAL; return -EINVAL;
return uvc_query_ctrl(video->dev, UVC_SET_CUR, return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
video->selector->id, video->dev->intfnum, chain->selector->id, chain->dev->intfnum,
UVC_SU_INPUT_SELECT_CONTROL, &input, 1); 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) ...@@ -1019,10 +1019,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
} }
case UVCIOC_CTRL_GET: case UVCIOC_CTRL_GET:
return uvc_xu_ctrl_query(video, arg, 0); return uvc_xu_ctrl_query(chain, arg, 0);
case UVCIOC_CTRL_SET: case UVCIOC_CTRL_SET:
return uvc_xu_ctrl_query(video, arg, 1); return uvc_xu_ctrl_query(chain, arg, 1);
default: default:
if ((ret = v4l_compat_translate_ioctl(file, cmd, arg, if ((ret = v4l_compat_translate_ioctl(file, cmd, arg,
......
...@@ -128,7 +128,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ...@@ -128,7 +128,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
if (data == NULL) if (data == NULL)
return -ENOMEM; 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; return -EIO;
ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
......
...@@ -80,9 +80,11 @@ struct uvc_xu_control { ...@@ -80,9 +80,11 @@ struct uvc_xu_control {
#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) #define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) #define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0)
#define UVC_ENTITY_IS_ITERM(entity) \ #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) \ #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 { ...@@ -402,10 +404,24 @@ struct uvc_video_queue {
struct list_head irqqueue; 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 uvc_streaming {
struct list_head list; struct list_head list;
struct uvc_device *dev; struct uvc_device *dev;
struct video_device *vdev; struct video_device *vdev;
struct uvc_video_chain *chain;
atomic_t active; atomic_t active;
struct usb_interface *intf; struct usb_interface *intf;
...@@ -446,18 +462,6 @@ struct uvc_streaming { ...@@ -446,18 +462,6 @@ struct uvc_streaming {
__u8 last_fid; __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 { enum uvc_device_state {
UVC_DEV_DISCONNECTED = 1, UVC_DEV_DISCONNECTED = 1,
}; };
...@@ -480,8 +484,7 @@ struct uvc_device { ...@@ -480,8 +484,7 @@ struct uvc_device {
__u32 clock_frequency; __u32 clock_frequency;
struct list_head entities; struct list_head entities;
struct list_head chains;
struct uvc_video_device video;
/* Video Streaming interfaces */ /* Video Streaming interfaces */
struct list_head streams; struct list_head streams;
...@@ -500,7 +503,7 @@ enum uvc_handle_state { ...@@ -500,7 +503,7 @@ enum uvc_handle_state {
}; };
struct uvc_fh { struct uvc_fh {
struct uvc_video_device *video; struct uvc_video_chain *chain;
struct uvc_streaming *stream; struct uvc_streaming *stream;
enum uvc_handle_state state; enum uvc_handle_state state;
}; };
...@@ -618,9 +621,9 @@ extern int uvc_status_suspend(struct uvc_device *dev); ...@@ -618,9 +621,9 @@ extern int uvc_status_suspend(struct uvc_device *dev);
extern int uvc_status_resume(struct uvc_device *dev); extern int uvc_status_resume(struct uvc_device *dev);
/* Controls */ /* 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); __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); struct v4l2_queryctrl *v4l2_ctrl);
extern int uvc_ctrl_add_info(struct uvc_control_info *info); 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); ...@@ -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 int uvc_ctrl_resume_device(struct uvc_device *dev);
extern void uvc_ctrl_init(void); extern void uvc_ctrl_init(void);
extern int uvc_ctrl_begin(struct uvc_video_device *video); extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback); extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
static inline int uvc_ctrl_commit(struct uvc_video_device *video) 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); 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); 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); struct uvc_xu_control *ctrl, int set);
/* Utility functions */ /* 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