summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNaushir Patuck <naush@raspberrypi.com>2023-11-08 11:09:51 +0000
committerNaushir Patuck <naush@raspberrypi.com>2023-11-29 09:05:37 +0000
commitbd6658943a97288b5673e65649a3a48dac7c78da (patch)
tree669e796221586b40d21639fa1f3c4de27fa15e08
parent61f6b372421ad96e283220c2c7beb1cb298b1eaf (diff)
controls: Add vendor control/property support to generation scripts
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 <naush@raspberrypi.com> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
-rw-r--r--include/libcamera/control_ids.h.in2
-rw-r--r--include/libcamera/meson.build15
-rw-r--r--include/libcamera/property_ids.h.in2
-rw-r--r--src/libcamera/control_ids.cpp.in5
-rw-r--r--src/libcamera/control_ids.yaml1
-rw-r--r--src/libcamera/meson.build5
-rw-r--r--src/libcamera/property_ids.cpp.in5
-rw-r--r--src/libcamera/property_ids.yaml1
-rwxr-xr-xsrc/py/libcamera/gen-py-controls.py81
-rw-r--r--src/py/libcamera/py_controls_generated.cpp.in3
-rw-r--r--src/py/libcamera/py_properties_generated.cpp.in3
-rwxr-xr-xutils/gen-controls.py120
12 files changed, 173 insertions, 70 deletions
diff --git a/include/libcamera/control_ids.h.in b/include/libcamera/control_ids.h.in
index 0718a888..c97b09a8 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 a24c50d6..2c8c0258 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 ff019408..47c5d6bf 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 5fb1c2c3..bdb31275 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,9 @@ 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 5827d7ec..ff74ce1d 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 d0e26f6b..e49bf850 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 f917e334..eed1124f 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,9 @@ 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 f3556384..45f3609b 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 9948c41e..dfd7c179 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_<Py{}Controls>(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<const libcamera::ControlId *>(&{ns}{name}));\n\n'
+ out += f'\t{container}.def_readonly_static("{name}", static_cast<const libcamera::ControlId *>(&{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 18fa57d9..ec4b55ef 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_<PyControls>(m, "controls");
auto draft = py::class_<PyDraftControls>(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 e49b6e91..f7b5ec8c 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_<PyProperties>(m, "properties");
auto draft = py::class_<PyDraftProperties>(controls, "draft");
+${vendors_defs}
${controls}
}
diff --git a/utils/gen-controls.py b/utils/gen-controls.py
index 1075ae30..c1172cb2 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:
@@ -90,6 +91,11 @@ class Control(object):
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)"""
return self.__name
@@ -145,15 +151,18 @@ ${description}
enum_values_start = string.Template('''extern const std::array<const ControlValue, ${size}> ${name}Values = {''')
enum_values_values = string.Template('''\tstatic_cast<int32_t>(${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,68 @@ ${description}
ctrls_map.append('\t{ ' + id_name + ', &' + ctrl.q_name + ' },')
+ vendor_ctrl_doc_sub = []
+ vendor_ctrl_template = string.Template('''
+/**
+ * \\brief Namespace for ${vendor} controls
+ */
+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<const ControlValue, ${size}> ${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 +292,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 +336,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')