[{"id":2109,"web_url":"https://patchwork.libcamera.org/comment/2109/","msgid":"<20190702002300.GJ9228@bigcity.dyn.berto.se>","date":"2019-07-02T00:23:00","subject":"Re: [libcamera-devel] [PATCH v4 08/13] libcamera: pipeline:\n\tuvcvideo: Add controls support","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Kieran, Laurent\n\nThanks for your work.\n\nOn 2019-07-01 23:14:59 +0300, Laurent Pinchart wrote:\n> From: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> Implement control support in the UVC pipeline handler by dynamically\n> querying the V4L2 device for the supported V4L2 controls and populating\n> the list of camera controls accordingly.\n> \n> Not-signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nMaybe drop the Not- before merging ;-)\n\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> Changes since v3:\n> \n> - Fixed error checking when setting controls\n> - Fixed handling of the failure to find a default video device\n> ---\n>  src/libcamera/pipeline/uvcvideo.cpp | 124 +++++++++++++++++++++++++---\n>  1 file changed, 114 insertions(+), 10 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n> index 2e22523d7cb1..b2f5b2eeed80 100644\n> --- a/src/libcamera/pipeline/uvcvideo.cpp\n> +++ b/src/libcamera/pipeline/uvcvideo.cpp\n> @@ -6,8 +6,11 @@\n>   */\n>  \n>  #include <algorithm>\n> +#include <iomanip>\n> +#include <tuple>\n>  \n>  #include <libcamera/camera.h>\n> +#include <libcamera/controls.h>\n>  #include <libcamera/request.h>\n>  #include <libcamera/stream.h>\n>  \n> @@ -16,6 +19,7 @@\n>  #include \"media_device.h\"\n>  #include \"pipeline_handler.h\"\n>  #include \"utils.h\"\n> +#include \"v4l2_controls.h\"\n>  #include \"v4l2_videodevice.h\"\n>  \n>  namespace libcamera {\n> @@ -35,6 +39,7 @@ public:\n>  \t\tdelete video_;\n>  \t}\n>  \n> +\tint init(MediaEntity *entity);\n>  \tvoid bufferReady(Buffer *buffer);\n>  \n>  \tV4L2VideoDevice *video_;\n> @@ -71,6 +76,8 @@ public:\n>  \tbool match(DeviceEnumerator *enumerator) override;\n>  \n>  private:\n> +\tint processControls(UVCCameraData *data, Request *request);\n> +\n>  \tUVCCameraData *cameraData(const Camera *camera)\n>  \t{\n>  \t\treturn static_cast<UVCCameraData *>(\n> @@ -216,6 +223,56 @@ void PipelineHandlerUVC::stop(Camera *camera)\n>  \tPipelineHandler::stop(camera);\n>  }\n>  \n> +int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)\n> +{\n> +\tV4L2ControlList controls;\n> +\n> +\tfor (auto it : request->controls()) {\n> +\t\tconst ControlInfo *ci = it.first;\n> +\t\tControlValue &value = it.second;\n> +\n> +\t\tswitch (ci->id()) {\n> +\t\tcase Brightness:\n> +\t\t\tcontrols.add(V4L2_CID_BRIGHTNESS, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase Contrast:\n> +\t\t\tcontrols.add(V4L2_CID_CONTRAST, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase Saturation:\n> +\t\t\tcontrols.add(V4L2_CID_SATURATION, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase ManualExposure:\n> +\t\t\tcontrols.add(V4L2_CID_EXPOSURE_AUTO, 1);\n> +\t\t\tcontrols.add(V4L2_CID_EXPOSURE_ABSOLUTE, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase ManualGain:\n> +\t\t\tcontrols.add(V4L2_CID_GAIN, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tdefault:\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +\n> +\tfor (const V4L2Control &ctrl : controls)\n> +\t\tLOG(UVC, Debug)\n> +\t\t\t<< \"Setting control 0x\"\n> +\t\t\t<< std::hex << std::setw(8) << ctrl.id() << std::dec\n> +\t\t\t<< \" to \" << ctrl.value();\n> +\n> +\tint ret = data->video_->setControls(&controls);\n> +\tif (ret) {\n> +\t\tLOG(UVC, Error) << \"Failed to set controls: \" << ret;\n> +\t\treturn ret < 0 ? ret : -EINVAL;\n> +\t}\n> +\n> +\treturn ret;\n> +}\n> +\n>  int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request)\n>  {\n>  \tUVCCameraData *data = cameraData(camera);\n> @@ -227,7 +284,11 @@ int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request)\n>  \t\treturn -ENOENT;\n>  \t}\n>  \n> -\tint ret = data->video_->queueBuffer(buffer);\n> +\tint ret = processControls(data, request);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = data->video_->queueBuffer(buffer);\n>  \tif (ret < 0)\n>  \t\treturn ret;\n>  \n> @@ -247,24 +308,20 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n>  \n>  \tstd::unique_ptr<UVCCameraData> data = utils::make_unique<UVCCameraData>(this);\n>  \n> -\t/* Locate and open the default video node. */\n> +\t/* Locate and initialise the camera data with the default video node. */\n>  \tfor (MediaEntity *entity : media->entities()) {\n>  \t\tif (entity->flags() & MEDIA_ENT_FL_DEFAULT) {\n> -\t\t\tdata->video_ = new V4L2VideoDevice(entity);\n> +\t\t\tif (data->init(entity))\n> +\t\t\t\treturn false;\n>  \t\t\tbreak;\n>  \t\t}\n>  \t}\n>  \n> -\tif (!data->video_) {\n> +\tif (!data) {\n>  \t\tLOG(UVC, Error) << \"Could not find a default video device\";\n> -\t\treturn false;\n> +\t\treturn -ENODEV;\n\nThis function returns a bool so returning -ENODEV would be interpret as \na successful operation by the caller.\n\n>  \t}\n>  \n> -\tif (data->video_->open())\n> -\t\treturn false;\n> -\n> -\tdata->video_->bufferReady.connect(data.get(), &UVCCameraData::bufferReady);\n> -\n>  \t/* Create and register the camera. */\n>  \tstd::set<Stream *> streams{ &data->stream_ };\n>  \tstd::shared_ptr<Camera> camera = Camera::create(this, media->model(), streams);\n> @@ -276,6 +333,53 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n>  \treturn true;\n>  }\n>  \n> +int UVCCameraData::init(MediaEntity *entity)\n> +{\n> +\tint ret;\n> +\n> +\t/* Create and open the video device. */\n> +\tvideo_ = new V4L2VideoDevice(entity);\n> +\tret = video_->open();\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tvideo_->bufferReady.connect(this, &UVCCameraData::bufferReady);\n> +\n> +\t/* Initialise the supported controls. */\n> +\tconst V4L2ControlInfoMap &controls = video_->controls();\n> +\tfor (const auto &ctrl : controls) {\n> +\t\tunsigned int v4l2Id = ctrl.first;\n> +\t\tconst V4L2ControlInfo &info = ctrl.second;\n> +\t\tControlId id;\n> +\n> +\t\tswitch (v4l2Id) {\n> +\t\tcase V4L2_CID_BRIGHTNESS:\n> +\t\t\tid = Brightness;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_CONTRAST:\n> +\t\t\tid = Contrast;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_SATURATION:\n> +\t\t\tid = Saturation;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_EXPOSURE_ABSOLUTE:\n> +\t\t\tid = ManualExposure;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_GAIN:\n> +\t\t\tid = ManualGain;\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tcontrolInfo_.emplace(std::piecewise_construct,\n> +\t\t\t\t     std::forward_as_tuple(id),\n> +\t\t\t\t     std::forward_as_tuple(id, info.min(), info.max()));\n\nI love C++, so easy to parse ;-)\n\nWith the small issues fixed,\n\nReviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n>  void UVCCameraData::bufferReady(Buffer *buffer)\n>  {\n>  \tRequest *request = queuedRequests_.front();\n> -- \n> Regards,\n> \n> Laurent Pinchart\n> \n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-lj1-x243.google.com (mail-lj1-x243.google.com\n\t[IPv6:2a00:1450:4864:20::243])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D506560C2B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Jul 2019 02:23:02 +0200 (CEST)","by mail-lj1-x243.google.com with SMTP id t28so15037809lje.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 01 Jul 2019 17:23:02 -0700 (PDT)","from localhost (customer-145-14-112-32.stosn.net. [145.14.112.32])\n\tby smtp.gmail.com with ESMTPSA id\n\tj23sm2784407lfb.93.2019.07.01.17.23.01\n\t(version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256);\n\tMon, 01 Jul 2019 17:23:01 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to\n\t:user-agent; bh=vtqVqhC9Ls6Qm76AAObpAHFrpWz1Q0pBEyTlwYYRx0c=;\n\tb=sfN8u9wnkaEoZx6vwTyqOedPiBu7W9gSNUunmnRbcKVYjaFX/miHuxKQP5+D4lNHY7\n\tWfGCUV2yplqLU1pT3mR3K3rxKsIjW0BddBDMSf2CzZIKEq6+td5ePAap/q2AJN6eeYN3\n\t/O/A0Ar2dxUnVk9msDbZAcBg5firXS8dRGEqZr6TfFkZb1AaJVvGEH5YX+Ho+ykR8qMv\n\tD1T7NLtdV772fFnq9+U0TcUtAXiWKpPQ2rHY5p95Lig+I0DFBurtAdUtLYuWyIFZcVj1\n\tnE/N9dJ2nKTs458WJRv9v3zIpYC23s5DcxoqsxxI3B7x/bAZExFm9zG3kLH0Q7Tq2zt+\n\tuWNQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to:user-agent;\n\tbh=vtqVqhC9Ls6Qm76AAObpAHFrpWz1Q0pBEyTlwYYRx0c=;\n\tb=DklA4x3XCEbjmopt5EVOpR7wrL3aKCn0/5xND3d8AMhI8n2YueiW97gBCYsWmqIm8m\n\tS09bTTOjH4619IR4EA+O7fH7AWOUDBAfcTOrAF/ruBDT9c4TNyHF8fAFZqePh2cWXsWI\n\tlM2u6pBkP8NXYR6YnGFBcWNJzBpLPJ+6KA2zE5SzeIVKPlSVn6P9YMb0UgHAEnB5DJwU\n\tpE+O06I7F8TtquGO1h+xrKwo8Lg7NcpFH5uEsBHbtvtJDFUzFK6PfJ5J3RTdj8PXAf0+\n\tESpMN1JPeKAUhg73Za1oYxTmlvDnvPXD5CKg4FV17mqg9boVxuhPXSEbh0PUmdbm1RC0\n\tjV7A==","X-Gm-Message-State":"APjAAAVvl1deWkZ7Iz2NjU7SO2E3Z9HzpqMqCi1hza7Gkbi70QZ9ckj5\n\tbP4AHwmQwNSUNKEufEMH11QvtQ==","X-Google-Smtp-Source":"APXvYqxJMpB/FcxC3A8F7ERv8rHslCLS30BJW9JdX31hEt8JRjJI8Ctd4FFRj8Qim7wABycik9t9VA==","X-Received":"by 2002:a2e:635d:: with SMTP id\n\tx90mr15768126ljb.140.1562026982317; \n\tMon, 01 Jul 2019 17:23:02 -0700 (PDT)","Date":"Tue, 2 Jul 2019 02:23:00 +0200","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190702002300.GJ9228@bigcity.dyn.berto.se>","References":"<20190701201504.28487-1-laurent.pinchart@ideasonboard.com>\n\t<20190701201504.28487-9-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20190701201504.28487-9-laurent.pinchart@ideasonboard.com>","User-Agent":"Mutt/1.12.1 (2019-06-15)","Subject":"Re: [libcamera-devel] [PATCH v4 08/13] libcamera: pipeline:\n\tuvcvideo: Add controls support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 02 Jul 2019 00:23:03 -0000"}},{"id":2114,"web_url":"https://patchwork.libcamera.org/comment/2114/","msgid":"<20190702071117.3d2xnjustq4sobqn@uno.localdomain>","date":"2019-07-02T07:11:17","subject":"Re: [libcamera-devel] [PATCH v4 08/13] libcamera: pipeline:\n\tuvcvideo: Add controls support","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Laurent,\nOn Mon, Jul 01, 2019 at 11:14:59PM +0300, Laurent Pinchart wrote:\n> From: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>\n> Implement control support in the UVC pipeline handler by dynamically\n> querying the V4L2 device for the supported V4L2 controls and populating\n> the list of camera controls accordingly.\n>\n> Not-signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> Changes since v3:\n>\n> - Fixed error checking when setting controls\n> - Fixed handling of the failure to find a default video device\n\nThanks\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\n> ---\n>  src/libcamera/pipeline/uvcvideo.cpp | 124 +++++++++++++++++++++++++---\n>  1 file changed, 114 insertions(+), 10 deletions(-)\n>\n> diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n> index 2e22523d7cb1..b2f5b2eeed80 100644\n> --- a/src/libcamera/pipeline/uvcvideo.cpp\n> +++ b/src/libcamera/pipeline/uvcvideo.cpp\n> @@ -6,8 +6,11 @@\n>   */\n>\n>  #include <algorithm>\n> +#include <iomanip>\n> +#include <tuple>\n>\n>  #include <libcamera/camera.h>\n> +#include <libcamera/controls.h>\n>  #include <libcamera/request.h>\n>  #include <libcamera/stream.h>\n>\n> @@ -16,6 +19,7 @@\n>  #include \"media_device.h\"\n>  #include \"pipeline_handler.h\"\n>  #include \"utils.h\"\n> +#include \"v4l2_controls.h\"\n>  #include \"v4l2_videodevice.h\"\n>\n>  namespace libcamera {\n> @@ -35,6 +39,7 @@ public:\n>  \t\tdelete video_;\n>  \t}\n>\n> +\tint init(MediaEntity *entity);\n>  \tvoid bufferReady(Buffer *buffer);\n>\n>  \tV4L2VideoDevice *video_;\n> @@ -71,6 +76,8 @@ public:\n>  \tbool match(DeviceEnumerator *enumerator) override;\n>\n>  private:\n> +\tint processControls(UVCCameraData *data, Request *request);\n> +\n>  \tUVCCameraData *cameraData(const Camera *camera)\n>  \t{\n>  \t\treturn static_cast<UVCCameraData *>(\n> @@ -216,6 +223,56 @@ void PipelineHandlerUVC::stop(Camera *camera)\n>  \tPipelineHandler::stop(camera);\n>  }\n>\n> +int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)\n> +{\n> +\tV4L2ControlList controls;\n> +\n> +\tfor (auto it : request->controls()) {\n> +\t\tconst ControlInfo *ci = it.first;\n> +\t\tControlValue &value = it.second;\n> +\n> +\t\tswitch (ci->id()) {\n> +\t\tcase Brightness:\n> +\t\t\tcontrols.add(V4L2_CID_BRIGHTNESS, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase Contrast:\n> +\t\t\tcontrols.add(V4L2_CID_CONTRAST, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase Saturation:\n> +\t\t\tcontrols.add(V4L2_CID_SATURATION, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase ManualExposure:\n> +\t\t\tcontrols.add(V4L2_CID_EXPOSURE_AUTO, 1);\n> +\t\t\tcontrols.add(V4L2_CID_EXPOSURE_ABSOLUTE, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase ManualGain:\n> +\t\t\tcontrols.add(V4L2_CID_GAIN, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tdefault:\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +\n> +\tfor (const V4L2Control &ctrl : controls)\n> +\t\tLOG(UVC, Debug)\n> +\t\t\t<< \"Setting control 0x\"\n> +\t\t\t<< std::hex << std::setw(8) << ctrl.id() << std::dec\n> +\t\t\t<< \" to \" << ctrl.value();\n> +\n> +\tint ret = data->video_->setControls(&controls);\n> +\tif (ret) {\n> +\t\tLOG(UVC, Error) << \"Failed to set controls: \" << ret;\n> +\t\treturn ret < 0 ? ret : -EINVAL;\n> +\t}\n> +\n> +\treturn ret;\n> +}\n> +\n>  int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request)\n>  {\n>  \tUVCCameraData *data = cameraData(camera);\n> @@ -227,7 +284,11 @@ int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request)\n>  \t\treturn -ENOENT;\n>  \t}\n>\n> -\tint ret = data->video_->queueBuffer(buffer);\n> +\tint ret = processControls(data, request);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = data->video_->queueBuffer(buffer);\n>  \tif (ret < 0)\n>  \t\treturn ret;\n>\n> @@ -247,24 +308,20 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n>\n>  \tstd::unique_ptr<UVCCameraData> data = utils::make_unique<UVCCameraData>(this);\n>\n> -\t/* Locate and open the default video node. */\n> +\t/* Locate and initialise the camera data with the default video node. */\n>  \tfor (MediaEntity *entity : media->entities()) {\n>  \t\tif (entity->flags() & MEDIA_ENT_FL_DEFAULT) {\n> -\t\t\tdata->video_ = new V4L2VideoDevice(entity);\n> +\t\t\tif (data->init(entity))\n> +\t\t\t\treturn false;\n>  \t\t\tbreak;\n>  \t\t}\n>  \t}\n>\n> -\tif (!data->video_) {\n> +\tif (!data) {\n>  \t\tLOG(UVC, Error) << \"Could not find a default video device\";\n> -\t\treturn false;\n> +\t\treturn -ENODEV;\n>  \t}\n>\n> -\tif (data->video_->open())\n> -\t\treturn false;\n> -\n> -\tdata->video_->bufferReady.connect(data.get(), &UVCCameraData::bufferReady);\n> -\n>  \t/* Create and register the camera. */\n>  \tstd::set<Stream *> streams{ &data->stream_ };\n>  \tstd::shared_ptr<Camera> camera = Camera::create(this, media->model(), streams);\n> @@ -276,6 +333,53 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n>  \treturn true;\n>  }\n>\n> +int UVCCameraData::init(MediaEntity *entity)\n> +{\n> +\tint ret;\n> +\n> +\t/* Create and open the video device. */\n> +\tvideo_ = new V4L2VideoDevice(entity);\n> +\tret = video_->open();\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tvideo_->bufferReady.connect(this, &UVCCameraData::bufferReady);\n> +\n> +\t/* Initialise the supported controls. */\n> +\tconst V4L2ControlInfoMap &controls = video_->controls();\n> +\tfor (const auto &ctrl : controls) {\n> +\t\tunsigned int v4l2Id = ctrl.first;\n> +\t\tconst V4L2ControlInfo &info = ctrl.second;\n> +\t\tControlId id;\n> +\n> +\t\tswitch (v4l2Id) {\n> +\t\tcase V4L2_CID_BRIGHTNESS:\n> +\t\t\tid = Brightness;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_CONTRAST:\n> +\t\t\tid = Contrast;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_SATURATION:\n> +\t\t\tid = Saturation;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_EXPOSURE_ABSOLUTE:\n> +\t\t\tid = ManualExposure;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_GAIN:\n> +\t\t\tid = ManualGain;\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tcontrolInfo_.emplace(std::piecewise_construct,\n> +\t\t\t\t     std::forward_as_tuple(id),\n> +\t\t\t\t     std::forward_as_tuple(id, info.min(), info.max()));\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n>  void UVCCameraData::bufferReady(Buffer *buffer)\n>  {\n>  \tRequest *request = queuedRequests_.front();\n> --\n> Regards,\n>\n> Laurent Pinchart\n>\n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net\n\t[217.70.183.193])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7D9AB60C01\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Jul 2019 09:10:01 +0200 (CEST)","from uno.localdomain (2-224-242-101.ip172.fastwebnet.it\n\t[2.224.242.101]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 03CBA240007;\n\tTue,  2 Jul 2019 07:10:00 +0000 (UTC)"],"X-Originating-IP":"2.224.242.101","Date":"Tue, 2 Jul 2019 09:11:17 +0200","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190702071117.3d2xnjustq4sobqn@uno.localdomain>","References":"<20190701201504.28487-1-laurent.pinchart@ideasonboard.com>\n\t<20190701201504.28487-9-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha256;\n\tprotocol=\"application/pgp-signature\"; boundary=\"zf54n6flgurh7pro\"","Content-Disposition":"inline","In-Reply-To":"<20190701201504.28487-9-laurent.pinchart@ideasonboard.com>","User-Agent":"NeoMutt/20180716","Subject":"Re: [libcamera-devel] [PATCH v4 08/13] libcamera: pipeline:\n\tuvcvideo: Add controls support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 02 Jul 2019 07:10:01 -0000"}},{"id":2121,"web_url":"https://patchwork.libcamera.org/comment/2121/","msgid":"<01d8122f-ddec-4655-6489-6e67392026d3@ideasonboard.com>","date":"2019-07-02T08:01:50","subject":"Re: [libcamera-devel] [PATCH v4 08/13] libcamera: pipeline:\n\tuvcvideo: Add controls support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Laurent,\n\nOn 01/07/2019 21:14, Laurent Pinchart wrote:\n> From: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> Implement control support in the UVC pipeline handler by dynamically\n> querying the V4L2 device for the supported V4L2 controls and populating\n> the list of camera controls accordingly.\n> \n> Not-signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nWith the bool issue Niklas spotted repaired, I think this patch has come\na long way forwards since the [PoC], and my SoB can have s/Not-s/S/\n\nWe are going to duplicate the conversion between libcamera control\nvalues and v4l2 controls on controls where there is a direct 1-1\nmapping. For that I think we'll need some internal helpers, but lets get\nthe code to grow organically first so we know what we need.\n\nThis means we'll have duplication here and at VIMC at least initially,\nand possibly in other pipeline handlers. The Out-of-tree RPi will also\nneed the same conversions currently. But that's fine for the moment.\n\nAlso, if relevant..\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> Changes since v3:\n> \n> - Fixed error checking when setting controls\n> - Fixed handling of the failure to find a default video device\n> ---\n>  src/libcamera/pipeline/uvcvideo.cpp | 124 +++++++++++++++++++++++++---\n>  1 file changed, 114 insertions(+), 10 deletions(-)\n> \n> diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\n> index 2e22523d7cb1..b2f5b2eeed80 100644\n> --- a/src/libcamera/pipeline/uvcvideo.cpp\n> +++ b/src/libcamera/pipeline/uvcvideo.cpp\n> @@ -6,8 +6,11 @@\n>   */\n>  \n>  #include <algorithm>\n> +#include <iomanip>\n> +#include <tuple>\n>  \n>  #include <libcamera/camera.h>\n> +#include <libcamera/controls.h>\n>  #include <libcamera/request.h>\n>  #include <libcamera/stream.h>\n>  \n> @@ -16,6 +19,7 @@\n>  #include \"media_device.h\"\n>  #include \"pipeline_handler.h\"\n>  #include \"utils.h\"\n> +#include \"v4l2_controls.h\"\n>  #include \"v4l2_videodevice.h\"\n>  \n>  namespace libcamera {\n> @@ -35,6 +39,7 @@ public:\n>  \t\tdelete video_;\n>  \t}\n>  \n> +\tint init(MediaEntity *entity);\n>  \tvoid bufferReady(Buffer *buffer);\n>  \n>  \tV4L2VideoDevice *video_;\n> @@ -71,6 +76,8 @@ public:\n>  \tbool match(DeviceEnumerator *enumerator) override;\n>  \n>  private:\n> +\tint processControls(UVCCameraData *data, Request *request);\n> +\n>  \tUVCCameraData *cameraData(const Camera *camera)\n>  \t{\n>  \t\treturn static_cast<UVCCameraData *>(\n> @@ -216,6 +223,56 @@ void PipelineHandlerUVC::stop(Camera *camera)\n>  \tPipelineHandler::stop(camera);\n>  }\n>  \n> +int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)\n> +{\n> +\tV4L2ControlList controls;\n> +\n> +\tfor (auto it : request->controls()) {\n> +\t\tconst ControlInfo *ci = it.first;\n> +\t\tControlValue &value = it.second;\n> +\n> +\t\tswitch (ci->id()) {\n> +\t\tcase Brightness:\n> +\t\t\tcontrols.add(V4L2_CID_BRIGHTNESS, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase Contrast:\n> +\t\t\tcontrols.add(V4L2_CID_CONTRAST, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase Saturation:\n> +\t\t\tcontrols.add(V4L2_CID_SATURATION, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase ManualExposure:\n> +\t\t\tcontrols.add(V4L2_CID_EXPOSURE_AUTO, 1);\n> +\t\t\tcontrols.add(V4L2_CID_EXPOSURE_ABSOLUTE, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tcase ManualGain:\n> +\t\t\tcontrols.add(V4L2_CID_GAIN, value.getInt());\n> +\t\t\tbreak;\n> +\n> +\t\tdefault:\n> +\t\t\tbreak;\n> +\t\t}\n> +\t}\n> +\n> +\tfor (const V4L2Control &ctrl : controls)\n> +\t\tLOG(UVC, Debug)\n> +\t\t\t<< \"Setting control 0x\"\n> +\t\t\t<< std::hex << std::setw(8) << ctrl.id() << std::dec\n> +\t\t\t<< \" to \" << ctrl.value();\n> +\n> +\tint ret = data->video_->setControls(&controls);\n> +\tif (ret) {\n> +\t\tLOG(UVC, Error) << \"Failed to set controls: \" << ret;\n> +\t\treturn ret < 0 ? ret : -EINVAL;\n> +\t}\n> +\n> +\treturn ret;\n> +}\n> +\n>  int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request)\n>  {\n>  \tUVCCameraData *data = cameraData(camera);\n> @@ -227,7 +284,11 @@ int PipelineHandlerUVC::queueRequest(Camera *camera, Request *request)\n>  \t\treturn -ENOENT;\n>  \t}\n>  \n> -\tint ret = data->video_->queueBuffer(buffer);\n> +\tint ret = processControls(data, request);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = data->video_->queueBuffer(buffer);\n>  \tif (ret < 0)\n>  \t\treturn ret;\n>  \n> @@ -247,24 +308,20 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n>  \n>  \tstd::unique_ptr<UVCCameraData> data = utils::make_unique<UVCCameraData>(this);\n>  \n> -\t/* Locate and open the default video node. */\n> +\t/* Locate and initialise the camera data with the default video node. */\n>  \tfor (MediaEntity *entity : media->entities()) {\n>  \t\tif (entity->flags() & MEDIA_ENT_FL_DEFAULT) {\n> -\t\t\tdata->video_ = new V4L2VideoDevice(entity);\n> +\t\t\tif (data->init(entity))\n> +\t\t\t\treturn false;\n>  \t\t\tbreak;\n>  \t\t}\n>  \t}\n>  \n> -\tif (!data->video_) {\n> +\tif (!data) {\n>  \t\tLOG(UVC, Error) << \"Could not find a default video device\";\n> -\t\treturn false;\n> +\t\treturn -ENODEV;\n>  \t}\n>  \n> -\tif (data->video_->open())\n> -\t\treturn false;\n> -\n> -\tdata->video_->bufferReady.connect(data.get(), &UVCCameraData::bufferReady);\n> -\n>  \t/* Create and register the camera. */\n>  \tstd::set<Stream *> streams{ &data->stream_ };\n>  \tstd::shared_ptr<Camera> camera = Camera::create(this, media->model(), streams);\n> @@ -276,6 +333,53 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n>  \treturn true;\n>  }\n>  \n> +int UVCCameraData::init(MediaEntity *entity)\n> +{\n> +\tint ret;\n> +\n> +\t/* Create and open the video device. */\n> +\tvideo_ = new V4L2VideoDevice(entity);\n> +\tret = video_->open();\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tvideo_->bufferReady.connect(this, &UVCCameraData::bufferReady);\n> +\n> +\t/* Initialise the supported controls. */\n> +\tconst V4L2ControlInfoMap &controls = video_->controls();\n> +\tfor (const auto &ctrl : controls) {\n> +\t\tunsigned int v4l2Id = ctrl.first;\n> +\t\tconst V4L2ControlInfo &info = ctrl.second;\n> +\t\tControlId id;\n> +\n> +\t\tswitch (v4l2Id) {\n> +\t\tcase V4L2_CID_BRIGHTNESS:\n> +\t\t\tid = Brightness;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_CONTRAST:\n> +\t\t\tid = Contrast;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_SATURATION:\n> +\t\t\tid = Saturation;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_EXPOSURE_ABSOLUTE:\n> +\t\t\tid = ManualExposure;\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_GAIN:\n> +\t\t\tid = ManualGain;\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tcontrolInfo_.emplace(std::piecewise_construct,\n> +\t\t\t\t     std::forward_as_tuple(id),\n> +\t\t\t\t     std::forward_as_tuple(id, info.min(), info.max()));\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n>  void UVCCameraData::bufferReady(Buffer *buffer)\n>  {\n>  \tRequest *request = queuedRequests_.front();\n>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3266E61F4A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Jul 2019 10:01:53 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 97204524;\n\tTue,  2 Jul 2019 10:01:52 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1562054512;\n\tbh=UO019DAE5iWomHJT51rCFP/OUmwS5o52uMOGcwr2iwQ=;\n\th=Reply-To:Subject:To:References:From:Date:In-Reply-To:From;\n\tb=SoFVVJUvgH5NdNbPLgFCkkTdRNux7rOAHE6AS9Z+q3VN82tqTVZUAvrlS9U4LSjf2\n\txIO2gO/iJ6Iz2bvlKiaE3MZ3H7EhA1U5bvYSx5DQiapZkWTX7EF79d9Mn+eXilmn6X\n\tPmeKBl9Dgw3T51I8r67Np6QzYU1Vr31N5i9g0OII=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20190701201504.28487-1-laurent.pinchart@ideasonboard.com>\n\t<20190701201504.28487-9-laurent.pinchart@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Openpgp":"preference=signencrypt","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tsbsRB8KNNvVXBOVNwko86rQqF9drZuw=","Organization":"Ideas on Board","Message-ID":"<01d8122f-ddec-4655-6489-6e67392026d3@ideasonboard.com>","Date":"Tue, 2 Jul 2019 09:01:50 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101\n\tThunderbird/60.7.0","MIME-Version":"1.0","In-Reply-To":"<20190701201504.28487-9-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH v4 08/13] libcamera: pipeline:\n\tuvcvideo: Add controls support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Tue, 02 Jul 2019 08:01:53 -0000"}}]