From patchwork Fri Nov 24 12:37:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 19234 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C74E5C3220 for ; Fri, 24 Nov 2023 12:37:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7D7F9629BF; Fri, 24 Nov 2023 13:37:20 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1700829440; bh=WzHbA91RccDLAaEK8gMjXDiGvvpl869JLB7tz96MCdw=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=3UWHinZgsPuEJOC652TE6Fb1k+VB/hGhOONrBB6MYeT9W/lxZhq8bGx3LHTmRCjIC wxpQKBsAGy71SFsyVMbcGFRgEuG96Ag+GKyQqiOLCZr2nA8XihKT8TUZxThkTft1Pt ZNljwPsGy6aDI6Md3CWjkNFRWO6J10lMvVi+hrZwLnuKeE4TmPmEZmTXs/wbwxXeaG A83nggjOVYAODhbAcoZsKcK3sLQbDYpMQa95OUeSkmBxV05veSMGDhBL//o5U/rRyg UeB6jmC4hucjlxIa6vuIiLuMnPa0DeUgku1n+x1z4PYBsjlJnXGBA+7C4SVtmiiFG4 I+wQ0axGdJlVQ== Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0A2CB629AF for ; Fri, 24 Nov 2023 13:37:17 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="dMV/5VYC"; dkim-atps=neutral Received: by mail-wm1-x32c.google.com with SMTP id 5b1f17b1804b1-40b2afd049aso12855635e9.0 for ; Fri, 24 Nov 2023 04:37:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1700829436; x=1701434236; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=nohTSKl3D4c/l1i+Uga5J7Ea2iqx/KDHfAMYupJkF0g=; b=dMV/5VYCx2rJRr/ovLGPvAJ61FtXzhmzaVtC2Df6eahCx8qlz6KG/gDRLbDhfLWscM JC5VdL239puHv15xyFfx7aVC2mLlvGgza1TUGdqUTDK/OuWealJoNXE47fIpIVv+iSXo /pYEcx71iR2GgQb0PddogbqW0UWea00aqF/A7x4/c8leVzup3Jul4/EimQxWlPcNyFYV JvYJdbVvA+9kdbeiMMiAqI7MpSAmXTzgALJngtAACAb7qLCAzSuzba0EPmu0MuOodYAc mds9HCI4LT4fs002J4x6q8zayGMnxWWANJ16+FZIHiXO2EDQ+AAV9WNzh/wHBYgBqOld 890A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700829436; x=1701434236; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=nohTSKl3D4c/l1i+Uga5J7Ea2iqx/KDHfAMYupJkF0g=; b=K4sihWTtoX6amVNtEmlspeZe47HNufCJQdt0yTMPTonKeuZlLiZygS/Y8OUfvcGieC XO/KR3J80axTRWaeMqZotlDnFcr16hg3BP698Fh9B5rbN3WDCDvEgxsxmvB4HtwDiRFc dB72rvT62lO/IvE+sUxRgd/PJlbMV0kuKlvwaH4A5odgbFwuBHQw5whAWDufkJdnO35C lwqp8P7WIFnC+hWLcRISUl/1kW0UNRy1Rb3ZgBS225atJ5NgWk6eVmIyNA8sq8wu5uGh /bnoot/X03Yr6BTqNV5TcjqNhGdz5VAyhpGbgNr4Ncs34eWDDmT8mWGBlKaQOSfqrD3d 7Hng== X-Gm-Message-State: AOJu0YwjgNTTM49kemj+TLmBpIwCy15fWk2GOcgeIp2ORBIWaDUghsq8 pjyb/kFoRYwTnUFxMzJPKaVbqvWhNPhrrwGG+jStwA== X-Google-Smtp-Source: AGHT+IHdFRd+LZ7/+h9i67jD/xVPpA6bHz+FHRCm7t8CZlBwNctFsXpxSa9qgT9i81sk3MSz8EbtXw== X-Received: by 2002:a05:600c:4f4b:b0:40b:36e9:bf4b with SMTP id m11-20020a05600c4f4b00b0040b36e9bf4bmr2613184wmq.41.1700829436151; Fri, 24 Nov 2023 04:37:16 -0800 (PST) Received: from localhost.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id v11-20020a05600c444b00b0040b3867a297sm3303690wmn.36.2023.11.24.04.37.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Nov 2023 04:37:15 -0800 (PST) To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Nov 2023 12:37:07 +0000 Message-Id: <20231124123713.22519-2-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231124123713.22519-1-naush@raspberrypi.com> References: <20231124123713.22519-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 1/7] controls: Add vendor control/property support to generation scripts X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add support for vendor-specific controls and properties to libcamera. The controls/properties are defined by a "vendor" tag in the YAML control description file, for example: vendor: rpi controls: - MyExampleControl: type: string description: | Test for libcamera vendor-specific controls. This will now generate a control id in the libcamera::controls::rpi namespace, ensuring no id conflict between different vendors, core or draft libcamera controls. Similarly, a ControlIdMap control is generated in the libcamera::controls::rpi namespace. A #define LIBCAMERA_HAS_RPI_VENDOR_CONTROLS is also generated to allow applications to conditionally compile code if the specific vendor controls are present. For the python bindings, the control is available with libcamera.controls.rpi.MyExampleControl. The above controls example applies similarly to properties. Existing libcamera controls defined in control_ids.yaml are given the "libcamera" vendor tag. A new --mode flag is added to gen-controls.py to specify the mode of operation, either 'controls' or 'properties' to allow the code generator to correctly set the #define string. As a drive-by, sort and redefine the output command line argument in gen-controls.py and gen-py-controls.py to ('--output', '-o') for consistency. Signed-off-by: Naushir Patuck --- include/libcamera/control_ids.h.in | 2 + include/libcamera/meson.build | 15 +-- include/libcamera/property_ids.h.in | 2 + src/libcamera/control_ids.cpp.in | 4 + src/libcamera/control_ids.yaml | 1 + src/libcamera/meson.build | 5 +- src/libcamera/property_ids.cpp.in | 4 + src/libcamera/property_ids.yaml | 1 + src/py/libcamera/gen-py-controls.py | 81 +++++++----- src/py/libcamera/py_controls_generated.cpp.in | 3 + .../libcamera/py_properties_generated.cpp.in | 3 + utils/gen-controls.py | 117 +++++++++++++----- 12 files changed, 168 insertions(+), 70 deletions(-) diff --git a/include/libcamera/control_ids.h.in b/include/libcamera/control_ids.h.in index 0718a8886f6c..c97b09a82450 100644 --- a/include/libcamera/control_ids.h.in +++ b/include/libcamera/control_ids.h.in @@ -32,6 +32,8 @@ ${draft_controls} } /* namespace draft */ +${vendor_controls} + } /* namespace controls */ } /* namespace libcamera */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index a24c50d66a82..2c8c0258c95e 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -32,20 +32,21 @@ install_headers(libcamera_public_headers, libcamera_headers_install_dir = get_option('includedir') / libcamera_include_dir -# control_ids.h and property_ids.h -control_source_files = [ - 'control_ids', - 'property_ids', -] +# control_ids.h and property_ids.h and associated modes +control_source_files = { + 'control_ids': 'controls', + 'property_ids': 'properties', +} control_headers = [] -foreach header : control_source_files +foreach header, mode : control_source_files input_files = files('../../src/libcamera/' + header +'.yaml', header + '.h.in') control_headers += custom_target(header + '_h', input : input_files, output : header + '.h', - command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@'], + command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@', + '--mode', mode], install : true, install_dir : libcamera_headers_install_dir) endforeach diff --git a/include/libcamera/property_ids.h.in b/include/libcamera/property_ids.h.in index ff0194083af0..47c5d6bf2e28 100644 --- a/include/libcamera/property_ids.h.in +++ b/include/libcamera/property_ids.h.in @@ -31,6 +31,8 @@ ${draft_controls} extern const ControlIdMap properties; +${vendor_controls} + } /* namespace properties */ } /* namespace libcamera */ diff --git a/src/libcamera/control_ids.cpp.in b/src/libcamera/control_ids.cpp.in index 5fb1c2c30558..be86548cf73f 100644 --- a/src/libcamera/control_ids.cpp.in +++ b/src/libcamera/control_ids.cpp.in @@ -33,6 +33,8 @@ ${draft_controls_doc} } /* namespace draft */ +${vendor_controls_doc} + #ifndef __DOXYGEN__ /* * Keep the controls definitions hidden from doxygen as it incorrectly parses @@ -45,6 +47,8 @@ namespace draft { ${draft_controls_def} } /* namespace draft */ + +${vendor_controls_def} #endif /** diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml index 5827d7ecef49..ff74ce1deedb 100644 --- a/src/libcamera/control_ids.yaml +++ b/src/libcamera/control_ids.yaml @@ -6,6 +6,7 @@ --- # Unless otherwise stated, all controls are bi-directional, i.e. they can be # set through Request::controls() and returned out through Request::metadata(). +vendor: libcamera controls: - AeEnable: type: bool diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index d0e26f6b4141..e49bf850b355 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -127,12 +127,13 @@ endif control_sources = [] -foreach source : control_source_files +foreach source, mode : control_source_files input_files = files(source +'.yaml', source + '.cpp.in') control_sources += custom_target(source + '_cpp', input : input_files, output : source + '.cpp', - command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@']) + command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@', + '--mode', mode]) endforeach libcamera_sources += control_sources diff --git a/src/libcamera/property_ids.cpp.in b/src/libcamera/property_ids.cpp.in index f917e3349766..0771ac5c091f 100644 --- a/src/libcamera/property_ids.cpp.in +++ b/src/libcamera/property_ids.cpp.in @@ -32,6 +32,8 @@ ${draft_controls_doc} } /* namespace draft */ +${vendor_controls_doc} + #ifndef __DOXYGEN__ /* * Keep the properties definitions hidden from doxygen as it incorrectly parses @@ -44,6 +46,8 @@ namespace draft { ${draft_controls_def} } /* namespace draft */ + +${vendor_controls_def} #endif /** diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids.yaml index f35563842a5a..45f3609b4236 100644 --- a/src/libcamera/property_ids.yaml +++ b/src/libcamera/property_ids.yaml @@ -4,6 +4,7 @@ # %YAML 1.1 --- +vendor: libcamera controls: - Location: type: int32_t diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py index 9948c41e42b1..dfd7c179a883 100755 --- a/src/py/libcamera/gen-py-controls.py +++ b/src/py/libcamera/gen-py-controls.py @@ -24,45 +24,59 @@ def find_common_prefix(strings): def generate_py(controls, mode): out = '' - for ctrl in controls: - name, ctrl = ctrl.popitem() - - if ctrl.get('draft'): - ns = 'libcamera::{}::draft::'.format(mode) - container = 'draft' - else: - ns = 'libcamera::{}::'.format(mode) - container = 'controls' + vendors_class_def = [] + vendor_defs = [] + vendors = [] + for vendor, ctrl_list in controls.items(): + for ctrls in ctrl_list: + name, ctrl = ctrls.popitem() + + if vendor not in vendors and vendor != 'libcamera': + vendors_class_def.append('class Py{}Controls\n{{\n}};\n'.format(vendor)) + vendor_defs.append('\tauto {} = py::class_(controls, \"{}\");'.format(vendor, vendor, vendor)) + vendors.append(vendor) + + if ctrl.get('draft'): + ns = 'libcamera::{}::draft::'.format(mode) + container = 'draft' + elif vendor != 'libcamera': + ns = 'libcamera::{}::{}::'.format(mode, vendor) + container = vendor + else: + ns = 'libcamera::{}::'.format(mode) + container = 'controls' - out += f'\t{container}.def_readonly_static("{name}", static_cast(&{ns}{name}));\n\n' + out += f'\t{container}.def_readonly_static("{name}", static_cast(&{ns}{name}));\n\n' - enum = ctrl.get('enum') - if not enum: - continue + enum = ctrl.get('enum') + if not enum: + continue - cpp_enum = name + 'Enum' + cpp_enum = name + 'Enum' - out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum) + out += '\tpy::enum_<{}{}>({}, \"{}\")\n'.format(ns, cpp_enum, container, cpp_enum) - if mode == 'controls': - # Adjustments for controls - if name == 'LensShadingMapMode': - prefix = 'LensShadingMapMode' + if mode == 'controls': + # Adjustments for controls + if name == 'LensShadingMapMode': + prefix = 'LensShadingMapMode' + else: + prefix = find_common_prefix([e['name'] for e in enum]) else: + # Adjustments for properties prefix = find_common_prefix([e['name'] for e in enum]) - else: - # Adjustments for properties - prefix = find_common_prefix([e['name'] for e in enum]) - for entry in enum: - cpp_enum = entry['name'] - py_enum = entry['name'][len(prefix):] + for entry in enum: + cpp_enum = entry['name'] + py_enum = entry['name'][len(prefix):] - out += '\t\t.value(\"{}\", {}{})\n'.format(py_enum, ns, cpp_enum) + out += '\t\t.value(\"{}\", {}{})\n'.format(py_enum, ns, cpp_enum) - out += '\t;\n\n' + out += '\t;\n\n' - return {'controls': out} + return {'controls': out, + 'vendors_class_def': '\n'.join(vendors_class_def), + 'vendors_defs': '\n'.join(vendor_defs)} def fill_template(template, data): @@ -75,14 +89,14 @@ def fill_template(template, data): def main(argv): # Parse command line arguments parser = argparse.ArgumentParser() - parser.add_argument('-o', dest='output', metavar='file', type=str, + parser.add_argument('--mode', '-m', type=str, required=True, + help='Mode is either "controls" or "properties"') + parser.add_argument('--output', '-o', metavar='file', type=str, help='Output file name. Defaults to standard output if not specified.') parser.add_argument('input', type=str, help='Input file name.') parser.add_argument('template', type=str, help='Template file name.') - parser.add_argument('--mode', type=str, required=True, - help='Mode is either "controls" or "properties"') args = parser.parse_args(argv[1:]) if args.mode not in ['controls', 'properties']: @@ -90,7 +104,10 @@ def main(argv): return -1 data = open(args.input, 'rb').read() - controls = yaml.safe_load(data)['controls'] + + controls = {} + vendor = yaml.safe_load(data)['vendor'] + controls[vendor] = yaml.safe_load(data)['controls'] data = generate_py(controls, args.mode) diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/libcamera/py_controls_generated.cpp.in index 18fa57d948ea..ec4b55ef2011 100644 --- a/src/py/libcamera/py_controls_generated.cpp.in +++ b/src/py/libcamera/py_controls_generated.cpp.in @@ -21,10 +21,13 @@ class PyDraftControls { }; +${vendors_class_def} + void init_py_controls_generated(py::module& m) { auto controls = py::class_(m, "controls"); auto draft = py::class_(controls, "draft"); +${vendors_defs} ${controls} } diff --git a/src/py/libcamera/py_properties_generated.cpp.in b/src/py/libcamera/py_properties_generated.cpp.in index e49b6e91bb83..f7b5ec8c635d 100644 --- a/src/py/libcamera/py_properties_generated.cpp.in +++ b/src/py/libcamera/py_properties_generated.cpp.in @@ -21,10 +21,13 @@ class PyDraftProperties { }; +${vendors_class_def} + void init_py_properties_generated(py::module& m) { auto controls = py::class_(m, "properties"); auto draft = py::class_(controls, "draft"); +${vendors_defs} ${controls} } diff --git a/utils/gen-controls.py b/utils/gen-controls.py index 1075ae302ce1..4c8d41eb9997 100755 --- a/utils/gen-controls.py +++ b/utils/gen-controls.py @@ -35,11 +35,12 @@ class ControlEnum(object): class Control(object): - def __init__(self, name, data): + def __init__(self, name, data, vendor): self.__name = name self.__data = data self.__enum_values = None self.__size = None + self.__vendor = vendor enum_values = data.get('enum') if enum_values is not None: @@ -89,6 +90,11 @@ class Control(object): """Is the control a draft control""" return self.__data.get('draft') is not None + @property + def vendor(self): + """The vendor string, or None""" + return self.__vendor + @property def name(self): """The control name (CamelCase)""" @@ -145,15 +151,18 @@ ${description} enum_values_start = string.Template('''extern const std::array ${name}Values = {''') enum_values_values = string.Template('''\tstatic_cast(${name}),''') - ctrls_doc = [] - ctrls_def = [] - draft_ctrls_doc = [] - draft_ctrls_def = [] + ctrls_doc = {} + ctrls_def = {} ctrls_map = [] for ctrl in controls: id_name = snake_case(ctrl.name).upper() + vendor = 'draft' if ctrl.is_draft else ctrl.vendor + if vendor not in ctrls_doc: + ctrls_doc[vendor] = [] + ctrls_def[vendor] = [] + info = { 'name': ctrl.name, 'type': ctrl.type, @@ -161,11 +170,8 @@ ${description} 'id_name': id_name, } - target_doc = ctrls_doc - target_def = ctrls_def - if ctrl.is_draft: - target_doc = draft_ctrls_doc - target_def = draft_ctrls_def + target_doc = ctrls_doc[vendor] + target_def = ctrls_def[vendor] if ctrl.is_enum: enum_doc = [] @@ -203,39 +209,65 @@ ${description} ctrls_map.append('\t{ ' + id_name + ', &' + ctrl.q_name + ' },') + vendor_ctrl_doc_sub = [] + vendor_ctrl_template = string.Template(''' +namespace ${vendor} { + +${vendor_controls_str} + +} /* namespace ${vendor} */''') + + for vendor in [v for v in ctrls_doc.keys() if v not in ['libcamera', 'draft']]: + vendor_ctrl_doc_sub.append(vendor_ctrl_template.substitute({'vendor': vendor, 'vendor_controls_str': '\n\n'.join(ctrls_doc[vendor])})) + + vendor_ctrl_def_sub = [] + for vendor in [v for v in ctrls_def.keys() if v not in ['libcamera', 'draft']]: + vendor_ctrl_def_sub.append(vendor_ctrl_template.substitute({'vendor': vendor, 'vendor_controls_str': '\n'.join(ctrls_def[vendor])})) + return { - 'controls_doc': '\n\n'.join(ctrls_doc), - 'controls_def': '\n'.join(ctrls_def), - 'draft_controls_doc': '\n\n'.join(draft_ctrls_doc), - 'draft_controls_def': '\n\n'.join(draft_ctrls_def), + 'controls_doc': '\n\n'.join(ctrls_doc['libcamera']), + 'controls_def': '\n'.join(ctrls_def['libcamera']), + 'draft_controls_doc': '\n\n'.join(ctrls_doc['draft']), + 'draft_controls_def': '\n\n'.join(ctrls_def['draft']), 'controls_map': '\n'.join(ctrls_map), + 'vendor_controls_doc': '\n'.join(vendor_ctrl_doc_sub), + 'vendor_controls_def': '\n'.join(vendor_ctrl_def_sub), } -def generate_h(controls): +def generate_h(controls, mode): enum_template_start = string.Template('''enum ${name}Enum {''') enum_value_template = string.Template('''\t${name} = ${value},''') enum_values_template = string.Template('''extern const std::array ${name}Values;''') template = string.Template('''extern const Control<${type}> ${name};''') - ctrls = [] - draft_ctrls = [] - ids = [] - id_value = 1 + ctrls = {} + ids = {} + id_value = {} for ctrl in controls: id_name = snake_case(ctrl.name).upper() - ids.append('\t' + id_name + ' = ' + str(id_value) + ',') + vendor = 'draft' if ctrl.is_draft else ctrl.vendor + if vendor not in ctrls: + ids[vendor] = [] + id_value[vendor] = 1 + ctrls[vendor] = [] + + # Core and draft controls use the same ID value + target_ids = ids['libcamera'] if vendor in ['libcamera', 'draft'] else ids[vendor] + target_ids.append('\t' + id_name + ' = ' + str(id_value[vendor]) + ',') info = { 'name': ctrl.name, 'type': ctrl.type, } - target_ctrls = ctrls + target_ctrls = ctrls['libcamera'] if ctrl.is_draft: - target_ctrls = draft_ctrls + target_ctrls = ctrls['draft'] + elif vendor != 'libcamera': + target_ctrls = ctrls[vendor] if ctrl.is_enum: target_ctrls.append(enum_template_start.substitute(info)) @@ -257,12 +289,35 @@ def generate_h(controls): target_ctrls.append(enum_values_template.substitute(values_info)) target_ctrls.append(template.substitute(info)) - id_value += 1 + id_value[vendor] += 1 + + vendor_template = string.Template(''' +namespace ${vendor} { + +#define LIBCAMERA_HAS_${vendor_def}_VENDOR_${mode} + +enum { +${vendor_enums} +}; + +${vendor_controls} + +} /* namespace ${vendor} */ +''') + + vendor_sub = [] + for vendor in [v for v in ctrls.keys() if v not in ['libcamera', 'draft']]: + vendor_sub.append(vendor_template.substitute({'mode': mode.upper(), + 'vendor': vendor, + 'vendor_def': vendor.upper(), + 'vendor_enums': '\n'.join(ids[vendor]), + 'vendor_controls': '\n'.join(ctrls[vendor])})) return { - 'ids': '\n'.join(ids), - 'controls': '\n'.join(ctrls), - 'draft_controls': '\n'.join(draft_ctrls) + 'ids': '\n'.join(ids['libcamera']), + 'controls': '\n'.join(ctrls['libcamera']), + 'draft_controls': '\n'.join(ctrls['draft']), + 'vendor_controls': '\n'.join(vendor_sub) } @@ -278,22 +333,26 @@ def main(argv): # Parse command line arguments parser = argparse.ArgumentParser() - parser.add_argument('-o', dest='output', metavar='file', type=str, + parser.add_argument('--mode', '-m', type=str, required=True, choices=['controls', 'properties'], + help='Mode of operation') + parser.add_argument('--output', '-o', metavar='file', type=str, help='Output file name. Defaults to standard output if not specified.') parser.add_argument('input', type=str, help='Input file name.') parser.add_argument('template', type=str, help='Template file name.') + args = parser.parse_args(argv[1:]) data = open(args.input, 'rb').read() + vendor = yaml.safe_load(data)['vendor'] controls = yaml.safe_load(data)['controls'] - controls = [Control(*ctrl.popitem()) for ctrl in controls] + controls = [Control(*ctrl.popitem(), vendor) for ctrl in controls] if args.template.endswith('.cpp.in'): data = generate_cpp(controls) elif args.template.endswith('.h.in'): - data = generate_h(controls) + data = generate_h(controls, args.mode) else: raise RuntimeError('Unknown template type') From patchwork Fri Nov 24 12:37:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 19235 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 25BF6C328F for ; Fri, 24 Nov 2023 12:37:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0E957629C5; Fri, 24 Nov 2023 13:37:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1700829441; bh=ehngwBkfs7R+bPnNwIVw1bFA5d7Nf3Z/b0qPu2pWLyo=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=U/hb5FoMt08YKW4ALXSG+35kfOOHCx5ZJjxJH2VrDDiN5/mrhG9E4SCA0thK/a4k8 16/EzN+AZ91Bk2LPnisnQR5sOzS9ovWtClWlPLi/eo/N5ltdve1+Vv7h03s5wibRj7 99qZJUyFW8iA+klBCtw9CXJHtnesRI64SPcr2+4f5dcBz30ENa98KqOXJhCeseoCiC DW2SoSuMSBHsYXahEqIrvhmwSqySQDFO+RYRByVZbcpWIt5+HI9vf8/ZY1nw5BgvVO 61SzdpZX80wwuQqPgSrXcycGo6i/tVdxQXCqsOo7XRyufMM6ZAGCa+0T74KMTxmK0I eSUmWWOPdWTAQ== Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 61D4F629B6 for ; Fri, 24 Nov 2023 13:37:18 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="o5O2RKjF"; dkim-atps=neutral Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-40b358f216dso12065605e9.1 for ; Fri, 24 Nov 2023 04:37:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1700829437; x=1701434237; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9RwY5vd7zi9A8JwMNsB0WJPcBdyXlwV+lPLOwLdH5O0=; b=o5O2RKjFKpQuRtp9rAGbqCTtGugfUjmPH4AuSI+5rrnY63yKKpGXips/GPWBgl9pEM FSbUMGi8YwQV8esuudp6xBq/GJFj7PY0CcQ2+zrQxvsG0iozCzt1D/eEvSLiDOLKDbF+ MLkWFnxSO1JCFWBiKECPzKWW3NEuS7t385zqscD3Nw0fEgdbVLV9e8NWlzVBcdMP+Bd5 UXbtdT3cd4VvDGz4SfXLx3u6HGOfuu1LyZjPGkBTII6/k8vvViTDRpxSVZsOh9q0TbQx Q3FMVtWHsktp95/Ewx//mx80gcd/UKz5kgzu7FN+y/q7Tz2iUoCs0SRL5KaL7Q34JRWD qpZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700829437; x=1701434237; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9RwY5vd7zi9A8JwMNsB0WJPcBdyXlwV+lPLOwLdH5O0=; b=vJmacJsCuF8dSKFUG2R/iuTwqDlWPAFm+Cqblrflz4iKex78WjvCYGyqPPzsieCkyC DgsfR5CoFO30oHXP5tMG1Yl0saEVK+/X5VjRLJapxeDaT/OULJj8fNEfsXMZZRSZugyS Tav9T2Mz3VmTWsjLmre5jEcS7RjFPlRCRfzni41zVI19Kp9mw5sBQ7i3DwSGmIEw3joM DZA+HKSl4Ru+QXv06vEH7/wAqCVtRoti+D469skIHNGHqd+BZxGLfcXa+yVhnDXQ2y2U ts9mHTuXa4UCBENJij8yGtDRx0dmyPA/Mfd/jmqFU4IL/uQKzZLj2PzcTCYXUMun/7aK g8oQ== X-Gm-Message-State: AOJu0Yx2TmVSS33lD3dn+rSzCkW1jX5s22KfaT6p2WUQvjT7pccSwQ/Y iVXShqYVY5SU52kUjHra6xLSL3kOJmHe0MI5kNcrgw== X-Google-Smtp-Source: AGHT+IGPOA49crTnC5DmcbdxMW7rZzCPdcitwvem7Vbs4JKfh6C5s69WEu9wkU8MMaKsRcWTTfSDow== X-Received: by 2002:a05:600c:2614:b0:408:3ea0:3026 with SMTP id h20-20020a05600c261400b004083ea03026mr2322994wma.11.1700829437600; Fri, 24 Nov 2023 04:37:17 -0800 (PST) Received: from localhost.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id v11-20020a05600c444b00b0040b3867a297sm3303690wmn.36.2023.11.24.04.37.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Nov 2023 04:37:16 -0800 (PST) To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Nov 2023 12:37:08 +0000 Message-Id: <20231124123713.22519-3-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231124123713.22519-1-naush@raspberrypi.com> References: <20231124123713.22519-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 2/7] controls: Update argument handling for controls generation scripts X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The template file to the gen-controls.py and gen-py-controls.py is now passed in through the '-t' or '--template' command line argument instead of being a positional argument. This will allow multiple input files to be provided to the scripts in a future commit. Signed-off-by: Naushir Patuck Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham --- include/libcamera/meson.build | 7 ++++--- src/libcamera/meson.build | 7 ++++--- src/py/libcamera/gen-py-controls.py | 4 ++-- src/py/libcamera/meson.build | 18 ++++++++---------- utils/gen-controls.py | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 2c8c0258c95e..5fb772e6dd14 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -41,12 +41,13 @@ control_source_files = { control_headers = [] foreach header, mode : control_source_files - input_files = files('../../src/libcamera/' + header +'.yaml', header + '.h.in') + input_files = files('../../src/libcamera/' + header +'.yaml') + template_file = files(header + '.h.in') control_headers += custom_target(header + '_h', input : input_files, output : header + '.h', - command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@', - '--mode', mode], + command : [gen_controls, '-o', '@OUTPUT@', + '--mode', mode, '-t', template_file, '@INPUT@'], install : true, install_dir : libcamera_headers_install_dir) endforeach diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index e49bf850b355..05ee38daf22b 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -128,12 +128,13 @@ endif control_sources = [] foreach source, mode : control_source_files - input_files = files(source +'.yaml', source + '.cpp.in') + input_files = files(source +'.yaml') + template_file = files(source + '.cpp.in') control_sources += custom_target(source + '_cpp', input : input_files, output : source + '.cpp', - command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@', - '--mode', mode]) + command : [gen_controls, '-o', '@OUTPUT@', + '--mode', mode, '-t', template_file, '@INPUT@']) endforeach libcamera_sources += control_sources diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py index dfd7c179a883..cfcfd4d16acf 100755 --- a/src/py/libcamera/gen-py-controls.py +++ b/src/py/libcamera/gen-py-controls.py @@ -93,10 +93,10 @@ def main(argv): help='Mode is either "controls" or "properties"') parser.add_argument('--output', '-o', metavar='file', type=str, help='Output file name. Defaults to standard output if not specified.') + parser.add_argument('--template', '-t', type=str, required=True, + help='Template file name.') parser.add_argument('input', type=str, help='Input file name.') - parser.add_argument('template', type=str, - help='Template file name.') args = parser.parse_args(argv[1:]) if args.mode not in ['controls', 'properties']: diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index f58c7198ee9e..1c3ea1843ac0 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -28,29 +28,27 @@ pycamera_sources = files([ # Generate controls -gen_py_controls_input_files = files([ - '../../libcamera/control_ids.yaml', - 'py_controls_generated.cpp.in', -]) +gen_py_controls_input_files = files('../../libcamera/control_ids.yaml') +gen_py_controls_template = files('py_controls_generated.cpp.in') gen_py_controls = files('gen-py-controls.py') pycamera_sources += custom_target('py_gen_controls', input : gen_py_controls_input_files, output : ['py_controls_generated.cpp'], - command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', '@INPUT@']) + command : [gen_py_controls, '--mode', 'controls', '-o', '@OUTPUT@', + '-t', gen_py_controls_template, '@INPUT@']) # Generate properties -gen_py_property_enums_input_files = files([ - '../../libcamera/property_ids.yaml', - 'py_properties_generated.cpp.in', -]) +gen_py_property_enums_input_files = files('../../libcamera/property_ids.yaml') +gen_py_properties_template = files('py_properties_generated.cpp.in') pycamera_sources += custom_target('py_gen_properties', input : gen_py_property_enums_input_files, output : ['py_properties_generated.cpp'], - command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', '@INPUT@']) + command : [gen_py_controls, '--mode', 'properties', '-o', '@OUTPUT@', + '-t', gen_py_properties_template, '@INPUT@']) # Generate formats diff --git a/utils/gen-controls.py b/utils/gen-controls.py index 4c8d41eb9997..6680ecf84acb 100755 --- a/utils/gen-controls.py +++ b/utils/gen-controls.py @@ -337,10 +337,10 @@ def main(argv): help='Mode of operation') parser.add_argument('--output', '-o', metavar='file', type=str, help='Output file name. Defaults to standard output if not specified.') + parser.add_argument('--template', '-t', dest='template', type=str, required=True, + help='Template file name.') parser.add_argument('input', type=str, help='Input file name.') - parser.add_argument('template', type=str, - help='Template file name.') args = parser.parse_args(argv[1:]) From patchwork Fri Nov 24 12:37:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 19236 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 056B7C3220 for ; Fri, 24 Nov 2023 12:37:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B1426629C9; Fri, 24 Nov 2023 13:37:21 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1700829441; bh=Kc5r5Hd304Wo/DdfgRXwsgGS0N0yybblcEsyXhe9HGU=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=GBr2YqI8SGoFkZKUoFJP96a123hHedj4a/+gRVJAIOajmJKeWaOiE5UyS0puxkuYN IQeljY/pAOuMXrISYpO0gteq61SyIvXlEYfPlq2PZUXUoDSNTqGVdEJ3iaLWzi61Bi q3/G3R1DBf1AuTjytMMP9K6E4rR3EzxXVri7j4ZvR+0bX4Pg33GHWnxLedKt+MQsAg s+HGUXO6E3CLK9Ta85BGALhZHIS7yi3PlDy873liqhrEd/VhzRWcR8K5bhD0YYwTv4 TZb5qoAReYHzoHa+MJNXNsi7nK5GQmYTQbtbXpdhxMMz7ztJI9r0K1WpAL1ZrhW18F nbvA9+j0VC8gg== Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CDEDF629BF for ; Fri, 24 Nov 2023 13:37:19 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="S1KVivJB"; dkim-atps=neutral Received: by mail-wm1-x329.google.com with SMTP id 5b1f17b1804b1-4083f61312eso14522855e9.3 for ; Fri, 24 Nov 2023 04:37:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1700829439; x=1701434239; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LrwzQEkn09KNa4CcThkl4P2nzzUI6MpLq4v6QB3BzSo=; b=S1KVivJBNvCdmRQ1E3ImDhyeXmzGtU/gzfQyZgrzFmcKSQYcTivT5ucdwKSS9hdcc9 SEk0RLLTfqxi44orEpRm4TheKoN0a+72DWy4y3TWDioO8zst7jD9lisrQdANn2LtnKFm AADtbzDkcJdCFHlIayfLlDiGYkhAvRQbsruYl77Q6ZC5esN13GOrNMWq3CS0hbTOdG8o 97B83twMt4u/i0ao/2UUE6oFSdRg7jtOb7iIyaUIu9Vv7EoE8xeKGarLBnifnUS/MXvX 8VKk7J1rKUkUfET8g0MGzNeufaQ12b0W9aY8GVq06neZhwmp8vdcX6TbXTsjIVRys0gl wfpQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700829439; x=1701434239; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=LrwzQEkn09KNa4CcThkl4P2nzzUI6MpLq4v6QB3BzSo=; b=q6/WbtD3HlPx3KVN9XwBqXCHnh1G+PYrNm4OwNgI8BFxcuWI8EMMnprkLIOrPH8j3b pW+htTBHcwVXqjBSLtxbkbnqYBGb+kbT1oQvgogDf0UZnPR1f8XJOUhvO+mLb0f3lKyL SigMqCvxvruSVkI1zMbq35ghoXZaaJIZgdzccMJY3Nbt9KUfPpHGqLZ2hQ75Yswe72qf n1kS5Lk0F6TEq/CLAOczq07+NO6fSD/x86A9/kZka38wg+GQzCgtLJ/w1SVNesdJmZaL bi/fpcehJZShbyTJp70JM4e5P2Uxm1t4jxNlZpFYu7EaRVCXJhXjCak5FxOIQEdMa0dC Angg== X-Gm-Message-State: AOJu0YwvrAsjViGEfCj42ygwNDvcyrMAHEZVGayWaUpIqtblUAjywfmE aY2qgrnj98LsQ4xzyP9mi1q24308smkQUlEs0ZVUeA== X-Google-Smtp-Source: AGHT+IGpnDbGOB2VEEiZefcx55PMrkSd8whnCJ4/Kj7P9UpIetQzJhcPV1im9wg45SgwSZAuByOZtA== X-Received: by 2002:a05:600c:1986:b0:40b:367a:801 with SMTP id t6-20020a05600c198600b0040b367a0801mr2619991wmq.41.1700829438976; Fri, 24 Nov 2023 04:37:18 -0800 (PST) Received: from localhost.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id v11-20020a05600c444b00b0040b3867a297sm3303690wmn.36.2023.11.24.04.37.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Nov 2023 04:37:17 -0800 (PST) To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Nov 2023 12:37:09 +0000 Message-Id: <20231124123713.22519-4-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231124123713.22519-1-naush@raspberrypi.com> References: <20231124123713.22519-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 3/7] build: controls: Rework how controls and properties are generated X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add support for using separate YAML files for controls and properties generation. The mapping of vendor/pipeline handler to control file is done through the controls_map variable in include/libcamera/meson.build. This simplifies management of vendor control definitions and avoids possible merge conflicts when changing the control_ids.yaml file for core and draft controls. With this change, libcamera and draft controls and properties files are designated the 'libcamera' vendor tag. In this change, we also rename control_ids.yaml -> control_ids_core.yaml and property_ids.yaml -> property_ids_core.yaml to designate these as core libcamera controls. Signed-off-by: Naushir Patuck Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham --- Documentation/guides/pipeline-handler.rst | 8 +-- include/libcamera/meson.build | 52 +++++++++++++++---- meson.build | 2 + ...control_ids.yaml => control_ids_core.yaml} | 0 src/libcamera/meson.build | 21 ++++++-- ...operty_ids.yaml => property_ids_core.yaml} | 0 src/py/libcamera/gen-py-controls.py | 10 ++-- src/py/libcamera/meson.build | 12 ++++- utils/gen-controls.py | 13 +++-- 9 files changed, 88 insertions(+), 30 deletions(-) rename src/libcamera/{control_ids.yaml => control_ids_core.yaml} (100%) rename src/libcamera/{property_ids.yaml => property_ids_core.yaml} (100%) diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst index 10b9c75c2a7f..66d428a19c4f 100644 --- a/Documentation/guides/pipeline-handler.rst +++ b/Documentation/guides/pipeline-handler.rst @@ -587,12 +587,12 @@ immutable properties of the ``Camera`` device. The libcamera controls and properties are defined in YAML form which is processed to automatically generate documentation and interfaces. Controls are -defined by the src/libcamera/`control_ids.yaml`_ file and camera properties -are defined by src/libcamera/`properties_ids.yaml`_. +defined by the src/libcamera/`control_ids_core.yaml`_ file and camera properties +are defined by src/libcamera/`properties_ids_core.yaml`_. .. _controls framework: https://libcamera.org/api-html/controls_8h.html -.. _control_ids.yaml: https://libcamera.org/api-html/control__ids_8h.html -.. _properties_ids.yaml: https://libcamera.org/api-html/property__ids_8h.html +.. _control_ids_core.yaml: https://libcamera.org/api-html/control__ids_8h.html +.. _properties_ids_core.yaml: https://libcamera.org/api-html/property__ids_8h.html Pipeline handlers can optionally register the list of controls an application can set as well as a list of immutable camera properties. Being both diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 5fb772e6dd14..a763c8ff4344 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -32,22 +32,56 @@ install_headers(libcamera_public_headers, libcamera_headers_install_dir = get_option('includedir') / libcamera_include_dir -# control_ids.h and property_ids.h and associated modes -control_source_files = { - 'control_ids': 'controls', - 'property_ids': 'properties', +controls_map = { + 'controls': { + 'core': 'control_ids_core.yaml', + }, + + 'properties': { + 'core': 'property_ids_core.yaml', + } } control_headers = [] +controls_files = [] +properties_files = [] + +foreach mode, entry : controls_map + files_list = [] + input_files = [] + foreach vendor, header : entry + if vendor != 'core' and vendor != 'draft' + if vendor not in pipelines + message('vendor not in pipelines') + continue + endif + endif + + if header in files_list + message('header in files list') + continue + endif + + files_list += header + input_files += files('../../src/libcamera/' + header) + endforeach + + outfile = '' + if mode == 'controls' + outfile = 'control_ids.h' + controls_files += files_list + else + outfile = 'property_ids.h' + properties_files += files_list + endif -foreach header, mode : control_source_files - input_files = files('../../src/libcamera/' + header +'.yaml') - template_file = files(header + '.h.in') + template_file = files(outfile + '.in') control_headers += custom_target(header + '_h', input : input_files, - output : header + '.h', + output : outfile, command : [gen_controls, '-o', '@OUTPUT@', - '--mode', mode, '-t', template_file, '@INPUT@'], + '--mode', mode, '-t', template_file, + '@INPUT@'], install : true, install_dir : libcamera_headers_install_dir) endforeach diff --git a/meson.build b/meson.build index e9a1c7e360ce..ee57cb780149 100644 --- a/meson.build +++ b/meson.build @@ -267,6 +267,8 @@ py_mod.find_installation('python3', modules : py_modules) summary({ 'Enabled pipelines': pipelines, 'Enabled IPA modules': enabled_ipa_names, + 'Controls files': controls_files, + 'Properties files': properties_files, 'Hotplug support': libudev.found(), 'Tracing support': tracing_enabled, 'Android support': android_enabled, diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids_core.yaml similarity index 100% rename from src/libcamera/control_ids.yaml rename to src/libcamera/control_ids_core.yaml diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 05ee38daf22b..6d9902e6ffd1 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -127,12 +127,23 @@ endif control_sources = [] -foreach source, mode : control_source_files - input_files = files(source +'.yaml') - template_file = files(source + '.cpp.in') - control_sources += custom_target(source + '_cpp', +controls_mode_files = { + 'controls' : controls_files, + 'properties' : properties_files, +} + +foreach mode, input_files : controls_mode_files + input_files = files(input_files) + + if mode == 'controls' + template_file = files('control_ids.cpp.in') + else + template_file = files('property_ids.cpp.in') + endif + + control_sources += custom_target(mode + '_cpp', input : input_files, - output : source + '.cpp', + output : mode + '_ids.cpp', command : [gen_controls, '-o', '@OUTPUT@', '--mode', mode, '-t', template_file, '@INPUT@']) endforeach diff --git a/src/libcamera/property_ids.yaml b/src/libcamera/property_ids_core.yaml similarity index 100% rename from src/libcamera/property_ids.yaml rename to src/libcamera/property_ids_core.yaml diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py index cfcfd4d16acf..8ae8d5126e39 100755 --- a/src/py/libcamera/gen-py-controls.py +++ b/src/py/libcamera/gen-py-controls.py @@ -95,7 +95,7 @@ def main(argv): help='Output file name. Defaults to standard output if not specified.') parser.add_argument('--template', '-t', type=str, required=True, help='Template file name.') - parser.add_argument('input', type=str, + parser.add_argument('input', type=str, nargs='+', help='Input file name.') args = parser.parse_args(argv[1:]) @@ -103,11 +103,11 @@ def main(argv): print(f'Invalid mode option "{args.mode}"', file=sys.stderr) return -1 - data = open(args.input, 'rb').read() - controls = {} - vendor = yaml.safe_load(data)['vendor'] - controls[vendor] = yaml.safe_load(data)['controls'] + for input in args.input: + data = open(input, 'rb').read() + vendor = yaml.safe_load(data)['vendor'] + controls[vendor] = yaml.safe_load(data)['controls'] data = generate_py(controls, args.mode) diff --git a/src/py/libcamera/meson.build b/src/py/libcamera/meson.build index 1c3ea1843ac0..31af63ec0dc6 100644 --- a/src/py/libcamera/meson.build +++ b/src/py/libcamera/meson.build @@ -28,11 +28,15 @@ pycamera_sources = files([ # Generate controls -gen_py_controls_input_files = files('../../libcamera/control_ids.yaml') +gen_py_controls_input_files = [] gen_py_controls_template = files('py_controls_generated.cpp.in') gen_py_controls = files('gen-py-controls.py') +foreach file : controls_files + gen_py_controls_input_files += files('../../libcamera/' + file) +endforeach + pycamera_sources += custom_target('py_gen_controls', input : gen_py_controls_input_files, output : ['py_controls_generated.cpp'], @@ -41,9 +45,13 @@ pycamera_sources += custom_target('py_gen_controls', # Generate properties -gen_py_property_enums_input_files = files('../../libcamera/property_ids.yaml') +gen_py_property_enums_input_files = [] gen_py_properties_template = files('py_properties_generated.cpp.in') +foreach file : properties_files + gen_py_property_enums_input_files += files('../../libcamera/' + file) +endforeach + pycamera_sources += custom_target('py_gen_properties', input : gen_py_property_enums_input_files, output : ['py_properties_generated.cpp'], diff --git a/utils/gen-controls.py b/utils/gen-controls.py index 6680ecf84acb..30c58f35473c 100755 --- a/utils/gen-controls.py +++ b/utils/gen-controls.py @@ -12,6 +12,7 @@ import operator import string import sys import yaml +import os class ControlEnum(object): @@ -339,15 +340,17 @@ def main(argv): help='Output file name. Defaults to standard output if not specified.') parser.add_argument('--template', '-t', dest='template', type=str, required=True, help='Template file name.') - parser.add_argument('input', type=str, + parser.add_argument('input', type=str, nargs='+', help='Input file name.') args = parser.parse_args(argv[1:]) - data = open(args.input, 'rb').read() - vendor = yaml.safe_load(data)['vendor'] - controls = yaml.safe_load(data)['controls'] - controls = [Control(*ctrl.popitem(), vendor) for ctrl in controls] + controls = [] + for input in args.input: + data = open(input, 'rb').read() + vendor = yaml.safe_load(data)['vendor'] + ctrls = yaml.safe_load(data)['controls'] + controls = controls + [Control(*ctrl.popitem(), vendor) for ctrl in ctrls] if args.template.endswith('.cpp.in'): data = generate_cpp(controls) From patchwork Fri Nov 24 12:37:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 19237 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id F0F9AC328F for ; Fri, 24 Nov 2023 12:37:24 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8FAAB629CB; Fri, 24 Nov 2023 13:37:24 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1700829444; bh=e+1S4cqQKkHtFDecfrZTtIrLbNKlOg57Jl+Kb5mGX84=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=OPG3ODJrZY32vaK960nuiUz/qG0c44ZdsejT3Vv/A8T9TFy5CQTYaPhxyPPiIStmB 91TDsYZ2SAIokBKapOBD20ObOYAglBiBci22lOA3b9Vdf2E5A+wd1Vbea0PrctyJVD YymDFy5ZsJqJT6ivbkVSqX/ienzq6uZUGv8tUs03SDf14CV4FLDSjWIbFG5NwE/dTz PHSJZmUygVLXXeX6jM5KssoA7r1GBTx7bCmlOYFAXjtRkUguO6Kpvo7FwePiC3VbjP n6sAmAdJZTKqUAfoQupqNPoCk0Jy8pVllxV1o8D7rmzj/v+jdndY2YY/kRekz/tlUN cwG4ERzaRO+QQ== Received: from mail-lj1-x22f.google.com (mail-lj1-x22f.google.com [IPv6:2a00:1450:4864:20::22f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8DE58629C8 for ; Fri, 24 Nov 2023 13:37:21 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="Tc94LqiC"; dkim-atps=neutral Received: by mail-lj1-x22f.google.com with SMTP id 38308e7fff4ca-2c876e44157so22649141fa.2 for ; Fri, 24 Nov 2023 04:37:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1700829440; x=1701434240; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7xQ3mT2fqmA7FxGo2hVa8X1pmLX80pzAqTF9y+yBgRw=; b=Tc94LqiC/rFD8BW3QuNTbJuP9aRAS47fVkC5GoaYuwHzpI1bYBFZ7N20SVUERPuerk vN6Kf2SXztJm9Hx6kbkUb4+TZtfmBnT0fXi/SGxcMT52IiMTKfD1yxIlsi7Dmbnn4ca3 nq59ga35YsQ/J74Dj+TS/9MvCxhyLjtkpmHdfylV483Tqza10nd42K0+cnfJMa69Z7H7 Vbf6rVxD82Df+Bc6M7XfNnpDkjW8uPgS+ikohmAX6CTVrPJU7xmKlJBh3wBpwddke1d/ ZcbXZ8w9uzEDGOUPkcWuxyPTraGlmLiwQ9ewwYaEN+v+qaTyvo4W0h/JI9iAWdoBXH7R PS+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700829440; x=1701434240; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7xQ3mT2fqmA7FxGo2hVa8X1pmLX80pzAqTF9y+yBgRw=; b=AcFNSfc1MFnU0zh+DCyZvCZc4KAq76M+7Yp3PqF7P1+7GBAvkHWj+g+JU0wbnPDp1P qAU/Izm27YSss/aKeHJa5JYZ0vy15IDrC6JtR6CoI7ZMQfPECI6VHwLPI5M79dmHXPZn edqxtFf6pixCNLh6wDfcuCCELOJKJd2d1gjgQOBxZY8N9hMf0KobX9wDDet9nplQXV/r CB5rkDUfuVwszU9enmO2q3C+ABPocCaJdCm0ugXEdSMAdV5rd/i88WA4wNgRmXaHH4tO QZbLlBFfO23PzEhVFAarGEgs39fa6Dc/MpejpuxZ6mwFjIqzmaeZfsfrKCKVC+GocdTh nCTA== X-Gm-Message-State: AOJu0Yy+nbgsp4vJF7LVpWDv16eiO+qy/QODOJpaaq+Jp6izIpwgwnAm gzVxTITUwtCt+ON1MYtqHeqWUOLeAc0iemTIyK2pxQ== X-Google-Smtp-Source: AGHT+IGoJMmTDjSfyZaQtOdqRxQKD10X9HFYeba4TAS1fFG+Z4HNQNApwfu6iu06s4XgMSBckrh/xg== X-Received: by 2002:a2e:9410:0:b0:2c8:74f6:8446 with SMTP id i16-20020a2e9410000000b002c874f68446mr2114231ljh.12.1700829440289; Fri, 24 Nov 2023 04:37:20 -0800 (PST) Received: from localhost.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id v11-20020a05600c444b00b0040b3867a297sm3303690wmn.36.2023.11.24.04.37.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Nov 2023 04:37:19 -0800 (PST) To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Nov 2023 12:37:10 +0000 Message-Id: <20231124123713.22519-5-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231124123713.22519-1-naush@raspberrypi.com> References: <20231124123713.22519-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 4/7] libcamera: control: Add vendor control id range reservation X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a new control_ranges.yaml file that is used to reserve control id ranges/offsets for libcamera and vendor specific controls. This file is used by the gen-controls.py script to generate control id values for each control. Draft controls now have a separate range from core libcamera controls, breaking the existing numbering behaviour. Signed-off-by: Naushir Patuck Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Jacopo Mondi --- include/libcamera/meson.build | 3 ++- src/libcamera/control_ranges.yaml | 18 ++++++++++++++++++ src/libcamera/meson.build | 4 +++- utils/gen-controls.py | 14 ++++++++++---- 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/libcamera/control_ranges.yaml diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index a763c8ff4344..79187d3fdfc9 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -76,12 +76,13 @@ foreach mode, entry : controls_map endif template_file = files(outfile + '.in') + ranges_file = files('../../src/libcamera/control_ranges.yaml') control_headers += custom_target(header + '_h', input : input_files, output : outfile, command : [gen_controls, '-o', '@OUTPUT@', '--mode', mode, '-t', template_file, - '@INPUT@'], + '-r', ranges_file, '@INPUT@'], install : true, install_dir : libcamera_headers_install_dir) endforeach diff --git a/src/libcamera/control_ranges.yaml b/src/libcamera/control_ranges.yaml new file mode 100644 index 000000000000..d42447d04647 --- /dev/null +++ b/src/libcamera/control_ranges.yaml @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2023, Raspberry Pi Ltd +# +%YAML 1.1 +--- +# Specifies the control id ranges/offsets for core/draft libcamera and vendor +# controls and properties. +ranges: + # Core libcamera controls + libcamera: 0 + # Draft designated libcamera controls + draft: 10000 + # Raspberry Pi vendor controls + rpi: 20000 + # Next range starts at 30000 + +... diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 6d9902e6ffd1..45f63e932e4f 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -141,11 +141,13 @@ foreach mode, input_files : controls_mode_files template_file = files('property_ids.cpp.in') endif + ranges_file = files('control_ranges.yaml') control_sources += custom_target(mode + '_cpp', input : input_files, output : mode + '_ids.cpp', command : [gen_controls, '-o', '@OUTPUT@', - '--mode', mode, '-t', template_file, '@INPUT@']) + '--mode', mode, '-t', template_file, + '-r', ranges_file, '@INPUT@']) endforeach libcamera_sources += control_sources diff --git a/utils/gen-controls.py b/utils/gen-controls.py index 30c58f35473c..04c63098b19b 100755 --- a/utils/gen-controls.py +++ b/utils/gen-controls.py @@ -236,7 +236,7 @@ ${vendor_controls_str} } -def generate_h(controls, mode): +def generate_h(controls, mode, ranges): enum_template_start = string.Template('''enum ${name}Enum {''') enum_value_template = string.Template('''\t${name} = ${value},''') enum_values_template = string.Template('''extern const std::array ${name}Values;''') @@ -251,8 +251,10 @@ def generate_h(controls, mode): vendor = 'draft' if ctrl.is_draft else ctrl.vendor if vendor not in ctrls: + if vendor not in ranges.keys(): + raise RuntimeError(f'Control id range is not defined for vendor {vendor}') + id_value[vendor] = ranges[vendor] + 1 ids[vendor] = [] - id_value[vendor] = 1 ctrls[vendor] = [] # Core and draft controls use the same ID value @@ -338,13 +340,17 @@ def main(argv): help='Mode of operation') parser.add_argument('--output', '-o', metavar='file', type=str, help='Output file name. Defaults to standard output if not specified.') + parser.add_argument('--ranges', '-r', type=str, required=True, + help='Control id range reservation file.') parser.add_argument('--template', '-t', dest='template', type=str, required=True, help='Template file name.') parser.add_argument('input', type=str, nargs='+', help='Input file name.') - args = parser.parse_args(argv[1:]) + data = open(args.ranges, 'rb').read() + ranges = yaml.safe_load(data)['ranges'] + controls = [] for input in args.input: data = open(input, 'rb').read() @@ -355,7 +361,7 @@ def main(argv): if args.template.endswith('.cpp.in'): data = generate_cpp(controls) elif args.template.endswith('.h.in'): - data = generate_h(controls, args.mode) + data = generate_h(controls, args.mode, ranges) else: raise RuntimeError('Unknown template type') From patchwork Fri Nov 24 12:37:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 19238 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id BB5ACC32B6 for ; Fri, 24 Nov 2023 12:37:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 50E0D629D1; Fri, 24 Nov 2023 13:37:25 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1700829445; bh=seTU+UQSsBaAMPpyKKZif1q9XqB8jxwEfOVXpwJ7DZA=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=xTar/AKPMjVV15UUXWEmmQbWrnhNmkPnv8Pt6vDV7ClSbHIF/K8Cul1jCE48ShUYg pSCXwsYqRUYU+qR+7X3ltqIOEO+9SMG/zxIcbVs4aUydehSq4M/cGiprsvy15wIJgd GHUL9hFccovG2gmHIdh/tDZTkMc4fyBvv/iGfhTPrGY2xc3SimoY2OfevcZRh7gt03 gar8KaZqJEcKlCLTAin6vMuPXEWocuJ7gTPQKY+oKHdJ+k3OQLz2HYwl8z1drJoNNt X8CWjE2bpPHVtpajA6iMJiK5UeldefvsPN0P0Utj6ln9BVpoNUtNKmnegPTtr5KEea OiEfycut98Wpg== Received: from mail-wm1-x330.google.com (mail-wm1-x330.google.com [IPv6:2a00:1450:4864:20::330]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6CBFA629C0 for ; Fri, 24 Nov 2023 13:37:22 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="rw5rKYyx"; dkim-atps=neutral Received: by mail-wm1-x330.google.com with SMTP id 5b1f17b1804b1-40b2a8c7ca9so14496925e9.2 for ; Fri, 24 Nov 2023 04:37:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1700829442; x=1701434242; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5kHClKoADatMBPStz60slui8CckL4jEgFwLCEP4EeyQ=; b=rw5rKYyxE00cIQRQV35NoX0tEM6rby7Rd7kW1ClH5MvbM4THPfbWVFrLRxBM5OtqnA qsaZjxTNEF3sOYl2CzuZozilsXW3SzTBX6n4mJdRtG1E6UzS8DT+ffQjWM2M7VFHF6nu 6l9LgdHR1djKv0hZXrHExhozlCu+JeT5G4YnPyKmpECQh0wPfbWjOveG4/HdByLpKC39 8FkE43ycP0HTRyZxkq4swmBh+4mD5gC3e1bvq+/XCc3O90925UwTGyf08pICqcOIDpXe GrTBZaFGXy92HZwf1i0u0Z3+W6e/wCmSmkZAYodlWZUQQFNCS8nKoWK0xBr2zC8i+74F gHoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700829442; x=1701434242; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5kHClKoADatMBPStz60slui8CckL4jEgFwLCEP4EeyQ=; b=AKoCUWjRygSKRnOD+8zbqzkSBNUA1VqYbS8l0GP0/grTx3Xz9JucSPHxIM2On2+yGT LSZmfu9QvrpKtZHxhvS3IVb73yNmvI2eLOjvPZdn37Acidb/Ly5nD3PmSZs5qr4JMasd h3B14MnYcpLGR626FG5lhR035vxzfOHSAPc03zmdFYeDoArs026u//FJT4Cqf+Olnf+1 R6M7+y/juENANob46C0SDkef+mR3I44zeXWJU3DXngXk6sYP4KHjXBMm6nVUPUXC5shn UQolj4iglOkKENle0ivvWH9CVSKzoClH2Ip7GH02KNMu/WGJ1XK0G2dWadRVdjqcCghZ RpCQ== X-Gm-Message-State: AOJu0Yz659hmMTetKlp+ppaiJ4imOBqAgiQvkC63ZKnuW6bJM7IK2Uwu wRnuP4J2OFypPUsSOabW8k+Jq4OjhXAaeogGTtaPDQ== X-Google-Smtp-Source: AGHT+IGxZy7ybFllRecZf5wsW3N9/aK4buQnYU4rcveTdErXXDSaXDJZmDb0gUfNqJyqauEOz9dXXg== X-Received: by 2002:a05:600c:4e8e:b0:40b:3e18:1f4 with SMTP id f14-20020a05600c4e8e00b0040b3e1801f4mr10582wmq.30.1700829441399; Fri, 24 Nov 2023 04:37:21 -0800 (PST) Received: from localhost.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id v11-20020a05600c444b00b0040b3867a297sm3303690wmn.36.2023.11.24.04.37.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Nov 2023 04:37:20 -0800 (PST) To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Nov 2023 12:37:11 +0000 Message-Id: <20231124123713.22519-6-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231124123713.22519-1-naush@raspberrypi.com> References: <20231124123713.22519-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 5/7] libcamera: controls: Use vendor tags for draft controls and properties X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Label draft controls and properties through the "draft" vendor tag and deprecate the existing "draft: true" mechanism. This uses the new vendor tags mechanism to place draft controls in the same libcamera::controls::draft namespace and provide a defined control id range for these controls. This requires moving all draft controls from control_ids.yaml to control_ids_draft.yaml. One breaking change in this commit is that draft control ids also move to the libcamera::controls::draft namespace from the existing libcamera::controls namespace. This is desirable to avoid API breakages when adding new libcamera controls. So, for example, the use of controls::NOISE_REDUCTION_MODE will need to be replaced with controls::draft::NOISE_REDUCTION_MODE. Signed-off-by: Naushir Patuck Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham --- include/libcamera/control_ids.h.in | 6 - include/libcamera/meson.build | 2 + include/libcamera/property_ids.h.in | 6 - src/ipa/rpi/common/ipa_base.cpp | 2 +- src/ipa/rpi/vc4/vc4.cpp | 2 +- src/libcamera/control_ids.cpp.in | 16 +- src/libcamera/control_ids_core.yaml | 232 ----------------- src/libcamera/control_ids_draft.yaml | 240 ++++++++++++++++++ src/libcamera/property_ids.cpp.in | 16 +- src/libcamera/property_ids_core.yaml | 33 --- src/libcamera/property_ids_draft.yaml | 39 +++ src/py/libcamera/gen-py-controls.py | 5 +- src/py/libcamera/py_controls_generated.cpp.in | 5 - .../libcamera/py_properties_generated.cpp.in | 1 - utils/gen-controls.py | 36 +-- 15 files changed, 295 insertions(+), 346 deletions(-) create mode 100644 src/libcamera/control_ids_draft.yaml create mode 100644 src/libcamera/property_ids_draft.yaml diff --git a/include/libcamera/control_ids.h.in b/include/libcamera/control_ids.h.in index c97b09a82450..d53b1cf7beb2 100644 --- a/include/libcamera/control_ids.h.in +++ b/include/libcamera/control_ids.h.in @@ -26,12 +26,6 @@ ${controls} extern const ControlIdMap controls; -namespace draft { - -${draft_controls} - -} /* namespace draft */ - ${vendor_controls} } /* namespace controls */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 79187d3fdfc9..1504f741ae2f 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -34,10 +34,12 @@ libcamera_headers_install_dir = get_option('includedir') / libcamera_include_dir controls_map = { 'controls': { + 'draft': 'control_ids_draft.yaml', 'core': 'control_ids_core.yaml', }, 'properties': { + 'draft': 'property_ids_draft.yaml', 'core': 'property_ids_core.yaml', } } diff --git a/include/libcamera/property_ids.h.in b/include/libcamera/property_ids.h.in index 47c5d6bf2e28..43372c718fc9 100644 --- a/include/libcamera/property_ids.h.in +++ b/include/libcamera/property_ids.h.in @@ -23,12 +23,6 @@ ${ids} ${controls} -namespace draft { - -${draft_controls} - -} /* namespace draft */ - extern const ControlIdMap properties; ${vendor_controls} diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp index a1fec3aa3dd1..6ac9d5de2f88 100644 --- a/src/ipa/rpi/common/ipa_base.cpp +++ b/src/ipa/rpi/common/ipa_base.cpp @@ -1024,7 +1024,7 @@ void IpaBase::applyControls(const ControlList &controls) break; } - case controls::NOISE_REDUCTION_MODE: + case controls::draft::NOISE_REDUCTION_MODE: /* Handled below in handleControls() */ libcameraMetadata_.set(controls::draft::NoiseReductionMode, ctrl.second.get()); diff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp index c4baf04fb1e7..c165a5b8b0b6 100644 --- a/src/ipa/rpi/vc4/vc4.cpp +++ b/src/ipa/rpi/vc4/vc4.cpp @@ -260,7 +260,7 @@ void IpaVc4::handleControls(const ControlList &controls) for (auto const &ctrl : controls) { switch (ctrl.first) { - case controls::NOISE_REDUCTION_MODE: { + case controls::draft::NOISE_REDUCTION_MODE: { RPiController::DenoiseAlgorithm *sdn = dynamic_cast( controller_.getAlgorithm("SDN")); /* Some platforms may have a combined "denoise" algorithm instead. */ diff --git a/src/libcamera/control_ids.cpp.in b/src/libcamera/control_ids.cpp.in index be86548cf73f..d283c1c1f401 100644 --- a/src/libcamera/control_ids.cpp.in +++ b/src/libcamera/control_ids.cpp.in @@ -24,15 +24,6 @@ namespace controls { ${controls_doc} -/** - * \brief Namespace for libcamera draft controls - */ -namespace draft { - -${draft_controls_doc} - -} /* namespace draft */ - ${vendor_controls_doc} #ifndef __DOXYGEN__ @@ -42,13 +33,8 @@ ${vendor_controls_doc} */ ${controls_def} -namespace draft { - -${draft_controls_def} - -} /* namespace draft */ - ${vendor_controls_def} + #endif /** diff --git a/src/libcamera/control_ids_core.yaml b/src/libcamera/control_ids_core.yaml index ff74ce1deedb..76fb9347d8e3 100644 --- a/src/libcamera/control_ids_core.yaml +++ b/src/libcamera/control_ids_core.yaml @@ -865,236 +865,4 @@ controls: description: | This is a long exposure image. - # ---------------------------------------------------------------------------- - # Draft controls section - - - AePrecaptureTrigger: - type: int32_t - draft: true - description: | - Control for AE metering trigger. Currently identical to - ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER. - - Whether the camera device will trigger a precapture metering sequence - when it processes this request. - enum: - - name: AePrecaptureTriggerIdle - value: 0 - description: The trigger is idle. - - name: AePrecaptureTriggerStart - value: 1 - description: The pre-capture AE metering is started by the camera. - - name: AePrecaptureTriggerCancel - value: 2 - description: | - The camera will cancel any active or completed metering sequence. - The AE algorithm is reset to its initial state. - - - NoiseReductionMode: - type: int32_t - draft: true - description: | - Control to select the noise reduction algorithm mode. Currently - identical to ANDROID_NOISE_REDUCTION_MODE. - - Mode of operation for the noise reduction algorithm. - enum: - - name: NoiseReductionModeOff - value: 0 - description: No noise reduction is applied - - name: NoiseReductionModeFast - value: 1 - description: | - Noise reduction is applied without reducing the frame rate. - - name: NoiseReductionModeHighQuality - value: 2 - description: | - High quality noise reduction at the expense of frame rate. - - name: NoiseReductionModeMinimal - value: 3 - description: | - Minimal noise reduction is applied without reducing the frame rate. - - name: NoiseReductionModeZSL - value: 4 - description: | - Noise reduction is applied at different levels to different streams. - - - ColorCorrectionAberrationMode: - type: int32_t - draft: true - description: | - Control to select the color correction aberration mode. Currently - identical to ANDROID_COLOR_CORRECTION_ABERRATION_MODE. - - Mode of operation for the chromatic aberration correction algorithm. - enum: - - name: ColorCorrectionAberrationOff - value: 0 - description: No aberration correction is applied. - - name: ColorCorrectionAberrationFast - value: 1 - description: Aberration correction will not slow down the frame rate. - - name: ColorCorrectionAberrationHighQuality - value: 2 - description: | - High quality aberration correction which might reduce the frame - rate. - - - AeState: - type: int32_t - draft: true - description: | - Control to report the current AE algorithm state. Currently identical to - ANDROID_CONTROL_AE_STATE. - - Current state of the AE algorithm. - enum: - - name: AeStateInactive - value: 0 - description: The AE algorithm is inactive. - - name: AeStateSearching - value: 1 - description: The AE algorithm has not converged yet. - - name: AeStateConverged - value: 2 - description: The AE algorithm has converged. - - name: AeStateLocked - value: 3 - description: The AE algorithm is locked. - - name: AeStateFlashRequired - value: 4 - description: The AE algorithm would need a flash for good results - - name: AeStatePrecapture - value: 5 - description: | - The AE algorithm has started a pre-capture metering session. - \sa AePrecaptureTrigger - - - AwbState: - type: int32_t - draft: true - description: | - Control to report the current AWB algorithm state. Currently identical - to ANDROID_CONTROL_AWB_STATE. - - Current state of the AWB algorithm. - enum: - - name: AwbStateInactive - value: 0 - description: The AWB algorithm is inactive. - - name: AwbStateSearching - value: 1 - description: The AWB algorithm has not converged yet. - - name: AwbConverged - value: 2 - description: The AWB algorithm has converged. - - name: AwbLocked - value: 3 - description: The AWB algorithm is locked. - - - SensorRollingShutterSkew: - type: int64_t - draft: true - description: | - Control to report the time between the start of exposure of the first - row and the start of exposure of the last row. Currently identical to - ANDROID_SENSOR_ROLLING_SHUTTER_SKEW - - - LensShadingMapMode: - type: int32_t - draft: true - description: | - Control to report if the lens shading map is available. Currently - identical to ANDROID_STATISTICS_LENS_SHADING_MAP_MODE. - enum: - - name: LensShadingMapModeOff - value: 0 - description: No lens shading map mode is available. - - name: LensShadingMapModeOn - value: 1 - description: The lens shading map mode is available. - - - PipelineDepth: - type: int32_t - draft: true - description: | - Specifies the number of pipeline stages the frame went through from when - it was exposed to when the final completed result was available to the - framework. Always less than or equal to PipelineMaxDepth. Currently - identical to ANDROID_REQUEST_PIPELINE_DEPTH. - - The typical value for this control is 3 as a frame is first exposed, - captured and then processed in a single pass through the ISP. Any - additional processing step performed after the ISP pass (in example face - detection, additional format conversions etc) count as an additional - pipeline stage. - - - MaxLatency: - type: int32_t - draft: true - description: | - The maximum number of frames that can occur after a request (different - than the previous) has been submitted, and before the result's state - becomes synchronized. A value of -1 indicates unknown latency, and 0 - indicates per-frame control. Currently identical to - ANDROID_SYNC_MAX_LATENCY. - - - TestPatternMode: - type: int32_t - draft: true - description: | - Control to select the test pattern mode. Currently identical to - ANDROID_SENSOR_TEST_PATTERN_MODE. - enum: - - name: TestPatternModeOff - value: 0 - description: | - No test pattern mode is used. The camera device returns frames from - the image sensor. - - name: TestPatternModeSolidColor - value: 1 - description: | - Each pixel in [R, G_even, G_odd, B] is replaced by its respective - color channel provided in test pattern data. - \todo Add control for test pattern data. - - name: TestPatternModeColorBars - value: 2 - description: | - All pixel data is replaced with an 8-bar color pattern. The vertical - bars (left-to-right) are as follows; white, yellow, cyan, green, - magenta, red, blue and black. Each bar should take up 1/8 of the - sensor pixel array width. When this is not possible, the bar size - should be rounded down to the nearest integer and the pattern can - repeat on the right side. Each bar's height must always take up the - full sensor pixel array height. - - name: TestPatternModeColorBarsFadeToGray - value: 3 - description: | - The test pattern is similar to TestPatternModeColorBars, - except that each bar should start at its specified color at the top - and fade to gray at the bottom. Furthermore each bar is further - subdevided into a left and right half. The left half should have a - smooth gradient, and the right half should have a quantized - gradient. In particular, the right half's should consist of blocks - of the same color for 1/16th active sensor pixel array width. The - least significant bits in the quantized gradient should be copied - from the most significant bits of the smooth gradient. The height of - each bar should always be a multiple of 128. When this is not the - case, the pattern should repeat at the bottom of the image. - - name: TestPatternModePn9 - value: 4 - description: | - All pixel data is replaced by a pseudo-random sequence generated - from a PN9 512-bit sequence (typically implemented in hardware with - a linear feedback shift register). The generator should be reset at - the beginning of each frame, and thus each subsequent raw frame with - this test pattern should be exactly the same as the last. - - name: TestPatternModeCustom1 - value: 256 - description: | - The first custom test pattern. All custom patterns that are - available only on this camera device are at least this numeric - value. All of the custom test patterns will be static (that is the - raw image must not vary from frame to frame). - ... diff --git a/src/libcamera/control_ids_draft.yaml b/src/libcamera/control_ids_draft.yaml new file mode 100644 index 000000000000..e4f53ea51d7a --- /dev/null +++ b/src/libcamera/control_ids_draft.yaml @@ -0,0 +1,240 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2019, Google Inc. +# +%YAML 1.1 +--- +# Unless otherwise stated, all controls are bi-directional, i.e. they can be +# set through Request::controls() and returned out through Request::metadata(). +vendor: draft +controls: + - AePrecaptureTrigger: + type: int32_t + draft: true + description: | + Control for AE metering trigger. Currently identical to + ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER. + + Whether the camera device will trigger a precapture metering sequence + when it processes this request. + enum: + - name: AePrecaptureTriggerIdle + value: 0 + description: The trigger is idle. + - name: AePrecaptureTriggerStart + value: 1 + description: The pre-capture AE metering is started by the camera. + - name: AePrecaptureTriggerCancel + value: 2 + description: | + The camera will cancel any active or completed metering sequence. + The AE algorithm is reset to its initial state. + + - NoiseReductionMode: + type: int32_t + draft: true + description: | + Control to select the noise reduction algorithm mode. Currently + identical to ANDROID_NOISE_REDUCTION_MODE. + + Mode of operation for the noise reduction algorithm. + enum: + - name: NoiseReductionModeOff + value: 0 + description: No noise reduction is applied + - name: NoiseReductionModeFast + value: 1 + description: | + Noise reduction is applied without reducing the frame rate. + - name: NoiseReductionModeHighQuality + value: 2 + description: | + High quality noise reduction at the expense of frame rate. + - name: NoiseReductionModeMinimal + value: 3 + description: | + Minimal noise reduction is applied without reducing the frame rate. + - name: NoiseReductionModeZSL + value: 4 + description: | + Noise reduction is applied at different levels to different streams. + + - ColorCorrectionAberrationMode: + type: int32_t + draft: true + description: | + Control to select the color correction aberration mode. Currently + identical to ANDROID_COLOR_CORRECTION_ABERRATION_MODE. + + Mode of operation for the chromatic aberration correction algorithm. + enum: + - name: ColorCorrectionAberrationOff + value: 0 + description: No aberration correction is applied. + - name: ColorCorrectionAberrationFast + value: 1 + description: Aberration correction will not slow down the frame rate. + - name: ColorCorrectionAberrationHighQuality + value: 2 + description: | + High quality aberration correction which might reduce the frame + rate. + + - AeState: + type: int32_t + draft: true + description: | + Control to report the current AE algorithm state. Currently identical to + ANDROID_CONTROL_AE_STATE. + + Current state of the AE algorithm. + enum: + - name: AeStateInactive + value: 0 + description: The AE algorithm is inactive. + - name: AeStateSearching + value: 1 + description: The AE algorithm has not converged yet. + - name: AeStateConverged + value: 2 + description: The AE algorithm has converged. + - name: AeStateLocked + value: 3 + description: The AE algorithm is locked. + - name: AeStateFlashRequired + value: 4 + description: The AE algorithm would need a flash for good results + - name: AeStatePrecapture + value: 5 + description: | + The AE algorithm has started a pre-capture metering session. + \sa AePrecaptureTrigger + + - AwbState: + type: int32_t + draft: true + description: | + Control to report the current AWB algorithm state. Currently identical + to ANDROID_CONTROL_AWB_STATE. + + Current state of the AWB algorithm. + enum: + - name: AwbStateInactive + value: 0 + description: The AWB algorithm is inactive. + - name: AwbStateSearching + value: 1 + description: The AWB algorithm has not converged yet. + - name: AwbConverged + value: 2 + description: The AWB algorithm has converged. + - name: AwbLocked + value: 3 + description: The AWB algorithm is locked. + + - SensorRollingShutterSkew: + type: int64_t + draft: true + description: | + Control to report the time between the start of exposure of the first + row and the start of exposure of the last row. Currently identical to + ANDROID_SENSOR_ROLLING_SHUTTER_SKEW + + - LensShadingMapMode: + type: int32_t + draft: true + description: | + Control to report if the lens shading map is available. Currently + identical to ANDROID_STATISTICS_LENS_SHADING_MAP_MODE. + enum: + - name: LensShadingMapModeOff + value: 0 + description: No lens shading map mode is available. + - name: LensShadingMapModeOn + value: 1 + description: The lens shading map mode is available. + + - PipelineDepth: + type: int32_t + draft: true + description: | + Specifies the number of pipeline stages the frame went through from when + it was exposed to when the final completed result was available to the + framework. Always less than or equal to PipelineMaxDepth. Currently + identical to ANDROID_REQUEST_PIPELINE_DEPTH. + + The typical value for this control is 3 as a frame is first exposed, + captured and then processed in a single pass through the ISP. Any + additional processing step performed after the ISP pass (in example face + detection, additional format conversions etc) count as an additional + pipeline stage. + + - MaxLatency: + type: int32_t + draft: true + description: | + The maximum number of frames that can occur after a request (different + than the previous) has been submitted, and before the result's state + becomes synchronized. A value of -1 indicates unknown latency, and 0 + indicates per-frame control. Currently identical to + ANDROID_SYNC_MAX_LATENCY. + + - TestPatternMode: + type: int32_t + draft: true + description: | + Control to select the test pattern mode. Currently identical to + ANDROID_SENSOR_TEST_PATTERN_MODE. + enum: + - name: TestPatternModeOff + value: 0 + description: | + No test pattern mode is used. The camera device returns frames from + the image sensor. + - name: TestPatternModeSolidColor + value: 1 + description: | + Each pixel in [R, G_even, G_odd, B] is replaced by its respective + color channel provided in test pattern data. + \todo Add control for test pattern data. + - name: TestPatternModeColorBars + value: 2 + description: | + All pixel data is replaced with an 8-bar color pattern. The vertical + bars (left-to-right) are as follows; white, yellow, cyan, green, + magenta, red, blue and black. Each bar should take up 1/8 of the + sensor pixel array width. When this is not possible, the bar size + should be rounded down to the nearest integer and the pattern can + repeat on the right side. Each bar's height must always take up the + full sensor pixel array height. + - name: TestPatternModeColorBarsFadeToGray + value: 3 + description: | + The test pattern is similar to TestPatternModeColorBars, + except that each bar should start at its specified color at the top + and fade to gray at the bottom. Furthermore each bar is further + subdevided into a left and right half. The left half should have a + smooth gradient, and the right half should have a quantized + gradient. In particular, the right half's should consist of blocks + of the same color for 1/16th active sensor pixel array width. The + least significant bits in the quantized gradient should be copied + from the most significant bits of the smooth gradient. The height of + each bar should always be a multiple of 128. When this is not the + case, the pattern should repeat at the bottom of the image. + - name: TestPatternModePn9 + value: 4 + description: | + All pixel data is replaced by a pseudo-random sequence generated + from a PN9 512-bit sequence (typically implemented in hardware with + a linear feedback shift register). The generator should be reset at + the beginning of each frame, and thus each subsequent raw frame with + this test pattern should be exactly the same as the last. + - name: TestPatternModeCustom1 + value: 256 + description: | + The first custom test pattern. All custom patterns that are + available only on this camera device are at least this numeric + value. All of the custom test patterns will be static (that is the + raw image must not vary from frame to frame). + +... diff --git a/src/libcamera/property_ids.cpp.in b/src/libcamera/property_ids.cpp.in index 0771ac5c091f..8b274c38c74b 100644 --- a/src/libcamera/property_ids.cpp.in +++ b/src/libcamera/property_ids.cpp.in @@ -23,15 +23,6 @@ namespace properties { ${controls_doc} -/** - * \brief Namespace for libcamera draft properties - */ -namespace draft { - -${draft_controls_doc} - -} /* namespace draft */ - ${vendor_controls_doc} #ifndef __DOXYGEN__ @@ -41,13 +32,8 @@ ${vendor_controls_doc} */ ${controls_def} -namespace draft { - -${draft_controls_def} - -} /* namespace draft */ - ${vendor_controls_def} + #endif /** diff --git a/src/libcamera/property_ids_core.yaml b/src/libcamera/property_ids_core.yaml index 45f3609b4236..834454a4e087 100644 --- a/src/libcamera/property_ids_core.yaml +++ b/src/libcamera/property_ids_core.yaml @@ -701,37 +701,4 @@ controls: Different cameras may report identical devices. - # ---------------------------------------------------------------------------- - # Draft properties section - - - ColorFilterArrangement: - type: int32_t - draft: true - description: | - The arrangement of color filters on sensor; represents the colors in the - top-left 2x2 section of the sensor, in reading order. Currently - identical to ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT. - enum: - - name: RGGB - value: 0 - description: RGGB Bayer pattern - - name: GRBG - value: 1 - description: GRBG Bayer pattern - - name: GBRG - value: 2 - description: GBRG Bayer pattern - - name: BGGR - value: 3 - description: BGGR Bayer pattern - - name: RGB - value: 4 - description: | - Sensor is not Bayer; output has 3 16-bit values for each pixel, - instead of just 1 16-bit value per pixel. - - name: MONO - value: 5 - description: | - Sensor is not Bayer; output consists of a single colour channel. - ... diff --git a/src/libcamera/property_ids_draft.yaml b/src/libcamera/property_ids_draft.yaml new file mode 100644 index 000000000000..62f0e242d0c6 --- /dev/null +++ b/src/libcamera/property_ids_draft.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2019, Google Inc. +# +%YAML 1.1 +--- +vendor: draft +controls: + - ColorFilterArrangement: + type: int32_t + vendor: draft + description: | + The arrangement of color filters on sensor; represents the colors in the + top-left 2x2 section of the sensor, in reading order. Currently + identical to ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT. + enum: + - name: RGGB + value: 0 + description: RGGB Bayer pattern + - name: GRBG + value: 1 + description: GRBG Bayer pattern + - name: GBRG + value: 2 + description: GBRG Bayer pattern + - name: BGGR + value: 3 + description: BGGR Bayer pattern + - name: RGB + value: 4 + description: | + Sensor is not Bayer; output has 3 16-bit values for each pixel, + instead of just 1 16-bit value per pixel. + - name: MONO + value: 5 + description: | + Sensor is not Bayer; output consists of a single colour channel. + +... diff --git a/src/py/libcamera/gen-py-controls.py b/src/py/libcamera/gen-py-controls.py index 8ae8d5126e39..55e0b4e8e3d6 100755 --- a/src/py/libcamera/gen-py-controls.py +++ b/src/py/libcamera/gen-py-controls.py @@ -36,10 +36,7 @@ def generate_py(controls, mode): vendor_defs.append('\tauto {} = py::class_(controls, \"{}\");'.format(vendor, vendor, vendor)) vendors.append(vendor) - if ctrl.get('draft'): - ns = 'libcamera::{}::draft::'.format(mode) - container = 'draft' - elif vendor != 'libcamera': + if vendor != 'libcamera': ns = 'libcamera::{}::{}::'.format(mode, vendor) container = vendor else: diff --git a/src/py/libcamera/py_controls_generated.cpp.in b/src/py/libcamera/py_controls_generated.cpp.in index ec4b55ef2011..8d282ce51856 100644 --- a/src/py/libcamera/py_controls_generated.cpp.in +++ b/src/py/libcamera/py_controls_generated.cpp.in @@ -17,16 +17,11 @@ class PyControls { }; -class PyDraftControls -{ -}; - ${vendors_class_def} void init_py_controls_generated(py::module& m) { auto controls = py::class_(m, "controls"); - auto draft = py::class_(controls, "draft"); ${vendors_defs} ${controls} diff --git a/src/py/libcamera/py_properties_generated.cpp.in b/src/py/libcamera/py_properties_generated.cpp.in index f7b5ec8c635d..ae61c5440d31 100644 --- a/src/py/libcamera/py_properties_generated.cpp.in +++ b/src/py/libcamera/py_properties_generated.cpp.in @@ -26,7 +26,6 @@ ${vendors_class_def} void init_py_properties_generated(py::module& m) { auto controls = py::class_(m, "properties"); - auto draft = py::class_(controls, "draft"); ${vendors_defs} ${controls} diff --git a/utils/gen-controls.py b/utils/gen-controls.py index 04c63098b19b..45da571e83e0 100755 --- a/utils/gen-controls.py +++ b/utils/gen-controls.py @@ -86,11 +86,6 @@ class Control(object): """Is the control an enumeration""" return self.__enum_values is not None - @property - def is_draft(self): - """Is the control a draft control""" - return self.__data.get('draft') is not None - @property def vendor(self): """The vendor string, or None""" @@ -101,12 +96,6 @@ class Control(object): """The control name (CamelCase)""" return self.__name - @property - def q_name(self): - """The control name, qualified with a namespace""" - ns = 'draft::' if self.is_draft else '' - return ns + self.__name - @property def type(self): typ = self.__data.get('type') @@ -159,7 +148,7 @@ ${description} for ctrl in controls: id_name = snake_case(ctrl.name).upper() - vendor = 'draft' if ctrl.is_draft else ctrl.vendor + vendor = ctrl.vendor if vendor not in ctrls_doc: ctrls_doc[vendor] = [] ctrls_def[vendor] = [] @@ -208,7 +197,8 @@ ${description} target_doc.append(doc_template.substitute(info)) target_def.append(def_template.substitute(info)) - ctrls_map.append('\t{ ' + id_name + ', &' + ctrl.q_name + ' },') + vendor_ns = vendor + '::' if vendor != "libcamera" else '' + ctrls_map.append('\t{ ' + vendor_ns + id_name + ', &' + vendor_ns + ctrl.name + ' },') vendor_ctrl_doc_sub = [] vendor_ctrl_template = string.Template(''' @@ -218,18 +208,16 @@ ${vendor_controls_str} } /* namespace ${vendor} */''') - for vendor in [v for v in ctrls_doc.keys() if v not in ['libcamera', 'draft']]: + for vendor in [v for v in ctrls_doc.keys() if v not in ['libcamera']]: vendor_ctrl_doc_sub.append(vendor_ctrl_template.substitute({'vendor': vendor, 'vendor_controls_str': '\n\n'.join(ctrls_doc[vendor])})) vendor_ctrl_def_sub = [] - for vendor in [v for v in ctrls_def.keys() if v not in ['libcamera', 'draft']]: + for vendor in [v for v in ctrls_def.keys() if v not in ['libcamera']]: vendor_ctrl_def_sub.append(vendor_ctrl_template.substitute({'vendor': vendor, 'vendor_controls_str': '\n'.join(ctrls_def[vendor])})) return { 'controls_doc': '\n\n'.join(ctrls_doc['libcamera']), 'controls_def': '\n'.join(ctrls_def['libcamera']), - 'draft_controls_doc': '\n\n'.join(ctrls_doc['draft']), - 'draft_controls_def': '\n\n'.join(ctrls_def['draft']), 'controls_map': '\n'.join(ctrls_map), 'vendor_controls_doc': '\n'.join(vendor_ctrl_doc_sub), 'vendor_controls_def': '\n'.join(vendor_ctrl_def_sub), @@ -249,7 +237,7 @@ def generate_h(controls, mode, ranges): for ctrl in controls: id_name = snake_case(ctrl.name).upper() - vendor = 'draft' if ctrl.is_draft else ctrl.vendor + vendor = ctrl.vendor if vendor not in ctrls: if vendor not in ranges.keys(): raise RuntimeError(f'Control id range is not defined for vendor {vendor}') @@ -257,8 +245,7 @@ def generate_h(controls, mode, ranges): ids[vendor] = [] ctrls[vendor] = [] - # Core and draft controls use the same ID value - target_ids = ids['libcamera'] if vendor in ['libcamera', 'draft'] else ids[vendor] + target_ids = ids[vendor] target_ids.append('\t' + id_name + ' = ' + str(id_value[vendor]) + ',') info = { @@ -266,11 +253,7 @@ def generate_h(controls, mode, ranges): 'type': ctrl.type, } - target_ctrls = ctrls['libcamera'] - if ctrl.is_draft: - target_ctrls = ctrls['draft'] - elif vendor != 'libcamera': - target_ctrls = ctrls[vendor] + target_ctrls = ctrls[vendor] if ctrl.is_enum: target_ctrls.append(enum_template_start.substitute(info)) @@ -309,7 +292,7 @@ ${vendor_controls} ''') vendor_sub = [] - for vendor in [v for v in ctrls.keys() if v not in ['libcamera', 'draft']]: + for vendor in [v for v in ctrls.keys() if v != 'libcamera']: vendor_sub.append(vendor_template.substitute({'mode': mode.upper(), 'vendor': vendor, 'vendor_def': vendor.upper(), @@ -319,7 +302,6 @@ ${vendor_controls} return { 'ids': '\n'.join(ids['libcamera']), 'controls': '\n'.join(ctrls['libcamera']), - 'draft_controls': '\n'.join(ctrls['draft']), 'vendor_controls': '\n'.join(vendor_sub) } From patchwork Fri Nov 24 12:37:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 19239 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id AC9A4C3220 for ; Fri, 24 Nov 2023 12:37:26 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 490F4629BD; Fri, 24 Nov 2023 13:37:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1700829446; bh=PHoOqQl1mU4rhGKOBZngIm5Cx+qXd9P3flwWMQ8qsvI=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=UyqJ3/rnpg4Qpn8RkJQUMsCuBQV1c/H60KWkyPmbgIxHd1rq52ISr70byt28HtJy7 5XGl3+9ykTBePGBIozhWZE/T2sxJ8ZpmwwoMkZAw+/Zkcqsr7lGP9PGoUkz/NkFfDn NDzCgvtzN8FfA74CRN/DJrHQexo3G+8Me6NUObs3+Auu98H0ypyhCTwhh9QOtAKxAi i24C96sbIZNdRvgBELJBfRrrWPiYMLWeAJck+a0HR9BT7F9JMIeQxz345+eYaE6TiP mGed+aadA2xs15qmrNl42xSRUKOk1n2XCCNsKBT5PrfG31uewuzRGyPNgjuAcGGXFI XNDPO+eLgxM6A== Received: from mail-lf1-x12e.google.com (mail-lf1-x12e.google.com [IPv6:2a00:1450:4864:20::12e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3A0C7629C4 for ; Fri, 24 Nov 2023 13:37:23 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="iqkuKDGU"; dkim-atps=neutral Received: by mail-lf1-x12e.google.com with SMTP id 2adb3069b0e04-50aab3bf71fso2436383e87.3 for ; Fri, 24 Nov 2023 04:37:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1700829442; x=1701434242; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5/UqwsW17Z81IVQJGqi2TT0SHMcji1M3CcEtztOhSqI=; b=iqkuKDGUtn8ES2+P7gzqJ/MSjsOiNhqz8vegnGcG/YUl7Yqb7vGi+rUAL0JO37IKkH VL8Sj43+FbRTIctBgkVKTU5XgbtIuZ8duw42SGbgUB9tvS6QaLUppJxxNkRLQNtDHLjJ m+XWW/SWjBR7VKhATgMpr8SysW7XVNexEPZBpqf7NFpAEWu3CuPiqf4Cxwq/JcG4BQd3 kekIrp8T671u8/Hfx/qrvi/zyJbJOapzEjiU6FP5zLA2d/jv27UC6EZS1yoN8H92g5Un CJz91U90MB6c4l1xFOcCRUPDqyfv5oTH6HHpSds9Blk4x34ws/1WIPbc8ecEXVHsiMvA 0afw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700829442; x=1701434242; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5/UqwsW17Z81IVQJGqi2TT0SHMcji1M3CcEtztOhSqI=; b=S817Xr97n96pTy2cVcBZPj/SkGHxL3OFLt1S+51VcaCJPVvSCNibqX2VI0HnkbWfLy z4f1FOEIiZJ7BUwxeLSlDnsd3I3i0yfTVXLrgRHZgPMwXioGgnB06tt9/Oe2F/ldEnwn fWQPBrou2RZ8SpXM8K/gHCTaEKiVBhAim36dGbe9z5n5OW8B1Aohkj4BqRzQC9nvJsYQ KU7rK/UKvNKtSw0oNSfqfRac6xPsfbqqPu6fYe0JbKpzm2tuBaewJjQ4M2vecyUfFj6Y /JMvJNRQON3PA0aKAi9qI1GbqZX8KasleVe3QTO9uUzHT52iuMhhIjFL7Nr/XwnDXBjb AFiw== X-Gm-Message-State: AOJu0YxyOKfJ6Jiasv3xKNjj6Q9LX+I98Uz2Q/JfgEmHq4QvyhN+35IR SnnJV3l/xBCWUigXAQNX+Oa5UQaVIjuTPd67HKIRSA== X-Google-Smtp-Source: AGHT+IHx9D7nhVs3WNZR6kF8nHwDE9d6iiNQUXUm5hNzPr63QKv3MWlHppNscyvM6zHqg5ZjT6NuXg== X-Received: by 2002:a05:6512:33cf:b0:503:1783:d5a9 with SMTP id d15-20020a05651233cf00b005031783d5a9mr2476765lfg.3.1700829442161; Fri, 24 Nov 2023 04:37:22 -0800 (PST) Received: from localhost.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id v11-20020a05600c444b00b0040b3867a297sm3303690wmn.36.2023.11.24.04.37.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Nov 2023 04:37:21 -0800 (PST) To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Nov 2023 12:37:12 +0000 Message-Id: <20231124123713.22519-7-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231124123713.22519-1-naush@raspberrypi.com> References: <20231124123713.22519-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 6/7] build: controls: Add Raspberry Pi vendor specific controls X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a new control_ids_rpi.yaml file to hold the Raspberry Pi specific vendor controls. This contains a single control PispConfigDumpFile that will be used in the Pi 5 pipeline handler as a trigger to dump the Backend configuration as a JSON file. Signed-off-by: Naushir Patuck Reviewed-by: Jacopo Mondi Reviewed-by: Kieran Bingham --- include/libcamera/meson.build | 2 ++ src/libcamera/control_ids_rpi.yaml | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/libcamera/control_ids_rpi.yaml diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 1504f741ae2f..5d20e4d869e3 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -36,6 +36,8 @@ controls_map = { 'controls': { 'draft': 'control_ids_draft.yaml', 'core': 'control_ids_core.yaml', + 'rpi/pisp': 'control_ids_rpi.yaml', + 'rpi/vc4': 'control_ids_rpi.yaml', }, 'properties': { diff --git a/src/libcamera/control_ids_rpi.yaml b/src/libcamera/control_ids_rpi.yaml new file mode 100644 index 000000000000..abf82098eb12 --- /dev/null +++ b/src/libcamera/control_ids_rpi.yaml @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2023, Raspberry Pi Ltd +# +%YAML 1.1 +--- +# Raspberry Pi (VC4 and PiSP) specific vendor controls +vendor: rpi +controls: + - PispConfigDumpFile: + type: string + description: | + Triggers the Raspberry Pi PiSP pipeline handler to generate a JSON + formatted dump of the Backend configuration to the filename given by the + value of the control. + +... \ No newline at end of file From patchwork Fri Nov 24 12:37:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 19240 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 5FC7CC32B7 for ; Fri, 24 Nov 2023 12:37:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E79C9629D4; Fri, 24 Nov 2023 13:37:26 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org; s=mail; t=1700829446; bh=eGqs129EM6+J8Mws/VYo52jC5pwtv4hgfPnByXcVwC0=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=PdzMDKnJc8QSn1/Mbx1uZyl9CadujJX5eAHvE/d/g8TBPL/e8UZ34hsyX6uNf7ZbP H0AoKvPnP15CQbNlJSSwFEr6U51nsS4ZtEwRRfpHhtNu9WwLX5c3df3TTXStarbK9S oLdZzlO3HL9FgxmuE12V8YIXxuC/hWFn3m6pCWPc3H31RGsnetY0CTMQyuEHBk+Ma5 veF6rQNumRUjucoo2qhEI9YtWxY0VLdahOJKGUcTsBSmOt9hfLQGutJ4VSpjR9lT3/ 93KHXc3anT/4SRUh3o2b7IHwZe0JbgoCwlxYphSkjzZYguIkwgjuBBr5v6lfttLZBH QlI05GG46nO8w== Received: from mail-wm1-x331.google.com (mail-wm1-x331.google.com [IPv6:2a00:1450:4864:20::331]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C9787629C8 for ; Fri, 24 Nov 2023 13:37:23 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="hoLwH1Va"; dkim-atps=neutral Received: by mail-wm1-x331.google.com with SMTP id 5b1f17b1804b1-4083f613272so13113745e9.1 for ; Fri, 24 Nov 2023 04:37:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; t=1700829443; x=1701434243; darn=lists.libcamera.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=kQC94eTP69cZcBBoIV68TpVGNbbk+m4+pPQAyOU5+U8=; b=hoLwH1VaATjiR2vD70pe+b1p/2vDrZNppmkH9oduTGJ4dGg4xV122ZPT4oF0Gcwh5H CO58VUzdb2wn0BckJ2dWuuu7oUAipDAZcNsocO1c6PoYhXk12ZomJRz2XfYcGEKAr6Da MARM/j/mJAtVfO1m3niqiHT5sDj4J5yVqfPcuCVa+18QwMoDRr8fuKLh71zoBQcO4NlU B0X4M/Jui/RIJbagB9oDFB4I5Q5Umsn9vDHQBCFOjF40Cj7GLXAY35oSl8ePn1HCCTlS CCqGwPGRwHVm9QQXAFwuiTQzWfasiFhXYSq/7tdN7u3tdWcP5zhxEosYyKB4K4z7rCDm /aJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1700829443; x=1701434243; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kQC94eTP69cZcBBoIV68TpVGNbbk+m4+pPQAyOU5+U8=; b=U7pyEz5SHiv+wThHztHHkG25ia+UW6SRAP/r5hJTNOQbQ99kUHsOs1VS0vWxYASKbB sluySNLk8g5fC0NVAXASLpBP/EEHi3flqR2g/vA0YXrTBShYxQ68ehVziBZJuXXmZ84R sGR1AsbDAMnd/TL64+6aM1dfgO3AoYJP7l9ZKnXumo++6V/B/ol1x0ABk/zsBjDqdn4z Rd0yl5slb2jisHYXmspnSRjuCMBdBFvWn3x4w33S/K2i9fNG+wOpaHOvAwNUk2JlG2Sh or03KSRy++WRsXAhLAoiXQrmC6Xgdj6FUi83E+fMRNN/0tIWslcdp0aH/uBzd/X6FhHJ 2xfg== X-Gm-Message-State: AOJu0Yxy+GgPx4oGZQPkMhloHCoAG7zIreQ3WZJNB/tqY1XqlFlxi0aO W/+e3VRB2Qot9PSlXukEuxdF5oHoOUShM+QucwwG/A== X-Google-Smtp-Source: AGHT+IFF/nzYuQObldNmcy6aHIUUzCIoWBEKza8Rdn5pMmpz3HzBlpV85AWMfJq5ztzrJRJyj4VipA== X-Received: by 2002:a05:600c:4f90:b0:40b:36e5:e1e9 with SMTP id n16-20020a05600c4f9000b0040b36e5e1e9mr1993845wmq.24.1700829443250; Fri, 24 Nov 2023 04:37:23 -0800 (PST) Received: from localhost.localdomain ([93.93.133.154]) by smtp.gmail.com with ESMTPSA id v11-20020a05600c444b00b0040b3867a297sm3303690wmn.36.2023.11.24.04.37.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Nov 2023 04:37:22 -0800 (PST) To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Nov 2023 12:37:13 +0000 Message-Id: <20231124123713.22519-8-naush@raspberrypi.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231124123713.22519-1-naush@raspberrypi.com> References: <20231124123713.22519-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 7/7] documentation: Document vendor specific controls and properties handling X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Naushir Patuck via libcamera-devel From: Naushir Patuck Reply-To: Naushir Patuck Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add some documentation to the pipeline handler file describing how to implement and handle vendor specific controls and properties with a small example. Signed-off-by: Naushir Patuck Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- Documentation/guides/pipeline-handler.rst | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst index 66d428a19c4f..5a3d00b1b879 100644 --- a/Documentation/guides/pipeline-handler.rst +++ b/Documentation/guides/pipeline-handler.rst @@ -672,6 +672,54 @@ handling controls: #include #include +Vendor-specific controls and properties +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Vendor-specific controls and properties must be defined in a separate YAML file +and included in the build by defining the pipeline handler to file mapping in +include/libcamera/meson.build. + +For example, adding a Raspberry Pi vendor control file for the PiSP pipeline +handler is done with the following mapping: + +.. code-block:: meson + + controls_map = { + 'controls': { + 'draft': 'control_ids_draft.yaml', + 'libcamera': 'control_ids.yaml', + 'rpi/pisp': 'control_ids_rpi.yaml', + }, + + 'properties': { + 'draft': 'property_ids_draft.yaml', + 'libcamera': 'property_ids.yaml', + } + } + +Vendor-specific controls and properties must contain a `vendor: ` +tag in the YAML file. Every unique vendor tag must define a unique and +non-overlapping range of reserved control IDs in src/libcamera/control_ranges.yaml. + +For example, the following block defines a vendor-specific control with the +`rpi` vendor tag: + +.. code-block:: yaml + + vendor: rpi + controls: + - PispConfigDumpFile: + type: string + description: | + Triggers the Raspberry Pi PiSP pipeline handler to generate a JSON + formatted dump of the Backend configuration to the filename given by the + value of the control. + +This controls will be generated in the vendor-specific namespace +`libcamera::controls::rpi`. Additionally a `#define +LIBCAMERA_HAS_RPI_VENDOR_CONTROLS` will be available to allow applications to +test for the availability of these controls. + Generating a default configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~