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: Laurent Pinchart <laurent.pinchart@skynet.be> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
35f02a681b
commit
8e113595ed
|
@ -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 @@ out:
|
|||
* (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 @@ out:
|
|||
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 @@ next_descriptor:
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* 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, &video->extensions);
|
||||
if (uvc_trace_param & UVC_TRACE_PROBE) {
|
||||
if (!found)
|
||||
printk(" (-> XU");
|
||||
list_add_tail(&forward->chain, &chain->extensions);
|
||||
if (uvc_trace_param & UVC_TRACE_PROBE) {
|
||||
if (!found)
|
||||
printk(" (->");
|
||||
|
||||
printk(" %d", forward->id);
|
||||
found = 1;
|
||||
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, &chain->oterms);
|
||||
if (uvc_trace_param & UVC_TRACE_PROBE) {
|
||||
if (!found)
|
||||
printk(" (->");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static int uvc_scan_device(struct uvc_device *dev)
|
||||
static unsigned int uvc_print_terms(struct list_head *terms, char *buffer)
|
||||
{
|
||||
struct uvc_entity *term;
|
||||
int found = 0;
|
||||
unsigned int nterms = 0;
|
||||
char *p = buffer;
|
||||
|
||||
/* 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))
|
||||
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)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(stream, &dev->streams, list) {
|
||||
if (stream->header.bTerminalLink ==
|
||||
dev->video.sterm->id) {
|
||||
uvc_register_video(dev, stream);
|
||||
found = 1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
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.
|
||||
*
|
||||
* 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;
|
||||
|
||||
list_for_each_entry(term, &dev->entities, list) {
|
||||
if (!UVC_ENTITY_IS_OTERM(term))
|
||||
continue;
|
||||
|
||||
/* If the terminal is already included in a chain, skip it.
|
||||
* This can happen for chains that have multiple output
|
||||
* terminals, where all output terminals beside the first one
|
||||
* will be inserted in the chain in forward scans.
|
||||
*/
|
||||
if (term->chain.next || term->chain.prev)
|
||||
continue;
|
||||
|
||||
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
||||
if (chain == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&chain->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 (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 */
|
||||
|
|
Loading…
Reference in New Issue