{"id":3068,"url":"https://patchwork.libcamera.org/api/1.1/patches/3068/?format=json","web_url":"https://patchwork.libcamera.org/patch/3068/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200309162414.720306-4-jacopo@jmondi.org>","date":"2020-03-09T16:24:06","name":"[libcamera-devel,03/11] libcamera: controls: Add support for string controls","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"25d3edaf2abb487ff5b8dbe56ca5049db189ad6f","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/1.1/people/3/?format=json","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/3068/mbox/","series":[{"id":716,"url":"https://patchwork.libcamera.org/api/1.1/series/716/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=716","date":"2020-03-09T16:24:03","name":"Adda support for V4L2 array control and strings","version":1,"mbox":"https://patchwork.libcamera.org/series/716/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/3068/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/3068/checks/","tags":{},"headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay3-d.mail.gandi.net (relay3-d.mail.gandi.net\n\t[217.70.183.195])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2A27262938\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  9 Mar 2020 17:21:29 +0100 (CET)","from uno.lan (93-34-114-233.ip49.fastwebnet.it [93.34.114.233])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay3-d.mail.gandi.net (Postfix) with ESMTPSA id E01DE60008\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  9 Mar 2020 16:21:28 +0000 (UTC)"],"X-Originating-IP":"93.34.114.233","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"libcamera-devel@lists.libcamera.org","Date":"Mon,  9 Mar 2020 17:24:06 +0100","Message-Id":"<20200309162414.720306-4-jacopo@jmondi.org>","X-Mailer":"git-send-email 2.25.0","In-Reply-To":"<20200309162414.720306-1-jacopo@jmondi.org>","References":"<20200309162414.720306-1-jacopo@jmondi.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH 03/11] libcamera: controls: Add support\n\tfor string controls","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>","X-List-Received-Date":"Mon, 09 Mar 2020 16:21:29 -0000"},"content":"From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nString controls are stored internally as an array of char, but the\nControlValue constructor, get() and set() functions operate on an\nstd::string for convenience. Array of strings are thus not supported.\n\nUnlike for other control types, the ControlInfo range reports the\nminimum and maximum allowed lengths of the string (the minimum will\nusually be 0), not the minimum and maximum value of each element.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n include/libcamera/controls.h         | 30 +++++++++++++++++++-----\n src/libcamera/control_serializer.cpp | 22 +++++++++++++++++\n src/libcamera/controls.cpp           | 35 ++++++++++++++++++++++++----\n src/libcamera/gen-controls.py        | 18 +++++++-------\n 4 files changed, 87 insertions(+), 18 deletions(-)","diff":"diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h\nindex 9c6cbffb88b5..5cf6280e612b 100644\n--- a/include/libcamera/controls.h\n+++ b/include/libcamera/controls.h\n@@ -26,6 +26,7 @@ enum ControlType {\n \tControlTypeInteger32,\n \tControlTypeInteger64,\n \tControlTypeFloat,\n+\tControlTypeString,\n };\n \n namespace details {\n@@ -64,6 +65,11 @@ struct control_type<float> {\n \tstatic constexpr ControlType value = ControlTypeFloat;\n };\n \n+template<>\n+struct control_type<std::string> {\n+\tstatic constexpr ControlType value = ControlTypeString;\n+};\n+\n template<typename T, std::size_t N>\n struct control_type<Span<T, N>> : public control_type<std::remove_cv_t<T>> {\n };\n@@ -76,7 +82,9 @@ public:\n \tControlValue();\n \n #ifndef __DOXYGEN__\n-\ttemplate<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr>\n+\ttemplate<typename T, typename std::enable_if_t<!details::is_span<T>::value &&\n+\t\t\t\t\t\t       !std::is_same<std::string, std::remove_cv_t<T>>::value,\n+\t\t\t\t\t\t       std::nullptr_t> = nullptr>\n \tControlValue(const T &value)\n \t\t: type_(ControlTypeNone), numElements_(0)\n \t{\n@@ -84,7 +92,9 @@ public:\n \t\t    &value, 1, sizeof(T));\n \t}\n \n-\ttemplate<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr>\n+\ttemplate<typename T, typename std::enable_if_t<details::is_span<T>::value ||\n+\t\t\t\t\t\t       std::is_same<std::string, std::remove_cv_t<T>>::value,\n+\t\t\t\t\t\t       std::nullptr_t> = nullptr>\n #else\n \ttemplate<typename T>\n #endif\n@@ -115,7 +125,9 @@ public:\n \t}\n \n #ifndef __DOXYGEN__\n-\ttemplate<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr>\n+\ttemplate<typename T, typename std::enable_if_t<!details::is_span<T>::value &&\n+\t\t\t\t\t\t       !std::is_same<std::string, std::remove_cv_t<T>>::value,\n+\t\t\t\t\t\t       std::nullptr_t> = nullptr>\n \tT get() const\n \t{\n \t\tassert(type_ == details::control_type<std::remove_cv_t<T>>::value);\n@@ -124,7 +136,9 @@ public:\n \t\treturn *reinterpret_cast<const T *>(data().data());\n \t}\n \n-\ttemplate<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr>\n+\ttemplate<typename T, typename std::enable_if_t<details::is_span<T>::value ||\n+\t\t\t\t\t\t       std::is_same<std::string, std::remove_cv_t<T>>::value,\n+\t\t\t\t\t\t       std::nullptr_t> = nullptr>\n #else\n \ttemplate<typename T>\n #endif\n@@ -139,14 +153,18 @@ public:\n \t}\n \n #ifndef __DOXYGEN__\n-\ttemplate<typename T, typename std::enable_if_t<!details::is_span<T>::value, std::nullptr_t> = nullptr>\n+\ttemplate<typename T, typename std::enable_if_t<!details::is_span<T>::value &&\n+\t\t\t\t\t\t       !std::is_same<std::string, std::remove_cv_t<T>>::value,\n+\t\t\t\t\t\t       std::nullptr_t> = nullptr>\n \tvoid set(const T &value)\n \t{\n \t\tset(details::control_type<std::remove_cv_t<T>>::value, false,\n \t\t    reinterpret_cast<const void *>(&value), 1, sizeof(T));\n \t}\n \n-\ttemplate<typename T, typename std::enable_if_t<details::is_span<T>::value, std::nullptr_t> = nullptr>\n+\ttemplate<typename T, typename std::enable_if_t<details::is_span<T>::value ||\n+\t\t\t\t\t\t       std::is_same<std::string, std::remove_cv_t<T>>::value,\n+\t\t\t\t\t\t       std::nullptr_t> = nullptr>\n #else\n \ttemplate<typename T>\n #endif\ndiff --git a/src/libcamera/control_serializer.cpp b/src/libcamera/control_serializer.cpp\nindex eef875f4d96c..808419f246c0 100644\n--- a/src/libcamera/control_serializer.cpp\n+++ b/src/libcamera/control_serializer.cpp\n@@ -314,6 +314,22 @@ ControlValue ControlSerializer::loadControlValue(ByteStreamBuffer &buffer,\n \treturn value;\n }\n \n+template<>\n+ControlValue ControlSerializer::loadControlValue<std::string>(ByteStreamBuffer &buffer,\n+\t\t\t\t\t\t bool isArray,\n+\t\t\t\t\t\t unsigned int count)\n+{\n+\tControlValue value;\n+\n+\tconst char *data = buffer.read<char>(count);\n+\tif (!data)\n+\t\treturn value;\n+\n+\tvalue.set(std::string{ data, count });\n+\n+\treturn value;\n+}\n+\n ControlValue ControlSerializer::loadControlValue(ControlType type,\n \t\t\t\t\t\t ByteStreamBuffer &buffer,\n \t\t\t\t\t\t bool isArray,\n@@ -335,6 +351,9 @@ ControlValue ControlSerializer::loadControlValue(ControlType type,\n \tcase ControlTypeFloat:\n \t\treturn loadControlValue<float>(buffer, isArray, count);\n \n+\tcase ControlTypeString:\n+\t\treturn loadControlValue<std::string>(buffer, isArray, count);\n+\n \tcase ControlTypeNone:\n \t\treturn ControlValue();\n \t}\n@@ -345,6 +364,9 @@ ControlValue ControlSerializer::loadControlValue(ControlType type,\n ControlInfo ControlSerializer::loadControlInfo(ControlType type,\n \t\t\t\t\t       ByteStreamBuffer &b)\n {\n+\tif (type == ControlTypeString)\n+\t\ttype = ControlTypeInteger32;\n+\n \tControlValue min = loadControlValue(type, b);\n \tControlValue max = loadControlValue(type, b);\n \ndiff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp\nindex 53649fe897db..68d040db4daa 100644\n--- a/src/libcamera/controls.cpp\n+++ b/src/libcamera/controls.cpp\n@@ -57,6 +57,7 @@ static constexpr size_t ControlValueSize[] = {\n \t[ControlTypeInteger32]\t\t= sizeof(int32_t),\n \t[ControlTypeInteger64]\t\t= sizeof(int64_t),\n \t[ControlTypeFloat]\t\t= sizeof(float),\n+\t[ControlTypeString]\t\t= sizeof(char),\n };\n \n } /* namespace */\n@@ -74,8 +75,8 @@ static constexpr size_t ControlValueSize[] = {\n  * The control stores a 32-bit integer value\n  * \\var ControlTypeInteger64\n  * The control stores a 64-bit integer value\n- * \\var ControlTypeFloat\n- * The control stores a 32-bit floating point value\n+ * \\var ControlTypeString\n+ * The control stores a string value as an array of char\n  */\n \n /**\n@@ -164,7 +165,8 @@ ControlValue &ControlValue::operator=(const ControlValue &other)\n  * \\brief Retrieve the number of elements stored in the ControlValue\n  *\n  * For instances storing an array, this function returns the number of elements\n- * in the array. Otherwise, it returns 1.\n+ * in the array. For instances storing a string, it returns the length of the\n+ * string, not counting the terminating '\\0'. Otherwise, it returns 1.\n  *\n  * \\return The number of elements stored in the ControlValue\n  */\n@@ -192,6 +194,11 @@ std::string ControlValue::toString() const\n \t\treturn \"<ValueType Error>\";\n \n \tconst uint8_t *data = ControlValue::data().data();\n+\n+\tif (type_ == ControlTypeString)\n+\t\treturn std::string(reinterpret_cast<const char *>(data),\n+\t\t\t\t   numElements_);\n+\n \tstd::string str(isArray_ ? \"[ \" : \"\");\n \n \tfor (unsigned int i = 0; i < numElements_; ++i) {\n@@ -222,6 +229,7 @@ std::string ControlValue::toString() const\n \t\t\tbreak;\n \t\t}\n \t\tcase ControlTypeNone:\n+\t\tcase ControlTypeString:\n \t\t\tbreak;\n \t\t}\n \n@@ -439,12 +447,22 @@ ControlInfo::ControlInfo(const ControlValue &min,\n /**\n  * \\fn ControlInfo::min()\n  * \\brief Retrieve the minimum value of the control\n+ *\n+ * For string controls, this is the minimum length of the string, not counting\n+ * the terminating '\\0'. For all other control types, this is the minimum value\n+ * of each element.\n+ *\n  * \\return A ControlValue with the minimum value for the control\n  */\n \n /**\n  * \\fn ControlInfo::max()\n  * \\brief Retrieve the maximum value of the control\n+ *\n+ * For string controls, this is the maximum length of the string, not counting\n+ * the terminating '\\0'. For all other control types, this is the maximum value\n+ * of each element.\n+ *\n  * \\return A ControlValue with the maximum value for the control\n  */\n \n@@ -653,7 +671,16 @@ void ControlInfoMap::generateIdmap()\n \tidmap_.clear();\n \n \tfor (const auto &ctrl : *this) {\n-\t\tif (ctrl.first->type() != ctrl.second.min().type()) {\n+\t\t/*\n+\t\t * For string controls, min and max define the valid\n+\t\t * range for the string size, not for the individual\n+\t\t * values.\n+\t\t */\n+\t\tControlType rangeType = ctrl.first->type() == ControlTypeString\n+\t\t\t\t      ? ControlTypeInteger32 : ctrl.first->type();\n+\t\tconst ControlInfo &info = ctrl.second;\n+\n+\t\tif (info.min().type() != rangeType) {\n \t\t\tLOG(Controls, Error)\n \t\t\t\t<< \"Control \" << utils::hex(ctrl.first->id())\n \t\t\t\t<< \" type and info type mismatch\";\ndiff --git a/src/libcamera/gen-controls.py b/src/libcamera/gen-controls.py\nindex ff8bda6b16c1..87c3d52ada6d 100755\n--- a/src/libcamera/gen-controls.py\n+++ b/src/libcamera/gen-controls.py\n@@ -42,10 +42,11 @@ ${description}\n         name, ctrl = ctrl.popitem()\n         id_name = snake_case(name).upper()\n \n-        if ctrl.get('size'):\n-            ctrl_type = 'Span<const %s>' % ctrl['type']\n-        else:\n-            ctrl_type = ctrl['type']\n+        ctrl_type = ctrl['type']\n+        if ctrl_type == 'string':\n+            ctrl_type = 'std::string'\n+        elif ctrl.get('size'):\n+            ctrl_type = 'Span<const %s>' % ctrl_type\n \n         info = {\n             'name': name,\n@@ -97,10 +98,11 @@ def generate_h(controls):\n \n         ids.append('\\t' + id_name + ' = ' + str(id_value) + ',')\n \n-        if ctrl.get('size'):\n-            ctrl_type = 'Span<const %s>' % ctrl['type']\n-        else:\n-            ctrl_type = ctrl['type']\n+        ctrl_type = ctrl['type']\n+        if ctrl_type == 'string':\n+            ctrl_type = 'std::string'\n+        elif ctrl.get('size'):\n+            ctrl_type = 'Span<const %s>' % ctrl_type\n \n         info = {\n             'name': name,\n","prefixes":["libcamera-devel","03/11"]}