[libcamera-devel] [PATCH/RFC 1/2] libcamera: Generate Control instances for V4L2 controls

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon Feb 8 23:54:28 CET 2021


The ControlList class is used for both libcamera controls and V4L2
controls, with very different APIs. For libcamera controls, all controls
have a corresponding Control instance, which can be used to access a
ControlList with a type-safe API. For V4L2 controls, on the other hand,
only numerical IDs are available, leading to the usage of an unsafe API.

Improve handling of V4L2 controls by generating Control instances for
each V4L2 control used by libcamera, using a similar code generation
mechanism as for libcamera controls.

Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
---
 include/libcamera/internal/meson.build        |  29 +++-
 .../libcamera/internal/v4l2_control_ids.h.in  |  29 ++++
 include/libcamera/internal/v4l2_controls.h    |   2 +
 src/libcamera/meson.build                     |   8 +-
 src/libcamera/v4l2_control_ids.cpp.in         |  34 +++++
 src/libcamera/v4l2_control_ids.yaml           |  62 +++++++++
 utils/gen-v4l2-controls.py                    | 126 ++++++++++++++++++
 utils/meson.build                             |   1 +
 8 files changed, 283 insertions(+), 8 deletions(-)
 create mode 100644 include/libcamera/internal/v4l2_control_ids.h.in
 create mode 100644 src/libcamera/v4l2_control_ids.cpp.in
 create mode 100644 src/libcamera/v4l2_control_ids.yaml
 create mode 100755 utils/gen-v4l2-controls.py

diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index a1071721967e..f96eec9602d8 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -2,13 +2,6 @@
 
 subdir('tracepoints')
 
