[{"id":11845,"web_url":"https://patchwork.libcamera.org/comment/11845/","msgid":"<20200804185247.GK6075@pendragon.ideasonboard.com>","date":"2020-08-04T18:52:47","subject":"Re: [libcamera-devel] [PATCH v7 7/9] libcamera: pipeline: uvcvideo:\n\tGenerate unique camera names","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nThank you for the patch.\n\nOn Tue, Aug 04, 2020 at 06:13:56PM +0200, Niklas Söderlund wrote:\n> Generate camera names that are unique and persistent between system\n> resets. The name is constructed from the USB device information as well\n> as the USB controller on the host.\n> \n> Before this change example of camera names:\n> \n> Venus USB2.0 Camera: Venus USB2\n> Logitech Webcam C930e\n> \n> After this change the same cameras are:\n> \n> \\_SB_.PCI0.RP05.PXSX-2.1.1:1.0-0ac8:3420\n> \\_SB_.PCI0.RP05.PXSX-2.4:1.0-046d:0843\n> \n> On OF-based system:\n> \n> base/soc/usb@7e980000/usb-port@1-1.3:1.0-0ac8:3420\n> \n> Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> ---\n> * Changes since v5\n> - New algorithm to generate IDs.\n> \n> * Changes since v3\n> - Switch argument to generateName() to UVCCameraData pointer.\n> ---\n>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 77 +++++++++++++++++++-\n>  1 file changed, 76 insertions(+), 1 deletion(-)\n> \n> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> index 93e3dc17e3a7105e..70c057b543f85c7b 100644\n> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> @@ -6,6 +6,7 @@\n>   */\n>  \n>  #include <algorithm>\n> +#include <fstream>\n>  #include <iomanip>\n>  #include <math.h>\n>  #include <tuple>\n> @@ -20,6 +21,7 @@\n>  #include \"libcamera/internal/log.h\"\n>  #include \"libcamera/internal/media_device.h\"\n>  #include \"libcamera/internal/pipeline_handler.h\"\n> +#include \"libcamera/internal/sysfs.h\"\n>  #include \"libcamera/internal/utils.h\"\n>  #include \"libcamera/internal/v4l2_controls.h\"\n>  #include \"libcamera/internal/v4l2_videodevice.h\"\n> @@ -81,6 +83,8 @@ public:\n>  \tbool match(DeviceEnumerator *enumerator) override;\n>  \n>  private:\n> +\tstd::string generateID(const UVCCameraData *data);\n\ns/ID/Id/\n\n> +\n>  \tint processControl(ControlList *controls, unsigned int id,\n>  \t\t\t   const ControlValue &value);\n>  \tint processControls(UVCCameraData *data, Request *request);\n> @@ -379,6 +383,71 @@ int PipelineHandlerUVC::queueRequestDevice(Camera *camera, Request *request)\n>  \treturn 0;\n>  }\n>  \n> +std::string PipelineHandlerUVC::generateID(const UVCCameraData *data)\n> +{\n> +\tconst std::string path = data->video_->devicePath();\n> +\n> +\t/* Creata a device ID from the USB devices vendor and product ID. */\n> +\tstd::string deviceId;\n> +\tfor (const std::string &name : { \"idVendor\", \"idProduct\" }) {\n> +\t\tstd::ifstream file(path + \"/../\" + name);\n> +\n> +\t\tif (!file.is_open())\n> +\t\t\treturn {};\n> +\n> +\t\tstd::string value;\n> +\t\tstd::getline(file, value);\n> +\t\tfile.close();\n> +\n> +\t\tdeviceId += value + (deviceId.empty() ? \":\" : \"\");\n\n\t\tif (!deviceId.empty())\n\t\t\tdeviceId += \":\";\n\n\t\tdeviceId += value;\n\nso this will work with more than two iterations :-)\n\n> +\t}\n\nOf course you could also use utils::join() if you wanted.\n\nI'd move this block after the next, to follow the same order as in the\nID string, but maybe it's just me.\n\n> +\n> +\t/*\n> +\t * Create a USB ID from the device path which has the known format:\n> +\t *\n> +\t *\tbus , \"-\", ports, \":\", config, \".\", interface ;\n\ns/bus ,/ bus,/ (maybe path = ... ?)\n\n> +\t *\tbus = number ;\n> +\t *\tports = port, [ \".\", ports ] ;\n> +\t *\tport = number ;\n> +\t *\tconfig = number ;\n> +\t *\tinterface = number ;\n> +\t *\n> +\t * Example: 3-2.4:1.0\n> +\t *\n> +\t * The bus is not guaranteed to be stable and needs to be stripped from\n> +\t * the USB ID. The final USB ID is built up of the ports, config and\n> +\t * interface properties.\n> +\t *\n> +\t * Example 2.4:1.0.\n> +\t */\n> +\tstd::string usbId = basename(path.c_str());\n> +\tusbId = usbId.substr(usbId.find('-') + 1, std::string::npos);\n\nThe second argument defaults to std::string::npos, so you could leave it\nout if you wanted.\n\n> +\n> +\t/* Create a controller ID from first device described in firmware. */\n> +\tstd::string controllerId;\n> +\tstd::string searchPath = path;\n> +\twhile (true) {\n> +\t\tsearchPath += \"/..\";\n> +\t\tchar *realPath = realpath(searchPath.c_str(), nullptr);\n> +\t\tif (!realPath) {\n> +\t\t\tLOG(UVC, Error) << \"Failed to lookup \" << searchPath;\n> +\t\t\treturn {};\n> +\t\t}\n> +\t\tsearchPath = realPath;\n> +\t\tfree(realPath);\n> +\n> +\t\tif (searchPath.empty() || searchPath == \"/\") {\n> +\t\t\tLOG(UVC, Error) << \"Can not find controller ID\";\n> +\t\t\treturn {};\n> +\t\t}\n\nAs path is guaranteed to be a real path, I think this could be\nsimplified.\n\n\t\tstd::string::size_type pos = searchPath.rfind('-');\n\t\tif (pos <= 1) {\n\t\t\tLOG(UVC, Error) << \"Can not find controller ID\";\n\t\t\treturn {};\n\t\t}\n\n\t\tsearchPath = searchPath::substr(0, pos);\n\n> +\n> +\t\tif (!sysfs::firmwareId(searchPath, &controllerId))\n> +\t\t\tbreak;\n> +\t}\n> +\n> +\treturn controllerId + \"-\" + usbId + \"-\" + deviceId;\n> +}\n> +\n>  bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n>  {\n>  \tMediaDevice *media;\n> @@ -405,8 +474,14 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n>  \t\treturn false;\n>  \n>  \t/* Create and register the camera. */\n> +\tstd::string id = generateID(data.get());\n> +\tif (id.empty()) {\n> +\t\tLOG(UVC, Error) << \"Failed to generate camera ID\";\n> +\t\treturn false;\n> +\t}\n> +\n>  \tstd::set<Stream *> streams{ &data->stream_ };\n> -\tstd::shared_ptr<Camera> camera = Camera::create(this, media->model(), streams);\n> +\tstd::shared_ptr<Camera> camera = Camera::create(this, id, streams);\n>  \tregisterCamera(std::move(camera), std::move(data));\n>  \n>  \t/* Enable hot-unplug notifications. */","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 6D074BD87A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  4 Aug 2020 18:53:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F26F46054A;\n\tTue,  4 Aug 2020 20:53:01 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C73A960545\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  4 Aug 2020 20:53:00 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5205E543;\n\tTue,  4 Aug 2020 20:52:59 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"P9AUtwB9\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1596567179;\n\tbh=Azm2sONiE4k6iAWE9GJDLdTXJ7Lm5PLN8IkFmk81lQI=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=P9AUtwB9Zxm/MkSsyIRmfQ+hcvRmt01dBmmHMRs9vlqpAl/uYi6PJ7LtyPXaY99Lm\n\tEpcnYQmFVFn2hBxuLBt94RRkj98EkP1Lv0Gq3PggoMAojv2QQAlFicMePt09OP78VC\n\tXy8yqx70lyTgZKOK0oSQf9usk5scfzu9Sb4T/91Y=","Date":"Tue, 4 Aug 2020 21:52:47 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","Message-ID":"<20200804185247.GK6075@pendragon.ideasonboard.com>","References":"<20200804161358.1628962-1-niklas.soderlund@ragnatech.se>\n\t<20200804161358.1628962-8-niklas.soderlund@ragnatech.se>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20200804161358.1628962-8-niklas.soderlund@ragnatech.se>","Subject":"Re: [libcamera-devel] [PATCH v7 7/9] libcamera: pipeline: uvcvideo:\n\tGenerate unique camera names","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":11872,"web_url":"https://patchwork.libcamera.org/comment/11872/","msgid":"<20200805094820.GC2712616@oden.dyn.berto.se>","date":"2020-08-05T09:48:20","subject":"Re: [libcamera-devel] [PATCH v7 7/9] libcamera: pipeline: uvcvideo:\n\tGenerate unique camera names","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Laurent,\n\nThanks for your feedback.\n\nOn 2020-08-04 21:52:47 +0300, Laurent Pinchart wrote:\n> Hi Niklas,\n> \n> Thank you for the patch.\n> \n> On Tue, Aug 04, 2020 at 06:13:56PM +0200, Niklas Söderlund wrote:\n> > Generate camera names that are unique and persistent between system\n> > resets. The name is constructed from the USB device information as well\n> > as the USB controller on the host.\n> > \n> > Before this change example of camera names:\n> > \n> > Venus USB2.0 Camera: Venus USB2\n> > Logitech Webcam C930e\n> > \n> > After this change the same cameras are:\n> > \n> > \\_SB_.PCI0.RP05.PXSX-2.1.1:1.0-0ac8:3420\n> > \\_SB_.PCI0.RP05.PXSX-2.4:1.0-046d:0843\n> > \n> > On OF-based system:\n> > \n> > base/soc/usb@7e980000/usb-port@1-1.3:1.0-0ac8:3420\n> > \n> > Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n> > ---\n> > * Changes since v5\n> > - New algorithm to generate IDs.\n> > \n> > * Changes since v3\n> > - Switch argument to generateName() to UVCCameraData pointer.\n> > ---\n> >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 77 +++++++++++++++++++-\n> >  1 file changed, 76 insertions(+), 1 deletion(-)\n> > \n> > diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> > index 93e3dc17e3a7105e..70c057b543f85c7b 100644\n> > --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> > +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> > @@ -6,6 +6,7 @@\n> >   */\n> >  \n> >  #include <algorithm>\n> > +#include <fstream>\n> >  #include <iomanip>\n> >  #include <math.h>\n> >  #include <tuple>\n> > @@ -20,6 +21,7 @@\n> >  #include \"libcamera/internal/log.h\"\n> >  #include \"libcamera/internal/media_device.h\"\n> >  #include \"libcamera/internal/pipeline_handler.h\"\n> > +#include \"libcamera/internal/sysfs.h\"\n> >  #include \"libcamera/internal/utils.h\"\n> >  #include \"libcamera/internal/v4l2_controls.h\"\n> >  #include \"libcamera/internal/v4l2_videodevice.h\"\n> > @@ -81,6 +83,8 @@ public:\n> >  \tbool match(DeviceEnumerator *enumerator) override;\n> >  \n> >  private:\n> > +\tstd::string generateID(const UVCCameraData *data);\n> \n> s/ID/Id/\n> \n> > +\n> >  \tint processControl(ControlList *controls, unsigned int id,\n> >  \t\t\t   const ControlValue &value);\n> >  \tint processControls(UVCCameraData *data, Request *request);\n> > @@ -379,6 +383,71 @@ int PipelineHandlerUVC::queueRequestDevice(Camera *camera, Request *request)\n> >  \treturn 0;\n> >  }\n> >  \n> > +std::string PipelineHandlerUVC::generateID(const UVCCameraData *data)\n> > +{\n> > +\tconst std::string path = data->video_->devicePath();\n> > +\n> > +\t/* Creata a device ID from the USB devices vendor and product ID. */\n> > +\tstd::string deviceId;\n> > +\tfor (const std::string &name : { \"idVendor\", \"idProduct\" }) {\n> > +\t\tstd::ifstream file(path + \"/../\" + name);\n> > +\n> > +\t\tif (!file.is_open())\n> > +\t\t\treturn {};\n> > +\n> > +\t\tstd::string value;\n> > +\t\tstd::getline(file, value);\n> > +\t\tfile.close();\n> > +\n> > +\t\tdeviceId += value + (deviceId.empty() ? \":\" : \"\");\n> \n> \t\tif (!deviceId.empty())\n> \t\t\tdeviceId += \":\";\n> \n> \t\tdeviceId += value;\n> \n> so this will work with more than two iterations :-)\n\n:-)\n\n> \n> > +\t}\n> \n> Of course you could also use utils::join() if you wanted.\n> \n> I'd move this block after the next, to follow the same order as in the\n> ID string, but maybe it's just me.\n\nI had not noticed but now that it's pointed out it drives me mad, I will \nsort them to match the output order :-)\n\n> \n> > +\n> > +\t/*\n> > +\t * Create a USB ID from the device path which has the known format:\n> > +\t *\n> > +\t *\tbus , \"-\", ports, \":\", config, \".\", interface ;\n> \n> s/bus ,/ bus,/ (maybe path = ... ?)\n\nGood catch, I also like adding of path = ... .\n\n> \n> > +\t *\tbus = number ;\n> > +\t *\tports = port, [ \".\", ports ] ;\n> > +\t *\tport = number ;\n> > +\t *\tconfig = number ;\n> > +\t *\tinterface = number ;\n> > +\t *\n> > +\t * Example: 3-2.4:1.0\n> > +\t *\n> > +\t * The bus is not guaranteed to be stable and needs to be stripped from\n> > +\t * the USB ID. The final USB ID is built up of the ports, config and\n> > +\t * interface properties.\n> > +\t *\n> > +\t * Example 2.4:1.0.\n> > +\t */\n> > +\tstd::string usbId = basename(path.c_str());\n> > +\tusbId = usbId.substr(usbId.find('-') + 1, std::string::npos);\n> \n> The second argument defaults to std::string::npos, so you could leave it\n> out if you wanted.\n> \n> > +\n> > +\t/* Create a controller ID from first device described in firmware. */\n> > +\tstd::string controllerId;\n> > +\tstd::string searchPath = path;\n> > +\twhile (true) {\n> > +\t\tsearchPath += \"/..\";\n> > +\t\tchar *realPath = realpath(searchPath.c_str(), nullptr);\n> > +\t\tif (!realPath) {\n> > +\t\t\tLOG(UVC, Error) << \"Failed to lookup \" << searchPath;\n> > +\t\t\treturn {};\n> > +\t\t}\n> > +\t\tsearchPath = realPath;\n> > +\t\tfree(realPath);\n> > +\n> > +\t\tif (searchPath.empty() || searchPath == \"/\") {\n> > +\t\t\tLOG(UVC, Error) << \"Can not find controller ID\";\n> > +\t\t\treturn {};\n> > +\t\t}\n> \n> As path is guaranteed to be a real path, I think this could be\n> simplified.\n> \n> \t\tstd::string::size_type pos = searchPath.rfind('-');\n\nWith s@'-'@'/'@ it works and is much nicer, thanks for this snippet.\n\n> \t\tif (pos <= 1) {\n> \t\t\tLOG(UVC, Error) << \"Can not find controller ID\";\n> \t\t\treturn {};\n> \t\t}\n> \n> \t\tsearchPath = searchPath::substr(0, pos);\n> \n> > +\n> > +\t\tif (!sysfs::firmwareId(searchPath, &controllerId))\n> > +\t\t\tbreak;\n> > +\t}\n> > +\n> > +\treturn controllerId + \"-\" + usbId + \"-\" + deviceId;\n> > +}\n> > +\n> >  bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n> >  {\n> >  \tMediaDevice *media;\n> > @@ -405,8 +474,14 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)\n> >  \t\treturn false;\n> >  \n> >  \t/* Create and register the camera. */\n> > +\tstd::string id = generateID(data.get());\n> > +\tif (id.empty()) {\n> > +\t\tLOG(UVC, Error) << \"Failed to generate camera ID\";\n> > +\t\treturn false;\n> > +\t}\n> > +\n> >  \tstd::set<Stream *> streams{ &data->stream_ };\n> > -\tstd::shared_ptr<Camera> camera = Camera::create(this, media->model(), streams);\n> > +\tstd::shared_ptr<Camera> camera = Camera::create(this, id, streams);\n> >  \tregisterCamera(std::move(camera), std::move(data));\n> >  \n> >  \t/* Enable hot-unplug notifications. */\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 66E1DBD86F\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Aug 2020 09:48:24 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EA40A60555;\n\tWed,  5 Aug 2020 11:48:23 +0200 (CEST)","from mail-lf1-x144.google.com (mail-lf1-x144.google.com\n\t[IPv6:2a00:1450:4864:20::144])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C3AEB60554\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Aug 2020 11:48:22 +0200 (CEST)","by mail-lf1-x144.google.com with SMTP id j22so18043598lfm.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 05 Aug 2020 02:48:22 -0700 (PDT)","from localhost (h-209-203.A463.priv.bahnhof.se. [155.4.209.203])\n\tby smtp.gmail.com with ESMTPSA id\n\tw19sm661027ljd.112.2020.08.05.02.48.20\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 05 Aug 2020 02:48:21 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=ragnatech-se.20150623.gappssmtp.com\n\theader.i=@ragnatech-se.20150623.gappssmtp.com\n\theader.b=\"DkjHPe5W\"; dkim-atps=neutral","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\tbh=ru4PcaWmUGyQF/3hneQgnTCvdfzEtNXjsSd8LOt4ue0=;\n\tb=DkjHPe5WbwHN3MbPdTEXHRbbUiaNfqOqSciCRpKY3OH2OLjyf8EkK6VMfXZx5IGshi\n\tiCVY9BcP/kAvWEOCCHZ16sbF0eidLDg8hk8pBQPOxEciLxFrLQ4bzYcnD/59/EbFmW/U\n\twmrFdSPLMJz+yT9j4hfYLUfDvB+WLJXraOzerNUKFXfv1NLoidz9h2E46zNTZUGBt7Z2\n\tCuG+H/0JZaQrJzUlqrZ3tMaz16PUx++D07uAx0/DLIZTfCqhoDIpCiqWLjBQah50Pelw\n\tRSPbaXjcWCoXpWC6E7eH+FVRMwbTWPrFyyVjKlEv4p2oFtwhu5Fu2VBogJfktmaggVr1\n\toFlA==","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;\n\tbh=ru4PcaWmUGyQF/3hneQgnTCvdfzEtNXjsSd8LOt4ue0=;\n\tb=Fn/p/Zus9HHyQdKhOXaYxEvclZJVnTvVsnbC6r4M/RIzq/CXxyyuhMOF5OYtV38gpo\n\th8UsXPKCOeJU6AKq6NK5W8INGaMB6L0Sihitw1FMWrYvb51lN62OF/TEaPmRecPlCNxh\n\tyq/V9OAZ/b9Py5YNihK1X0ZqrCkWzl6ayeHZ91u0jQ7Qki9fRwKKOKTH6vYjVFD0z0IO\n\t3a/aE36ha5hQwM5evsyuadwldwdmryf/8FNBnS01BUx2k+wRAhqPqd+V5v+GhEWNhqRE\n\twaGmh8VJyE4+BRhlvRNhqwC1PT8gHvc2QMxfF7oPrEeL2xrEzGynXDMf/Hg4RDI18sPG\n\tL5aA==","X-Gm-Message-State":"AOAM531t1PseDQ3m+GY1KQDi9mkPB3RshErPy+VJtHaysoCy6uCePCWc\n\tPQ/PCe+StGGm261wF2yXq/DdWg==","X-Google-Smtp-Source":"ABdhPJyebMLNoyCHPk+o2c1nfnTUpfVs7XS/O/h7JI5pljlS9fPCCuR1V8K9IFVw5iYathjHlzOujw==","X-Received":"by 2002:ac2:5dc1:: with SMTP id x1mr1152811lfq.217.1596620902023;\n\tWed, 05 Aug 2020 02:48:22 -0700 (PDT)","Date":"Wed, 5 Aug 2020 11:48:20 +0200","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20200805094820.GC2712616@oden.dyn.berto.se>","References":"<20200804161358.1628962-1-niklas.soderlund@ragnatech.se>\n\t<20200804161358.1628962-8-niklas.soderlund@ragnatech.se>\n\t<20200804185247.GK6075@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20200804185247.GK6075@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v7 7/9] libcamera: pipeline: uvcvideo:\n\tGenerate unique camera names","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"iso-8859-1\"","Content-Transfer-Encoding":"quoted-printable","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]