|
@@ -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, &chain->extensions);
|
|
|
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
|
|
|
+ if (!found)
|
|
|
+ printk(" (->");
|
|
|
+
|
|
|
+ printk(" XU %d", forward->id);
|
|
|
+ found = 1;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case UVC_OTT_VENDOR_SPECIFIC:
|
|
|
+ case UVC_OTT_DISPLAY:
|
|
|
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
|
|
|
+ case UVC_TT_STREAMING:
|
|
|
+ if (UVC_ENTITY_IS_ITERM(forward)) {
|
|
|
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
|
|
|
+ "terminal %u.\n", forward->id);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
- list_add_tail(&forward->chain, &video->extensions);
|
|
|
- if (uvc_trace_param & UVC_TRACE_PROBE) {
|
|
|
- if (!found)
|
|
|
- printk(" (-> XU");
|
|
|
+ list_add_tail(&forward->chain, &chain->oterms);
|
|
|
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
|
|
|
+ if (!found)
|
|
|
+ printk(" (->");
|
|
|
|
|
|
- printk(" %d", forward->id);
|
|
|
- found = 1;
|
|
|
+ printk(" OT %d", forward->id);
|
|
|
+ found = 1;
|
|
|
+ }
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
if (found)
|
|
@@ -1380,7 +1343,7 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int uvc_scan_chain_backward(struct uvc_video_device *video,
|
|
|
+static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
|
|
|
struct uvc_entity *entity)
|
|
|
{
|
|
|
struct uvc_entity *term;
|
|
@@ -1405,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
|
|
|
if (uvc_trace_param & UVC_TRACE_PROBE)
|
|
|
printk(" <- IT");
|
|
|
|
|
|
- video->selector = entity;
|
|
|
+ chain->selector = entity;
|
|
|
for (i = 0; i < entity->selector.bNrInPins; ++i) {
|
|
|
id = entity->selector.baSourceID[i];
|
|
|
- term = uvc_entity_by_id(video->dev, id);
|
|
|
+ term = uvc_entity_by_id(chain->dev, id);
|
|
|
if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
|
|
|
uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
|
|
|
"input %d isn't connected to an "
|
|
@@ -1419,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
|
|
|
if (uvc_trace_param & UVC_TRACE_PROBE)
|
|
|
printk(" %d", term->id);
|
|
|
|
|
|
- list_add_tail(&term->chain, &video->iterms);
|
|
|
- uvc_scan_chain_forward(video, term, entity);
|
|
|
+ list_add_tail(&term->chain, &chain->iterms);
|
|
|
+ uvc_scan_chain_forward(chain, term, entity);
|
|
|
}
|
|
|
|
|
|
if (uvc_trace_param & UVC_TRACE_PROBE)
|
|
@@ -1433,100 +1396,129 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video,
|
|
|
return id;
|
|
|
}
|
|
|
|
|
|
-static int uvc_scan_chain(struct uvc_video_device *video)
|
|
|
+static int uvc_scan_chain(struct uvc_video_chain *chain,
|
|
|
+ struct uvc_entity *oterm)
|
|
|
{
|
|
|
struct uvc_entity *entity, *prev;
|
|
|
int id;
|
|
|
|
|
|
- entity = video->oterm;
|
|
|
+ entity = oterm;
|
|
|
+ list_add_tail(&entity->chain, &chain->oterms);
|
|
|
uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
|
|
|
|
|
|
- if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
|
|
|
- video->sterm = entity;
|
|
|
-
|
|
|
id = entity->output.bSourceID;
|
|
|
while (id != 0) {
|
|
|
prev = entity;
|
|
|
- entity = uvc_entity_by_id(video->dev, id);
|
|
|
+ entity = uvc_entity_by_id(chain->dev, id);
|
|
|
if (entity == NULL) {
|
|
|
uvc_trace(UVC_TRACE_DESCR, "Found reference to "
|
|
|
"unknown entity %d.\n", id);
|
|
|
- return -1;
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (entity->chain.next || entity->chain.prev) {
|
|
|
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
|
|
|
+ "entity %d already in chain.\n", id);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
/* Process entity */
|
|
|
- if (uvc_scan_chain_entity(video, entity) < 0)
|
|
|
- return -1;
|
|
|
+ if (uvc_scan_chain_entity(chain, entity) < 0)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
/* Forward scan */
|
|
|
- if (uvc_scan_chain_forward(video, entity, prev) < 0)
|
|
|
- return -1;
|
|
|
+ if (uvc_scan_chain_forward(chain, entity, prev) < 0)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
/* Stop when a terminal is found. */
|
|
|
- if (!UVC_ENTITY_IS_UNIT(entity))
|
|
|
+ if (UVC_ENTITY_IS_TERM(entity))
|
|
|
break;
|
|
|
|
|
|
/* Backward scan */
|
|
|
- id = uvc_scan_chain_backward(video, entity);
|
|
|
+ id = uvc_scan_chain_backward(chain, entity);
|
|
|
if (id < 0)
|
|
|
return id;
|
|
|
}
|
|
|
|
|
|
- if (video->sterm == NULL) {
|
|
|
- uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
|
|
|
- "chain.\n");
|
|
|
- return -1;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned int uvc_print_terms(struct list_head *terms, char *buffer)
|
|
|
+{
|
|
|
+ struct uvc_entity *term;
|
|
|
+ unsigned int nterms = 0;
|
|
|
+ char *p = buffer;
|
|
|
+
|
|
|
+ list_for_each_entry(term, terms, chain) {
|
|
|
+ p += sprintf(p, "%u", term->id);
|
|
|
+ if (term->chain.next != terms) {
|
|
|
+ p += sprintf(p, ",");
|
|
|
+ if (++nterms >= 4) {
|
|
|
+ p += sprintf(p, "...");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
+ return p - buffer;
|
|
|
+}
|
|
|
+
|
|
|
+static const char *uvc_print_chain(struct uvc_video_chain *chain)
|
|
|
+{
|
|
|
+ static char buffer[43];
|
|
|
+ char *p = buffer;
|
|
|
+
|
|
|
+ p += uvc_print_terms(&chain->iterms, p);
|
|
|
+ p += sprintf(p, " -> ");
|
|
|
+ uvc_print_terms(&chain->oterms, p);
|
|
|
+
|
|
|
+ return buffer;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Scan the device for video chains and register video devices.
|
|
|
*
|
|
|
- * The driver currently supports a single video device per control interface
|
|
|
- * only. The terminal and units must match the following structure:
|
|
|
- *
|
|
|
- * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
|
|
|
- * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*
|
|
|
- *
|
|
|
- * The Extension Units, if present, must have a single input pin. The
|
|
|
- * Processing Unit and Extension Units can be in any order. Additional
|
|
|
- * Extension Units connected to the main chain as single-unit branches are
|
|
|
- * also supported.
|
|
|
+ * Chains are scanned starting at their output terminals and walked backwards.
|
|
|
*/
|
|
|
static int uvc_scan_device(struct uvc_device *dev)
|
|
|
{
|
|
|
+ struct uvc_video_chain *chain;
|
|
|
struct uvc_entity *term;
|
|
|
- int found = 0;
|
|
|
|
|
|
- /* Check if the control interface matches the structure we expect. */
|
|
|
list_for_each_entry(term, &dev->entities, list) {
|
|
|
- struct uvc_streaming *stream;
|
|
|
-
|
|
|
- if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
|
|
|
+ if (!UVC_ENTITY_IS_OTERM(term))
|
|
|
continue;
|
|
|
|
|
|
- memset(&dev->video, 0, sizeof dev->video);
|
|
|
- mutex_init(&dev->video.ctrl_mutex);
|
|
|
- INIT_LIST_HEAD(&dev->video.iterms);
|
|
|
- INIT_LIST_HEAD(&dev->video.extensions);
|
|
|
- dev->video.oterm = term;
|
|
|
- dev->video.dev = dev;
|
|
|
- if (uvc_scan_chain(&dev->video) < 0)
|
|
|
+ /* If the terminal is already included in a chain, skip it.
|
|
|
+ * This can happen for chains that have multiple output
|
|
|
+ * terminals, where all output terminals beside the first one
|
|
|
+ * will be inserted in the chain in forward scans.
|
|
|
+ */
|
|
|
+ if (term->chain.next || term->chain.prev)
|
|
|
continue;
|
|
|
|
|
|
- list_for_each_entry(stream, &dev->streams, list) {
|
|
|
- if (stream->header.bTerminalLink ==
|
|
|
- dev->video.sterm->id) {
|
|
|
- uvc_register_video(dev, stream);
|
|
|
- found = 1;
|
|
|
- break;
|
|
|
- }
|
|
|
+ chain = kzalloc(sizeof(*chain), GFP_KERNEL);
|
|
|
+ if (chain == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&chain->iterms);
|
|
|
+ INIT_LIST_HEAD(&chain->oterms);
|
|
|
+ INIT_LIST_HEAD(&chain->extensions);
|
|
|
+ mutex_init(&chain->ctrl_mutex);
|
|
|
+ chain->dev = dev;
|
|
|
+
|
|
|
+ if (uvc_scan_chain(chain, term) < 0) {
|
|
|
+ kfree(chain);
|
|
|
+ continue;
|
|
|
}
|
|
|
+
|
|
|
+ uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
|
|
|
+ uvc_print_chain(chain));
|
|
|
+
|
|
|
+ list_add_tail(&chain->list, &dev->chains);
|
|
|
}
|
|
|
|
|
|
- if (!found) {
|
|
|
+ if (list_empty(&dev->chains)) {
|
|
|
uvc_printk(KERN_INFO, "No valid video chain found.\n");
|
|
|
return -1;
|
|
|
}
|
|
@@ -1534,6 +1526,133 @@ static int uvc_scan_device(struct uvc_device *dev)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* ------------------------------------------------------------------------
|
|
|
+ * Video device registration and unregistration
|
|
|
+ */
|
|
|
+
|
|
|
+/*
|
|
|
+ * Unregister the video devices.
|
|
|
+ */
|
|
|
+static void uvc_unregister_video(struct uvc_device *dev)
|
|
|
+{
|
|
|
+ struct uvc_streaming *stream;
|
|
|
+
|
|
|
+ list_for_each_entry(stream, &dev->streams, list) {
|
|
|
+ if (stream->vdev == NULL)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (stream->vdev->minor == -1)
|
|
|
+ video_device_release(stream->vdev);
|
|
|
+ else
|
|
|
+ video_unregister_device(stream->vdev);
|
|
|
+ stream->vdev = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int uvc_register_video(struct uvc_device *dev,
|
|
|
+ struct uvc_streaming *stream)
|
|
|
+{
|
|
|
+ struct video_device *vdev;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Initialize the streaming interface with default streaming
|
|
|
+ * parameters.
|
|
|
+ */
|
|
|
+ ret = uvc_video_init(stream);
|
|
|
+ if (ret < 0) {
|
|
|
+ uvc_printk(KERN_ERR, "Failed to initialize the device "
|
|
|
+ "(%d).\n", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Register the device with V4L. */
|
|
|
+ vdev = video_device_alloc();
|
|
|
+ if (vdev == NULL) {
|
|
|
+ uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
|
|
|
+ ret);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We already hold a reference to dev->udev. The video device will be
|
|
|
+ * unregistered before the reference is released, so we don't need to
|
|
|
+ * get another one.
|
|
|
+ */
|
|
|
+ vdev->parent = &dev->intf->dev;
|
|
|
+ vdev->minor = -1;
|
|
|
+ vdev->fops = &uvc_fops;
|
|
|
+ vdev->release = video_device_release;
|
|
|
+ strlcpy(vdev->name, dev->name, sizeof vdev->name);
|
|
|
+
|
|
|
+ /* Set the driver data before calling video_register_device, otherwise
|
|
|
+ * uvc_v4l2_open might race us.
|
|
|
+ */
|
|
|
+ stream->vdev = vdev;
|
|
|
+ video_set_drvdata(vdev, stream);
|
|
|
+
|
|
|
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
|
|
|
+ if (ret < 0) {
|
|
|
+ uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
|
|
|
+ ret);
|
|
|
+ stream->vdev = NULL;
|
|
|
+ video_device_release(vdev);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Register all video devices in all chains.
|
|
|
+ */
|
|
|
+static int uvc_register_terms(struct uvc_device *dev,
|
|
|
+ struct uvc_video_chain *chain, struct list_head *terms)
|
|
|
+{
|
|
|
+ struct uvc_streaming *stream;
|
|
|
+ struct uvc_entity *term;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ list_for_each_entry(term, terms, chain) {
|
|
|
+ if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ stream = uvc_stream_by_id(dev, term->id);
|
|
|
+ if (stream == NULL) {
|
|
|
+ uvc_printk(KERN_INFO, "No streaming interface found "
|
|
|
+ "for terminal %u.", term->id);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ stream->chain = chain;
|
|
|
+ ret = uvc_register_video(dev, stream);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int uvc_register_chains(struct uvc_device *dev)
|
|
|
+{
|
|
|
+ struct uvc_video_chain *chain;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ list_for_each_entry(chain, &dev->chains, list) {
|
|
|
+ ret = uvc_register_terms(dev, chain, &chain->iterms);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = uvc_register_terms(dev, chain, &chain->oterms);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* ------------------------------------------------------------------------
|
|
|
+ * USB probe, disconnect, suspend and resume
|
|
|
+ */
|
|
|
+
|
|
|
/*
|
|
|
* Delete the UVC device.
|
|
|
*
|
|
@@ -1555,7 +1674,7 @@ void uvc_delete(struct kref *kref)
|
|
|
struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
|
|
|
struct list_head *p, *n;
|
|
|
|
|
|
- /* Unregister the video device. */
|
|
|
+ /* Unregister the video devices. */
|
|
|
uvc_unregister_video(dev);
|
|
|
usb_put_intf(dev->intf);
|
|
|
usb_put_dev(dev->udev);
|
|
@@ -1563,6 +1682,12 @@ void uvc_delete(struct kref *kref)
|
|
|
uvc_status_cleanup(dev);
|
|
|
uvc_ctrl_cleanup_device(dev);
|
|
|
|
|
|
+ list_for_each_safe(p, n, &dev->chains) {
|
|
|
+ struct uvc_video_chain *chain;
|
|
|
+ chain = list_entry(p, struct uvc_video_chain, list);
|
|
|
+ kfree(chain);
|
|
|
+ }
|
|
|
+
|
|
|
list_for_each_safe(p, n, &dev->entities) {
|
|
|
struct uvc_entity *entity;
|
|
|
entity = list_entry(p, struct uvc_entity, list);
|
|
@@ -1603,6 +1728,7 @@ static int uvc_probe(struct usb_interface *intf,
|
|
|
return -ENOMEM;
|
|
|
|
|
|
INIT_LIST_HEAD(&dev->entities);
|
|
|
+ INIT_LIST_HEAD(&dev->chains);
|
|
|
INIT_LIST_HEAD(&dev->streams);
|
|
|
kref_init(&dev->kref);
|
|
|
atomic_set(&dev->users, 0);
|
|
@@ -1644,10 +1770,14 @@ static int uvc_probe(struct usb_interface *intf,
|
|
|
if (uvc_ctrl_init_device(dev) < 0)
|
|
|
goto error;
|
|
|
|
|
|
- /* Scan the device for video chains and register video devices. */
|
|
|
+ /* Scan the device for video chains. */
|
|
|
if (uvc_scan_device(dev) < 0)
|
|
|
goto error;
|
|
|
|
|
|
+ /* Register video devices. */
|
|
|
+ if (uvc_register_chains(dev) < 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
/* Save our data pointer in the interface data. */
|
|
|
usb_set_intfdata(intf, dev);
|
|
|
|