Show a patch.

GET /api/1.1/patches/11687/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 11687,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/11687/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/11687/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20210324112527.63701-5-jacopo@jmondi.org>",
    "date": "2021-03-24T11:25:24",
    "name": "[libcamera-devel,4/7] android: Add CameraHalConfig class",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "1a4d8d4d0bab767698dd8c518f93d0c0363d3021",
    "submitter": {
        "id": 3,
        "url": "https://patchwork.libcamera.org/api/1.1/people/3/?format=api",
        "name": "Jacopo Mondi",
        "email": "jacopo@jmondi.org"
    },
    "delegate": {
        "id": 15,
        "url": "https://patchwork.libcamera.org/api/1.1/users/15/?format=api",
        "username": "jmondi",
        "first_name": "Jacopo",
        "last_name": "Mondi",
        "email": "jacopo@jmondi.org"
    },
    "mbox": "https://patchwork.libcamera.org/patch/11687/mbox/",
    "series": [
        {
            "id": 1826,
            "url": "https://patchwork.libcamera.org/api/1.1/series/1826/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1826",
            "date": "2021-03-24T11:25:20",
            "name": "android: Add support for HAL configuration file",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/1826/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/11687/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/11687/checks/",
    "tags": {},
    "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 AFF8CC32E7\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 24 Mar 2021 11:25:21 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7B23A68D70;\n\tWed, 24 Mar 2021 12:25:21 +0100 (CET)",
            "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 F16F9602E3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 24 Mar 2021 12:25:20 +0100 (CET)",
            "from uno.homenet.telecomitalia.it\n\t(host-82-63-7-72.business.telecomitalia.it [82.63.7.72])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay1-d.mail.gandi.net (Postfix) with ESMTPSA id DC86124000C;\n\tWed, 24 Mar 2021 11:25:18 +0000 (UTC)"
        ],
        "X-Originating-IP": "82.63.7.72",
        "From": "Jacopo Mondi <jacopo@jmondi.org>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Wed, 24 Mar 2021 12:25:24 +0100",
        "Message-Id": "<20210324112527.63701-5-jacopo@jmondi.org>",
        "X-Mailer": "git-send-email 2.30.0",
        "In-Reply-To": "<20210324112527.63701-1-jacopo@jmondi.org>",
        "References": "<20210324112527.63701-1-jacopo@jmondi.org>",
        "MIME-Version": "1.0",
        "Subject": "[libcamera-devel] [PATCH 4/7] android: Add CameraHalConfig class",
        "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>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Add a CameraHalConfig class to the Android Camera3 HAL layer.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\n---\n src/android/camera_hal_config.cpp | 385 ++++++++++++++++++++++++++++++\n src/android/camera_hal_config.h   |  54 +++++\n src/android/meson.build           |   1 +\n 3 files changed, 440 insertions(+)\n create mode 100644 src/android/camera_hal_config.cpp\n create mode 100644 src/android/camera_hal_config.h",
    "diff": "diff --git a/src/android/camera_hal_config.cpp b/src/android/camera_hal_config.cpp\nnew file mode 100644\nindex 000000000000..7e33dfb750ff\n--- /dev/null\n+++ b/src/android/camera_hal_config.cpp\n@@ -0,0 +1,385 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Google Inc.\n+ *\n+ * camera_hal_config.cpp - Camera HAL configuration file manager\n+ */\n+#include \"camera_hal_config.h\"\n+\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include \"libcamera/internal/file.h\"\n+#include \"libcamera/internal/log.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(HALConfig)\n+\n+const std::string CameraHalConfig::CameraBlock::toString() const\n+{\n+\tstd::stringstream ss;\n+\n+\tss << \"\\'\" << name << \"\\'\"\n+\t   << \" model: \" << model\n+\t   << \"(\" << location << \")[\" << rotation << \"]\";\n+\n+\treturn ss.str();\n+}\n+\n+CameraHalConfig::CameraHalConfig()\n+{\n+\tif (!yaml_parser_initialize(&parser_))\n+\t\tLOG(HALConfig, Error) << \"Failed to initialize yaml parser\";\n+}\n+\n+CameraHalConfig::~CameraHalConfig()\n+{\n+\tyaml_parser_delete(&parser_);\n+}\n+\n+/*\n+ * Configuration files can be stored in system paths, which are identified\n+ * through the build configuration.\n+ *\n+ * However, when running uninstalled - the source location takes precedence.\n+ */\n+const std::string CameraHalConfig::findFilePath(const std::string &filename)\n+{\n+\tstatic std::array<std::string, 2> searchPaths = {\n+\t\tLIBCAMERA_SYSCONF_DIR,\n+\t\tLIBCAMERA_DATA_DIR,\n+\t};\n+\n+\tif (File::exists(filename))\n+\t\treturn filename;\n+\n+\tstd::string root = utils::libcameraSourcePath();\n+\tif (!root.empty()) {\n+\t\tstd::string configurationPath = root + \"data/\" + filename;\n+\n+\t\tif (File::exists(configurationPath))\n+\t\t\treturn configurationPath;\n+\t}\n+\n+\tfor (std::string &path : searchPaths) {\n+\t\tstd::string configurationPath = path + \"/\" + filename;\n+\t\tif (File::exists(configurationPath))\n+\t\t\treturn configurationPath;\n+\t}\n+\n+\treturn \"\";\n+}\n+\n+/*\n+ * \\brief Open the HAL configuration file and validate its content\n+ * \\return 0 on success, a negative error code otherwise\n+ */\n+int CameraHalConfig::open()\n+{\n+\tint ret;\n+\n+\tconst std::string configPath = \"camera_hal.yaml\";\n+\tconst std::string filePath = findFilePath(configPath);\n+\tif (filePath.empty()) {\n+\t\tLOG(HALConfig, Warning)\n+\t\t\t<< \"File: \\\"\" << configPath << \"\\\" not found\";\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tFILE *fh = fopen(filePath.c_str(), \"r\");\n+\tif (!fh) {\n+\t\tret = -errno;\n+\t\tLOG(HALConfig, Error) << \"Failed to open file \" << filePath\n+\t\t\t\t      << \": \" << strerror(-ret);\n+\t\treturn ret;\n+\t}\n+\n+\tyaml_parser_set_input_file(&parser_, fh);\n+\n+\tLOG(HALConfig, Debug) << \"Reading configuration from \" << filePath;\n+\n+\tret = parseConfigFile();\n+\tfclose(fh);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tLOG(HALConfig, Info) << \"Device model: \" << model_;\n+\tLOG(HALConfig, Info) << \"Device maker: \" << maker_;\n+\tfor (const auto &c : cameras_) {\n+\t\tconst CameraBlock camera = c.second;\n+\t\tLOG(HALConfig, Info) << camera.toString();\n+\t}\n+\n+\treturn 0;\n+}\n+\n+const std::string &CameraHalConfig::cameraLocation(const std::string &camera) const\n+{\n+\tstatic const std::string empty(\"\");\n+\tconst auto &it = cameras_.find(camera);\n+\tif (it == cameras_.end()) {\n+\t\tLOG(HALConfig, Error)\n+\t\t\t<< \"Camera '\" << camera\n+\t\t\t<< \"' not described in the HAL configuration file\";\n+\t\treturn empty;\n+\t}\n+\n+\tconst CameraBlock &cam = it->second;\n+\treturn cam.location;\n+}\n+\n+unsigned int CameraHalConfig::cameraRotation(const std::string &camera) const\n+{\n+\tstatic const std::string empty(\"\");\n+\tconst auto &it = cameras_.find(camera);\n+\tif (it == cameras_.end()) {\n+\t\tLOG(HALConfig, Error)\n+\t\t\t<< \"Camera '\" << camera\n+\t\t\t<< \"' not described in the HAL configuration file\";\n+\t\treturn 0;\n+\t}\n+\n+\tconst CameraBlock &cam = it->second;\n+\treturn cam.rotation;\n+}\n+\n+const std::string &CameraHalConfig::cameraModel(const std::string &camera) const\n+{\n+\tstatic const std::string empty(\"\");\n+\tconst auto &it = cameras_.find(camera);\n+\tif (it == cameras_.end()) {\n+\t\tLOG(HALConfig, Error)\n+\t\t\t<< \"Camera '\" << camera\n+\t\t\t<< \"' not described in the HAL configuration file\";\n+\t\treturn empty;\n+\t}\n+\n+\tconst CameraBlock &cam = it->second;\n+\treturn cam.model;\n+}\n+\n+std::string CameraHalConfig::parseValue(yaml_token_t &token)\n+{\n+\t/* Make sure the token type is a value and get its content. */\n+\tyaml_parser_scan(&parser_, &token);\n+\tif (token.type != YAML_VALUE_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn \"\";\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\tyaml_parser_scan(&parser_, &token);\n+\tif (token.type != YAML_SCALAR_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn \"\";\n+\t}\n+\n+\tchar *scalar = reinterpret_cast<char *>(token.data.scalar.value);\n+\tstd::string value(scalar, token.data.scalar.length);\n+\tyaml_token_delete(&token);\n+\n+\treturn value;\n+}\n+\n+std::string CameraHalConfig::parseKey(yaml_token_t &token)\n+{\n+\t/* Make sure the token type is a key and get its value. */\n+\tyaml_parser_scan(&parser_, &token);\n+\tif (token.type != YAML_KEY_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn \"\";\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\tyaml_parser_scan(&parser_, &token);\n+\tif (token.type != YAML_SCALAR_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn \"\";\n+\t}\n+\n+\tchar *scalar = reinterpret_cast<char *>(token.data.scalar.value);\n+\tstd::string key(scalar, token.data.scalar.length);\n+\tyaml_token_delete(&token);\n+\n+\treturn key;\n+}\n+\n+int CameraHalConfig::parseCameraBlock(yaml_token_t &token)\n+{\n+\t/* The 'camera' block is a VALUE token type. */\n+\tyaml_parser_scan(&parser_, &token);\n+\tif (token.type != YAML_VALUE_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn -EINVAL;\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\t/*\n+\t * Parse the content of the camera block until we have an unbalanced\n+\t * BLOCK_END_TOKEN which closes the camera block.\n+\t *\n+\t * Add a safety counter to make sure we don't loop indefinitely in case\n+\t * the configuration file is malformed.\n+\t */\n+\tunsigned int sentinel = 100;\n+\tCameraBlock cameraBlock{};\n+\tint blockCounter = 0;\n+\tdo {\n+\t\tyaml_parser_scan(&parser_, &token);\n+\t\tswitch (token.type) {\n+\t\tcase YAML_BLOCK_ENTRY_TOKEN:\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tblockCounter++;\n+\t\t\tbreak;\n+\t\tcase YAML_BLOCK_END_TOKEN:\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tblockCounter--;\n+\t\t\tbreak;\n+\t\tcase YAML_BLOCK_MAPPING_START_TOKEN: {\n+\t\t\tyaml_token_delete(&token);\n+\n+\t\t\tstd::string key = parseKey(token);\n+\t\t\tstd::string value = parseValue(token);\n+\t\t\tif (key.empty() || value.empty()) {\n+\t\t\t\tLOG(HALConfig, Error)\n+\t\t\t\t\t<< \"Configuration file is not valid\";\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\n+\t\t\t/* Validate that the parsed key is valid. */\n+\t\t\tif (key == \"location\") {\n+\t\t\t\tif (value != \"front\" && value != \"back\" &&\n+\t\t\t\t    value != \"external\") {\n+\t\t\t\t\tLOG(HALConfig, Error)\n+\t\t\t\t\t\t<< \"Unknown location: \" << value;\n+\t\t\t\t\treturn -EINVAL;\n+\t\t\t\t}\n+\t\t\t\tcameraBlock.location = value;\n+\t\t\t} else if (key == \"rotation\") {\n+\t\t\t\tcameraBlock.rotation = strtoul(value.c_str(),\n+\t\t\t\t\t\t\t       NULL, 10);\n+\t\t\t\tif (cameraBlock.rotation < 0) {\n+\t\t\t\t\tLOG(HALConfig, Error)\n+\t\t\t\t\t\t<< \"Unknown rotation: \"\n+\t\t\t\t\t\t<< cameraBlock.rotation;\n+\t\t\t\t\treturn -EINVAL;\n+\t\t\t\t}\n+\t\t\t} else if (key == \"name\") {\n+\t\t\t\tcameraBlock.name = value;\n+\t\t\t} else if (key == \"model\") {\n+\t\t\t\tcameraBlock.model = value;\n+\t\t\t} else {\n+\t\t\t\tLOG(HALConfig, Error)\n+\t\t\t\t\t<< \"Configuration file is not valid \"\n+\t\t\t\t\t<< \"unknown key: \" << key;\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t\tdefault:\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tbreak;\n+\t\t}\n+\t\t--sentinel;\n+\t} while (blockCounter >= 0 && sentinel);\n+\tif (!sentinel) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid \";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tcameras_[cameraBlock.name] = cameraBlock;\n+\n+\treturn 0;\n+}\n+\n+int CameraHalConfig::parseEntryBlock(yaml_token_t &token)\n+{\n+\tint ret;\n+\n+\tyaml_parser_scan(&parser_, &token);\n+\tif (token.type != YAML_BLOCK_MAPPING_START_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn -EINVAL;\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\tstd::string key = parseKey(token);\n+\tif (key.empty()) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (key == \"camera\") {\n+\t\tyaml_token_delete(&token);\n+\t\tret = parseCameraBlock(token);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t} else if (key == \"manufacturer\") {\n+\t\tyaml_token_delete(&token);\n+\t\tmaker_ = parseValue(token);\n+\t\tif (maker_.empty()) {\n+\t\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t} else if (key == \"model\") {\n+\t\tyaml_token_delete(&token);\n+\t\tmodel_ = parseValue(token);\n+\t\tif (model_.empty()) {\n+\t\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t} else {\n+\t\tyaml_token_delete(&token);\n+\t\tLOG(HALConfig, Error) << \"Unknown key \" << key;\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+int CameraHalConfig::parseConfigFile()\n+{\n+\tyaml_token_t token;\n+\tint ret;\n+\n+\tyaml_parser_scan(&parser_, &token);\n+\tif (token.type != YAML_STREAM_START_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn EINVAL;\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\t/*\n+\t * Start parsing the content of the configuration file which\n+\t * starts as with sequence block.\n+\t */\n+\tyaml_parser_scan(&parser_, &token);\n+\tif (token.type != YAML_BLOCK_SEQUENCE_START_TOKEN) {\n+\t\tLOG(HALConfig, Error) << \"Configuration file is not valid\";\n+\t\treturn -EINVAL;\n+\t}\n+\tyaml_token_delete(&token);\n+\n+\t/* Parse the single entry blocks. */\n+\tdo {\n+\t\tyaml_parser_scan(&parser_, &token);\n+\t\tswitch (token.type) {\n+\t\tcase YAML_BLOCK_ENTRY_TOKEN:\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tret = parseEntryBlock(token);\n+\t\t\tbreak;\n+\t\tcase YAML_STREAM_END_TOKEN:\n+\t\t\t/* Resources are released after the loop exit. */\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t/* Release resources before re-parsing. */\n+\t\t\tyaml_token_delete(&token);\n+\t\t\tbreak;\n+\t\t}\n+\t} while (token.type != YAML_STREAM_END_TOKEN && ret >= 0);\n+\tyaml_token_delete(&token);\n+\n+\treturn ret;\n+}\ndiff --git a/src/android/camera_hal_config.h b/src/android/camera_hal_config.h\nnew file mode 100644\nindex 000000000000..69d42658e90a\n--- /dev/null\n+++ b/src/android/camera_hal_config.h\n@@ -0,0 +1,54 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2021, Google Inc.\n+ *\n+ * camera_hal_config.h - Camera HAL configuration file manager\n+ */\n+#ifndef __ANDROID_CAMERA_HAL_CONFIG_H__\n+#define __ANDROID_CAMERA_HAL_CONFIG_H__\n+\n+#include <map>\n+#include <string>\n+#include <yaml.h>\n+\n+class CameraHalConfig\n+{\n+public:\n+\tCameraHalConfig();\n+\t~CameraHalConfig();\n+\n+\tint open();\n+\n+\tconst std::string &deviceModel() const { return model_; }\n+\tconst std::string &deviceMaker() const { return maker_; }\n+\n+\tconst std::string &cameraLocation(const std::string &camera) const;\n+\tunsigned int cameraRotation(const std::string &camera) const;\n+\tconst std::string &cameraModel(const std::string &camera) const;\n+\n+private:\n+\tyaml_parser_t parser_;\n+\n+\tstd::string model_;\n+\tstd::string maker_;\n+\tclass CameraBlock\n+\t{\n+\tpublic:\n+\t\tstd::string name;\n+\t\tstd::string location;\n+\t\tstd::string model;\n+\t\tunsigned int rotation;\n+\n+\t\tconst std::string toString() const;\n+\t};\n+\tstd::map<std::string, CameraBlock> cameras_;\n+\n+\tconst std::string findFilePath(const std::string &filename);\n+\tstd::string parseValue(yaml_token_t &token);\n+\tstd::string parseKey(yaml_token_t &token);\n+\tint parseCameraBlock(yaml_token_t &token);\n+\tint parseEntryBlock(yaml_token_t &token);\n+\tint parseConfigFile();\n+};\n+\n+#endif /* __ANDROID_CAMERA_HAL_CONFIG_H__ */\ndiff --git a/src/android/meson.build b/src/android/meson.build\nindex 19f94a4073f1..34e5c494a492 100644\n--- a/src/android/meson.build\n+++ b/src/android/meson.build\n@@ -44,6 +44,7 @@ android_hal_sources = files([\n     'camera3_hal.cpp',\n     'camera_hal_manager.cpp',\n     'camera_device.cpp',\n+    'camera_hal_config.cpp',\n     'camera_metadata.cpp',\n     'camera_ops.cpp',\n     'camera_stream.cpp',\n",
    "prefixes": [
        "libcamera-devel",
        "4/7"
    ]
}