@@ -358,6 +358,14 @@ static struct uvc_control_info uvc_ctrls[] = {
*/
{
.entity = UVC_GUID_UVC_ENCODING,
+ .selector = UVC_EU_SELECT_LAYER_CONTROL,
+ .index = 0,
+ .size = 2,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_ENCODING,
.selector = UVC_EU_PROFILE_TOOLSET_CONTROL,
.index = 1,
.size = 6,
@@ -367,6 +375,16 @@ static struct uvc_control_info uvc_ctrls[] = {
},
{
.entity = UVC_GUID_UVC_ENCODING,
+ .selector = UVC_EU_VIDEO_RESOLUTION_CONTROL,
+ .index = 2,
+ .size = 4,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_ENCODING,
.selector = UVC_EU_MIN_FRAME_INTERVAL_CONTROL,
.index = 3,
.size = 4,
@@ -505,6 +523,14 @@ static struct uvc_control_info uvc_ctrls[] = {
},
{
.entity = UVC_GUID_UVC_ENCODING,
+ .selector = UVC_EU_START_OR_STOP_LAYER_CONTROL,
+ .index = 18,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_AUTO_UPDATE,
+ },
+ {
+ .entity = UVC_GUID_UVC_ENCODING,
.selector = UVC_EU_ERROR_RESILIENCY_CONTROL,
.index = 19,
.size = 2,
@@ -1131,6 +1157,16 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.v4l2_type = V4L2_CTRL_TYPE_BITMASK,
.data_type = UVC_CTRL_DATA_TYPE_BITMASK,
},
+ {
+ .id = V4L2_CID_ENCODER_TEMPORAL_LAYER_ENABLE,
+ .name = "Encoder, temporal layer enable",
+ .entity = UVC_GUID_UVC_ENCODING,
+ .selector = UVC_EU_START_OR_STOP_LAYER_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
};
/* ------------------------------------------------------------------------
@@ -1918,6 +1954,20 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl,
return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
}
+int uvc_ctrl_set_raw_val(struct uvc_control *ctrl,
+ struct uvc_control_mapping *mapping, u32 value)
+{
+ struct uvc_device *dev = ctrl->entity->chain->dev;
+
+ mapping->set(mapping, value,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
+ return uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id,
+ dev->intfnum, ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+}
+
int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl,
bool streaming)
{
@@ -2307,6 +2357,239 @@ done:
}
/* --------------------------------------------------------------------------
+ * Non-exported encoder controls
+ */
+/* Must be called under chain lock */
+int __uvc_video_layer_select(struct uvc_stream_layer *layer, int temporal_id)
+{
+ struct uvc_streaming *stream = layer->stream;
+ struct uvc_video_chain *chain = stream->chain;
+ struct uvc_control *ctrl;
+ __u8 data[2];
+
+ if (!chain->encoding || !chain->layer_ctrl)
+ return 0;
+
+ uvc_trace(UVC_TRACE_CONTROL, "Selecting layer: %d temporal_id: %d\n",
+ layer->layer_id, temporal_id);
+
+ if (uvc_cur_fmt_has_tmprl_layers(stream)) {
+ if (temporal_id > stream->cur_frame->bmScalabilityCapabilities
+ && temporal_id != UVC_VP8_TEMPORAL_LAYOUT_WILDCARD) {
+ uvc_trace(UVC_TRACE_CONTROL,
+ "Invalid temporal id %d\n", temporal_id);
+ return -EINVAL;
+ }
+ } else {
+ temporal_id = 0;
+ }
+
+ ctrl = chain->layer_ctrl;
+
+ memset(data, 0, 2);
+ __uvc_set_le_value(3, 7, temporal_id, data, false);
+ __uvc_set_le_value(3, 10, layer->layer_id, data, true);
+
+ return uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ data, ctrl->info.size);
+}
+
+bool uvc_is_layer_negotiated(struct uvc_streaming *stream, int layer_id)
+{
+ if (layer_id > stream->num_layers)
+ return false;
+
+ if (stream->ctrl.bmLayoutPerStream[layer_id] & 0x1)
+ return true;
+ else
+ return false;
+}
+
+/* Must be called under stream->mutex. */
+int uvc_ctrl_layer_enable(struct uvc_stream_layer *layer, int enable)
+{
+ struct uvc_video_chain *chain = layer->stream->chain;
+ struct uvc_control *ctrl;
+ __u8 data[4];
+ int ret;
+
+ if (!chain->encoding || !chain->startstop_ctrl)
+ return -EINVAL;
+
+ if (layer->stream->streaming_layers) {
+ /* We are streaming. If we can't enable/disable during
+ * streaming, check if stream has already been enabled on P&C,
+ * if so, it couldn't have been changed during streaming, so
+ * just return success. If it hasn't been enabled on P&C,
+ * we can't do it now so return EBUSY.
+ */
+ if (!(layer->stream->simulcast_caps
+ & UVC_SIMUL_CAP_STARTSTOP_LAYER_STREAMTIME)) {
+ if (uvc_is_layer_negotiated(layer->stream,
+ layer->layer_id))
+ return 0;
+ else
+ return -EBUSY;
+ } /* else we can enable/disable and will do so below. */
+ } else if (!(layer->stream->simulcast_caps
+ & UVC_SIMUL_CAP_STARTSTOP_LAYER)) {
+ /* Not streaming, but change not allowed even now.
+ * If the layer has been negotiated on P&C, then it's already
+ * enabled, return success, else we can't enable it at all.
+ */
+ if (uvc_is_layer_negotiated(layer->stream, layer->layer_id))
+ return 0;
+ else
+ return -EBUSY;
+ } /* else not streaming and can enable/disable, do so below. */
+
+ ret = __uvc_ctrl_lock(chain);
+ if (ret < 0)
+ return ret;
+
+ /* TODO: might need to cache current temporal id configuration
+ * for suspend/resume.
+ */
+ ret = __uvc_video_layer_select(layer, 0x7);
+ if (ret)
+ goto unlock;
+
+ ctrl = chain->startstop_ctrl;
+ memset(data, 0, 4);
+ __uvc_set_le_value(1, 0, !!enable, data, false);
+ ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ data, ctrl->info.size);
+unlock:
+ __uvc_ctrl_unlock(chain);
+ return ret;
+}
+
+int __uvc_video_layer_query_resolution(struct uvc_stream_layer *layer,
+ int width, int height,
+ __u8 query)
+{
+ struct uvc_video_chain *chain = layer->stream->chain;
+ struct uvc_control *ctrl;
+ __u8 data[4];
+ int ret;
+
+ ret = __uvc_ctrl_lock(chain);
+ if (ret < 0)
+ return ret;
+
+ ret = __uvc_video_layer_select(layer, 0);
+ if (ret)
+ goto unlock;
+
+ ctrl = chain->res_ctrl;
+ memset(data, 0, 4);
+ __uvc_set_le_value(16, 0, width, data, false);
+ __uvc_set_le_value(16, 16, height, data, true);
+ ret = uvc_query_ctrl(chain->dev, query, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ data, ctrl->info.size);
+
+ if (ret == 0 && (query == UVC_SET_CUR || query == UVC_GET_CUR)) {
+ layer->width = __uvc_get_le_value(16, 0, data,
+ UVC_CTRL_DATA_TYPE_UNSIGNED);
+ layer->height = __uvc_get_le_value(16, 16, data,
+ UVC_CTRL_DATA_TYPE_UNSIGNED);
+ }
+
+unlock:
+ __uvc_ctrl_unlock(chain);
+ return ret;
+}
+
+int uvc_video_layer_set_resolution(struct uvc_stream_layer *layer,
+ int width, int height)
+{
+ struct uvc_video_chain *chain = layer->stream->chain;
+ int ret;
+
+ /* TODO: will fail if not exactly supported resolution. Not a huge
+ * problem, but would be nice to use bResolutionScaling in format
+ * descriptors to adjust to the nearest supported resolution instead.
+ */
+ if (!chain->encoding || !chain->res_ctrl)
+ return -EINVAL;
+
+ if (layer->stream->streaming_layers) {
+ if (!(layer->stream->simulcast_caps
+ & UVC_SIMUL_CAP_RES_STREAMTIME))
+ return -EBUSY;
+ } else if (!(layer->stream->simulcast_caps & UVC_SIMUL_CAP_RES)) {
+ /* This function is only called if we are simulcasting.
+ * Resolution control has to be usable in that case at least
+ * when not streaming, otherwise there would be no other
+ * way to set/get stream resolution. So if we get here,
+ * the device is not really usable.
+ */
+ return -EINVAL;
+ }
+
+ ret = __uvc_video_layer_query_resolution(layer, width, height,
+ UVC_SET_CUR);
+ if (ret == 0) {
+ uvc_trace(UVC_TRACE_CONTROL,
+ "Layer %d resolution set to %dx%d\n",
+ layer->layer_id, width, height);
+ }
+
+ return ret;
+}
+
+int uvc_video_layer_get_resolution(struct uvc_stream_layer *layer)
+{
+ struct uvc_video_chain *chain = layer->stream->chain;
+
+ if (!chain->encoding || !uvc_is_simulcasting(layer->stream)
+ || !chain->res_ctrl) {
+ layer->width = layer->stream->cur_frame->wWidth;
+ layer->height = layer->stream->cur_frame->wHeight;
+ return 0;
+ }
+
+ return __uvc_video_layer_query_resolution(layer, 0, 0, UVC_GET_CUR);
+}
+
+void uvc_ctrl_simulcast_init(struct uvc_streaming *stream)
+{
+ struct uvc_control *ctrl;
+
+ ctrl = stream->chain->startstop_ctrl;
+ if (ctrl) {
+ if (ctrl->on_init)
+ stream->simulcast_caps |= UVC_SIMUL_CAP_STARTSTOP_LAYER;
+ if (ctrl->in_runtime)
+ stream->simulcast_caps |=
+ UVC_SIMUL_CAP_STARTSTOP_LAYER_STREAMTIME;
+ }
+
+ ctrl = stream->chain->res_ctrl;
+ if (ctrl) {
+ if (ctrl->on_init)
+ stream->simulcast_caps |= UVC_SIMUL_CAP_RES;
+ if (ctrl->in_runtime)
+ stream->simulcast_caps |=
+ UVC_SIMUL_CAP_RES_STREAMTIME;
+ }
+
+ uvc_trace(UVC_TRACE_CONTROL, "Available simulcast capabilities: "
+ "start/stop layer: %c (while streaming: %c), "
+ "resolution change: %c (while streaming: %c).\n",
+ stream->simulcast_caps
+ & UVC_SIMUL_CAP_STARTSTOP_LAYER ? 'Y' : 'N',
+ stream->simulcast_caps
+ & UVC_SIMUL_CAP_STARTSTOP_LAYER_STREAMTIME ? 'Y' : 'N',
+ stream->simulcast_caps & UVC_SIMUL_CAP_RES ? 'Y' : 'N',
+ stream->simulcast_caps
+ & UVC_SIMUL_CAP_RES_STREAMTIME ? 'Y' : 'N');
+}
+
+/* --------------------------------------------------------------------------
* Suspend/resume
*/
@@ -402,14 +402,23 @@ static int uvc_parse_format(struct uvc_device *dev,
break;
case UVC_VS_FORMAT_VP8:
+ case UVC_VS_FORMAT_VP8_SIMULCAST:
if (buflen < 13)
goto format_error;
format->bpp = 0;
format->flags = UVC_FMT_FLAG_COMPRESSED;
ftype = UVC_VS_FRAME_VP8;
- strlcpy(format->name, "VP8", sizeof(format->name));
- format->fcc = V4L2_PIX_FMT_VP8;
+ if (buffer[2] == UVC_VS_FORMAT_VP8) {
+ strlcpy(format->name, "VP8", sizeof(format->name));
+ format->fcc = V4L2_PIX_FMT_VP8;
+ } else {
+ strlcpy(format->name, "VP8 Simulcast",
+ sizeof(format->name));
+ format->fcc = V4L2_PIX_FMT_VP8_SIMULCAST;
+ streaming->num_layers = UVC_SIMULCAST_MAX_LAYERS;
+ format->flags |= UVC_FMT_FLAG_SIMULCAST;
+ }
break;
@@ -635,6 +644,12 @@ static int uvc_parse_streaming(struct uvc_device *dev,
streaming->dev = dev;
streaming->intf = usb_get_intf(intf);
streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+ streaming->num_layers = 1;
+ atomic_set(&dev->num_vdevs, 0);
+ streaming->streaming_layers = 0;
+ streaming->allocated_layers = 0;
+ for (i = 0; i < UVC_SIMULCAST_MAX_LAYERS; ++i)
+ streaming->layers[i].layer_id = i;
/* The Pico iMage webcam has its class-specific interface descriptors
* after the endpoint descriptors.
@@ -731,6 +746,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
case UVC_VS_FORMAT_MJPEG:
case UVC_VS_FORMAT_FRAME_BASED:
case UVC_VS_FORMAT_VP8:
+ case UVC_VS_FORMAT_VP8_SIMULCAST:
nformats++;
break;
@@ -804,6 +820,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
case UVC_VS_FORMAT_DV:
case UVC_VS_FORMAT_FRAME_BASED:
case UVC_VS_FORMAT_VP8:
+ case UVC_VS_FORMAT_VP8_SIMULCAST:
format->frame = frame;
ret = uvc_parse_format(dev, streaming, format,
&interval, buffer, buflen);
@@ -1732,6 +1749,7 @@ static int uvc_scan_device(struct uvc_device *dev)
static void uvc_delete(struct uvc_device *dev)
{
struct list_head *p, *n;
+ int i;
usb_put_intf(dev->intf);
usb_put_dev(dev->udev);
@@ -1760,16 +1778,17 @@ static void uvc_delete(struct uvc_device *dev)
#ifdef CONFIG_MEDIA_CONTROLLER
uvc_mc_cleanup_entity(entity);
#endif
- if (entity->vdev) {
- video_device_release(entity->vdev);
- entity->vdev = NULL;
- }
kfree(entity);
}
list_for_each_safe(p, n, &dev->streams) {
struct uvc_streaming *streaming;
streaming = list_entry(p, struct uvc_streaming, list);
+ for (i = 0; i < streaming->num_layers; ++i) {
+ if (streaming->vdev[i])
+ video_device_release(streaming->vdev[i]);
+ streaming->vdev[i] = NULL;
+ }
usb_driver_release_interface(&uvc_driver.driver,
streaming->intf);
usb_put_intf(streaming->intf);
@@ -1783,13 +1802,13 @@ static void uvc_delete(struct uvc_device *dev)
static void uvc_release(struct video_device *vdev)
{
- struct uvc_streaming *stream = video_get_drvdata(vdev);
- struct uvc_device *dev = stream->dev;
+ struct uvc_stream_layer *layer = video_get_drvdata(vdev);
+ struct uvc_device *dev = layer->stream->dev;
- /* Decrement the registered streams count and delete the device when it
+ /* Decrement the registered vdevs count and delete the device when it
* reaches zero.
*/
- if (atomic_dec_and_test(&dev->nstreams))
+ if (atomic_dec_and_test(&dev->num_vdevs))
uvc_delete(dev);
}
@@ -1799,20 +1818,23 @@ static void uvc_release(struct video_device *vdev)
static void uvc_unregister_video(struct uvc_device *dev)
{
struct uvc_streaming *stream;
+ int i;
/* Unregistering all video devices might result in uvc_delete() being
* called from inside the loop if there's no open file handle. To avoid
- * that, increment the stream count before iterating over the streams
+ * that, increment the vdevs count before iterating over the vdevs
* and decrement it when done.
*/
- atomic_inc(&dev->nstreams);
+ atomic_inc(&dev->num_vdevs);
list_for_each_entry(stream, &dev->streams, list) {
- if (stream->vdev == NULL)
- continue;
+ for (i = 0; i < stream->num_layers; ++i) {
+ if (stream->vdev[i] == NULL)
+ continue;
- video_unregister_device(stream->vdev);
- stream->vdev = NULL;
+ video_unregister_device(stream->vdev[i]);
+ stream->vdev[i] = NULL;
+ }
uvc_debugfs_cleanup_stream(stream);
}
@@ -1820,7 +1842,7 @@ static void uvc_unregister_video(struct uvc_device *dev)
/* Decrement the stream count and call uvc_delete explicitly if there
* are no stream left.
*/
- if (atomic_dec_and_test(&dev->nstreams))
+ if (atomic_dec_and_test(&dev->num_vdevs))
uvc_delete(dev);
}
@@ -1828,7 +1850,9 @@ static int uvc_register_video(struct uvc_device *dev,
struct uvc_streaming *stream)
{
struct video_device *vdev;
+ struct uvc_stream_layer *layer;
int ret;
+ int i;
/* Initialize the streaming interface with default streaming
* parameters.
@@ -1843,39 +1867,60 @@ static int uvc_register_video(struct uvc_device *dev,
uvc_debugfs_init_stream(stream);
/* 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;
- }
+ for (i = 0; i < stream->num_layers; ++i) {
+ /* 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->v4l2_dev = &dev->vdev;
- vdev->fops = &uvc_fops;
- vdev->release = uvc_release;
- vdev->prio = &stream->chain->prio;
- set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags);
- if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- vdev->vfl_dir = VFL_DIR_TX;
- 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);
+ /* 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->v4l2_dev = &dev->vdev;
+ vdev->fops = &uvc_fops;
+ vdev->release = uvc_release;
+ vdev->prio = &stream->chain->prio;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags);
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ vdev->vfl_dir = VFL_DIR_TX;
+ 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[i] = vdev;
+ layer = &stream->layers[i];
+ layer->layer_id = i;
+ layer->stream = stream;
+ uvc_video_layer_get_resolution(layer);
+ atomic_set(&layer->active, 0);
+ video_set_drvdata(vdev, layer);
+ ret = uvc_queue_init(&layer->queue, stream->type,
+ !uvc_no_drop_param, stream);
+ if (ret < 0) {
+ uvc_printk(KERN_ERR,
+ "Failed to initialize video queue.\n");
+ stream->vdev[i] = NULL;
+ video_device_release(vdev);
+ return ret;
+ }
- 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;
+ 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[i] = NULL;
+ video_device_release(vdev);
+ return ret;
+ }
+
+ atomic_inc(&dev->num_vdevs);
+ uvc_trace(UVC_TRACE_PROBE,
+ "Registered video device /dev/video%d\n", vdev->minor);
}
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1883,7 +1928,6 @@ static int uvc_register_video(struct uvc_device *dev,
else
stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT;
- atomic_inc(&dev->nstreams);
return 0;
}
@@ -1897,6 +1941,7 @@ static int uvc_register_terms(struct uvc_device *dev,
struct uvc_entity *term;
struct uvc_chain_entry *entry;
int ret;
+ int i;
list_for_each_entry(entry, &chain->entities, chain_entry) {
term = entry->entity;
@@ -1915,7 +1960,8 @@ static int uvc_register_terms(struct uvc_device *dev,
if (ret < 0)
return ret;
- term->vdev = stream->vdev;
+ for (i = 0; i < stream->num_layers; ++i)
+ term->vdev[i] = stream->vdev[i];
}
return 0;
@@ -1924,9 +1970,30 @@ static int uvc_register_terms(struct uvc_device *dev,
static int uvc_register_chains(struct uvc_device *dev)
{
struct uvc_video_chain *chain;
+ struct uvc_control *ctrl;
int ret;
+ int i;
list_for_each_entry(chain, &dev->chains, list) {
+ if (chain->encoding) {
+ for (i = 0; i < chain->encoding->ncontrols; ++i) {
+ ctrl = &chain->encoding->controls[i];
+ if (!ctrl->initialized)
+ continue;
+ switch (ctrl->info.selector) {
+ case UVC_EU_SELECT_LAYER_CONTROL:
+ chain->layer_ctrl = ctrl;
+ break;
+ case UVC_EU_VIDEO_RESOLUTION_CONTROL:
+ chain->res_ctrl = ctrl;
+ break;
+ case UVC_EU_START_OR_STOP_LAYER_CONTROL:
+ chain->startstop_ctrl = ctrl;
+ break;
+ }
+ }
+ }
+
ret = uvc_register_terms(dev, chain);
if (ret < 0)
return ret;
@@ -1934,7 +2001,7 @@ static int uvc_register_chains(struct uvc_device *dev)
#ifdef CONFIG_MEDIA_CONTROLLER
ret = uvc_mc_register_entities(chain);
if (ret < 0) {
- uvc_printk(KERN_INFO, "Failed to register entites "
+ uvc_printk(KERN_INFO, "Failed to register entities "
"(%d).\n", ret);
}
#endif
@@ -1954,13 +2021,14 @@ static int uvc_probe(struct usb_interface *intf,
struct uvc_device *dev;
int ret;
- if (id->idVendor && id->idProduct)
+ if (id->idVendor && id->idProduct) {
uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
"(%04x:%04x)\n", udev->devpath, id->idVendor,
id->idProduct);
- else
+ } else {
uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
udev->devpath);
+ }
/* Allocate memory for the device and initialize it. */
if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
@@ -1969,7 +2037,7 @@ static int uvc_probe(struct usb_interface *intf,
INIT_LIST_HEAD(&dev->entities);
INIT_LIST_HEAD(&dev->chains);
INIT_LIST_HEAD(&dev->streams);
- atomic_set(&dev->nstreams, 0);
+ atomic_set(&dev->num_vdevs, 0);
atomic_set(&dev->nmappings, 0);
mutex_init(&dev->lock);
@@ -23,20 +23,13 @@
* Video subdevices registration and unregistration
*/
-static int uvc_mc_register_entity(struct uvc_video_chain *chain,
- struct uvc_entity *entity)
+static int uvc_mc_register_media_entity(struct uvc_entity *entity,
+ struct media_entity *sink)
{
const u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
- struct media_entity *sink;
unsigned int i;
int ret;
- sink = (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
- ? (entity->vdev ? &entity->vdev->entity : NULL)
- : &entity->subdev.entity;
- if (sink == NULL)
- return 0;
-
for (i = 0; i < entity->num_pads; ++i) {
struct media_entity *source;
struct uvc_entity *remote;
@@ -45,12 +38,17 @@ static int uvc_mc_register_entity(struct uvc_video_chain *chain,
if (!(entity->pads[i].flags & MEDIA_PAD_FL_SINK))
continue;
- remote = uvc_entity_by_id(chain->dev, entity->baSourceID[i]);
+ remote = uvc_entity_by_id(entity->chain->dev,
+ entity->baSourceID[i]);
if (remote == NULL)
return -EINVAL;
+ /*
+ * Take vdev[0], because it has to be an ITERM to be a source
+ * and we don't create more than 1 vdev for those.
+ */
source = (UVC_ENTITY_TYPE(remote) == UVC_TT_STREAMING)
- ? (remote->vdev ? &remote->vdev->entity : NULL)
+ ? (remote->vdev[0] ? &remote->vdev[0]->entity : NULL)
: &remote->subdev.entity;
if (source == NULL)
continue;
@@ -62,10 +60,36 @@ static int uvc_mc_register_entity(struct uvc_video_chain *chain,
return ret;
}
- if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING)
- return 0;
+ return 0;
+}
- return v4l2_device_register_subdev(&chain->dev->vdev, &entity->subdev);
+static int uvc_mc_register_entity(struct uvc_video_chain *chain,
+ struct uvc_entity *entity)
+{
+ struct media_entity *mentity;
+ int ret = 0;
+ int i;
+
+ if (UVC_ENTITY_TYPE(entity) == UVC_TT_STREAMING) {
+ for (i = 0; i < UVC_SIMULCAST_MAX_LAYERS; ++i) {
+ if (entity->vdev[i]) {
+ mentity = &entity->vdev[i]->entity;
+ ret = uvc_mc_register_media_entity(entity,
+ mentity);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ } else {
+ mentity = &entity->subdev.entity;
+ ret = uvc_mc_register_media_entity(entity, mentity);
+ if (ret < 0)
+ return ret;
+ ret = v4l2_device_register_subdev(&chain->dev->vdev,
+ &entity->subdev);
+ }
+
+ return ret;
}
static struct v4l2_subdev_ops uvc_subdev_ops = {
@@ -73,15 +97,26 @@ static struct v4l2_subdev_ops uvc_subdev_ops = {
void uvc_mc_cleanup_entity(struct uvc_entity *entity)
{
- if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING)
+ int i;
+
+ if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) {
media_entity_cleanup(&entity->subdev.entity);
- else if (entity->vdev != NULL)
- media_entity_cleanup(&entity->vdev->entity);
+ } else {
+ for (i = 0; i < UVC_SIMULCAST_MAX_LAYERS; ++i) {
+ if (entity->vdev[i] != NULL)
+ media_entity_cleanup(&entity->vdev[i]->entity);
+ }
+ }
+
+ entity->registered = false;
}
static int uvc_mc_init_entity(struct uvc_entity *entity)
{
- int ret;
+ struct media_pad *pads;
+ struct device *dev = &entity->chain->dev->udev->dev;
+ int ret = 0;
+ int i;
if (UVC_ENTITY_TYPE(entity) != UVC_TT_STREAMING) {
v4l2_subdev_init(&entity->subdev, &uvc_subdev_ops);
@@ -90,13 +125,42 @@ static int uvc_mc_init_entity(struct uvc_entity *entity)
ret = media_entity_init(&entity->subdev.entity,
entity->num_pads, entity->pads, 0);
- } else if (entity->vdev != NULL) {
- ret = media_entity_init(&entity->vdev->entity,
- entity->num_pads, entity->pads, 0);
- if (entity->flags & UVC_ENTITY_FLAG_DEFAULT)
- entity->vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
- } else
- ret = 0;
+ } else {
+ for (i = 0; i < UVC_SIMULCAST_MAX_LAYERS; ++i) {
+ if (entity->vdev[i] != NULL) {
+ /*
+ * As we may have multiple media entities
+ * (vdevs) per one STREAMING UVC entity in
+ * simulcast situations, we have separate memory
+ * allocated for pads for each media entity
+ * to have its own.
+ */
+ pads = devm_kzalloc(dev,
+ sizeof(*pads) * entity->num_pads,
+ GFP_KERNEL);
+ if (!pads) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ memcpy(pads, entity->pads,
+ sizeof(*pads) * entity->num_pads);
+
+ ret = media_entity_init(
+ &entity->vdev[i]->entity,
+ entity->num_pads, pads, 0);
+ if (ret)
+ goto cleanup;
+ }
+ }
+ }
+
+ return ret;
+
+cleanup:
+ for (i = i - 1; i >= 0; ++i) {
+ if (entity->vdev[i] != NULL)
+ media_entity_cleanup(&entity->vdev[i]->entity);
+ }
return ret;
}
@@ -103,8 +103,9 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream)
{
int ret, i;
struct uvc_buffer *buf;
+ struct uvc_video_queue *queue = &stream->layers[0].queue;
- buf = uvc_queue_get_first_buf(&stream->queue);
+ buf = uvc_queue_get_first_buf(queue);
for (i = 0; i < urb->number_of_packets; ++i) {
if (urb->iso_frame_desc[i].status < 0) {
@@ -122,7 +123,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream)
* processes the data of the first payload of the new frame.
*/
do {
- ret = isight_decode(&stream->queue, buf,
+ ret = isight_decode(queue, buf,
urb->transfer_buffer +
urb->iso_frame_desc[i].offset,
urb->iso_frame_desc[i].actual_length);
@@ -132,8 +133,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream)
if (buf->state == UVC_BUF_STATE_DONE ||
buf->state == UVC_BUF_STATE_ERROR)
- buf = uvc_queue_next_buffer(&stream->queue,
- buf);
+ buf = uvc_queue_next_buffer(queue, buf);
} while (ret == -EAGAIN);
}
}
@@ -45,15 +45,13 @@ static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
unsigned int sizes[], void *alloc_ctxs[])
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
- struct uvc_streaming *stream =
- container_of(queue, struct uvc_streaming, queue);
if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
*nbuffers = UVC_MAX_VIDEO_BUFFERS;
*nplanes = 1;
- sizes[0] = stream->ctrl.dwMaxVideoFrameSize;
+ sizes[0] = queue->stream->ctrl.dwMaxVideoFrameSize;
return 0;
}
@@ -107,11 +105,9 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
static int uvc_buffer_finish(struct vb2_buffer *vb)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
- struct uvc_streaming *stream =
- container_of(queue, struct uvc_streaming, queue);
struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
- uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
+ uvc_video_clock_update(queue->stream, &vb->v4l2_buf, buf);
return 0;
}
@@ -139,7 +135,7 @@ static struct vb2_ops uvc_queue_qops = {
};
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
- int drop_corrupted)
+ int drop_corrupted, struct uvc_streaming *stream)
{
int ret;
@@ -158,6 +154,7 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->irqqueue);
queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
+ queue->stream = stream;
return 0;
}
@@ -143,12 +143,63 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
return interval;
}
+static void uvc_init_vp8_probe_features(struct v4l2_format *fmt,
+ struct uvc_streaming *stream, struct uvc_frame *frame,
+ struct uvc_streaming_control *probe)
+{
+ int i;
+ __u16 layout = 0;
+ int num_streams;
+ int num_temprl_layers;
+
+ for (i = 0; i < UVC_NUM_SIMULCAST_STREAMS; ++i)
+ probe->bmLayoutPerStream[i] = 0;
+
+ /* Values to request in P&C. */
+ num_streams = fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_VP8 ?
+ 1 : stream->num_layers;
+
+ if (frame->bmSupportedUsages
+ & UVC_FRM_VP8_USAGE_REAL_TIME_TEMPORAL_LAYERS) {
+ /* Temporal layering supported, specify config for each
+ * temporal layer for each stream.
+ */
+ probe->bUsage = 2;
+ num_temprl_layers = frame->bmScalabilityCapabilities + 1;
+ num_temprl_layers = clamp(num_temprl_layers, 1,
+ UVC_VP8_MAX_TEMPORAL_LAYERS);
+
+ /* Allow all special frames for all temporal layers. */
+ for (i = 0; i < num_temprl_layers; ++i)
+ layout |= UVC_VP8_TEMPORAL_LAYOUT_WILDCARD_LAYER(i);
+ layout |= UVC_VP8_TEMPRL_LAYOUT_NUM(num_temprl_layers);
+ /* Stream enable */
+ layout |= 0x1;
+
+ } else if (frame->bmSupportedUsages & UVC_FRM_VP8_USAGE_REAL_TIME) {
+ probe->bUsage = 1;
+ /* Stream enable */
+ layout = 0x1;
+
+ } else {
+ /* Let the device decide. */
+ probe->bUsage = 0;
+ return;
+ }
+
+ for (i = 0; i < num_streams; ++i)
+ probe->bmLayoutPerStream[i] = layout;
+
+ probe->bmRateControlModes = 0x1111;
+}
+
static int uvc_v4l2_try_format(struct uvc_streaming *stream,
struct v4l2_format *fmt, struct uvc_streaming_control *probe,
struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
{
struct uvc_format *format = NULL;
struct uvc_frame *frame = NULL;
+ struct uvc_frame *simulcast_frame = NULL;
__u16 rw, rh;
unsigned int d, maxd;
unsigned int i;
@@ -218,7 +269,26 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
memset(probe, 0, sizeof *probe);
probe->bmHint = 1; /* dwFrameInterval */
probe->bFormatIndex = format->index;
- probe->bFrameIndex = frame->bFrameIndex;
+ /*
+ * If we are doing simulcast, we'd like to select the highest possible
+ * resolution available and then scale down and back up as needed
+ * via set resolution EU control.
+ */
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_VP8_SIMULCAST) {
+ /* TODO: resolution-framerate tradeoff negotiation logic
+ * for simulcast would be nice to have. */
+ simulcast_frame = &format->frame[0];
+ for (i = 0; i < format->nframes; ++i) {
+ if (format->frame[i].wWidth > simulcast_frame->wWidth)
+ simulcast_frame = &format->frame[i];
+ }
+ uvc_trace(UVC_TRACE_FORMAT, "Selected %dx%d max resolution "
+ "for simulcast\n", simulcast_frame->wWidth,
+ simulcast_frame->wHeight);
+ probe->bFrameIndex = simulcast_frame->bFrameIndex;
+ } else {
+ probe->bFrameIndex = frame->bFrameIndex;
+ }
probe->dwFrameInterval = uvc_try_frame_interval(frame, interval);
/* Some webcams stall the probe control set request when the
* dwMaxVideoFrameSize field is set to zero. The UVC specification
@@ -237,6 +307,12 @@ static int uvc_v4l2_try_format(struct uvc_streaming *stream,
probe->dwMaxVideoFrameSize =
stream->ctrl.dwMaxVideoFrameSize;
+ /* UVC 1.5 features */
+ if (uvc_get_probe_ctrl_size(stream) > 34
+ && (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_VP8
+ || fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_VP8_SIMULCAST))
+ uvc_init_vp8_probe_features(fmt, stream, frame, probe);
+
/* Probe the device. */
ret = uvc_probe_video(stream, probe);
mutex_unlock(&stream->mutex);
@@ -260,11 +336,12 @@ done:
return ret;
}
-static int uvc_v4l2_get_format(struct uvc_streaming *stream,
- struct v4l2_format *fmt)
+static int uvc_v4l2_get_format(struct uvc_stream_layer *layer,
+ struct v4l2_format *fmt)
{
struct uvc_format *format;
struct uvc_frame *frame;
+ struct uvc_streaming *stream = layer->stream;
int ret = 0;
if (fmt->type != stream->type)
@@ -280,8 +357,11 @@ static int uvc_v4l2_get_format(struct uvc_streaming *stream,
}
fmt->fmt.pix.pixelformat = format->fcc;
- fmt->fmt.pix.width = frame->wWidth;
- fmt->fmt.pix.height = frame->wHeight;
+ ret = uvc_video_layer_get_resolution(layer);
+ if (ret)
+ goto done;
+ fmt->fmt.pix.width = layer->width;
+ fmt->fmt.pix.height = layer->height;
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
@@ -293,13 +373,15 @@ done:
return ret;
}
-static int uvc_v4l2_set_format(struct uvc_streaming *stream,
- struct v4l2_format *fmt)
+static int uvc_v4l2_set_format(struct uvc_stream_layer *layer,
+ struct v4l2_format *fmt)
{
struct uvc_streaming_control probe;
struct uvc_format *format;
struct uvc_frame *frame;
+ struct uvc_streaming *stream = layer->stream;
int ret;
+ int i;
if (fmt->type != stream->type)
return -EINVAL;
@@ -309,18 +391,50 @@ static int uvc_v4l2_set_format(struct uvc_streaming *stream,
return ret;
mutex_lock(&stream->mutex);
+ if (stream->allocated_layers == 0) {
+ /* No queue has yet allocated buffers, so we can
+ * set format via a commit.
+ */
+ stream->ctrl = probe;
+ uvc_update_stream_format(stream, format, frame);
- if (uvc_queue_allocated(&stream->queue)) {
- ret = -EBUSY;
- goto done;
- }
+ /* Commit the streaming parameters. */
+ ret = uvc_commit_video(stream, &stream->ctrl);
+ if (ret < 0) {
+ uvc_trace(UVC_TRACE_FORMAT,
+ "Failed committing video\n");
+ goto end;
+ }
- stream->ctrl = probe;
- stream->cur_format = format;
- stream->cur_frame = frame;
+ if (uvc_is_simulcasting(stream)) {
+ ret = uvc_video_layer_set_resolution(layer,
+ fmt->fmt.pix.width, fmt->fmt.pix.height);
+ if (ret)
+ goto end;
+ for (i = 0; i < stream->num_negotiated_layers; ++i) {
+ uvc_video_layer_get_resolution(
+ &stream->layers[i]);
+ }
+ }
+ } else {
+ /* At least one queue has already allocated buffers, so we
+ * can only change resolution and only if simulcasting.
+ */
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_VP8_SIMULCAST
+ && fmt->fmt.pix.pixelformat == stream->cur_format->fcc) {
+ ret = uvc_video_layer_set_resolution(layer,
+ fmt->fmt.pix.width, fmt->fmt.pix.height);
+ } else {
+ uvc_trace(UVC_TRACE_FORMAT, "Cannot change format, "
+ "already initialized/streaming.\n");
+ ret = -EBUSY;
+ }
+ }
-done:
+end:
mutex_unlock(&stream->mutex);
+ if (ret < 0)
+ uvc_trace(UVC_TRACE_FORMAT, "Failed setting format\n");
return ret;
}
@@ -382,7 +496,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
mutex_lock(&stream->mutex);
- if (uvc_queue_streaming(&stream->queue)) {
+ if (stream->allocated_layers != 0) {
mutex_unlock(&stream->mutex);
return -EBUSY;
}
@@ -399,8 +513,15 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
}
stream->ctrl = probe;
+
+ ret = uvc_commit_video(stream, &stream->ctrl);
mutex_unlock(&stream->mutex);
+ if (ret < 0) {
+ uvc_trace(UVC_TRACE_FORMAT, "Failed committing video\n");
+ return ret;
+ }
+
/* Return the actual frame period. */
timeperframe.numerator = probe.dwFrameInterval;
timeperframe.denominator = 10000000;
@@ -449,8 +570,8 @@ static int uvc_acquire_privileges(struct uvc_fh *handle)
return 0;
/* Check if the device already has a privileged handle. */
- if (atomic_inc_return(&handle->stream->active) != 1) {
- atomic_dec(&handle->stream->active);
+ if (atomic_inc_return(&handle->layer->active) != 1) {
+ atomic_dec(&handle->layer->active);
return -EBUSY;
}
@@ -461,7 +582,7 @@ static int uvc_acquire_privileges(struct uvc_fh *handle)
static void uvc_dismiss_privileges(struct uvc_fh *handle)
{
if (handle->state == UVC_HANDLE_ACTIVE)
- atomic_dec(&handle->stream->active);
+ atomic_dec(&handle->layer->active);
handle->state = UVC_HANDLE_PASSIVE;
}
@@ -478,11 +599,16 @@ static int uvc_has_privileges(struct uvc_fh *handle)
static int uvc_v4l2_open(struct file *file)
{
struct uvc_streaming *stream;
+ struct uvc_stream_layer *layer;
struct uvc_fh *handle;
int ret = 0;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
- stream = video_drvdata(file);
+ layer = video_drvdata(file);
+ WARN_ON(!layer);
+ stream = layer->stream;
+ WARN_ON(!stream);
+ WARN_ON(!stream->dev);
if (stream->dev->state & UVC_DEV_DISCONNECTED)
return -ENODEV;
@@ -512,10 +638,10 @@ static int uvc_v4l2_open(struct file *file)
stream->dev->users++;
mutex_unlock(&stream->dev->lock);
- v4l2_fh_init(&handle->vfh, stream->vdev);
+ v4l2_fh_init(&handle->vfh, stream->vdev[layer->layer_id]);
v4l2_fh_add(&handle->vfh);
handle->chain = stream->chain;
- handle->stream = stream;
+ handle->layer = layer;
handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle;
@@ -525,14 +651,19 @@ static int uvc_v4l2_open(struct file *file)
static int uvc_v4l2_release(struct file *file)
{
struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
+ struct uvc_streaming *stream = handle->layer->stream;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
/* Only free resources if this is a privileged handle. */
if (uvc_has_privileges(handle)) {
- uvc_video_enable(stream, 0);
- uvc_free_buffers(&stream->queue);
+ mutex_lock(&stream->mutex);
+
+ uvc_video_layer_disable(handle->layer);
+ clear_bit(handle->layer->layer_id, &stream->allocated_layers);
+
+ mutex_unlock(&stream->mutex);
+ uvc_free_buffers(&handle->layer->queue);
}
/* Release the file handle. */
@@ -556,7 +687,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 = file->private_data;
struct uvc_video_chain *chain = handle->chain;
- struct uvc_streaming *stream = handle->stream;
+ struct uvc_streaming *stream = handle->layer->stream;
long ret = 0;
switch (cmd) {
@@ -612,6 +743,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (ret < 0)
return ret;
+ ret = __uvc_video_layer_select(handle->layer, 0);
+ if (ret < 0) {
+ uvc_ctrl_rollback(handle);
+ return ret;
+ }
+
ret = uvc_ctrl_get(chain, &xctrl,
uvc_is_stream_streaming(stream));
uvc_ctrl_rollback(handle);
@@ -637,6 +774,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (ret < 0)
return ret;
+ /* Apply to all temporal layers. */
+ ret = __uvc_video_layer_select(handle->layer, 0x7);
+ if (ret < 0) {
+ uvc_ctrl_rollback(handle);
+ return ret;
+ }
+
ret = uvc_ctrl_set(chain, &xctrl,
uvc_is_stream_streaming(stream));
if (ret < 0) {
@@ -663,6 +807,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (ret < 0)
return ret;
+ ret = __uvc_video_layer_select(handle->layer,
+ ctrls->reserved[0]);
+ if (ret < 0) {
+ uvc_ctrl_rollback(handle);
+ return ret;
+ }
+
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_get(chain, ctrl,
uvc_is_stream_streaming(stream));
@@ -692,6 +843,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (ret < 0)
return ret;
+ ret = __uvc_video_layer_select(handle->layer,
+ ctrls->reserved[0]);
+ if (ret < 0) {
+ uvc_ctrl_rollback(handle);
+ return ret;
+ }
+
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_set(chain, ctrl,
uvc_is_stream_streaming(stream));
@@ -845,10 +1003,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if ((ret = uvc_acquire_privileges(handle)) < 0)
return ret;
- return uvc_v4l2_set_format(stream, arg);
+ return uvc_v4l2_set_format(handle->layer, arg);
case VIDIOC_G_FMT:
- return uvc_v4l2_get_format(stream, arg);
+ return uvc_v4l2_get_format(handle->layer, arg);
/* Frame size enumeration */
case VIDIOC_ENUM_FRAMESIZES:
@@ -990,15 +1148,18 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return ret;
mutex_lock(&stream->mutex);
- ret = uvc_alloc_buffers(&stream->queue, arg);
- mutex_unlock(&stream->mutex);
- if (ret < 0)
- return ret;
-
- if (ret == 0)
+ ret = uvc_alloc_buffers(&handle->layer->queue, arg);
+ if (ret > 0) {
+ set_bit(handle->layer->layer_id,
+ &stream->allocated_layers);
+ ret = 0;
+ } else if (ret == 0) {
+ clear_bit(handle->layer->layer_id,
+ &stream->allocated_layers);
uvc_dismiss_privileges(handle);
+ }
+ mutex_unlock(&stream->mutex);
- ret = 0;
break;
case VIDIOC_QUERYBUF:
@@ -1008,20 +1169,20 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (!uvc_has_privileges(handle))
return -EBUSY;
- return uvc_query_buffer(&stream->queue, buf);
+ return uvc_query_buffer(&handle->layer->queue, buf);
}
case VIDIOC_QBUF:
if (!uvc_has_privileges(handle))
return -EBUSY;
- return uvc_queue_buffer(&stream->queue, arg);
+ return uvc_queue_buffer(&handle->layer->queue, arg);
case VIDIOC_DQBUF:
if (!uvc_has_privileges(handle))
return -EBUSY;
- return uvc_dequeue_buffer(&stream->queue, arg,
+ return uvc_dequeue_buffer(&handle->layer->queue, arg,
file->f_flags & O_NONBLOCK);
case VIDIOC_STREAMON:
@@ -1039,10 +1200,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return -EBUSY;
mutex_lock(&stream->mutex);
- ret = uvc_video_enable(stream, 1);
+ ret = uvc_video_layer_enable(handle->layer);
mutex_unlock(&stream->mutex);
- if (ret < 0)
- return ret;
+
break;
}
@@ -1060,7 +1220,11 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (!uvc_has_privileges(handle))
return -EBUSY;
- return uvc_video_enable(stream, 0);
+ mutex_lock(&stream->mutex);
+ uvc_video_layer_disable(handle->layer);
+ mutex_unlock(&stream->mutex);
+
+ break;
}
case VIDIOC_SUBSCRIBE_EVENT:
@@ -1334,21 +1498,19 @@ static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
{
struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n");
- return uvc_queue_mmap(&stream->queue, vma);
+ return uvc_queue_mmap(&handle->layer->queue, vma);
}
static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait)
{
struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
- return uvc_queue_poll(&stream->queue, file, wait);
+ return uvc_queue_poll(&handle->layer->queue, file, wait);
}
#ifndef CONFIG_MMU
@@ -1357,11 +1519,10 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
unsigned long flags)
{
struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n");
- return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
+ return uvc_queue_get_unmapped_area(&handle->layer->queue, pgoff);
}
#endif
@@ -344,6 +344,14 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream,
ret = 0;
}
+ /* Set EU control streaming state for each layer. */
+ if (uvc_is_eu_active(stream)) {
+ for (i = 0; i < stream->num_negotiated_layers; ++i) {
+ uvc_ctrl_layer_enable(&stream->layers[i],
+ !!test_bit(i, &stream->streaming_layers));
+ }
+ }
+
kfree(data);
return ret;
}
@@ -404,13 +412,17 @@ int uvc_probe_video(struct uvc_streaming *stream,
probe->wPFrameRate = probe_min.wPFrameRate;
probe->wCompQuality = probe_max.wCompQuality;
probe->wCompWindowSize = probe_min.wCompWindowSize;
+
+ /* TODO: negotiate bmLayoutPerStream for simulcast if
+ * adjusted by the camera.
+ */
}
done:
return ret;
}
-static int uvc_commit_video(struct uvc_streaming *stream,
+int uvc_commit_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe)
{
return uvc_set_video_ctrl(stream, probe, 0);
@@ -808,7 +820,7 @@ static void uvc_video_stats_update(struct uvc_streaming *stream)
uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, "
"%u/%u/%u pts (%searly %sinitial), %u/%u scr, "
"last pts/stc/sof %u/%u/%u\n",
- stream->sequence, frame->first_data,
+ stream->layers[stream->layer_id].sequence, frame->first_data,
frame->nb_packets - frame->nb_empty, frame->nb_packets,
frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts,
frame->has_early_pts ? "" : "!",
@@ -943,13 +955,14 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
struct uvc_buffer *buf, struct uvc_payload_header *header)
{
u8 fid = header->fid;
+ struct uvc_stream_layer *layer = uvc_get_curr_layer(stream);
/* Increase the sequence number regardless of any buffer states, so
* that discontinuous sequence numbers always indicate lost frames.
*/
- if (stream->last_fid != fid) {
- stream->sequence++;
- if (stream->sequence)
+ if (layer->last_fid != fid) {
+ layer->sequence++;
+ if (layer->sequence)
uvc_video_stats_update(stream);
}
@@ -960,7 +973,9 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
* NULL.
*/
if (buf == NULL) {
- stream->last_fid = fid;
+ uvc_trace(UVC_TRACE_FRAME, "No buffers available for layer %d, "
+ "dropping payload\n", layer->layer_id);
+ layer->last_fid = fid;
return -ENODATA;
}
@@ -982,12 +997,13 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
if (buf->state != UVC_BUF_STATE_ACTIVE) {
struct timespec ts;
- if (fid == stream->last_fid) {
+ if (fid == layer->last_fid) {
uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
- "sync).\n");
+ "sync: fid: %d, last: %d) for layer: %d.\n",
+ fid, layer->last_fid, layer->layer_id);
if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
(header->has_eof))
- stream->last_fid ^= UVC_STREAM_FID;
+ layer->last_fid ^= UVC_STREAM_FID;
return -ENODATA;
}
@@ -996,7 +1012,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
else
ktime_get_real_ts(&ts);
- buf->buf.v4l2_buf.sequence = stream->sequence;
+ buf->buf.v4l2_buf.sequence = layer->sequence;
buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
buf->buf.v4l2_buf.timestamp.tv_usec =
ts.tv_nsec / NSEC_PER_USEC;
@@ -1012,7 +1028,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
* last payload can be lost anyway). We thus must check if the FID has
* been toggled.
*
- * stream->last_fid is initialized to -1, so the first isochronous
+ * layer->last_fid is initialized to -1, so the first isochronous
* frame will never trigger an end of frame detection.
*
* Empty buffers (bytesused == 0) don't trigger end of frame detection
@@ -1020,14 +1036,14 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
* avoids detecting end of frame conditions at FID toggling if the
* previous payload had the EOF bit set.
*/
- if (fid != stream->last_fid && buf->bytesused != 0) {
+ if (fid != layer->last_fid && buf->bytesused != 0) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
"toggled).\n");
buf->state = UVC_BUF_STATE_READY;
return -EAGAIN;
}
- stream->last_fid = fid;
+ layer->last_fid = fid;
return 0;
}
@@ -1060,6 +1076,8 @@ static void uvc_video_decode_data(struct uvc_streaming *stream,
static void uvc_video_decode_end(struct uvc_streaming *stream,
struct uvc_buffer *buf, struct uvc_payload_header *header)
{
+ struct uvc_stream_layer *layer = uvc_get_curr_layer(stream);
+
/* Mark the buffer as done if the EOF marker is set. */
if (header->has_eof && buf->bytesused != 0) {
uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
@@ -1067,7 +1085,7 @@ static void uvc_video_decode_end(struct uvc_streaming *stream,
uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
buf->state = UVC_BUF_STATE_READY;
if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
- stream->last_fid ^= UVC_STREAM_FID;
+ layer->last_fid ^= UVC_STREAM_FID;
}
}
@@ -1087,14 +1105,14 @@ static int uvc_video_encode_header(struct uvc_streaming *stream,
{
data[0] = 2; /* Header length */
data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
- | (stream->last_fid & UVC_STREAM_FID);
+ | (stream->layers[0].last_fid & UVC_STREAM_FID);
return 2;
}
static int uvc_video_encode_data(struct uvc_streaming *stream,
struct uvc_buffer *buf, __u8 *data, int len)
{
- struct uvc_video_queue *queue = &stream->queue;
+ struct uvc_video_queue *queue = &stream->layers[0].queue;
unsigned int nbytes;
void *mem;
@@ -1110,6 +1128,37 @@ static int uvc_video_encode_data(struct uvc_streaming *stream,
return nbytes;
}
+static int uvc_video_decode_layer_id(struct uvc_streaming *stream,
+ struct uvc_payload_header *header)
+{
+ unsigned int layer_id;
+
+ if (header->has_sli) {
+ layer_id = (header->sli >> UVC_STREAM_STREAM_ID_SHIFT)
+ & UVC_STREAM_STREAM_ID_MASK;
+ if (layer_id >= stream->num_layers) {
+ uvc_trace(UVC_TRACE_FRAME, "Invalid layer id (%d)\n",
+ layer_id);
+ return -EINVAL;
+ }
+
+ stream->prev_layer_id = stream->layer_id;
+ stream->layer_id = layer_id;
+ stream->temporal_id = (header->sli >> UVC_STREAM_TMPRL_ID_SHIFT)
+ & UVC_STREAM_TEMPRL_ID_MASK;
+
+ header->buf_flags |= (stream->temporal_id
+ & V4L2_BUF_FLAG_LAYER_STRUCTURE_MASK)
+ << V4L2_BUF_FLAG_LAYER_STRUCTURE_SHIFT;
+ } else {
+ stream->prev_layer_id = stream->layer_id;
+ stream->layer_id = 0;
+ stream->temporal_id = 0;
+ }
+
+ return 0;
+}
+
static int uvc_video_parse_header(struct uvc_streaming *stream,
const __u8 *data, int len, struct uvc_payload_header *header)
{
@@ -1138,7 +1187,8 @@ static int uvc_video_parse_header(struct uvc_streaming *stream,
header->buf_flags = 0;
- if (stream->cur_format->fcc == V4L2_PIX_FMT_VP8) {
+ if (stream->cur_format->fcc == V4L2_PIX_FMT_VP8
+ || stream->cur_format->fcc == V4L2_PIX_FMT_VP8_SIMULCAST) {
/* VP8 payload has 2 additional bytes of BFH. */
header->length += 2;
off += 2;
@@ -1203,6 +1253,7 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream)
unsigned int len;
u8 *mem;
int ret, i;
+ struct uvc_video_queue *queue;
struct uvc_buffer *buf = NULL;
struct uvc_payload_header header;
@@ -1223,12 +1274,17 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream)
if (ret < 0)
continue;
- buf = uvc_queue_get_first_buf(&stream->queue);
+ ret = uvc_video_decode_layer_id(stream, &header);
+ if (ret < 0)
+ continue;
+
+ queue = &stream->layers[stream->layer_id].queue;
+ buf = uvc_queue_get_first_buf(queue);
+
do {
ret = uvc_video_decode_start(stream, buf, &header);
if (ret == -EAGAIN)
- buf = uvc_queue_next_buffer(&stream->queue,
- buf);
+ buf = uvc_queue_next_buffer(queue, buf);
} while (ret == -EAGAIN);
if (ret < 0)
@@ -1249,7 +1305,7 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream)
UVC_FMT_FLAG_COMPRESSED))
buf->error = 1;
- buf = uvc_queue_next_buffer(&stream->queue, buf);
+ buf = uvc_queue_next_buffer(queue, buf);
}
}
}
@@ -1258,6 +1314,7 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream)
{
u8 *mem;
int len, ret;
+ struct uvc_video_queue *queue;
struct uvc_payload_header header;
struct uvc_buffer *buf;
@@ -1276,7 +1333,12 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream)
if (ret < 0)
return;
- buf = uvc_queue_get_first_buf(&stream->queue);
+ ret = uvc_video_decode_layer_id(stream, &header);
+ if (ret < 0)
+ return;
+
+ queue = &stream->layers[stream->layer_id].queue;
+ buf = uvc_queue_get_first_buf(queue);
/* If the URB is the first of its payload, decode and save the
* header.
@@ -1285,8 +1347,7 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream)
do {
ret = uvc_video_decode_start(stream, buf, &header);
if (ret == -EAGAIN)
- buf = uvc_queue_next_buffer(&stream->queue,
- buf);
+ buf = uvc_queue_next_buffer(queue, buf);
} while (ret == -EAGAIN);
/* If an error occurred skip the rest of the payload. */
@@ -1320,8 +1381,7 @@ static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream)
if (!stream->bulk.skip_payload && buf != NULL) {
uvc_video_decode_end(stream, buf, &header);
if (buf->state == UVC_BUF_STATE_READY)
- buf = uvc_queue_next_buffer(&stream->queue,
- buf);
+ buf = uvc_queue_next_buffer(queue, buf);
}
stream->bulk.header_size = 0;
@@ -1334,9 +1394,10 @@ static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream)
{
u8 *mem = urb->transfer_buffer;
int len = stream->urb_size, ret;
+ struct uvc_stream_layer *layer = &stream->layers[0];
struct uvc_buffer *buf;
- buf = uvc_queue_get_first_buf(&stream->queue);
+ buf = uvc_queue_get_first_buf(&layer->queue);
if (buf == NULL) {
urb->transfer_buffer_length = 0;
return;
@@ -1357,14 +1418,15 @@ static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream)
stream->bulk.payload_size += ret;
len -= ret;
- if (buf->bytesused == stream->queue.buf_used ||
+ if (buf->bytesused == layer->queue.buf_used ||
stream->bulk.payload_size == stream->bulk.max_payload_size) {
- if (buf->bytesused == stream->queue.buf_used) {
- stream->queue.buf_used = 0;
+ if (buf->bytesused == layer->queue.buf_used) {
+ layer->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_READY;
- buf->buf.v4l2_buf.sequence = ++stream->sequence;
- uvc_queue_next_buffer(&stream->queue, buf);
- stream->last_fid ^= UVC_STREAM_FID;
+ buf->buf.v4l2_buf.sequence =
+ ++layer->sequence;
+ uvc_queue_next_buffer(&layer->queue, buf);
+ layer->last_fid ^= UVC_STREAM_FID;
}
stream->bulk.header_size = 0;
@@ -1378,6 +1440,7 @@ static void uvc_video_complete(struct urb *urb)
{
struct uvc_streaming *stream = urb->context;
int ret;
+ int i;
switch (urb->status) {
case 0:
@@ -1393,7 +1456,9 @@ static void uvc_video_complete(struct urb *urb)
case -ECONNRESET: /* usb_unlink_urb() called. */
case -ESHUTDOWN: /* The endpoint is being disabled. */
- uvc_queue_cancel(&stream->queue, urb->status == -ESHUTDOWN);
+ for (i = 0; i < stream->num_layers; ++i)
+ uvc_queue_cancel(&stream->layers[i].queue,
+ urb->status == -ESHUTDOWN);
return;
}
@@ -1648,8 +1713,10 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
unsigned int i;
int ret;
- stream->sequence = -1;
- stream->last_fid = -1;
+ for (i = 0; i < stream->num_layers; ++i) {
+ stream->layers[i].sequence = -1;
+ stream->layers[i].last_fid = -1;
+ }
stream->bulk.header_size = 0;
stream->bulk.skip_payload = 0;
stream->bulk.payload_size = 0;
@@ -1748,7 +1815,7 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
*/
int uvc_video_suspend(struct uvc_streaming *stream)
{
- if (!uvc_queue_streaming(&stream->queue))
+ if (stream->streaming_layers == 0)
return 0;
stream->frozen = 1;
@@ -1761,13 +1828,14 @@ int uvc_video_suspend(struct uvc_streaming *stream)
* Reconfigure the video interface and restart streaming if it was enabled
* before suspend.
*
- * If an error occurs, disable the video queue. This will wake all pending
+ * If an error occurs, disable video queues. This will wake all pending
* buffers, making sure userspace applications are notified of the problem
* instead of waiting forever.
*/
int uvc_video_resume(struct uvc_streaming *stream, int reset)
{
int ret;
+ int i;
/* If the bus has been reset on resume, set the alternate setting to 0.
* This should be the default value, but some devices crash or otherwise
@@ -1782,18 +1850,23 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset)
uvc_video_clock_reset(stream);
ret = uvc_commit_video(stream, &stream->ctrl);
- if (ret < 0) {
- uvc_queue_enable(&stream->queue, 0);
- return ret;
- }
+ if (ret < 0)
+ goto fail;
- if (!uvc_queue_streaming(&stream->queue))
+ /* At least one queue must be streaming for us to re-enable video. */
+ if (stream->streaming_layers == 0)
return 0;
ret = uvc_init_video(stream, GFP_NOIO);
if (ret < 0)
- uvc_queue_enable(&stream->queue, 0);
+ goto fail;
+ return 0;
+
+fail:
+ for (i = 0; i < stream->num_layers; ++i)
+ uvc_queue_enable(&stream->layers[i].queue, 0);
+ stream->streaming_layers = 0;
return ret;
}
@@ -1824,13 +1897,6 @@ int uvc_video_init(struct uvc_streaming *stream)
return -EINVAL;
}
- atomic_set(&stream->active, 0);
-
- /* Initialize the video buffers queue. */
- ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
- if (ret)
- return ret;
-
/* Alternate setting 0 should be the default, yet the XBox Live Vision
* Cam (and possibly other devices) crash or otherwise misbehave if
* they don't receive a SET_INTERFACE request before any other video
@@ -1884,9 +1950,7 @@ int uvc_video_init(struct uvc_streaming *stream)
probe->bFormatIndex = format->index;
probe->bFrameIndex = frame->bFrameIndex;
- stream->def_format = format;
- stream->cur_format = format;
- stream->cur_frame = frame;
+ uvc_update_stream_format(stream, format, frame);
/* Select the video decoding function */
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
@@ -1906,37 +1970,43 @@ int uvc_video_init(struct uvc_streaming *stream)
}
}
+ /* If an encoding unit is present, initialize simulcast capabilities. */
+ uvc_ctrl_simulcast_init(stream);
+
return 0;
}
/*
- * Enable or disable the video stream.
+ * Called on STREAMOFF/release for the last layer to be disabled.
*/
-int uvc_video_enable(struct uvc_streaming *stream, int enable)
+static void uvc_video_disable(struct uvc_streaming *stream)
+{
+ uvc_trace(UVC_TRACE_VIDEO, "Disabling video\n");
+
+ uvc_uninit_video(stream, 1);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+ uvc_video_clock_cleanup(stream);
+ /*
+ * UVC standard unfortunately specifies that after setting interface
+ * to 0, EU state is undefined until next commit.
+ * So commit to be able to query EU controls.
+ */
+ uvc_commit_video(stream, &stream->ctrl);
+}
+
+/*
+ * Called on STREAMON for the first layer to be enabled.
+ */
+static int uvc_video_enable(struct uvc_streaming *stream)
{
int ret;
- if (!enable) {
- uvc_uninit_video(stream, 1);
- usb_set_interface(stream->dev->udev, stream->intfnum, 0);
- uvc_queue_enable(&stream->queue, 0);
- uvc_video_clock_cleanup(stream);
- return 0;
- }
+ uvc_trace(UVC_TRACE_VIDEO, "Enabling video\n");
ret = uvc_video_clock_init(stream);
if (ret < 0)
return ret;
- ret = uvc_queue_enable(&stream->queue, 1);
- if (ret < 0)
- goto error_queue;
-
- /* Commit the streaming parameters. */
- ret = uvc_commit_video(stream, &stream->ctrl);
- if (ret < 0)
- goto error_commit;
-
ret = uvc_init_video(stream, GFP_KERNEL);
if (ret < 0)
goto error_video;
@@ -1944,11 +2014,89 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable)
return 0;
error_video:
+ uvc_printk(KERN_ERR, "Failed to enable video!\n");
usb_set_interface(stream->dev->udev, stream->intfnum, 0);
-error_commit:
- uvc_queue_enable(&stream->queue, 0);
-error_queue:
uvc_video_clock_cleanup(stream);
return ret;
}
+
+/* Must be called with stream->mutex taken. */
+int uvc_video_layer_enable(struct uvc_stream_layer *layer)
+{
+ struct uvc_streaming *stream = layer->stream;
+ int ret = 0;
+
+ uvc_trace(UVC_TRACE_VIDEO, "Enabling layer: %d\n", layer->layer_id);
+
+ ret = uvc_queue_enable(&layer->queue, 1);
+ /* This will also guarantee that we don't enable streaming more than
+ * once on this layer.
+ */
+ if (ret)
+ return ret;
+
+ /* Enable via EU control first if present/possible. This will not enable
+ * streaming if video is not active.
+ */
+ if (uvc_is_eu_active(stream)) {
+ ret = uvc_ctrl_layer_enable(layer, 1);
+ if (ret) {
+ uvc_queue_enable(&layer->queue, 0);
+ return ret;
+ }
+ }
+
+ if (stream->streaming_layers == 0) {
+ ret = uvc_video_enable(stream);
+ if (ret) {
+ uvc_ctrl_layer_enable(layer, 0);
+ uvc_queue_enable(&layer->queue, 0);
+ return ret;
+ }
+ }
+
+ set_bit(layer->layer_id, &stream->streaming_layers);
+ return ret;
+}
+
+/* Must be called with stream->mutex taken. */
+void uvc_video_layer_disable(struct uvc_stream_layer *layer)
+{
+ struct uvc_streaming *stream = layer->stream;
+
+ uvc_trace(UVC_TRACE_VIDEO, "Disabling layer: %d\n", layer->layer_id);
+
+ if (uvc_is_eu_active(stream))
+ uvc_ctrl_layer_enable(layer, 0);
+
+ clear_bit(layer->layer_id, &stream->streaming_layers);
+ if (stream->streaming_layers == 0)
+ uvc_video_disable(stream);
+
+ uvc_queue_enable(&layer->queue, 0);
+}
+
+void uvc_update_stream_format(struct uvc_streaming *stream,
+ struct uvc_format *format, struct uvc_frame *frame)
+{
+ int i;
+
+ stream->def_format = format;
+ stream->cur_format = format;
+ stream->cur_frame = frame;
+ stream->num_negotiated_layers = 0;
+
+ if (uvc_is_eu_active(stream)) {
+ for (i = 0; i < stream->num_layers; ++i) {
+ stream->num_negotiated_layers +=
+ uvc_is_layer_negotiated(stream, i) ? 1 : 0;
+ }
+ } else {
+ stream->num_negotiated_layers = 1;
+ }
+
+ uvc_trace(UVC_TRACE_PROBE, "Negotiated max layers: %d\n",
+ stream->num_negotiated_layers);
+}
+
@@ -130,6 +130,19 @@
#define UVC_MAX_CONTROL_MAPPINGS 1024
#define UVC_MAX_CONTROL_MENU_ENTRIES 32
+#define UVC_SIMULCAST_MAX_LAYERS (UVC_NUM_SIMULCAST_STREAMS)
+#define UVC_VP8_MAX_TEMPORAL_LAYERS 4
+
+/* VP8 format/P&C features */
+#define UVC_FRM_VP8_USAGE_REAL_TIME (1 << 1)
+#define UVC_FRM_VP8_USAGE_REAL_TIME_TEMPORAL_LAYERS (1 << 2)
+
+/* VP8 P&C stream layout configuration (bmLayoutPerStream). */
+#define UVC_VP8_TEMPORAL_LAYOUT_WILDCARD 0x7
+#define UVC_VP8_TEMPORAL_LAYOUT_WILDCARD_LAYER(layer) \
+ ((UVC_VP8_TEMPORAL_LAYOUT_WILDCARD) << (3 + (layer) * 3))
+#define UVC_VP8_TEMPRL_LAYOUT_NUM(n) ((((n) - 1) & 0x3) << 1)
+
/* Devices quirks */
#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
#define UVC_QUIRK_PROBE_MINMAX 0x00000002
@@ -144,6 +157,7 @@
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
#define UVC_FMT_FLAG_STREAM 0x00000002
+#define UVC_FMT_FLAG_SIMULCAST 0x00000004
/* v4l2_buffer codec flags */
#define UVC_V4L2_BUFFER_CODEC_FLAGS (V4L2_BUF_FLAG_KEYFRAME | \
@@ -251,7 +265,7 @@ struct uvc_entity {
char name[64];
/* Media controller-related fields. */
- struct video_device *vdev;
+ struct video_device *vdev[UVC_SIMULCAST_MAX_LAYERS];
struct v4l2_subdev subdev;
unsigned int num_pads;
unsigned int num_links;
@@ -392,6 +406,8 @@ struct uvc_video_queue {
spinlock_t irqlock; /* Protects irqqueue */
struct list_head irqqueue;
+
+ struct uvc_streaming *stream;
};
struct uvc_video_pipeline {
@@ -409,6 +425,10 @@ struct uvc_video_chain {
struct uvc_entity *selector; /* Selector unit */
struct uvc_entity *encoding; /* Encoding unit */
+ struct uvc_control *layer_ctrl;
+ struct uvc_control *res_ctrl;
+ struct uvc_control *startstop_ctrl;
+
struct uvc_video_pipeline *pipeline; /* Pipeline this chain
belongs to */
@@ -484,12 +504,30 @@ struct uvc_payload_header {
__u32 buf_flags; /* v4l2_buffer flags */
};
+struct uvc_stream_layer {
+ int layer_id;
+ int width;
+ int height;
+
+ __u32 sequence;
+ __u8 last_fid;
+
+ struct uvc_video_queue queue;
+ struct uvc_streaming *stream;
+
+ atomic_t active;
+};
+
+#define UVC_SIMUL_CAP_STARTSTOP_LAYER (1 << 0)
+#define UVC_SIMUL_CAP_STARTSTOP_LAYER_STREAMTIME (1 << 1)
+#define UVC_SIMUL_CAP_RES (1 << 2)
+#define UVC_SIMUL_CAP_RES_STREAMTIME (1 << 3)
+
struct uvc_streaming {
struct list_head list;
struct uvc_device *dev;
- struct video_device *vdev;
+ struct video_device *vdev[UVC_SIMULCAST_MAX_LAYERS];
struct uvc_video_chain *chain;
- atomic_t active;
struct usb_interface *intf;
int intfnum;
@@ -506,13 +544,17 @@ struct uvc_streaming {
struct uvc_format *cur_format;
struct uvc_frame *cur_frame;
/* Protect access to ctrl, cur_format, cur_frame and hardware video
- * probe control.
+ * probe control as well as layer allocated/streaming state.
*/
struct mutex mutex;
/* Buffers queue. */
unsigned int frozen : 1;
- struct uvc_video_queue queue;
+
+ /* Protected by mutex. */
+ unsigned long allocated_layers;
+ unsigned long streaming_layers;
+
void (*decode) (struct urb *urb, struct uvc_streaming *video);
/* Context data used by the bulk completion handler. */
@@ -529,9 +571,6 @@ struct uvc_streaming {
dma_addr_t urb_dma[UVC_URBS];
unsigned int urb_size;
- __u32 sequence;
- __u8 last_fid;
-
/* debugfs */
struct dentry *debugfs_dir;
struct {
@@ -557,6 +596,19 @@ struct uvc_streaming {
spinlock_t lock;
} clock;
+
+ /* Max number of layers this streaming interface
+ * can potentially support.
+ */
+ int num_layers;
+ /* Negotiated in P&C. */
+ int num_negotiated_layers;
+ struct uvc_stream_layer layers[UVC_SIMULCAST_MAX_LAYERS];
+ unsigned long simulcast_caps;
+
+ int layer_id;
+ int prev_layer_id;
+ int temporal_id;
};
enum uvc_device_state {
@@ -589,7 +641,7 @@ struct uvc_device {
/* Video Streaming interfaces */
struct list_head streams;
- atomic_t nstreams;
+ atomic_t num_vdevs;
/* Status Interrupt Endpoint */
struct usb_host_endpoint *int_ep;
@@ -607,7 +659,7 @@ enum uvc_handle_state {
struct uvc_fh {
struct v4l2_fh vfh;
struct uvc_video_chain *chain;
- struct uvc_streaming *stream;
+ struct uvc_stream_layer *layer;
enum uvc_handle_state state;
};
@@ -677,7 +729,8 @@ extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
/* Video buffers queue management. */
extern int uvc_queue_init(struct uvc_video_queue *queue,
- enum v4l2_buf_type type, int drop_corrupted);
+ enum v4l2_buf_type type, int drop_corrupted,
+ struct uvc_streaming *stream);
extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
struct v4l2_requestbuffers *rb);
extern void uvc_free_buffers(struct uvc_video_queue *queue);
@@ -717,17 +770,21 @@ extern void uvc_mc_cleanup_entity(struct uvc_entity *entity);
extern int uvc_video_init(struct uvc_streaming *stream);
extern int uvc_video_suspend(struct uvc_streaming *stream);
extern int uvc_video_resume(struct uvc_streaming *stream, int reset);
-extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
+int uvc_video_layer_enable(struct uvc_stream_layer *layer);
+void uvc_video_layer_disable(struct uvc_stream_layer *layer);
extern int uvc_probe_video(struct uvc_streaming *stream,
struct uvc_streaming_control *probe);
+int uvc_commit_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe);
extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
__u8 intfnum, __u8 cs, void *data, __u16 size);
void uvc_video_clock_update(struct uvc_streaming *stream,
struct v4l2_buffer *v4l2_buf,
struct uvc_buffer *buf);
+int uvc_get_probe_ctrl_size(struct uvc_streaming *stream);
static inline bool uvc_is_stream_streaming(struct uvc_streaming *stream)
{
- return vb2_is_streaming(&stream->queue.queue);
+ return stream->streaming_layers != 0;
}
/* Status */
@@ -773,6 +830,13 @@ extern int uvc_ctrl_set(struct uvc_video_chain *chain,
extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry);
+int uvc_ctrl_layer_enable(struct uvc_stream_layer *layer, int enable);
+int uvc_video_layer_set_resolution(struct uvc_stream_layer *layer,
+ int width, int height);
+int uvc_video_layer_get_resolution(struct uvc_stream_layer *layer);
+void uvc_ctrl_simulcast_init(struct uvc_streaming *stream);
+int __uvc_video_layer_select(struct uvc_stream_layer *layer, int temporal_id);
+
/* Utility functions */
extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
unsigned int n_terms, unsigned int threshold);
@@ -781,6 +845,35 @@ extern uint32_t uvc_fraction_to_interval(uint32_t numerator,
extern struct usb_host_endpoint *uvc_find_endpoint(
struct usb_host_interface *alts, __u8 epaddr);
+void uvc_update_stream_format(struct uvc_streaming *stream,
+ struct uvc_format *format, struct uvc_frame *frame);
+bool uvc_is_layer_negotiated(struct uvc_streaming *stream, int layer_id);
+
+static inline bool uvc_is_simulcasting(struct uvc_streaming *stream)
+{
+ return stream->cur_format
+ && (stream->cur_format->flags & UVC_FMT_FLAG_SIMULCAST);
+}
+
+static inline bool uvc_is_eu_active(struct uvc_streaming *stream)
+{
+ return stream->cur_format
+ && (stream->cur_format->fcc == V4L2_PIX_FMT_VP8
+ || stream->cur_format->fcc == V4L2_PIX_FMT_VP8_SIMULCAST);
+}
+
+static inline struct uvc_stream_layer *uvc_get_curr_layer(
+ struct uvc_streaming *stream)
+{
+ return &stream->layers[stream->layer_id];
+}
+
+static inline bool uvc_cur_fmt_has_tmprl_layers(struct uvc_streaming *stream)
+{
+ return stream->cur_format->fcc == V4L2_PIX_FMT_VP8
+ || stream->cur_format->fcc == V4L2_PIX_FMT_VP8_SIMULCAST;
+}
+
/* Quirks support */
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream);
@@ -56,6 +56,7 @@
#define UVC_VS_FORMAT_STREAM_BASED 0x12
#define UVC_VS_FORMAT_VP8 0x16
#define UVC_VS_FRAME_VP8 0x17
+#define UVC_VS_FORMAT_VP8_SIMULCAST 0x18
/* A.7. Video Class-Specific Endpoint Descriptor Subtypes */
#define UVC_EP_UNDEFINED 0x00
@@ -202,6 +203,11 @@
#define UVC_STREAM_VP8_ARF (1 << 1)
#define UVC_STREAM_VP8_PRF (1 << 0)
+#define UVC_STREAM_STREAM_ID_SHIFT 10
+#define UVC_STREAM_STREAM_ID_MASK 0x3
+#define UVC_STREAM_TMPRL_ID_SHIFT 7
+#define UVC_STREAM_TEMPRL_ID_MASK 0x3
+
/* 4.1.2. Control Capabilities */
#define UVC_CONTROL_CAP_GET (1 << 0)
#define UVC_CONTROL_CAP_SET (1 << 1)