-libcamera_tracepoint_header = custom_target(
-    'tp_header',
-    input: ['tracepoints.h.in', tracepoint_files],
-    output: 'tracepoints.h',
-    command: [gen_tracepoints_header, '@OUTPUT@', '@INPUT@'],
-)
-
 libcamera_internal_headers = files([
     'bayer_format.h',
     'buffer.h',
@@ -49,3 +42,25 @@ libcamera_internal_headers = files([
     'v4l2_subdevice.h',
     'v4l2_videodevice.h',
 ])
+
+#
+# Generate headers from templates.
+#
+
+v4l2_control_header = custom_target(
+    'v4l2_control_ids_h',
+    input : files('../../../src/libcamera/v4l2_control_ids.yaml', 'v4l2_control_ids.h.in'),
+    output : 'v4l2_control_ids.h',
+    command : [gen_v4l2_controls, '-o', '@OUTPUT@', '@INPUT@'],
+)
+
+libcamera_internal_headers += v4l2_control_header
+
+tracepoint_header = custom_target(
+    'tp_header',
+    input: ['tracepoints.h.in', tracepoint_files],
+    output: 'tracepoints.h',
+    command: [gen_tracepoints_header, '@OUTPUT@', '@INPUT@'],
+)
+
+libcamera_internal_headers += tracepoint_header
diff --git a/include/libcamera/internal/v4l2_control_ids.h.in b/include/libcamera/internal/v4l2_control_ids.h.in
new file mode 100644
index 000000000000..3f00449c9adf
--- /dev/null
+++ b/include/libcamera/internal/v4l2_control_ids.h.in
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021, Google Inc.
+ *
+ * v4l2_control_ids.h - V4L2 Control ID list
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#ifndef __LIBCAMERA_V4L2_CONTROL_IDS_H__
+#define __LIBCAMERA_V4L2_CONTROL_IDS_H__
+
+#include <stdint.h>
+
+#include <libcamera/controls.h>
+
+namespace libcamera {
+
+namespace v4l2 {
+
+#ifndef __DOXYGEN__
+${controls}
+#endif
+
+} /* namespace v4l2 */
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_V4L2_CONTROL_IDS_H__ */
diff --git a/include/libcamera/internal/v4l2_controls.h b/include/libcamera/internal/v4l2_controls.h
index 0851b8ddb128..c54aed00cfae 100644
--- a/include/libcamera/internal/v4l2_controls.h
+++ b/include/libcamera/internal/v4l2_controls.h
@@ -12,6 +12,8 @@
 
 #include <libcamera/controls.h>
 
+#include "libcamera/internal/v4l2_control_ids.h"
+
 namespace libcamera {
 
 class V4L2ControlId : public ControlId
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index ebce19d90c1e..36f79e11e1ac 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -57,7 +57,7 @@ libcamera_sources = files([
 ])
 
 libcamera_sources += libcamera_public_headers
-libcamera_sources += libcamera_tracepoint_header
+libcamera_sources += libcamera_internal_headers
 
 includes = [
     libcamera_includes,
@@ -97,6 +97,12 @@ foreach source : control_source_files
                                      command : [gen_controls, '-o', '@OUTPUT@', '@INPUT@'])
 endforeach
 
+control_sources += custom_target('v4l2_control_ids_cpp',
+                                 input : files('v4l2_control_ids.yaml',
+                                               'v4l2_control_ids.cpp.in'),
+                                 output : 'v4l2_control_ids.cpp',
+                                 command : [gen_v4l2_controls, '-o', '@OUTPUT@', '@INPUT@'])
+
 libcamera_sources += control_sources
 
 gen_version = join_paths(meson.source_root(), 'utils', 'gen-version.sh')
diff --git a/src/libcamera/v4l2_control_ids.cpp.in b/src/libcamera/v4l2_control_ids.cpp.in
new file mode 100644
index 000000000000..6c57a5b0e4eb
--- /dev/null
+++ b/src/libcamera/v4l2_control_ids.cpp.in
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2031, Google Inc.
+ *
+ * v4l2_control_ids.cpp : V4L2 Control ID list
+ *
+ * This file is auto-generated. Do not edit.
+ */
+
+#include <linux/v4l2-controls.h>
+
+#include <libcamera/controls.h>
+
+#include "libcamera/internal/v4l2_control_ids.h"
+
+/**
+ * \file v4l2_control_ids.h
+ * \brief V4L2 control identifiers
+ */
+
+namespace libcamera {
+
+/**
+ * \brief Namespace for V4L2 controls
+ */
+namespace v4l2 {
+
+#ifndef __DOXYGEN__
+${controls_def}
+#endif
+
+} /* namespace v4l2 */
+
+} /* namespace libcamera */
diff --git a/src/libcamera/v4l2_control_ids.yaml b/src/libcamera/v4l2_control_ids.yaml
new file mode 100644
index 000000000000..ec0adb21695f
--- /dev/null
+++ b/src/libcamera/v4l2_control_ids.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# Copyright (C) 2021, Google Inc.
+#
+%YAML 1.2
+---
+
+controls:
+  - V4L2_CID_ANALOGUE_GAIN:
+      type: int32_t
+
+  - V4L2_CID_BLUE_BALANCE:
+      type: int32_t
+
+  - V4L2_CID_BRIGHTNESS:
+      type: int32_t
+
+  - V4L2_CID_CAMERA_ORIENTATION:
+      type: int32_t
+
+  - V4L2_CID_CAMERA_SENSOR_ROTATION:
+      type: int32_t
+
+  - V4L2_CID_CONTRAST:
+      type: int32_t
+
+  - V4L2_CID_DIGITAL_GAIN:
+      type: int32_t
+
+  - V4L2_CID_EXPOSURE:
+      type: int32_t
+
+  - V4L2_CID_EXPOSURE_ABSOLUTE:
+      type: int32_t
+
+  - V4L2_CID_EXPOSURE_AUTO:
+      type: int32_t
+
+  - V4L2_CID_GAIN:
+      type: int32_t
+
+  - V4L2_CID_HBLANK:
+      type: int32_t
+
+  - V4L2_CID_HFLIP:
+      type: int32_t
+
+  - V4L2_CID_PIXEL_RATE:
+      type: int64_t
+
+  - V4L2_CID_RED_BALANCE:
+      type: int32_t
+
+  - V4L2_CID_SATURATION:
+      type: int32_t
+
+  - V4L2_CID_VBLANK:
+      type: int32_t
+
+  - V4L2_CID_VFLIP:
+      type: int32_t
+...
diff --git a/utils/gen-v4l2-controls.py b/utils/gen-v4l2-controls.py
new file mode 100755
index 000000000000..2eb964f6f459
--- /dev/null
+++ b/utils/gen-v4l2-controls.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021, Google Inc.
+#
+# Author: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
+#
+# gen-v4l2-controls.py - Generate V4L2 control definitions from YAML
+
+import argparse
+import string
+import sys
+import yaml
+
+PREFIX = 'V4L2_CID_'
+
+
+def generate_cpp(controls):
+    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, "${name}");')
+
+    ctrls_def = []
+
+    for ctrl in controls:
+        name, ctrl = ctrl.popitem()
+
+        if not name.startswith(PREFIX):
+            raise RuntimeError(f'Invalid control name {name}')
+
+        id_name = name
+        name = name[len(PREFIX):]
+
+        ctrl_type = ctrl['type']
+        if ctrl_type == 'string':
+            ctrl_type = 'std::string'
+        elif ctrl.get('size'):
+            ctrl_type = 'Span<const %s>' % ctrl_type
+
+        info = {
+            'name': name,
+            'type': ctrl_type,
+            'id_name': id_name,
+        }
+
+        ctrls_def.append(def_template.substitute(info))
+
+    return {
+        'controls_def': '\n'.join(ctrls_def),
+    }
+
+
+def generate_h(controls):
+    template = string.Template('''extern const Control<${type}> ${name};''')
+
+    ctrls = []
+    draft_ctrls = []
+    ids = []
+
+    for ctrl in controls:
+        name, ctrl = ctrl.popitem()
+
+        if not name.startswith(PREFIX):
+            raise RuntimeError(f'Invalid control name {name}')
+
+        name = name[len(PREFIX):]
+
+        ctrl_type = ctrl['type']
+        if ctrl_type == 'string':
+            ctrl_type = 'std::string'
+        elif ctrl.get('size'):
+            ctrl_type = 'Span<const %s>' % ctrl_type
+
+        info = {
+            'name': name,
+            'type': ctrl_type,
+        }
+
+        ctrls.append(template.substitute(info))
+
+    return {
+        'controls': '\n'.join(ctrls),
+    }
+
+
+def fill_template(template, data):
+
+    template = open(template, 'rb').read()
+    template = template.decode('utf-8')
+    template = string.Template(template)
+    return template.substitute(data)
+
+
+def main(argv):
+
+    # Parse command line arguments
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-o', dest='output', 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()
+    controls = yaml.safe_load(data)['controls']
+
+    if args.template.endswith('.cpp.in'):
+        data = generate_cpp(controls)
+    elif args.template.endswith('.h.in'):
+        data = generate_h(controls)
+    else:
+        raise RuntimeError('Unknown template type')
+
+    data = fill_template(args.template, data)
+
+    if args.output:
+        output = open(args.output, 'wb')
+        output.write(data.encode('utf-8'))
+        output.close()
+    else:
+        sys.stdout.write(data)
+
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
diff --git a/utils/meson.build b/utils/meson.build
index 8e28ada7165a..8ff1e40dbe81 100644
--- a/utils/meson.build
+++ b/utils/meson.build
@@ -9,6 +9,7 @@ py_modules += ['yaml']
 gen_controls = files('gen-controls.py')
 gen_formats = files('gen-formats.py')
 gen_header = files('gen-header.sh')
+gen_v4l2_controls = files('gen-v4l2-controls.py')
 
 ## Module signing
 gen_ipa_priv_key = files('gen-ipa-priv-key.sh')
-- 
Regards,

Laurent Pinchart



More information about the libcamera-devel mailing list